Skip to content

Commit f85c7f5

Browse files
committed
[FOLD] add more tests, diagnose ambiguous member function specializations
1 parent 76051c7 commit f85c7f5

File tree

4 files changed

+122
-33
lines changed

4 files changed

+122
-33
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5437,6 +5437,11 @@ def note_function_template_spec_matched : Note<
54375437
def err_function_template_partial_spec : Error<
54385438
"function template partial specialization is not allowed">;
54395439

5440+
def err_function_member_spec_ambiguous : Error<
5441+
"ambiguous member function specialization of %q0">;
5442+
def note_function_member_spec_matched : Note<
5443+
"member function specialization matches %0">;
5444+
54405445
// C++ Template Instantiation
54415446
def err_template_recursion_depth_exceeded : Error<
54425447
"recursive template instantiation exceeded maximum depth of %0">,

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9739,6 +9739,9 @@ class Sema final : public SemaBase {
97399739
const PartialDiagnostic &CandidateDiag,
97409740
bool Complain = true, QualType TargetType = QualType());
97419741

9742+
FunctionDecl *getMoreConstrainedFunction(FunctionDecl *FD1,
9743+
FunctionDecl *FD2);
9744+
97429745
///@}
97439746

97449747
//

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 56 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10312,23 +10312,27 @@ bool Sema::CheckFunctionTemplateSpecialization(
1031210312
return false;
1031310313
}
1031410314

10315-
static bool IsMoreConstrainedFunction(Sema &S, FunctionDecl *FD1,
10316-
FunctionDecl *FD2) {
10315+
FunctionDecl *Sema::getMoreConstrainedFunction(FunctionDecl *FD1,
10316+
FunctionDecl *FD2) {
10317+
assert(!FD1->getDescribedTemplate() && !FD2->getDescribedTemplate() &&
10318+
"not for function templates");
10319+
FunctionDecl *F1 = FD1;
1031710320
if (FunctionDecl *MF = FD1->getInstantiatedFromMemberFunction())
10318-
FD1 = MF;
10321+
F1 = MF;
10322+
FunctionDecl *F2 = FD2;
1031910323
if (FunctionDecl *MF = FD2->getInstantiatedFromMemberFunction())
10320-
FD2 = MF;
10324+
F2 = MF;
1032110325
llvm::SmallVector<const Expr *, 3> AC1, AC2;
10322-
FD1->getAssociatedConstraints(AC1);
10323-
FD2->getAssociatedConstraints(AC2);
10326+
F1->getAssociatedConstraints(AC1);
10327+
F2->getAssociatedConstraints(AC2);
1032410328
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
10325-
if (S.IsAtLeastAsConstrained(FD1, AC1, FD2, AC2, AtLeastAsConstrained1))
10326-
return false;
10327-
if (S.IsAtLeastAsConstrained(FD2, AC2, FD1, AC1, AtLeastAsConstrained2))
10328-
return false;
10329+
if (IsAtLeastAsConstrained(F1, AC1, F2, AC2, AtLeastAsConstrained1))
10330+
return nullptr;
10331+
if (IsAtLeastAsConstrained(F2, AC2, F1, AC1, AtLeastAsConstrained2))
10332+
return nullptr;
1032910333
if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
10330-
return false;
10331-
return AtLeastAsConstrained1;
10334+
return nullptr;
10335+
return AtLeastAsConstrained1 ? FD1 : FD2;
1033210336
}
1033310337

1033410338
/// Perform semantic analysis for the given non-template member
@@ -10358,35 +10362,54 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous) {
1035810362
if (Previous.empty()) {
1035910363
// Nowhere to look anyway.
1036010364
} else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Member)) {
10365+
SmallVector<FunctionDecl *> Candidates;
10366+
bool Ambiguous = false;
1036110367
for (LookupResult::iterator I = Previous.begin(), E = Previous.end();
1036210368
I != E; ++I) {
10363-
NamedDecl *D = (*I)->getUnderlyingDecl();
10364-
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) {
10365-
QualType Adjusted = Function->getType();
10366-
if (!hasExplicitCallingConv(Adjusted))
10367-
Adjusted = adjustCCAndNoReturn(Adjusted, Method->getType());
10368-
if (!Context.hasSameType(Adjusted, Method->getType()))
10369-
continue;
10370-
if (Method->getTrailingRequiresClause()) {
10371-
ConstraintSatisfaction Satisfaction;
10372-
if (CheckFunctionConstraints(Method, Satisfaction,
10373-
/*UsageLoc=*/Member->getLocation(),
10374-
/*ForOverloadResolution=*/true) ||
10375-
!Satisfaction.IsSatisfied)
10376-
continue;
10377-
if (Instantiation &&
10378-
!IsMoreConstrainedFunction(*this, Method,
10379-
cast<CXXMethodDecl>(Instantiation)))
10380-
continue;
10381-
}
10382-
// This doesn't handle deduced return types, but both function
10383-
// declarations should be undeduced at this point.
10369+
CXXMethodDecl *Method =
10370+
dyn_cast<CXXMethodDecl>((*I)->getUnderlyingDecl());
10371+
if (!Method)
10372+
continue;
10373+
QualType Adjusted = Function->getType();
10374+
if (!hasExplicitCallingConv(Adjusted))
10375+
Adjusted = adjustCCAndNoReturn(Adjusted, Method->getType());
10376+
// This doesn't handle deduced return types, but both function
10377+
// declarations should be undeduced at this point.
10378+
if (!Context.hasSameType(Adjusted, Function->getType()))
10379+
continue;
10380+
// FIXME: What if neither function is more constrained than the other?
10381+
if (ConstraintSatisfaction Satisfaction;
10382+
Method->getTrailingRequiresClause() &&
10383+
(CheckFunctionConstraints(Method, Satisfaction,
10384+
/*UsageLoc=*/Member->getLocation(),
10385+
/*ForOverloadResolution=*/true) ||
10386+
!Satisfaction.IsSatisfied))
10387+
continue;
10388+
Candidates.push_back(Method);
10389+
FunctionDecl *MoreConstrained =
10390+
Instantiation ? getMoreConstrainedFunction(
10391+
Method, cast<FunctionDecl>(Instantiation))
10392+
: Method;
10393+
if (!MoreConstrained) {
10394+
Ambiguous = true;
10395+
continue;
10396+
}
10397+
if (MoreConstrained == Method) {
10398+
Ambiguous = false;
1038410399
FoundInstantiation = *I;
1038510400
Instantiation = Method;
1038610401
InstantiatedFrom = Method->getInstantiatedFromMemberFunction();
1038710402
MSInfo = Method->getMemberSpecializationInfo();
1038810403
}
1038910404
}
10405+
if (Ambiguous) {
10406+
Diag(Member->getLocation(), diag::err_function_member_spec_ambiguous)
10407+
<< Member;
10408+
for (FunctionDecl *Candidate : Candidates)
10409+
Diag(Candidate->getLocation(), diag::note_function_member_spec_matched)
10410+
<< Candidate;
10411+
return true;
10412+
}
1039010413
} else if (isa<VarDecl>(Member)) {
1039110414
VarDecl *PrevVar;
1039210415
if (Previous.isSingleResult() &&
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// RUN: %clang_cc1 -std=c++20 -verify %s
2+
3+
template<int I>
4+
concept C = I >= 4;
5+
6+
template<int I>
7+
concept D = I < 8;
8+
9+
template<int I>
10+
struct A {
11+
constexpr static int f() { return 0; }
12+
constexpr static int f() requires C<I> && D<I> { return 1; }
13+
constexpr static int f() requires C<I> { return 2; }
14+
15+
constexpr static int g() requires C<I> { return 0; } // expected-note {{member function specialization matches 'g'}}
16+
constexpr static int g() requires D<I> { return 1; } // expected-note {{member function specialization matches 'g'}}
17+
18+
constexpr static int h() requires C<I> { return 0; } // expected-note {{member declaration nearly matches}}
19+
};
20+
21+
template<>
22+
constexpr int A<2>::f() { return 3; }
23+
24+
template<>
25+
constexpr int A<4>::f() { return 4; }
26+
27+
template<>
28+
constexpr int A<8>::f() { return 5; }
29+
30+
static_assert(A<3>::f() == 0);
31+
static_assert(A<5>::f() == 1);
32+
static_assert(A<9>::f() == 2);
33+
static_assert(A<2>::f() == 3);
34+
static_assert(A<4>::f() == 4);
35+
static_assert(A<8>::f() == 5);
36+
37+
template<>
38+
constexpr int A<0>::g() { return 2; }
39+
40+
template<>
41+
constexpr int A<8>::g() { return 3; }
42+
43+
template<>
44+
constexpr int A<6>::g() { return 4; } // expected-error {{ambiguous member function specialization of 'A<6>::g'}}
45+
46+
static_assert(A<9>::g() == 0);
47+
static_assert(A<1>::g() == 1);
48+
static_assert(A<0>::g() == 2);
49+
static_assert(A<8>::g() == 3);
50+
51+
template<>
52+
constexpr int A<4>::h() { return 1; }
53+
54+
template<>
55+
constexpr int A<0>::h() { return 2; } // expected-error {{out-of-line definition of 'h' does not match any declaration in 'A<0>'}}
56+
57+
static_assert(A<5>::h() == 0);
58+
static_assert(A<4>::h() == 1);

0 commit comments

Comments
 (0)