Skip to content

[Clang][Sema] Diagnose declarative nested-name-specifiers naming alias templates #80842

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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}}
}