From e18c2c5548f6864def4a110239395a18afc195d6 Mon Sep 17 00:00:00 2001 From: Yuanfang Chen Date: Sun, 30 Oct 2022 21:33:22 -0700 Subject: [PATCH] [Clang] use non-instantiated function declaration for constraints partial ordering Per wordings in - https://eel.is/c++draft/over.match#best.general-2.6 - https://eel.is/c++draft/temp.constr.order - https://eel.is/c++draft/temp.constr#atomic-1 constraints partial ordering should use the unsubstituted template parameters of the constrained entity, not the instantiated entity. Fix #56154 Reviewed By: erichkeane, royjacobson, mizvekov Differential Revision: https://reviews.llvm.org/D136545 --- clang/docs/ReleaseNotes.rst | 2 + clang/lib/Sema/SemaConcept.cpp | 12 ++++++ clang/lib/Sema/SemaDecl.cpp | 13 ++++-- clang/lib/Sema/SemaOverload.cpp | 47 +++++++++++++--------- .../CXX/over/over.match/over.match.best/p2.cpp | 6 ++- .../temp.constr.order/non-template-functions.cpp | 27 +++++++++++++ 6 files changed, 85 insertions(+), 22 deletions(-) create mode 100644 clang/test/CXX/temp/temp.constr/temp.constr.order/non-template-functions.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index bd4c0bf..641f75e 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -264,6 +264,8 @@ Bug Fixes 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 index d3d87c8..54d8a74 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1300,6 +1300,18 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, 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 index 686a126..a164d9a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -18218,13 +18218,20 @@ static void SetEligibleMethods(Sema &S, CXXRecordDecl *Record, 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 @@ static void SetEligibleMethods(Sema &S, CXXRecordDecl *Record, 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 index d21de7d..45bca3a 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -10039,13 +10039,20 @@ bool clang::isBetterOverloadCandidate( // 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 @@ Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) { 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 index 3a44436..e0f7dfb 100644 --- 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 @@ namespace PR44761 { 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 index 0000000..4ef8403 --- /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(*)()>); + } +} -- 2.7.4