Skip to content

Commit

Permalink
[Clang][Sema] Improve support for explicit specializations of constra…
Browse files Browse the repository at this point in the history
…ined member functions & member function templates
  • Loading branch information
sdkrystian committed Apr 16, 2024
1 parent 91dd844 commit ede52d5
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 11 deletions.
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaConcept.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,7 @@ static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
// this may happen while we're comparing two templates' constraint
// equivalence.
LocalInstantiationScope ScopeForParameters(S);
if (auto *FD = llvm::dyn_cast<FunctionDecl>(DeclInfo.getDecl()))
if (auto *FD = DeclInfo.getDecl()->getAsFunction())
for (auto *PVD : FD->parameters())
ScopeForParameters.InstantiatedLocal(PVD, PVD);

Expand Down
11 changes: 8 additions & 3 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,8 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
if (New->isMSVCRTEntryPoint())
return false;

NamedDecl *OldDecl = Old;
NamedDecl *NewDecl = New;
FunctionTemplateDecl *OldTemplate = Old->getDescribedFunctionTemplate();
FunctionTemplateDecl *NewTemplate = New->getDescribedFunctionTemplate();

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

if (!UseOverrideRules) {
if (!UseOverrideRules &&
New->getTemplateSpecializationKind() != TSK_ExplicitSpecialization) {
Expr *NewRC = New->getTrailingRequiresClause(),
*OldRC = Old->getTrailingRequiresClause();
if ((NewRC != nullptr) != (OldRC != nullptr))
return true;

if (NewRC && !SemaRef.AreConstraintExpressionsEqual(Old, OldRC, New, NewRC))
if (NewRC &&
!SemaRef.AreConstraintExpressionsEqual(OldDecl, OldRC, NewDecl, NewRC))
return true;
}

Expand Down
44 changes: 37 additions & 7 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10338,6 +10338,25 @@ bool Sema::CheckFunctionTemplateSpecialization(
return false;
}

static bool IsMoreConstrainedFunction(Sema &S, FunctionDecl *FD1,
FunctionDecl *FD2) {
if (FunctionDecl *MF = FD1->getInstantiatedFromMemberFunction())
FD1 = MF;
if (FunctionDecl *MF = FD2->getInstantiatedFromMemberFunction())
FD2 = MF;
llvm::SmallVector<const Expr *, 3> AC1, AC2;
FD1->getAssociatedConstraints(AC1);
FD2->getAssociatedConstraints(AC2);
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
if (S.IsAtLeastAsConstrained(FD1, AC1, FD2, AC2, AtLeastAsConstrained1))
return false;
if (S.IsAtLeastAsConstrained(FD2, AC2, FD1, AC1, AtLeastAsConstrained2))
return false;
if (AtLeastAsConstrained1 == AtLeastAsConstrained2)
return false;
return AtLeastAsConstrained1;
}

/// Perform semantic analysis for the given non-template member
/// specialization.
///
Expand Down Expand Up @@ -10372,15 +10391,26 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous) {
QualType Adjusted = Function->getType();
if (!hasExplicitCallingConv(Adjusted))
Adjusted = adjustCCAndNoReturn(Adjusted, Method->getType());
if (!Context.hasSameType(Adjusted, Method->getType()))
continue;
if (Method->getTrailingRequiresClause()) {
ConstraintSatisfaction Satisfaction;
if (CheckFunctionConstraints(Method, Satisfaction,
/*UsageLoc=*/Member->getLocation(),
/*ForOverloadResolution=*/true) ||
!Satisfaction.IsSatisfied)
continue;
if (Instantiation &&
!IsMoreConstrainedFunction(*this, Method,
cast<CXXMethodDecl>(Instantiation)))
continue;
}
// 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;
}
FoundInstantiation = *I;
Instantiation = Method;
InstantiatedFrom = Method->getInstantiatedFromMemberFunction();
MSInfo = Method->getMemberSpecializationInfo();
}
}
} else if (isa<VarDecl>(Member)) {
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,10 @@ Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function,
TemplateArgs->asArray(),
/*Final=*/false);

if (RelativeToPrimary &&
Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
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?");
Expand Down
74 changes: 74 additions & 0 deletions clang/test/CXX/temp/temp.spec/temp.expl.spec/p8.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
// expected-no-diagnostics

template<typename T>
concept C = sizeof(T) <= sizeof(long);

template<typename T>
struct A {
template<typename U>
void f(U) requires C<U>;

void g() requires C<T>;

template<typename U>
void h(U) requires C<T>;

constexpr int i() requires C<T> {
return 0;
}

constexpr int i() requires C<T> && true {
return 1;
}

template<>
void f(char);
};

template<>
template<typename U>
void A<short>::f(U) requires C<U>;

template<>
template<typename U>
void A<short>::h(U) requires C<short>;

template<>
template<>
void A<int>::f(int);

template<>
void A<long>::g();

template<>
constexpr int A<long>::i() {
return 2;
}

static_assert(A<long>().i() == 2);

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<char>::f(0) == 1);
static_assert(D<char[2]>::f(0) == 1);
static_assert(D<signed char>::f(0) == 1);
static_assert(D<signed char>::f(0.0) == 0);

0 comments on commit ede52d5

Please sign in to comment.