Skip to content

Commit e18c2c5

Browse files
author
Yuanfang Chen
committed
[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 llvm#56154 Reviewed By: erichkeane, royjacobson, mizvekov Differential Revision: https://reviews.llvm.org/D136545
1 parent 5d086cc commit e18c2c5

File tree

6 files changed

+85
-22
lines changed

6 files changed

+85
-22
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ Bug Fixes
264264
constraint, which re-evaluated the same constraint.
265265
`Issue 53213 <https://github.com/llvm/llvm-project/issues/53213>`_
266266
`Issue 45736 <https://github.com/llvm/llvm-project/issues/45736>`_
267+
- Fix an issue when performing constraints partial ordering on non-template
268+
functions. `Issue 56154 <https://github.com/llvm/llvm-project/issues/56154>`_
267269

268270
Improvements to Clang's diagnostics
269271
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaConcept.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,18 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1,
13001300
NamedDecl *D2,
13011301
MutableArrayRef<const Expr *> AC2,
13021302
bool &Result) {
1303+
if (const auto *FD1 = dyn_cast<FunctionDecl>(D1)) {
1304+
auto IsExpectedEntity = [](const FunctionDecl *FD) {
1305+
FunctionDecl::TemplatedKind Kind = FD->getTemplatedKind();
1306+
return Kind == FunctionDecl::TK_NonTemplate ||
1307+
Kind == FunctionDecl::TK_FunctionTemplate;
1308+
};
1309+
const auto *FD2 = dyn_cast<FunctionDecl>(D2);
1310+
assert(IsExpectedEntity(FD1) && FD2 && IsExpectedEntity(FD2) &&
1311+
"use non-instantiated function declaration for constraints partial "
1312+
"ordering");
1313+
}
1314+
13031315
if (AC1.empty()) {
13041316
Result = AC2.empty();
13051317
return false;

clang/lib/Sema/SemaDecl.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18218,13 +18218,20 @@ static void SetEligibleMethods(Sema &S, CXXRecordDecl *Record,
1821818218
if (!SatisfactionStatus[i])
1821918219
continue;
1822018220
CXXMethodDecl *Method = Methods[i];
18221-
const Expr *Constraints = Method->getTrailingRequiresClause();
18221+
CXXMethodDecl *OrigMethod = Method;
18222+
if (FunctionDecl *MF = OrigMethod->getInstantiatedFromMemberFunction())
18223+
OrigMethod = cast<CXXMethodDecl>(MF);
18224+
18225+
const Expr *Constraints = OrigMethod->getTrailingRequiresClause();
1822218226
bool AnotherMethodIsMoreConstrained = false;
1822318227
for (size_t j = 0; j < Methods.size(); j++) {
1822418228
if (i == j || !SatisfactionStatus[j])
1822518229
continue;
1822618230
CXXMethodDecl *OtherMethod = Methods[j];
18227-
if (!AreSpecialMemberFunctionsSameKind(S.Context, Method, OtherMethod,
18231+
if (FunctionDecl *MF = OtherMethod->getInstantiatedFromMemberFunction())
18232+
OtherMethod = cast<CXXMethodDecl>(MF);
18233+
18234+
if (!AreSpecialMemberFunctionsSameKind(S.Context, OrigMethod, OtherMethod,
1822818235
CSM))
1822918236
continue;
1823018237

@@ -18235,7 +18242,7 @@ static void SetEligibleMethods(Sema &S, CXXRecordDecl *Record,
1823518242
AnotherMethodIsMoreConstrained = true;
1823618243
break;
1823718244
}
18238-
if (S.IsAtLeastAsConstrained(OtherMethod, {OtherConstraints}, Method,
18245+
if (S.IsAtLeastAsConstrained(OtherMethod, {OtherConstraints}, OrigMethod,
1823918246
{Constraints},
1824018247
AnotherMethodIsMoreConstrained)) {
1824118248
// There was an error with the constraints comparison. Exit the loop

clang/lib/Sema/SemaOverload.cpp

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10039,13 +10039,20 @@ bool clang::isBetterOverloadCandidate(
1003910039
// parameter-type-lists, and F1 is more constrained than F2 [...],
1004010040
if (!Cand1IsSpecialization && !Cand2IsSpecialization &&
1004110041
sameFunctionParameterTypeLists(S, Cand1, Cand2)) {
10042-
const Expr *RC1 = Cand1.Function->getTrailingRequiresClause();
10043-
const Expr *RC2 = Cand2.Function->getTrailingRequiresClause();
10042+
FunctionDecl *Function1 = Cand1.Function;
10043+
FunctionDecl *Function2 = Cand2.Function;
10044+
if (FunctionDecl *MF = Function1->getInstantiatedFromMemberFunction())
10045+
Function1 = MF;
10046+
if (FunctionDecl *MF = Function2->getInstantiatedFromMemberFunction())
10047+
Function2 = MF;
10048+
10049+
const Expr *RC1 = Function1->getTrailingRequiresClause();
10050+
const Expr *RC2 = Function2->getTrailingRequiresClause();
1004410051
if (RC1 && RC2) {
1004510052
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
10046-
if (S.IsAtLeastAsConstrained(Cand1.Function, RC1, Cand2.Function, RC2,
10053+
if (S.IsAtLeastAsConstrained(Function1, RC1, Function2, RC2,
1004710054
AtLeastAsConstrained1) ||
10048-
S.IsAtLeastAsConstrained(Cand2.Function, RC2, Cand1.Function, RC1,
10055+
S.IsAtLeastAsConstrained(Function2, RC2, Function1, RC1,
1004910056
AtLeastAsConstrained2))
1005010057
return false;
1005110058
if (AtLeastAsConstrained1 != AtLeastAsConstrained2)
@@ -12630,20 +12637,24 @@ Sema::resolveAddressOfSingleOverloadCandidate(Expr *E, DeclAccessPair &Pair) {
1263012637
DeclAccessPair DAP;
1263112638
SmallVector<FunctionDecl *, 2> AmbiguousDecls;
1263212639

12633-
auto CheckMoreConstrained =
12634-
[&] (FunctionDecl *FD1, FunctionDecl *FD2) -> Optional<bool> {
12635-
SmallVector<const Expr *, 1> AC1, AC2;
12636-
FD1->getAssociatedConstraints(AC1);
12637-
FD2->getAssociatedConstraints(AC2);
12638-
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
12639-
if (IsAtLeastAsConstrained(FD1, AC1, FD2, AC2, AtLeastAsConstrained1))
12640-
return None;
12641-
if (IsAtLeastAsConstrained(FD2, AC2, FD1, AC1, AtLeastAsConstrained2))
12642-
return None;
12643-
if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
12644-
return None;
12645-
return AtLeastAsConstrained1;
12646-
};
12640+
auto CheckMoreConstrained = [&](FunctionDecl *FD1,
12641+
FunctionDecl *FD2) -> Optional<bool> {
12642+
if (FunctionDecl *MF = FD1->getInstantiatedFromMemberFunction())
12643+
FD1 = MF;
12644+
if (FunctionDecl *MF = FD2->getInstantiatedFromMemberFunction())
12645+
FD2 = MF;
12646+
SmallVector<const Expr *, 1> AC1, AC2;
12647+
FD1->getAssociatedConstraints(AC1);
12648+
FD2->getAssociatedConstraints(AC2);
12649+
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
12650+
if (IsAtLeastAsConstrained(FD1, AC1, FD2, AC2, AtLeastAsConstrained1))
12651+
return None;
12652+
if (IsAtLeastAsConstrained(FD2, AC2, FD1, AC1, AtLeastAsConstrained2))
12653+
return None;
12654+
if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
12655+
return None;
12656+
return AtLeastAsConstrained1;
12657+
};
1264712658

1264812659
// Don't use the AddressOfResolver because we're specifically looking for
1264912660
// cases where we have one overload candidate that lacks

clang/test/CXX/over/over.match/over.match.best/p2.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ namespace PR44761 {
77
bool operator<(const A&) const & requires X<T>; // #1
88
int operator<=>(const A&) const & requires X<T> && X<int> = delete; // #2
99
};
10-
bool k1 = A<int>() < A<int>(); // not ordered by constraints: prefer non-rewritten form
10+
bool k1 = A<int>() < A<int>(); // prefer more-constrained 'operator<=>'
11+
// expected-error@-1 {{deleted}}
12+
// expected-note@#1 {{candidate}}
13+
// expected-note@#2 {{candidate function has been explicitly deleted}}
14+
// expected-note@#2 {{candidate function (with reversed parameter order) has been explicitly deleted}}
1115
bool k2 = A<float>() < A<float>(); // prefer more-constrained 'operator<=>'
1216
// expected-error@-1 {{deleted}}
1317
// expected-note@#1 {{candidate}}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: %clang_cc1 -std=c++2a -x c++ -verify %s
2+
// expected-no-diagnostics
3+
4+
template<typename T, typename U>
5+
constexpr static bool is_same_v = false;
6+
7+
template<typename T>
8+
constexpr static bool is_same_v<T, T> = true;
9+
10+
namespace PR56154 {
11+
template <int N> concept C0 = (N == 0);
12+
template <int N, int N2> concept C0x = C0<N>;
13+
template <int N1, int N2> concept C00 = C0x<N1, N2> && C0<N2>;
14+
15+
template<int N1, int N2>
16+
struct A {
17+
void f() requires C00<N1, N2>;
18+
void f() requires C0x<N1, N2> = delete;
19+
20+
static short g() requires C00<N1, N2>;
21+
static int g() requires C0x<N1, N2>;
22+
};
23+
void h(A<0, 0> a) {
24+
a.f();
25+
static_assert(is_same_v<decltype(&A<0, 0>::g), short(*)()>);
26+
}
27+
}

0 commit comments

Comments
 (0)