-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[Clang] Implement CWG2918 'Consideration of constraints for address of overloaded function' #127773
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2d9c248
5388856
09ba784
e2ef79e
714c40d
0a7d7a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 @@ class AddressOfFunctionResolver { | |
// 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 @@ class AddressOfFunctionResolver { | |
} | ||
} | ||
|
||
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); | ||
Comment on lines
+13285
to
+13300
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this work if there are pairs of functions in the set which are not more constrained than the other both ways? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is supposed to work, though I haven't yet devised a test to exercise that branch. My understanding is that if there were more than one candidate that isn't more constrained than the others, we should have ruled them out earlier when checking the full address-taken expression, namely in We unfortunately ended up with these separate pieces of address-of resolution logic for different scenarios such as type deduction and initialization sequence construction. While we might be doing some redundant work, from my understanding, the function in its current form aligns with the standard's intent. |
||
} | ||
|
||
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(); | ||
|
Uh oh!
There was an error while loading. Please reload this page.