mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-24 06:10:12 +00:00
[Clang][Sema] Start fixing handling of out-of-line definitions of constrained templates
This diff starts fixing our handling of out-of-line definitions of constrained templates. Initially it was motivated by https://github.com/llvm/llvm-project/issues/49620 and https://github.com/llvm/llvm-project/issues/60231. In particular, this diff adjusts Sema::computeDeclContext to work properly in the case of constrained template parameters. Test plan: 1/ ninja check-all 2/ Bootstrapped Clang passes all the tests 3/ Internal testing Differential revision: https://reviews.llvm.org/D145034
This commit is contained in:
parent
bf00eda69f
commit
421c098b32
@ -78,6 +78,8 @@ C++ Language Changes
|
||||
|
||||
C++20 Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
- Support for out-of-line definitions of constrained templates has been improved.
|
||||
This partially fixes `https://github.com/llvm/llvm-project/issues/49620`.
|
||||
|
||||
C++2b Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -2513,7 +2513,8 @@ private:
|
||||
/// this is a constructor declarator.
|
||||
bool isConstructorDeclarator(
|
||||
bool Unqualified, bool DeductionGuide = false,
|
||||
DeclSpec::FriendSpecified IsFriend = DeclSpec::FriendSpecified::No);
|
||||
DeclSpec::FriendSpecified IsFriend = DeclSpec::FriendSpecified::No,
|
||||
const ParsedTemplateInfo *TemplateInfo = nullptr);
|
||||
|
||||
/// Specifies the context in which type-id/expression
|
||||
/// disambiguation will occur.
|
||||
|
@ -62,9 +62,18 @@ namespace clang {
|
||||
/// often used as if it meant "present".
|
||||
///
|
||||
/// The actual scope is described by getScopeRep().
|
||||
///
|
||||
/// If the kind of getScopeRep() is TypeSpec then TemplateParamLists may be empty
|
||||
/// or contain the template parameter lists attached to the current declaration.
|
||||
/// Consider the following example:
|
||||
/// template <class T> void SomeType<T>::some_method() {}
|
||||
/// If CXXScopeSpec refers to SomeType<T> then TemplateParamLists will contain
|
||||
/// a single element referring to template <class T>.
|
||||
|
||||
class CXXScopeSpec {
|
||||
SourceRange Range;
|
||||
NestedNameSpecifierLocBuilder Builder;
|
||||
ArrayRef<TemplateParameterList *> TemplateParamLists;
|
||||
|
||||
public:
|
||||
SourceRange getRange() const { return Range; }
|
||||
@ -74,6 +83,13 @@ public:
|
||||
SourceLocation getBeginLoc() const { return Range.getBegin(); }
|
||||
SourceLocation getEndLoc() const { return Range.getEnd(); }
|
||||
|
||||
void setTemplateParamLists(ArrayRef<TemplateParameterList *> L) {
|
||||
TemplateParamLists = L;
|
||||
}
|
||||
ArrayRef<TemplateParameterList *> getTemplateParamLists() const {
|
||||
return TemplateParamLists;
|
||||
}
|
||||
|
||||
/// Retrieve the representation of the nested-name-specifier.
|
||||
NestedNameSpecifier *getScopeRep() const {
|
||||
return Builder.getRepresentation();
|
||||
|
@ -3380,6 +3380,8 @@ void Parser::ParseDeclarationSpecifiers(
|
||||
goto DoneWithDeclSpec;
|
||||
|
||||
CXXScopeSpec SS;
|
||||
if (TemplateInfo.TemplateParams)
|
||||
SS.setTemplateParamLists(*TemplateInfo.TemplateParams);
|
||||
Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
|
||||
Tok.getAnnotationRange(),
|
||||
SS);
|
||||
@ -3475,7 +3477,8 @@ void Parser::ParseDeclarationSpecifiers(
|
||||
&SS) &&
|
||||
isConstructorDeclarator(/*Unqualified=*/false,
|
||||
/*DeductionGuide=*/false,
|
||||
DS.isFriendSpecified()))
|
||||
DS.isFriendSpecified(),
|
||||
&TemplateInfo))
|
||||
goto DoneWithDeclSpec;
|
||||
|
||||
// C++20 [temp.spec] 13.9/6.
|
||||
@ -4957,6 +4960,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
|
||||
assert(TemplateInfo.TemplateParams && "no template parameters");
|
||||
TParams = MultiTemplateParamsArg(TemplateInfo.TemplateParams->data(),
|
||||
TemplateInfo.TemplateParams->size());
|
||||
SS.setTemplateParamLists(TParams);
|
||||
}
|
||||
|
||||
if (!Name && TUK != Sema::TUK_Definition) {
|
||||
@ -5679,11 +5683,15 @@ bool Parser::isDeclarationSpecifier(
|
||||
}
|
||||
|
||||
bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
|
||||
DeclSpec::FriendSpecified IsFriend) {
|
||||
DeclSpec::FriendSpecified IsFriend,
|
||||
const ParsedTemplateInfo *TemplateInfo) {
|
||||
TentativeParsingAction TPA(*this);
|
||||
|
||||
// Parse the C++ scope specifier.
|
||||
CXXScopeSpec SS;
|
||||
if (TemplateInfo && TemplateInfo->TemplateParams)
|
||||
SS.setTemplateParamLists(*TemplateInfo->TemplateParams);
|
||||
|
||||
if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
|
||||
/*ObjectHasErrors=*/false,
|
||||
/*EnteringContext=*/true)) {
|
||||
@ -6075,6 +6083,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
|
||||
bool EnteringContext = D.getContext() == DeclaratorContext::File ||
|
||||
D.getContext() == DeclaratorContext::Member;
|
||||
CXXScopeSpec SS;
|
||||
SS.setTemplateParamLists(D.getTemplateParameterLists());
|
||||
ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/nullptr,
|
||||
/*ObjectHasErrors=*/false, EnteringContext);
|
||||
|
||||
|
@ -1676,6 +1676,9 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
|
||||
ColonProtectionRAIIObject X(*this);
|
||||
|
||||
CXXScopeSpec Spec;
|
||||
if (TemplateInfo.TemplateParams)
|
||||
Spec.setTemplateParamLists(*TemplateInfo.TemplateParams);
|
||||
|
||||
bool HasValidSpec = true;
|
||||
if (ParseOptionalCXXScopeSpecifier(Spec, /*ObjectType=*/nullptr,
|
||||
/*ObjectHasErrors=*/false,
|
||||
|
@ -99,34 +99,52 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
|
||||
if (ClassTemplateDecl *ClassTemplate
|
||||
= dyn_cast_or_null<ClassTemplateDecl>(
|
||||
SpecType->getTemplateName().getAsTemplateDecl())) {
|
||||
QualType ContextType
|
||||
= Context.getCanonicalType(QualType(SpecType, 0));
|
||||
QualType ContextType =
|
||||
Context.getCanonicalType(QualType(SpecType, 0));
|
||||
|
||||
// FIXME: The fallback on the search of partial
|
||||
// specialization using ContextType should be eventually removed since
|
||||
// it doesn't handle the case of constrained template parameters
|
||||
// correctly. Currently removing this fallback would change the
|
||||
// diagnostic output for invalid code in a number of tests.
|
||||
ClassTemplatePartialSpecializationDecl *PartialSpec = nullptr;
|
||||
ArrayRef<TemplateParameterList *> TemplateParamLists =
|
||||
SS.getTemplateParamLists();
|
||||
if (!TemplateParamLists.empty()) {
|
||||
unsigned Depth = ClassTemplate->getTemplateParameters()->getDepth();
|
||||
auto L = find_if(TemplateParamLists,
|
||||
[Depth](TemplateParameterList *TPL) {
|
||||
return TPL->getDepth() == Depth;
|
||||
});
|
||||
if (L != TemplateParamLists.end()) {
|
||||
void *Pos = nullptr;
|
||||
PartialSpec = ClassTemplate->findPartialSpecialization(
|
||||
SpecType->template_arguments(), *L, Pos);
|
||||
}
|
||||
} else {
|
||||
PartialSpec = ClassTemplate->findPartialSpecialization(ContextType);
|
||||
}
|
||||
|
||||
if (PartialSpec) {
|
||||
// A declaration of the partial specialization must be visible.
|
||||
// We can always recover here, because this only happens when we're
|
||||
// entering the context, and that can't happen in a SFINAE context.
|
||||
assert(!isSFINAEContext() && "partial specialization scope "
|
||||
"specifier in SFINAE context?");
|
||||
if (!hasReachableDefinition(PartialSpec))
|
||||
diagnoseMissingImport(SS.getLastQualifierNameLoc(), PartialSpec,
|
||||
MissingImportKind::PartialSpecialization,
|
||||
true);
|
||||
return PartialSpec;
|
||||
}
|
||||
|
||||
// If the type of the nested name specifier is the same as the
|
||||
// injected class name of the named class template, we're entering
|
||||
// into that class template definition.
|
||||
QualType Injected
|
||||
= ClassTemplate->getInjectedClassNameSpecialization();
|
||||
QualType Injected =
|
||||
ClassTemplate->getInjectedClassNameSpecialization();
|
||||
if (Context.hasSameType(Injected, ContextType))
|
||||
return ClassTemplate->getTemplatedDecl();
|
||||
|
||||
// If the type of the nested name specifier is the same as the
|
||||
// type of one of the class template's class template partial
|
||||
// specializations, we're entering into the definition of that
|
||||
// class template partial specialization.
|
||||
if (ClassTemplatePartialSpecializationDecl *PartialSpec
|
||||
= ClassTemplate->findPartialSpecialization(ContextType)) {
|
||||
// A declaration of the partial specialization must be visible.
|
||||
// We can always recover here, because this only happens when we're
|
||||
// entering the context, and that can't happen in a SFINAE context.
|
||||
assert(!isSFINAEContext() &&
|
||||
"partial specialization scope specifier in SFINAE context?");
|
||||
if (!hasReachableDefinition(PartialSpec))
|
||||
diagnoseMissingImport(SS.getLastQualifierNameLoc(), PartialSpec,
|
||||
MissingImportKind::PartialSpecialization,
|
||||
/*Recover*/true);
|
||||
return PartialSpec;
|
||||
}
|
||||
}
|
||||
} else if (const RecordType *RecordT = NNSType->getAs<RecordType>()) {
|
||||
// The nested name specifier refers to a member of a class template.
|
||||
|
@ -3,7 +3,7 @@
|
||||
template<typename T, int N>
|
||||
struct A;
|
||||
|
||||
template<typename T> // expected-note{{previous template declaration}}
|
||||
template<typename T>
|
||||
struct A<T*, 2> {
|
||||
void f0();
|
||||
void f1();
|
||||
@ -15,11 +15,10 @@ struct A<int, 1> {
|
||||
void g0();
|
||||
};
|
||||
|
||||
// FIXME: We should probably give more precise diagnostics here, but the
|
||||
// diagnostics we give aren't terrible.
|
||||
// FIXME: why not point to the first parameter that's "too many"?
|
||||
template<typename T, int N> // expected-error{{too many template parameters}}
|
||||
void A<T*, 2>::f0() { }
|
||||
// FIXME: We should produce diagnostics pointing out the
|
||||
// non-matching candidates.
|
||||
template<typename T, int N>
|
||||
void A<T*, 2>::f0() { } // expected-error{{does not refer into a class, class template or class template partial specialization}}
|
||||
|
||||
template<typename T, int N>
|
||||
void A<T, N>::f1() { } // expected-error{{out-of-line definition}}
|
||||
|
129
clang/test/SemaTemplate/concepts-out-of-line-def.cpp
Normal file
129
clang/test/SemaTemplate/concepts-out-of-line-def.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
// RUN: %clang_cc1 -std=c++20 -verify %s
|
||||
// expected-no-diagnostics
|
||||
|
||||
static constexpr int PRIMARY = 0;
|
||||
static constexpr int SPECIALIZATION_CONCEPT = 1;
|
||||
static constexpr int SPECIALIZATION_REQUIRES = 2;
|
||||
|
||||
template <class T>
|
||||
concept Concept = (sizeof(T) >= 2 * sizeof(int));
|
||||
|
||||
struct XY {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
namespace members {
|
||||
|
||||
template <class T, class U> struct S {
|
||||
static constexpr int primary();
|
||||
};
|
||||
|
||||
template <class T, class U> constexpr int S<T, U>::primary() {
|
||||
return PRIMARY;
|
||||
};
|
||||
|
||||
template <Concept C, class U> struct S<C, U> {
|
||||
static constexpr int specialization();
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
requires(sizeof(T) == sizeof(int))
|
||||
struct S<T, U> {
|
||||
static constexpr int specialization();
|
||||
};
|
||||
|
||||
template <Concept C, class U> constexpr int S<C, U>::specialization() {
|
||||
return SPECIALIZATION_CONCEPT;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
requires(sizeof(T) == sizeof(int))
|
||||
constexpr int S<T, U>::specialization() {
|
||||
return SPECIALIZATION_REQUIRES;
|
||||
}
|
||||
|
||||
static_assert(S<char, double>::primary() == PRIMARY);
|
||||
static_assert(S<XY, double>::specialization() == SPECIALIZATION_CONCEPT);
|
||||
static_assert(S<int, double>::specialization() == SPECIALIZATION_REQUIRES);
|
||||
|
||||
} // namespace members
|
||||
|
||||
namespace enumerations {
|
||||
|
||||
template <class T, class U> struct S {
|
||||
enum class E : int;
|
||||
};
|
||||
|
||||
template <class T, class U> enum class S<T, U>::E { Value = PRIMARY };
|
||||
|
||||
template <Concept C, class U> struct S<C, U> {
|
||||
enum class E : int;
|
||||
};
|
||||
|
||||
template <Concept C, class U>
|
||||
enum class S<C, U>::E {
|
||||
Value = SPECIALIZATION_CONCEPT
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
requires(sizeof(T) == sizeof(int))
|
||||
struct S<T, U> {
|
||||
enum class E : int;
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
requires(sizeof(T) == sizeof(int))
|
||||
enum class S<T, U>::E {
|
||||
Value = SPECIALIZATION_REQUIRES
|
||||
};
|
||||
|
||||
static_assert(static_cast<int>(S<char, double>::E::Value) == PRIMARY);
|
||||
static_assert(static_cast<int>(S<XY, double>::E::Value) ==
|
||||
SPECIALIZATION_CONCEPT);
|
||||
static_assert(static_cast<int>(S<int, double>::E::Value) ==
|
||||
SPECIALIZATION_REQUIRES);
|
||||
|
||||
} // namespace enumerations
|
||||
|
||||
namespace multiple_template_parameter_lists {
|
||||
|
||||
template <class Outer>
|
||||
struct S {
|
||||
template <class Inner>
|
||||
static constexpr int primary(Inner);
|
||||
};
|
||||
|
||||
template <class Outer>
|
||||
template <class Inner>
|
||||
constexpr int S<Outer>::primary(Inner) {
|
||||
return PRIMARY;
|
||||
};
|
||||
|
||||
template <Concept Outer>
|
||||
struct S<Outer> {
|
||||
template <class Inner>
|
||||
static constexpr int specialization(Inner);
|
||||
};
|
||||
|
||||
template <Concept Outer>
|
||||
template <class Inner>
|
||||
constexpr int S<Outer>::specialization(Inner) { return SPECIALIZATION_CONCEPT; }
|
||||
|
||||
template <class Outer>
|
||||
requires(sizeof(Outer) == sizeof(int))
|
||||
struct S<Outer> {
|
||||
template <class Inner>
|
||||
static constexpr int specialization(Inner);
|
||||
};
|
||||
|
||||
template <class Outer>
|
||||
requires(sizeof(Outer) == sizeof(int))
|
||||
template <class Inner>
|
||||
constexpr int S<Outer>::specialization(Inner) { return SPECIALIZATION_REQUIRES; }
|
||||
|
||||
static_assert(S<char>::primary("str") == PRIMARY);
|
||||
static_assert(S<XY>::specialization("str") == SPECIALIZATION_CONCEPT);
|
||||
static_assert(S<int>::specialization("str") == SPECIALIZATION_REQUIRES);
|
||||
|
||||
} // namespace multiple_template_parameter_lists
|
Loading…
Reference in New Issue
Block a user