diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -264,6 +264,8 @@ constraint, which re-evaluated the same constraint. `Issue 53213 `_ `Issue 45736 `_ +- Fix an issue when performing constraints partial ordering on non-template + functions. `Issue 56154 `_ Improvements to Clang's diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1300,6 +1300,18 @@ NamedDecl *D2, MutableArrayRef AC2, bool &Result) { + if (const auto *FD1 = dyn_cast(D1)) { + auto IsExpectedEntity = [](const FunctionDecl *FD) { + FunctionDecl::TemplatedKind Kind = FD->getTemplatedKind(); + return Kind == FunctionDecl::TK_NonTemplate || + Kind == FunctionDecl::TK_FunctionTemplate; + }; + const auto *FD2 = dyn_cast(D2); + assert(IsExpectedEntity(FD1) && FD2 && IsExpectedEntity(FD2) && + "use non-instantiated function declaration for constraints partial " + "ordering"); + } + if (AC1.empty()) { Result = AC2.empty(); return false; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -18218,13 +18218,20 @@ if (!SatisfactionStatus[i]) continue; CXXMethodDecl *Method = Methods[i]; - const Expr *Constraints = Method->getTrailingRequiresClause(); + CXXMethodDecl *OrigMethod = Method; + if (FunctionDecl *MF = OrigMethod->getInstantiatedFromMemberFunction()) + OrigMethod = cast(MF); + + const Expr *Constraints = OrigMethod->getTrailingRequiresClause(); bool AnotherMethodIsMoreConstrained = false; for (size_t j = 0; j < Methods.size(); j++) { if (i == j || !SatisfactionStatus[j]) continue; CXXMethodDecl *OtherMethod = Methods[j]; - if (!AreSpecialMemberFunctionsSameKind(S.Context, Method, OtherMethod, + if (FunctionDecl *MF = OtherMethod->getInstantiatedFromMemberFunction()) + OtherMethod = cast(MF); + + if (!AreSpecialMemberFunctionsSameKind(S.Context, OrigMethod, OtherMethod, CSM)) continue; @@ -18235,7 +18242,7 @@ AnotherMethodIsMoreConstrained = true; break; } - if (S.IsAtLeastAsConstrained(OtherMethod, {OtherConstraints}, Method, + if (S.IsAtLeastAsConstrained(OtherMethod, {OtherConstraints}, OrigMethod, {Constraints}, AnotherMethodIsMoreConstrained)) { // There was an error with the constraints comparison. Exit the loop diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -10039,13 +10039,20 @@ // parameter-type-lists, and F1 is more constrained than F2 [...], if (!Cand1IsSpecialization && !Cand2IsSpecialization && sameFunctionParameterTypeLists(S, Cand1, Cand2)) { - const Expr *RC1 = Cand1.Function->getTrailingRequiresClause(); - const Expr *RC2 = Cand2.Function->getTrailingRequiresClause(); + FunctionDecl *Function1 = Cand1.Function; + FunctionDecl *Function2 = Cand2.Function; + if (FunctionDecl *MF = Function1->getInstantiatedFromMemberFunction()) + Function1 = MF; + if (FunctionDecl *MF = Function2->getInstantiatedFromMemberFunction()) + Function2 = MF; + + const Expr *RC1 = Function1->getTrailingRequiresClause(); + const Expr *RC2 = Function2->getTrailingRequiresClause(); if (RC1 && RC2) { bool AtLeastAsConstrained1, AtLeastAsConstrained2; - if (S.IsAtLeastAsConstrained(Cand1.Function, RC1, Cand2.Function, RC2, + if (S.IsAtLeastAsConstrained(Function1, RC1, Function2, RC2, AtLeastAsConstrained1) || - S.IsAtLeastAsConstrained(Cand2.Function, RC2, Cand1.Function, RC1, + S.IsAtLeastAsConstrained(Function2, RC2, Function1, RC1, AtLeastAsConstrained2)) return false; if (AtLeastAsConstrained1 != AtLeastAsConstrained2) @@ -12630,20 +12637,24 @@ DeclAccessPair DAP; SmallVector AmbiguousDecls; - auto CheckMoreConstrained = - [&] (FunctionDecl *FD1, FunctionDecl *FD2) -> Optional { - SmallVector AC1, AC2; - FD1->getAssociatedConstraints(AC1); - FD2->getAssociatedConstraints(AC2); - bool AtLeastAsConstrained1, AtLeastAsConstrained2; - if (IsAtLeastAsConstrained(FD1, AC1, FD2, AC2, AtLeastAsConstrained1)) - return None; - if (IsAtLeastAsConstrained(FD2, AC2, FD1, AC1, AtLeastAsConstrained2)) - return None; - if (AtLeastAsConstrained1 == AtLeastAsConstrained2) - return None; - return AtLeastAsConstrained1; - }; + auto CheckMoreConstrained = [&](FunctionDecl *FD1, + FunctionDecl *FD2) -> Optional { + if (FunctionDecl *MF = FD1->getInstantiatedFromMemberFunction()) + FD1 = MF; + if (FunctionDecl *MF = FD2->getInstantiatedFromMemberFunction()) + FD2 = MF; + SmallVector AC1, AC2; + FD1->getAssociatedConstraints(AC1); + FD2->getAssociatedConstraints(AC2); + bool AtLeastAsConstrained1, AtLeastAsConstrained2; + if (IsAtLeastAsConstrained(FD1, AC1, FD2, AC2, AtLeastAsConstrained1)) + return None; + if (IsAtLeastAsConstrained(FD2, AC2, FD1, AC1, AtLeastAsConstrained2)) + return None; + if (AtLeastAsConstrained1 == AtLeastAsConstrained2) + return None; + return AtLeastAsConstrained1; + }; // Don't use the AddressOfResolver because we're specifically looking for // cases where we have one overload candidate that lacks diff --git a/clang/test/CXX/over/over.match/over.match.best/p2.cpp b/clang/test/CXX/over/over.match/over.match.best/p2.cpp --- a/clang/test/CXX/over/over.match/over.match.best/p2.cpp +++ b/clang/test/CXX/over/over.match/over.match.best/p2.cpp @@ -7,7 +7,11 @@ bool operator<(const A&) const & requires X; // #1 int operator<=>(const A&) const & requires X && X = delete; // #2 }; - bool k1 = A() < A(); // not ordered by constraints: prefer non-rewritten form + bool k1 = A() < A(); // prefer more-constrained 'operator<=>' + // expected-error@-1 {{deleted}} + // expected-note@#1 {{candidate}} + // expected-note@#2 {{candidate function has been explicitly deleted}} + // expected-note@#2 {{candidate function (with reversed parameter order) has been explicitly deleted}} bool k2 = A() < A(); // prefer more-constrained 'operator<=>' // expected-error@-1 {{deleted}} // expected-note@#1 {{candidate}} diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.order/non-template-functions.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.order/non-template-functions.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/temp/temp.constr/temp.constr.order/non-template-functions.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -std=c++2a -x c++ -verify %s +// expected-no-diagnostics + +template +constexpr static bool is_same_v = false; + +template +constexpr static bool is_same_v = true; + +namespace PR56154 { + template concept C0 = (N == 0); + template concept C0x = C0; + template concept C00 = C0x && C0; + + template + struct A { + void f() requires C00; + void f() requires C0x = delete; + + static short g() requires C00; + static int g() requires C0x; + }; + void h(A<0, 0> a) { + a.f(); + static_assert(is_same_v::g), short(*)()>); + } +}