aboutsummaryrefslogtreecommitdiff
path: root/clang/lib
diff options
context:
space:
mode:
authorYounan Zhang <zyn7109@gmail.com>2025-02-26 15:34:06 +0800
committerGitHub <noreply@github.com>2025-02-26 15:34:06 +0800
commit2015626783aa7510ccdf6098f2112417cf56a8d0 (patch)
tree7289b5c0c3aa2a54cf2b45b8805ad56a2ce13100 /clang/lib
parent29c5e4289f53a8abf0ffffb7074d2af2d4d0a26b (diff)
downloadllvm-2015626783aa7510ccdf6098f2112417cf56a8d0.zip
llvm-2015626783aa7510ccdf6098f2112417cf56a8d0.tar.gz
llvm-2015626783aa7510ccdf6098f2112417cf56a8d0.tar.bz2
[Clang] Implement CWG2918 'Consideration of constraints for address of overloaded function' (#127773)
Closes https://github.com/llvm/llvm-project/issues/122523
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/Sema/SemaConcept.cpp5
-rw-r--r--clang/lib/Sema/SemaOverload.cpp115
-rw-r--r--clang/lib/Sema/SemaTemplateDeduction.cpp9
3 files changed, 98 insertions, 31 deletions
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index a7b609f..57dc415 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -1796,6 +1796,7 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1,
NamedDecl *D2,
MutableArrayRef<const Expr *> AC2,
bool &Result) {
+#ifndef NDEBUG
if (const auto *FD1 = dyn_cast<FunctionDecl>(D1)) {
auto IsExpectedEntity = [](const FunctionDecl *FD) {
FunctionDecl::TemplatedKind Kind = FD->getTemplatedKind();
@@ -1803,13 +1804,11 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1,
Kind == FunctionDecl::TK_FunctionTemplate;
};
const auto *FD2 = dyn_cast<FunctionDecl>(D2);
- (void)IsExpectedEntity;
- (void)FD1;
- (void)FD2;
assert(IsExpectedEntity(FD1) && FD2 && IsExpectedEntity(FD2) &&
"use non-instantiated function declaration for constraints partial "
"ordering");
}
+#endif
if (AC1.empty()) {
Result = AC2.empty();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 8d5b5ac..08586b4 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -10369,20 +10369,16 @@ static bool allowAmbiguity(ASTContext &Context, const FunctionDecl *F1,
/// [over.match.best.general]p2.6
/// F1 and F2 are non-template functions with the same
/// non-object-parameter-type-lists, and F1 is more constrained than F2 [...]
-static bool sameFunctionParameterTypeLists(Sema &S,
- const OverloadCandidate &Cand1,
- const OverloadCandidate &Cand2) {
- if (!Cand1.Function || !Cand2.Function)
- return false;
-
- FunctionDecl *Fn1 = Cand1.Function;
- FunctionDecl *Fn2 = Cand2.Function;
-
+static bool sameFunctionParameterTypeLists(Sema &S, FunctionDecl *Fn1,
+ FunctionDecl *Fn2,
+ bool IsFn1Reversed,
+ bool IsFn2Reversed) {
+ assert(Fn1 && Fn2);
if (Fn1->isVariadic() != Fn2->isVariadic())
return false;
- if (!S.FunctionNonObjectParamTypesAreEqual(
- Fn1, Fn2, nullptr, Cand1.isReversed() ^ Cand2.isReversed()))
+ if (!S.FunctionNonObjectParamTypesAreEqual(Fn1, Fn2, nullptr,
+ IsFn1Reversed ^ IsFn2Reversed))
return false;
auto *Mem1 = dyn_cast<CXXMethodDecl>(Fn1);
@@ -10403,6 +10399,30 @@ static bool sameFunctionParameterTypeLists(Sema &S,
return true;
}
+static FunctionDecl *
+getMorePartialOrderingConstrained(Sema &S, FunctionDecl *Fn1, FunctionDecl *Fn2,
+ bool IsFn1Reversed, bool IsFn2Reversed) {
+ if (!Fn1 || !Fn2)
+ return nullptr;
+
+ // C++ [temp.constr.order]:
+ // A non-template function F1 is more partial-ordering-constrained than a
+ // non-template function F2 if:
+ bool Cand1IsSpecialization = Fn1->getPrimaryTemplate();
+ bool Cand2IsSpecialization = Fn2->getPrimaryTemplate();
+
+ if (Cand1IsSpecialization || Cand2IsSpecialization)
+ return nullptr;
+
+ // - they have the same non-object-parameter-type-lists, and [...]
+ if (!sameFunctionParameterTypeLists(S, Fn1, Fn2, IsFn1Reversed,
+ IsFn2Reversed))
+ return nullptr;
+
+ // - the declaration of F1 is more constrained than the declaration of F2.
+ return S.getMoreConstrainedFunction(Fn1, Fn2);
+}
+
/// isBetterOverloadCandidate - Determines whether the first overload
/// candidate is a better candidate than the second (C++ 13.3.3p1).
bool clang::isBetterOverloadCandidate(
@@ -10649,12 +10669,12 @@ bool clang::isBetterOverloadCandidate(
}
}
- // -— F1 and F2 are non-template functions with the same
- // parameter-type-lists, and F1 is more constrained than F2 [...],
- if (!Cand1IsSpecialization && !Cand2IsSpecialization &&
- sameFunctionParameterTypeLists(S, Cand1, Cand2) &&
- S.getMoreConstrainedFunction(Cand1.Function, Cand2.Function) ==
- Cand1.Function)
+ // -— F1 and F2 are non-template functions and F1 is more
+ // partial-ordering-constrained than F2 [...],
+ if (FunctionDecl *F = getMorePartialOrderingConstrained(
+ S, Cand1.Function, Cand2.Function, Cand1.isReversed(),
+ Cand2.isReversed());
+ F && F == Cand1.Function)
return true;
// -- F1 is a constructor for a class D, F2 is a constructor for a base
@@ -12999,9 +13019,10 @@ public:
// C++ [over.over]p4:
// If more than one function is selected, [...]
if (Matches.size() > 1 && !eliminiateSuboptimalOverloadCandidates()) {
- if (FoundNonTemplateFunction)
+ if (FoundNonTemplateFunction) {
EliminateAllTemplateMatches();
- else
+ EliminateLessPartialOrderingConstrainedMatches();
+ } else
EliminateAllExceptMostSpecializedTemplate();
}
}
@@ -13252,6 +13273,33 @@ private:
}
}
+ void EliminateLessPartialOrderingConstrainedMatches() {
+ // C++ [over.over]p5:
+ // [...] Any given non-template function F0 is eliminated if the set
+ // contains a second non-template function that is more
+ // partial-ordering-constrained than F0. [...]
+ assert(Matches[0].second->getPrimaryTemplate() == nullptr &&
+ "Call EliminateAllTemplateMatches() first");
+ SmallVector<std::pair<DeclAccessPair, FunctionDecl *>, 4> Results;
+ Results.push_back(Matches[0]);
+ for (unsigned I = 1, N = Matches.size(); I < N; ++I) {
+ assert(Matches[I].second->getPrimaryTemplate() == nullptr);
+ FunctionDecl *F = getMorePartialOrderingConstrained(
+ S, Matches[I].second, Results[0].second,
+ /*IsFn1Reversed=*/false,
+ /*IsFn2Reversed=*/false);
+ if (!F) {
+ Results.push_back(Matches[I]);
+ continue;
+ }
+ if (F == Matches[I].second) {
+ Results.clear();
+ Results.push_back(Matches[I]);
+ }
+ }
+ std::swap(Matches, Results);
+ }
+
void EliminateSuboptimalCudaMatches() {
S.CUDA().EraseUnwantedMatches(S.getCurFunctionDecl(/*AllowLambda=*/true),
Matches);
@@ -13408,8 +13456,8 @@ Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) {
Result = FD;
};
- // We have more than one result - see if it is more constrained than the
- // previous one.
+ // We have more than one result - see if it is more
+ // partial-ordering-constrained than the previous one.
if (Result) {
// Check CUDA preference first. If the candidates have differennt CUDA
// preference, choose the one with higher CUDA preference. Otherwise,
@@ -13424,9 +13472,17 @@ Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) {
continue;
}
}
- // FD has the same CUDA prefernece than Result. Continue check
+ // FD has the same CUDA preference than Result. Continue to check
// constraints.
- FunctionDecl *MoreConstrained = getMoreConstrainedFunction(FD, Result);
+
+ // C++ [over.over]p5:
+ // [...] Any given non-template function F0 is eliminated if the set
+ // contains a second non-template function that is more
+ // partial-ordering-constrained than F0 [...]
+ FunctionDecl *MoreConstrained =
+ getMorePartialOrderingConstrained(*this, FD, Result,
+ /*IsFn1Reversed=*/false,
+ /*IsFn2Reversed=*/false);
if (MoreConstrained != FD) {
if (!MoreConstrained) {
IsResultAmbiguous = true;
@@ -13443,7 +13499,6 @@ Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) {
return nullptr;
if (Result) {
- SmallVector<const Expr *, 1> ResultAC;
// We skipped over some ambiguous declarations which might be ambiguous with
// the selected result.
for (FunctionDecl *Skipped : AmbiguousDecls) {
@@ -13489,7 +13544,7 @@ bool Sema::resolveAndFixAddressOfSingleOverloadCandidate(
FunctionDecl *Sema::ResolveSingleFunctionTemplateSpecialization(
OverloadExpr *ovl, bool Complain, DeclAccessPair *FoundResult,
- TemplateSpecCandidateSet *FailedTSC) {
+ TemplateSpecCandidateSet *FailedTSC, bool ForTypeDeduction) {
// C++ [over.over]p1:
// [...] [Note: any redundant set of parentheses surrounding the
// overloaded function name is ignored (5.1). ]
@@ -13540,8 +13595,16 @@ FunctionDecl *Sema::ResolveSingleFunctionTemplateSpecialization(
assert(Specialization && "no specialization and no error?");
- // Multiple matches; we can't resolve to a single declaration.
+ // C++ [temp.deduct.call]p6:
+ // [...] If all successful deductions yield the same deduced A, that
+ // deduced A is the result of deduction; otherwise, the parameter is
+ // treated as a non-deduced context.
if (Matched) {
+ if (ForTypeDeduction &&
+ isSameOrCompatibleFunctionType(Matched->getType(),
+ Specialization->getType()))
+ continue;
+ // Multiple matches; we can't resolve to a single declaration.
if (Complain) {
Diag(ovl->getExprLoc(), diag::err_addr_ovl_ambiguous)
<< ovl->getName();
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index eb70dd7..627cd82 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4252,7 +4252,8 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams,
if (FunctionDecl *ExplicitSpec =
S.ResolveSingleFunctionTemplateSpecialization(
Ovl, /*Complain=*/false,
- /*FoundDeclAccessPair=*/nullptr, FailedTSC))
+ /*Found=*/nullptr, FailedTSC,
+ /*ForTypeDeduction=*/true))
return GetTypeOfFunction(S, R, ExplicitSpec);
}
@@ -4321,7 +4322,11 @@ ResolveOverloadForDeduction(Sema &S, TemplateParameterList *TemplateParams,
/*HasDeducedAnyParam=*/nullptr);
if (Result != TemplateDeductionResult::Success)
continue;
- if (!Match.isNull())
+ // C++ [temp.deduct.call]p6:
+ // [...] If all successful deductions yield the same deduced A, that
+ // deduced A is the result of deduction; otherwise, the parameter is
+ // treated as a non-deduced context. [...]
+ if (!Match.isNull() && !S.isSameOrCompatibleFunctionType(Match, ArgType))
return {};
Match = ArgType;
}