aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Sema/SemaDecl.cpp
diff options
context:
space:
mode:
authorKrystian Stasiowski <sdkrystian@gmail.com>2024-01-30 08:28:13 -0500
committerGitHub <noreply@github.com>2024-01-30 08:28:13 -0500
commita0d266d705d6c145e8daa08a08f70e9498ec3d0b (patch)
treeef03372bb5f05229af5483962beefc6b2daaac07 /clang/lib/Sema/SemaDecl.cpp
parent6251b6bd8d219fe2d99d125095622566721fe6f4 (diff)
downloadllvm-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.cpp46
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;