-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[Clang][Sema] Improve support for explicit specializations of constrained member functions & member function templates #88963
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5957a41
76051c7
f85c7f5
6bfa0f7
392ff1b
9d40864
c5071eb
d1889b8
0fada09
535ee42
20c8a68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10339,24 +10339,53 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous) { | |
if (Previous.empty()) { | ||
// Nowhere to look anyway. | ||
} else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Member)) { | ||
SmallVector<FunctionDecl *> Candidates; | ||
bool Ambiguous = false; | ||
for (LookupResult::iterator I = Previous.begin(), E = Previous.end(); | ||
I != E; ++I) { | ||
NamedDecl *D = (*I)->getUnderlyingDecl(); | ||
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) { | ||
QualType Adjusted = Function->getType(); | ||
if (!hasExplicitCallingConv(Adjusted)) | ||
Adjusted = adjustCCAndNoReturn(Adjusted, Method->getType()); | ||
// This doesn't handle deduced return types, but both function | ||
// declarations should be undeduced at this point. | ||
if (Context.hasSameType(Adjusted, Method->getType())) { | ||
FoundInstantiation = *I; | ||
Instantiation = Method; | ||
InstantiatedFrom = Method->getInstantiatedFromMemberFunction(); | ||
MSInfo = Method->getMemberSpecializationInfo(); | ||
break; | ||
} | ||
CXXMethodDecl *Method = | ||
dyn_cast<CXXMethodDecl>((*I)->getUnderlyingDecl()); | ||
if (!Method) | ||
continue; | ||
QualType Adjusted = Function->getType(); | ||
if (!hasExplicitCallingConv(Adjusted)) | ||
Adjusted = adjustCCAndNoReturn(Adjusted, Method->getType()); | ||
// This doesn't handle deduced return types, but both function | ||
// declarations should be undeduced at this point. | ||
if (!Context.hasSameType(Adjusted, Method->getType())) | ||
continue; | ||
if (ConstraintSatisfaction Satisfaction; | ||
Method->getTrailingRequiresClause() && | ||
(CheckFunctionConstraints(Method, Satisfaction, | ||
/*UsageLoc=*/Member->getLocation(), | ||
/*ForOverloadResolution=*/true) || | ||
!Satisfaction.IsSatisfied)) | ||
continue; | ||
Candidates.push_back(Method); | ||
FunctionDecl *MoreConstrained = | ||
Instantiation ? getMoreConstrainedFunction( | ||
Method, cast<FunctionDecl>(Instantiation)) | ||
: Method; | ||
if (!MoreConstrained) { | ||
Ambiguous = true; | ||
continue; | ||
} | ||
if (MoreConstrained == Method) { | ||
Ambiguous = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't look right to me. Suppose we see declaration I think we need to do the same two-pass dance we do in overload resolution: first do a walk through the declarations tracking the best so far, then go through them again and make sure the best so far is actually better than everything else. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whoops, good catch! I'll open a patch to fix this :) |
||
FoundInstantiation = *I; | ||
Instantiation = Method; | ||
InstantiatedFrom = Method->getInstantiatedFromMemberFunction(); | ||
MSInfo = Method->getMemberSpecializationInfo(); | ||
} | ||
} | ||
if (Ambiguous) { | ||
Diag(Member->getLocation(), diag::err_function_member_spec_ambiguous) | ||
<< Member << (InstantiatedFrom ? InstantiatedFrom : Instantiation); | ||
for (FunctionDecl *Candidate : Candidates) | ||
Diag(Candidate->getLocation(), diag::note_function_member_spec_matched) | ||
<< Candidate; | ||
return true; | ||
} | ||
} else if (isa<VarDecl>(Member)) { | ||
VarDecl *PrevVar; | ||
if (Previous.isSingleResult() && | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -275,6 +275,13 @@ Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function, | |
TemplateArgs->asArray(), | ||
/*Final=*/false); | ||
|
||
if (RelativeToPrimary && | ||
(Function->getTemplateSpecializationKind() == | ||
TSK_ExplicitSpecialization || | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this condition There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jcsxky There already is a test case which will fail without the condition in this PR: template<typename T>
struct D {
template<typename U>
static constexpr int f(U);
template<typename U>
static constexpr int f(U) requires (sizeof(T) == 1);
template<>
constexpr int f(int) {
return 1;
}
};
template<>
template<typename U>
constexpr int D<signed char>::f(U) requires (sizeof(signed char) == 1) {
return 0;
}
static_assert(D<signed char>::f(0) == 1); // fails There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This testcase runs OK before be79079 applied and it may not be related to the condition There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @sdkrystian but gcc fails on the testcase you provided. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gcc does not support class scope explicit specializations. |
||
(Function->getFriendObjectKind() && | ||
!Function->getPrimaryTemplate()->getFriendObjectKind()))) | ||
return Response::UseNextDecl(Function); | ||
|
||
// If this function was instantiated from a specialized member that is | ||
// a function template, we're done. | ||
assert(Function->getPrimaryTemplate() && "No function template?"); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// RUN: %clang_cc1 -std=c++20 -verify %s | ||
|
||
template<int I> | ||
concept C = I >= 4; | ||
|
||
template<int I> | ||
concept D = I < 8; | ||
|
||
template<int I> | ||
struct A { | ||
constexpr static int f() { return 0; } | ||
constexpr static int f() requires C<I> && D<I> { return 1; } | ||
constexpr static int f() requires C<I> { return 2; } | ||
|
||
constexpr static int g() requires C<I> { return 0; } // #candidate-0 | ||
constexpr static int g() requires D<I> { return 1; } // #candidate-1 | ||
|
||
constexpr static int h() requires C<I> { return 0; } // expected-note {{member declaration nearly matches}} | ||
}; | ||
|
||
template<> | ||
constexpr int A<2>::f() { return 3; } | ||
|
||
template<> | ||
constexpr int A<4>::f() { return 4; } | ||
|
||
template<> | ||
constexpr int A<8>::f() { return 5; } | ||
|
||
static_assert(A<3>::f() == 0); | ||
static_assert(A<5>::f() == 1); | ||
static_assert(A<9>::f() == 2); | ||
static_assert(A<2>::f() == 3); | ||
static_assert(A<4>::f() == 4); | ||
static_assert(A<8>::f() == 5); | ||
|
||
template<> | ||
constexpr int A<0>::g() { return 2; } | ||
|
||
template<> | ||
constexpr int A<8>::g() { return 3; } | ||
|
||
template<> | ||
constexpr int A<6>::g() { return 4; } // expected-error {{ambiguous member function specialization 'A<6>::g' of 'A::g'}} | ||
// expected-note@#candidate-0 {{member function specialization matches 'g'}} | ||
// expected-note@#candidate-1 {{member function specialization matches 'g'}} | ||
|
||
static_assert(A<9>::g() == 0); | ||
static_assert(A<1>::g() == 1); | ||
static_assert(A<0>::g() == 2); | ||
static_assert(A<8>::g() == 3); | ||
|
||
template<> | ||
constexpr int A<4>::h() { return 1; } | ||
|
||
template<> | ||
constexpr int A<0>::h() { return 2; } // expected-error {{out-of-line definition of 'h' does not match any declaration in 'A<0>'}} | ||
|
||
static_assert(A<5>::h() == 0); | ||
static_assert(A<4>::h() == 1); |
Uh oh!
There was an error while loading. Please reload this page.