diff options
author | Krystian Stasiowski <sdkrystian@gmail.com> | 2024-01-30 08:28:13 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-30 08:28:13 -0500 |
commit | a0d266d705d6c145e8daa08a08f70e9498ec3d0b (patch) | |
tree | ef03372bb5f05229af5483962beefc6b2daaac07 /clang/lib/Sema/SemaDecl.cpp | |
parent | 6251b6bd8d219fe2d99d125095622566721fe6f4 (diff) | |
download | llvm-a0d266d705d6c145e8daa08a08f70e9498ec3d0b.zip llvm-a0d266d705d6c145e8daa08a08f70e9498ec3d0b.tar.gz llvm-a0d266d705d6c145e8daa08a08f70e9498ec3d0b.tar.bz2 |
[Clang][Sema] Allow elaborated-type-specifiers that declare member class template explict specializations (#78720)
According to [[dcl.type.elab]
p2](http://eel.is/c++draft/dcl.type.elab#2):
> If an
[elaborated-type-specifier](http://eel.is/c++draft/dcl.type.elab#nt:elaborated-type-specifier)
is the sole constituent of a declaration, the declaration is ill-formed
unless it is an explicit specialization, an explicit instantiation or it
has one of the following forms [...]
Consider the following:
```cpp
template<typename T>
struct A
{
template<typename U>
struct B;
};
template<>
template<typename U>
struct A<int>::B; // #1
```
The _elaborated-type-specifier_ at `#1` declares an explicit
specialization (which is itself a template). We currently (incorrectly)
reject this, and this PR fixes that.
I moved the point at which _elaborated-type-specifiers_ with
_nested-name-specifiers_ are diagnosed from `ParsedFreeStandingDeclSpec`
to `ActOnTag` for two reasons: `ActOnTag` isn't called for explicit
instantiations and partial/explicit specializations, and because it's
where we determine if a member specialization is being declared.
With respect to diagnostics, I am currently issuing the diagnostic
without marking the declaration as invalid or returning early, which
results in more diagnostics that I think is necessary. I would like
feedback regarding what the "correct" behavior should be here.
Diffstat (limited to 'clang/lib/Sema/SemaDecl.cpp')
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 46 |
1 files changed, 23 insertions, 23 deletions
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 9bf1e9d..c89c3c4 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -5207,25 +5207,6 @@ Decl *Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, return ActOnFriendTypeDecl(S, DS, TemplateParams); } - const CXXScopeSpec &SS = DS.getTypeSpecScope(); - bool IsExplicitSpecialization = - !TemplateParams.empty() && TemplateParams.back()->size() == 0; - if (Tag && SS.isNotEmpty() && !Tag->isCompleteDefinition() && - !IsExplicitInstantiation && !IsExplicitSpecialization && - !isa<ClassTemplatePartialSpecializationDecl>(Tag)) { - // Per C++ [dcl.type.elab]p1, a class declaration cannot have a - // nested-name-specifier unless it is an explicit instantiation - // or an explicit specialization. - // - // FIXME: We allow class template partial specializations here too, per the - // obvious intent of DR1819. - // - // Per C++ [dcl.enum]p1, an opaque-enum-declaration can't either. - Diag(SS.getBeginLoc(), diag::err_standalone_class_nested_name_specifier) - << GetDiagnosticTypeSpecifierID(DS) << SS.getRange(); - return nullptr; - } - // Track whether this decl-specifier declares anything. bool DeclaresAnything = true; @@ -17222,10 +17203,29 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc, // for non-C++ cases. if (TemplateParameterLists.size() > 0 || (SS.isNotEmpty() && TUK != TUK_Reference)) { - if (TemplateParameterList *TemplateParams = - MatchTemplateParametersToScopeSpecifier( - KWLoc, NameLoc, SS, nullptr, TemplateParameterLists, - TUK == TUK_Friend, isMemberSpecialization, Invalid)) { + TemplateParameterList *TemplateParams = + MatchTemplateParametersToScopeSpecifier( + KWLoc, NameLoc, SS, nullptr, TemplateParameterLists, + TUK == TUK_Friend, isMemberSpecialization, Invalid); + + // C++23 [dcl.type.elab] p2: + // If an elaborated-type-specifier is the sole constituent of a + // declaration, the declaration is ill-formed unless it is an explicit + // specialization, an explicit instantiation or it has one of the + // following forms: [...] + // C++23 [dcl.enum] p1: + // If the enum-head-name of an opaque-enum-declaration contains a + // nested-name-specifier, the declaration shall be an explicit + // specialization. + // + // FIXME: Class template partial specializations can be forward declared + // per CWG2213, but the resolution failed to allow qualified forward + // declarations. This is almost certainly unintentional, so we allow them. + if (TUK == TUK_Declaration && SS.isNotEmpty() && !isMemberSpecialization) + Diag(SS.getBeginLoc(), diag::err_standalone_class_nested_name_specifier) + << TypeWithKeyword::getTagTypeKindName(Kind) << SS.getRange(); + + if (TemplateParams) { if (Kind == TagTypeKind::Enum) { Diag(KWLoc, diag::err_enum_template); return true; |