diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -255,6 +255,8 @@ - Reject non-type template arguments formed by casting a non-zero integer to a pointer in pre-C++17 modes, instead of treating them as null pointers. +- 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 @@ -1276,6 +1276,18 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, ArrayRef AC1, NamedDecl *D2, ArrayRef 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 @@ -18211,13 +18211,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; @@ -18228,7 +18235,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 @@ -10033,13 +10033,20 @@ // parameter-type-lists, and F1 is more constrained than F2 [...], if (!Cand1IsSpecialization && !Cand2IsSpecialization && sameFunctionParameterTypeLists(S, Cand1, Cand2)) { - Expr *RC1 = Cand1.Function->getTrailingRequiresClause(); - 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; + + Expr *RC1 = Function1->getTrailingRequiresClause(); + 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) @@ -12624,20 +12631,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(*)()>); + } +}