Reverting accidental git-revert commits.

Revert "Revert "Revert "[Concepts] Fix overload resolution bug with constrained candidates"""

This reverts commit a0636b5855.
This commit is contained in:
Nick Kreeger 2022-04-23 20:11:58 -05:00
parent d1b73f3412
commit 2d80889b2a
5 changed files with 44 additions and 128 deletions

View File

@ -123,11 +123,6 @@ Bug Fixes
a lambda expression that shares the name of a variable in a containing
if/while/for/switch init statement as a redeclaration.
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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -3580,8 +3580,7 @@ public:
QualType& ConvertedType);
bool FunctionParamTypesAreEqual(const FunctionProtoType *OldType,
const FunctionProtoType *NewType,
unsigned *ArgPos = nullptr,
bool Reversed = false);
unsigned *ArgPos = nullptr);
void HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
QualType FromType, QualType ToType);
@ -8750,8 +8749,7 @@ public:
FunctionTemplateDecl *getMoreSpecializedTemplate(
FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
unsigned NumCallArguments2, bool Reversed = false,
bool AllowOrderingByConstraints = true);
unsigned NumCallArguments2, bool Reversed = false);
UnresolvedSetIterator
getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd,
TemplateSpecCandidateSet &FailedCandidates,

View File

@ -2945,30 +2945,24 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
}
/// FunctionParamTypesAreEqual - This routine checks two function proto types
/// for equality of their parameter types. Caller has already checked that
/// they have same number of parameters. If the parameters are different,
/// for equality of their argument types. Caller has already checked that
/// they have same number of arguments. If the parameters are different,
/// 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,
const FunctionProtoType *NewType,
unsigned *ArgPos, bool Reversed) {
assert(OldType->getNumParams() == NewType->getNumParams() &&
"Can't compare parameters of functions with different number of "
"parameters!");
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;
unsigned *ArgPos) {
for (FunctionProtoType::param_type_iterator O = OldType->param_type_begin(),
N = NewType->param_type_begin(),
E = OldType->param_type_end();
O && (O != E); ++O, ++N) {
// Ignore address spaces in pointee type. This is to disallow overloading
// on __ptr32/__ptr64 address spaces.
QualType Old = Context.removePtrSizeAddrSpace(OldType->getParamType(I).getUnqualifiedType());
QualType New = Context.removePtrSizeAddrSpace(NewType->getParamType(J).getUnqualifiedType());
QualType Old = Context.removePtrSizeAddrSpace(O->getUnqualifiedType());
QualType New = Context.removePtrSizeAddrSpace(N->getUnqualifiedType());
if (!Context.hasSameType(Old, New)) {
if (ArgPos)
*ArgPos = I;
*ArgPos = O - OldType->param_type_begin();
return false;
}
}
@ -9590,32 +9584,6 @@ static bool haveSameParameterTypes(ASTContext &Context, const FunctionDecl *F1,
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
/// candidate is a better candidate than the second (C++ 13.3.3p1).
bool clang::isBetterOverloadCandidate(
@ -9847,23 +9815,28 @@ bool clang::isBetterOverloadCandidate(
isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion
: TPOC_Call,
Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments,
Cand1.isReversed() ^ Cand2.isReversed(),
canCompareFunctionConstraints(S, Cand1, Cand2)))
Cand1.isReversed() ^ Cand2.isReversed()))
return BetterTemplate == Cand1.Function->getPrimaryTemplate();
}
// -— F1 and F2 are non-template functions with the same
// parameter-type-lists, and F1 is more constrained than F2 [...],
if (!Cand1IsSpecialization && !Cand2IsSpecialization &&
canCompareFunctionConstraints(S, Cand1, Cand2)) {
if (Cand1.Function && Cand2.Function && !Cand1IsSpecialization &&
!Cand2IsSpecialization && 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)) {
Expr *RC1 = Cand1.Function->getTrailingRequiresClause();
Expr *RC2 = Cand2.Function->getTrailingRequiresClause();
if (RC1 && RC2) {
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
if (S.IsAtLeastAsConstrained(Cand1.Function, {RC1}, Cand2.Function, {RC2},
AtLeastAsConstrained1) ||
S.IsAtLeastAsConstrained(Cand2.Function, {RC2}, Cand1.Function, {RC1},
AtLeastAsConstrained2))
if (S.IsAtLeastAsConstrained(Cand1.Function, {RC1}, Cand2.Function,
{RC2}, AtLeastAsConstrained1) ||
S.IsAtLeastAsConstrained(Cand2.Function, {RC2}, Cand1.Function,
{RC1}, AtLeastAsConstrained2))
return false;
if (AtLeastAsConstrained1 != AtLeastAsConstrained2)
return AtLeastAsConstrained1;
@ -9871,6 +9844,7 @@ bool clang::isBetterOverloadCandidate(
return RC1 != nullptr;
}
}
}
// -- F1 is a constructor for a class D, F2 is a constructor for a base
// class B of D, and for all arguments the corresponding parameters of

View File

@ -5143,20 +5143,18 @@ static bool isVariadicFunctionTemplate(FunctionTemplateDecl *FunTmpl) {
/// candidate with a reversed parameter order. In this case, the corresponding
/// 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
/// template is more specialized, returns NULL.
FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
unsigned NumCallArguments2, bool Reversed,
bool AllowOrderingByConstraints) {
FunctionTemplateDecl *
Sema::getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
FunctionTemplateDecl *FT2,
SourceLocation Loc,
TemplatePartialOrderingContext TPOC,
unsigned NumCallArguments1,
unsigned NumCallArguments2,
bool Reversed) {
auto JudgeByConstraints = [&] () -> FunctionTemplateDecl * {
if (!AllowOrderingByConstraints)
return nullptr;
llvm::SmallVector<const Expr *, 3> AC1, AC2;
FT1->getAssociatedConstraints(AC1);
FT2->getAssociatedConstraints(AC2);

View File

@ -1,49 +0,0 @@
// 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}}
}
}