Skip to content

Commit ede52d5

Browse files
committed
[Clang][Sema] Improve support for explicit specializations of constrained member functions & member function templates
1 parent 91dd844 commit ede52d5

File tree

5 files changed

+124
-11
lines changed

5 files changed

+124
-11
lines changed

clang/lib/Sema/SemaConcept.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
811811
// this may happen while we're comparing two templates' constraint
812812
// equivalence.
813813
LocalInstantiationScope ScopeForParameters(S);
814-
if (auto *FD = llvm::dyn_cast<FunctionDecl>(DeclInfo.getDecl()))
814+
if (auto *FD = DeclInfo.getDecl()->getAsFunction())
815815
for (auto *PVD : FD->parameters())
816816
ScopeForParameters.InstantiatedLocal(PVD, PVD);
817817

clang/lib/Sema/SemaOverload.cpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,8 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
13031303
if (New->isMSVCRTEntryPoint())
13041304
return false;
13051305

1306+
NamedDecl *OldDecl = Old;
1307+
NamedDecl *NewDecl = New;
13061308
FunctionTemplateDecl *OldTemplate = Old->getDescribedFunctionTemplate();
13071309
FunctionTemplateDecl *NewTemplate = New->getDescribedFunctionTemplate();
13081310

@@ -1347,6 +1349,8 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
13471349
// references to non-instantiated entities during constraint substitution.
13481350
// GH78101.
13491351
if (NewTemplate) {
1352+
OldDecl = OldTemplate;
1353+
NewDecl = NewTemplate;
13501354
// C++ [temp.over.link]p4:
13511355
// The signature of a function template consists of its function
13521356
// signature, its return type and its template parameter list. The names
@@ -1506,13 +1510,14 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
15061510
}
15071511
}
15081512

1509-
if (!UseOverrideRules) {
1513+
if (!UseOverrideRules &&
1514+
New->getTemplateSpecializationKind() != TSK_ExplicitSpecialization) {
15101515
Expr *NewRC = New->getTrailingRequiresClause(),
15111516
*OldRC = Old->getTrailingRequiresClause();
15121517
if ((NewRC != nullptr) != (OldRC != nullptr))
15131518
return true;
1514-
1515-
if (NewRC && !SemaRef.AreConstraintExpressionsEqual(Old, OldRC, New, NewRC))
1519+
if (NewRC &&
1520+
!SemaRef.AreConstraintExpressionsEqual(OldDecl, OldRC, NewDecl, NewRC))
15161521
return true;
15171522
}
15181523

clang/lib/Sema/SemaTemplate.cpp

+37-7
Original file line numberDiff line numberDiff line change
@@ -10338,6 +10338,25 @@ bool Sema::CheckFunctionTemplateSpecialization(
1033810338
return false;
1033910339
}
1034010340

10341+
static bool IsMoreConstrainedFunction(Sema &S, FunctionDecl *FD1,
10342+
FunctionDecl *FD2) {
10343+
if (FunctionDecl *MF = FD1->getInstantiatedFromMemberFunction())
10344+
FD1 = MF;
10345+
if (FunctionDecl *MF = FD2->getInstantiatedFromMemberFunction())
10346+
FD2 = MF;
10347+
llvm::SmallVector<const Expr *, 3> AC1, AC2;
10348+
FD1->getAssociatedConstraints(AC1);
10349+
FD2->getAssociatedConstraints(AC2);
10350+
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
10351+
if (S.IsAtLeastAsConstrained(FD1, AC1, FD2, AC2, AtLeastAsConstrained1))
10352+
return false;
10353+
if (S.IsAtLeastAsConstrained(FD2, AC2, FD1, AC1, AtLeastAsConstrained2))
10354+
return false;
10355+
if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
10356+
return false;
10357+
return AtLeastAsConstrained1;
10358+
}
10359+
1034110360
/// Perform semantic analysis for the given non-template member
1034210361
/// specialization.
1034310362
///
@@ -10372,15 +10391,26 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous) {
1037210391
QualType Adjusted = Function->getType();
1037310392
if (!hasExplicitCallingConv(Adjusted))
1037410393
Adjusted = adjustCCAndNoReturn(Adjusted, Method->getType());
10394+
if (!Context.hasSameType(Adjusted, Method->getType()))
10395+
continue;
10396+
if (Method->getTrailingRequiresClause()) {
10397+
ConstraintSatisfaction Satisfaction;
10398+
if (CheckFunctionConstraints(Method, Satisfaction,
10399+
/*UsageLoc=*/Member->getLocation(),
10400+
/*ForOverloadResolution=*/true) ||
10401+
!Satisfaction.IsSatisfied)
10402+
continue;
10403+
if (Instantiation &&
10404+
!IsMoreConstrainedFunction(*this, Method,
10405+
cast<CXXMethodDecl>(Instantiation)))
10406+
continue;
10407+
}
1037510408
// This doesn't handle deduced return types, but both function
1037610409
// declarations should be undeduced at this point.
10377-
if (Context.hasSameType(Adjusted, Method->getType())) {
10378-
FoundInstantiation = *I;
10379-
Instantiation = Method;
10380-
InstantiatedFrom = Method->getInstantiatedFromMemberFunction();
10381-
MSInfo = Method->getMemberSpecializationInfo();
10382-
break;
10383-
}
10410+
FoundInstantiation = *I;
10411+
Instantiation = Method;
10412+
InstantiatedFrom = Method->getInstantiatedFromMemberFunction();
10413+
MSInfo = Method->getMemberSpecializationInfo();
1038410414
}
1038510415
}
1038610416
} else if (isa<VarDecl>(Member)) {

clang/lib/Sema/SemaTemplateInstantiate.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
275275
TemplateArgs->asArray(),
276276
/*Final=*/false);
277277

278+
if (RelativeToPrimary &&
279+
Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
280+
return Response::UseNextDecl(Function);
281+
278282
// If this function was instantiated from a specialized member that is
279283
// a function template, we're done.
280284
assert(Function->getPrimaryTemplate() && "No function template?");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
2+
// expected-no-diagnostics
3+
4+
template<typename T>
5+
concept C = sizeof(T) <= sizeof(long);
6+
7+
template<typename T>
8+
struct A {
9+
template<typename U>
10+
void f(U) requires C<U>;
11+
12+
void g() requires C<T>;
13+
14+
template<typename U>
15+
void h(U) requires C<T>;
16+
17+
constexpr int i() requires C<T> {
18+
return 0;
19+
}
20+
21+
constexpr int i() requires C<T> && true {
22+
return 1;
23+
}
24+
25+
template<>
26+
void f(char);
27+
};
28+
29+
template<>
30+
template<typename U>
31+
void A<short>::f(U) requires C<U>;
32+
33+
template<>
34+
template<typename U>
35+
void A<short>::h(U) requires C<short>;
36+
37+
template<>
38+
template<>
39+
void A<int>::f(int);
40+
41+
template<>
42+
void A<long>::g();
43+
44+
template<>
45+
constexpr int A<long>::i() {
46+
return 2;
47+
}
48+
49+
static_assert(A<long>().i() == 2);
50+
51+
template<typename T>
52+
struct D {
53+
template<typename U>
54+
static constexpr int f(U);
55+
56+
template<typename U>
57+
static constexpr int f(U) requires (sizeof(T) == 1);
58+
59+
template<>
60+
constexpr int f(int) {
61+
return 1;
62+
}
63+
};
64+
65+
template<>
66+
template<typename U>
67+
constexpr int D<signed char>::f(U) requires (sizeof(signed char) == 1) {
68+
return 0;
69+
}
70+
71+
static_assert(D<char>::f(0) == 1);
72+
static_assert(D<char[2]>::f(0) == 1);
73+
static_assert(D<signed char>::f(0) == 1);
74+
static_assert(D<signed char>::f(0.0) == 0);

0 commit comments

Comments
 (0)