Revert "Revert "[Concepts] Fix overload resolution bug with constrained candidates""
This reverts commit f6af446b66
.
This commit is contained in:
parent
f6af446b66
commit
a0636b5855
|
@ -123,6 +123,11 @@ Bug Fixes
|
||||||
a lambda expression that shares the name of a variable in a containing
|
a lambda expression that shares the name of a variable in a containing
|
||||||
if/while/for/switch init statement as a redeclaration.
|
if/while/for/switch init statement as a redeclaration.
|
||||||
This fixes `Issue 54913 <https://github.com/llvm/llvm-project/issues/54913>`_.
|
This fixes `Issue 54913 <https://github.com/llvm/llvm-project/issues/54913>`_.
|
||||||
|
- Overload resolution for constrained function templates could use the partial
|
||||||
|
order of constraints to select an overload, even if the parameter types of
|
||||||
|
the functions were different. It now diagnoses this case correctly as an
|
||||||
|
ambiguous call and an error. Fixes
|
||||||
|
`Issue 53640 <https://github.com/llvm/llvm-project/issues/53640>`_.
|
||||||
|
|
||||||
Improvements to Clang's diagnostics
|
Improvements to Clang's diagnostics
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
@ -3580,7 +3580,8 @@ public:
|
||||||
QualType& ConvertedType);
|
QualType& ConvertedType);
|
||||||
bool FunctionParamTypesAreEqual(const FunctionProtoType *OldType,
|
bool FunctionParamTypesAreEqual(const FunctionProtoType *OldType,
|
||||||
const FunctionProtoType *NewType,
|
const FunctionProtoType *NewType,
|
||||||
unsigned *ArgPos = nullptr);
|
unsigned *ArgPos = nullptr,
|
||||||
|
bool Reversed = false);
|
||||||
void HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
|
void HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
|
||||||
QualType FromType, QualType ToType);
|
QualType FromType, QualType ToType);
|
||||||
|
|
||||||
|
@ -8749,7 +8750,8 @@ public:
|
||||||
FunctionTemplateDecl *getMoreSpecializedTemplate(
|
FunctionTemplateDecl *getMoreSpecializedTemplate(
|
||||||
FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
|
FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
|
||||||
TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
|
TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
|
||||||
unsigned NumCallArguments2, bool Reversed = false);
|
unsigned NumCallArguments2, bool Reversed = false,
|
||||||
|
bool AllowOrderingByConstraints = true);
|
||||||
UnresolvedSetIterator
|
UnresolvedSetIterator
|
||||||
getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd,
|
getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd,
|
||||||
TemplateSpecCandidateSet &FailedCandidates,
|
TemplateSpecCandidateSet &FailedCandidates,
|
||||||
|
|
|
@ -2945,24 +2945,30 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FunctionParamTypesAreEqual - This routine checks two function proto types
|
/// FunctionParamTypesAreEqual - This routine checks two function proto types
|
||||||
/// for equality of their argument types. Caller has already checked that
|
/// for equality of their parameter types. Caller has already checked that
|
||||||
/// they have same number of arguments. If the parameters are different,
|
/// they have same number of parameters. If the parameters are different,
|
||||||
/// ArgPos will have the parameter index of the first different parameter.
|
/// ArgPos will have the parameter index of the first different parameter.
|
||||||
|
/// If `Reversed` is true, the parameters of `NewType` will be compared in
|
||||||
|
/// reverse order. That's useful if one of the functions is being used as a C++20
|
||||||
|
/// synthesized operator overload with a reversed parameter order.
|
||||||
bool Sema::FunctionParamTypesAreEqual(const FunctionProtoType *OldType,
|
bool Sema::FunctionParamTypesAreEqual(const FunctionProtoType *OldType,
|
||||||
const FunctionProtoType *NewType,
|
const FunctionProtoType *NewType,
|
||||||
unsigned *ArgPos) {
|
unsigned *ArgPos, bool Reversed) {
|
||||||
for (FunctionProtoType::param_type_iterator O = OldType->param_type_begin(),
|
assert(OldType->getNumParams() == NewType->getNumParams() &&
|
||||||
N = NewType->param_type_begin(),
|
"Can't compare parameters of functions with different number of "
|
||||||
E = OldType->param_type_end();
|
"parameters!");
|
||||||
O && (O != E); ++O, ++N) {
|
for (size_t I = 0; I < OldType->getNumParams(); I++) {
|
||||||
|
// Reverse iterate over the parameters of `OldType` if `Reversed` is true.
|
||||||
|
size_t J = Reversed ? (OldType->getNumParams() - I - 1) : I;
|
||||||
|
|
||||||
// Ignore address spaces in pointee type. This is to disallow overloading
|
// Ignore address spaces in pointee type. This is to disallow overloading
|
||||||
// on __ptr32/__ptr64 address spaces.
|
// on __ptr32/__ptr64 address spaces.
|
||||||
QualType Old = Context.removePtrSizeAddrSpace(O->getUnqualifiedType());
|
QualType Old = Context.removePtrSizeAddrSpace(OldType->getParamType(I).getUnqualifiedType());
|
||||||
QualType New = Context.removePtrSizeAddrSpace(N->getUnqualifiedType());
|
QualType New = Context.removePtrSizeAddrSpace(NewType->getParamType(J).getUnqualifiedType());
|
||||||
|
|
||||||
if (!Context.hasSameType(Old, New)) {
|
if (!Context.hasSameType(Old, New)) {
|
||||||
if (ArgPos)
|
if (ArgPos)
|
||||||
*ArgPos = O - OldType->param_type_begin();
|
*ArgPos = I;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9584,6 +9590,32 @@ static bool haveSameParameterTypes(ASTContext &Context, const FunctionDecl *F1,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We're allowed to use constraints partial ordering only if the candidates
|
||||||
|
/// have the same parameter types:
|
||||||
|
/// [temp.func.order]p6.2.2 [...] or if the function parameters that
|
||||||
|
/// positionally correspond between the two templates are not of the same type,
|
||||||
|
/// neither template is more specialized than the other.
|
||||||
|
/// [over.match.best]p2.6
|
||||||
|
/// F1 and F2 are non-template functions with the same parameter-type-lists,
|
||||||
|
/// and F1 is more constrained than F2 [...]
|
||||||
|
static bool canCompareFunctionConstraints(Sema &S,
|
||||||
|
const OverloadCandidate &Cand1,
|
||||||
|
const OverloadCandidate &Cand2) {
|
||||||
|
// FIXME: Per P2113R0 we also need to compare the template parameter lists
|
||||||
|
// when comparing template functions.
|
||||||
|
if (Cand1.Function && Cand2.Function && Cand1.Function->hasPrototype() &&
|
||||||
|
Cand2.Function->hasPrototype()) {
|
||||||
|
auto *PT1 = cast<FunctionProtoType>(Cand1.Function->getFunctionType());
|
||||||
|
auto *PT2 = cast<FunctionProtoType>(Cand2.Function->getFunctionType());
|
||||||
|
if (PT1->getNumParams() == PT2->getNumParams() &&
|
||||||
|
PT1->isVariadic() == PT2->isVariadic() &&
|
||||||
|
S.FunctionParamTypesAreEqual(PT1, PT2, nullptr,
|
||||||
|
Cand1.isReversed() ^ Cand2.isReversed()))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// isBetterOverloadCandidate - Determines whether the first overload
|
/// isBetterOverloadCandidate - Determines whether the first overload
|
||||||
/// candidate is a better candidate than the second (C++ 13.3.3p1).
|
/// candidate is a better candidate than the second (C++ 13.3.3p1).
|
||||||
bool clang::isBetterOverloadCandidate(
|
bool clang::isBetterOverloadCandidate(
|
||||||
|
@ -9815,34 +9847,28 @@ bool clang::isBetterOverloadCandidate(
|
||||||
isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion
|
isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion
|
||||||
: TPOC_Call,
|
: TPOC_Call,
|
||||||
Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments,
|
Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments,
|
||||||
Cand1.isReversed() ^ Cand2.isReversed()))
|
Cand1.isReversed() ^ Cand2.isReversed(),
|
||||||
|
canCompareFunctionConstraints(S, Cand1, Cand2)))
|
||||||
return BetterTemplate == Cand1.Function->getPrimaryTemplate();
|
return BetterTemplate == Cand1.Function->getPrimaryTemplate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -— F1 and F2 are non-template functions with the same
|
// -— F1 and F2 are non-template functions with the same
|
||||||
// parameter-type-lists, and F1 is more constrained than F2 [...],
|
// parameter-type-lists, and F1 is more constrained than F2 [...],
|
||||||
if (Cand1.Function && Cand2.Function && !Cand1IsSpecialization &&
|
if (!Cand1IsSpecialization && !Cand2IsSpecialization &&
|
||||||
!Cand2IsSpecialization && Cand1.Function->hasPrototype() &&
|
canCompareFunctionConstraints(S, Cand1, Cand2)) {
|
||||||
Cand2.Function->hasPrototype()) {
|
Expr *RC1 = Cand1.Function->getTrailingRequiresClause();
|
||||||
auto *PT1 = cast<FunctionProtoType>(Cand1.Function->getFunctionType());
|
Expr *RC2 = Cand2.Function->getTrailingRequiresClause();
|
||||||
auto *PT2 = cast<FunctionProtoType>(Cand2.Function->getFunctionType());
|
if (RC1 && RC2) {
|
||||||
if (PT1->getNumParams() == PT2->getNumParams() &&
|
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
|
||||||
PT1->isVariadic() == PT2->isVariadic() &&
|
if (S.IsAtLeastAsConstrained(Cand1.Function, {RC1}, Cand2.Function, {RC2},
|
||||||
S.FunctionParamTypesAreEqual(PT1, PT2)) {
|
AtLeastAsConstrained1) ||
|
||||||
Expr *RC1 = Cand1.Function->getTrailingRequiresClause();
|
S.IsAtLeastAsConstrained(Cand2.Function, {RC2}, Cand1.Function, {RC1},
|
||||||
Expr *RC2 = Cand2.Function->getTrailingRequiresClause();
|
AtLeastAsConstrained2))
|
||||||
if (RC1 && RC2) {
|
return false;
|
||||||
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
|
if (AtLeastAsConstrained1 != AtLeastAsConstrained2)
|
||||||
if (S.IsAtLeastAsConstrained(Cand1.Function, {RC1}, Cand2.Function,
|
return AtLeastAsConstrained1;
|
||||||
{RC2}, AtLeastAsConstrained1) ||
|
} else if (RC1 || RC2) {
|
||||||
S.IsAtLeastAsConstrained(Cand2.Function, {RC2}, Cand1.Function,
|
return RC1 != nullptr;
|
||||||
{RC1}, AtLeastAsConstrained2))
|
|
||||||
return false;
|
|
||||||
if (AtLeastAsConstrained1 != AtLeastAsConstrained2)
|
|
||||||
return AtLeastAsConstrained1;
|
|
||||||
} else if (RC1 || RC2) {
|
|
||||||
return RC1 != nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5143,18 +5143,20 @@ static bool isVariadicFunctionTemplate(FunctionTemplateDecl *FunTmpl) {
|
||||||
/// candidate with a reversed parameter order. In this case, the corresponding
|
/// candidate with a reversed parameter order. In this case, the corresponding
|
||||||
/// P/A pairs between FT1 and FT2 are reversed.
|
/// P/A pairs between FT1 and FT2 are reversed.
|
||||||
///
|
///
|
||||||
|
/// \param AllowOrderingByConstraints If \c is false, don't check whether one
|
||||||
|
/// of the templates is more constrained than the other. Default is true.
|
||||||
|
///
|
||||||
/// \returns the more specialized function template. If neither
|
/// \returns the more specialized function template. If neither
|
||||||
/// template is more specialized, returns NULL.
|
/// template is more specialized, returns NULL.
|
||||||
FunctionTemplateDecl *
|
FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
|
||||||
Sema::getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
|
FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
|
||||||
FunctionTemplateDecl *FT2,
|
TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
|
||||||
SourceLocation Loc,
|
unsigned NumCallArguments2, bool Reversed,
|
||||||
TemplatePartialOrderingContext TPOC,
|
bool AllowOrderingByConstraints) {
|
||||||
unsigned NumCallArguments1,
|
|
||||||
unsigned NumCallArguments2,
|
|
||||||
bool Reversed) {
|
|
||||||
|
|
||||||
auto JudgeByConstraints = [&] () -> FunctionTemplateDecl * {
|
auto JudgeByConstraints = [&]() -> FunctionTemplateDecl * {
|
||||||
|
if (!AllowOrderingByConstraints)
|
||||||
|
return nullptr;
|
||||||
llvm::SmallVector<const Expr *, 3> AC1, AC2;
|
llvm::SmallVector<const Expr *, 3> AC1, AC2;
|
||||||
FT1->getAssociatedConstraints(AC1);
|
FT1->getAssociatedConstraints(AC1);
|
||||||
FT2->getAssociatedConstraints(AC2);
|
FT2->getAssociatedConstraints(AC2);
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
|
||||||
|
|
||||||
|
struct A;
|
||||||
|
struct B;
|
||||||
|
|
||||||
|
template <typename> constexpr bool True = true;
|
||||||
|
template <typename T> concept C = True<T>;
|
||||||
|
|
||||||
|
void f(C auto &, auto &) = delete;
|
||||||
|
template <C Q> void f(Q &, C auto &);
|
||||||
|
|
||||||
|
void g(struct A *ap, struct B *bp) {
|
||||||
|
f(*ap, *bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename U> struct X {};
|
||||||
|
|
||||||
|
template <typename T, C U, typename V> bool operator==(X<T, U>, V) = delete;
|
||||||
|
template <C T, C U, C V> bool operator==(T, X<U, V>);
|
||||||
|
|
||||||
|
bool h() {
|
||||||
|
return X<void *, int>{} == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace PR53640 {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept C = true;
|
||||||
|
|
||||||
|
template <C T>
|
||||||
|
void f(T t) {} // expected-note {{candidate function [with T = int]}}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void f(const T &t) {} // expected-note {{candidate function [with T = int]}}
|
||||||
|
|
||||||
|
int g() {
|
||||||
|
f(0); // expected-error {{call to 'f' is ambiguous}}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
template <typename T> explicit S(T) noexcept requires C<T> {} // expected-note {{candidate constructor}}
|
||||||
|
template <typename T> explicit S(const T &) noexcept {} // expected-note {{candidate constructor}}
|
||||||
|
};
|
||||||
|
|
||||||
|
int h() {
|
||||||
|
S s(4); // expected-error-re {{call to constructor of {{.*}} is ambiguous}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue