Skip to content

Commit

Permalink
[Clang][Sema] Diagnose declarative nested-name-specifiers naming alia…
Browse files Browse the repository at this point in the history
…s templates (#80842)

According to [expr.prim.id.qual] p3:
> The _nested-name-specifier_ `​::` nominates the global namespace. A
_nested-name-specifier_ with a _computed-type-specifier_ nominates the
type denoted by the _computed-type-specifier_, which shall be a class or
enumeration type. **If a _nested-name-specifier_ `N` is declarative and
has a _simple-template-id_ with a template argument list `A` that
involves a template parameter, let `T` be the template nominated by `N`
without `A`. `T` shall be a class template.**

Meaning, the out-of-line definition of `A::f` in the following example
is ill-formed:
```
template<typename T>
struct A 
{ 
    void f(); 
};

template<typename T>
using B = A<T>;

template<typename T>
void B<T>::f() { } // error: a declarative nested name specifier cannot name an alias template
```

This patch diagnoses such cases as an extension (in group `alias-template-in-declaration-name`).
  • Loading branch information
sdkrystian committed Feb 20, 2024
1 parent c625b99 commit fb615cf
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 11 deletions.
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ Improvements to Clang's diagnostics

- Clang now diagnoses extraneous template parameter lists as a language extension.

- Clang now diagnoses declarative nested name specifiers that name alias templates.

Improvements to Clang's time-trace
----------------------------------

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -8255,6 +8255,9 @@ def err_not_tag_in_scope : Error<
def ext_template_after_declarative_nns : ExtWarn<
"'template' cannot be used after a declarative nested name specifier">,
InGroup<DiagGroup<"template-in-declaration-name">>;
def ext_alias_template_in_declarative_nns : ExtWarn<
"a declarative nested name specifier cannot name an alias template">,
InGroup<DiagGroup<"alias-template-in-declaration-name">>;

def err_no_typeid_with_fno_rtti : Error<
"use of typeid requires -frtti">;
Expand Down
36 changes: 26 additions & 10 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6210,6 +6210,8 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
SourceLocation Loc,
TemplateIdAnnotation *TemplateId,
bool IsMemberSpecialization) {
assert(SS.isValid() && "diagnoseQualifiedDeclaration called for declaration "
"without nested-name-specifier");
DeclContext *Cur = CurContext;
while (isa<LinkageSpecDecl>(Cur) || isa<CapturedDecl>(Cur))
Cur = Cur->getParent();
Expand Down Expand Up @@ -6298,22 +6300,36 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec &SS, DeclContext *DC,
<< FixItHint::CreateRemoval(TemplateId->TemplateKWLoc);

NestedNameSpecifierLoc SpecLoc(SS.getScopeRep(), SS.location_data());
while (SpecLoc.getPrefix()) {
do {
if (SpecLoc.getNestedNameSpecifier()->getKind() ==
NestedNameSpecifier::TypeSpecWithTemplate)
Diag(Loc, diag::ext_template_after_declarative_nns)
<< FixItHint::CreateRemoval(
SpecLoc.getTypeLoc().getTemplateKeywordLoc());

SpecLoc = SpecLoc.getPrefix();
}
// C++11 [dcl.meaning]p1:
// [...] "The nested-name-specifier of the qualified declarator-id shall
// not begin with a decltype-specifer"
if (isa_and_nonnull<DecltypeType>(
SpecLoc.getNestedNameSpecifier()->getAsType()))
Diag(Loc, diag::err_decltype_in_declarator)
<< SpecLoc.getTypeLoc().getSourceRange();
if (const Type *T = SpecLoc.getNestedNameSpecifier()->getAsType()) {
if (const auto *TST = T->getAsAdjusted<TemplateSpecializationType>()) {
// C++23 [expr.prim.id.qual]p3:
// [...] If a nested-name-specifier N is declarative and has a
// simple-template-id with a template argument list A that involves a
// template parameter, let T be the template nominated by N without A.
// T shall be a class template.
if (TST->isDependentType() && TST->isTypeAlias())
Diag(Loc, diag::ext_alias_template_in_declarative_nns)
<< SpecLoc.getLocalSourceRange();
} else if (T->isDecltypeType()) {
// C++23 [expr.prim.id.qual]p2:
// [...] A declarative nested-name-specifier shall not have a
// decltype-specifier.
//
// FIXME: This wording appears to be defective as it does not forbid
// declarative nested-name-specifiers with pack-index-specifiers.
// See https://github.com/cplusplus/CWG/issues/499.
Diag(Loc, diag::err_decltype_in_declarator)
<< SpecLoc.getTypeLoc().getSourceRange();
}
}
} while ((SpecLoc = SpecLoc.getPrefix()));

return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: %clang_cc1 -verify %s

template<typename T>
struct A {
void f();
};

template<typename T>
using B = A<T>;

template<typename T>
void B<T>::f() { } // expected-warning {{a declarative nested name specifier cannot name an alias template}}

template<>
void B<int>::f() { } // ok, template argument list of simple-template-id doesn't involve template parameters

namespace N {

template<typename T>
struct D {
void f();
};

template<typename T>
using E = D<T>;
}

template<typename T>
void N::E<T>::f() { } // expected-warning {{a declarative nested name specifier cannot name an alias template}}
2 changes: 1 addition & 1 deletion clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ namespace Example2 {
void g();
};
template<class T> using B = A<T>;
template<class T> void B<T>::g() {} // ok.
template<class T> void B<T>::g() {} // // expected-warning {{a declarative nested name specifier cannot name an alias template}}
}

0 comments on commit fb615cf

Please sign in to comment.