Skip to content

Commit c1d8d0a

Browse files
authored
Reapply "[Clang][Sema] Diagnose function/variable templates that shadow their own template parameters (#78274)" (#79683)
Reapplies #78274 with the addition of a default-error warning (`strict-primary-template-shadow`) that is issued for instances of shadowing which were previously accepted prior to this patch. I couldn't find an established convention for naming diagnostics related to compatibility with previous versions of clang, so I just used the prefix `ext_compat_`.
1 parent 1e429ff commit c1d8d0a

File tree

9 files changed

+88
-45
lines changed

9 files changed

+88
-45
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ C/C++ Language Potentially Breaking Changes
4242

4343
C++ Specific Potentially Breaking Changes
4444
-----------------------------------------
45+
- Clang now diagnoses function/variable templates that shadow their own template parameters, e.g. ``template<class T> void T();``.
46+
This error can be disabled via `-Wno-strict-primary-template-shadow` for compatibility with previous versions of clang.
4547

4648
ABI Changes in This Version
4749
---------------------------

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4977,6 +4977,9 @@ def err_template_param_shadow : Error<
49774977
"declaration of %0 shadows template parameter">;
49784978
def ext_template_param_shadow : ExtWarn<
49794979
err_template_param_shadow.Summary>, InGroup<MicrosoftTemplateShadow>;
4980+
def ext_compat_template_param_shadow : ExtWarn<
4981+
err_template_param_shadow.Summary>, InGroup<
4982+
DiagGroup<"strict-primary-template-shadow">>, DefaultError;
49804983
def note_template_param_here : Note<"template parameter is declared here">;
49814984
def note_template_param_external : Note<
49824985
"template parameter from hidden source: %0">;

clang/include/clang/Sema/Scope.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ class Scope {
206206
/// other template parameter scopes as parents.
207207
Scope *TemplateParamParent;
208208

209+
/// DeclScopeParent - This is a direct link to the immediately containing
210+
/// DeclScope, i.e. scope which can contain declarations.
211+
Scope *DeclParent;
212+
209213
/// DeclsInScope - This keeps track of all declarations in this scope. When
210214
/// the declaration is added to the scope, it is set as the current
211215
/// declaration for the identifier in the IdentifierTable. When the scope is
@@ -305,6 +309,9 @@ class Scope {
305309
Scope *getTemplateParamParent() { return TemplateParamParent; }
306310
const Scope *getTemplateParamParent() const { return TemplateParamParent; }
307311

312+
Scope *getDeclParent() { return DeclParent; }
313+
const Scope *getDeclParent() const { return DeclParent; }
314+
308315
/// Returns the depth of this scope. The translation-unit has scope depth 0.
309316
unsigned getDepth() const { return Depth; }
310317

clang/include/clang/Sema/Sema.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8386,7 +8386,21 @@ class Sema final {
83868386
TemplateSpecializationKind TSK,
83878387
bool Complain = true);
83888388

8389-
void DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl);
8389+
/// DiagnoseTemplateParameterShadow - Produce a diagnostic complaining
8390+
/// that the template parameter 'PrevDecl' is being shadowed by a new
8391+
/// declaration at location Loc. Returns true to indicate that this is
8392+
/// an error, and false otherwise.
8393+
///
8394+
/// \param Loc The location of the declaration that shadows a template
8395+
/// parameter.
8396+
///
8397+
/// \param PrevDecl The template parameter that the declaration shadows.
8398+
///
8399+
/// \param SupportedForCompatibility Whether to issue the diagnostic as
8400+
/// a warning for compatibility with older versions of clang.
8401+
/// Ignored when MSVC compatibility is enabled.
8402+
void DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl,
8403+
bool SupportedForCompatibility = false);
83908404
TemplateDecl *AdjustDeclIfTemplate(Decl *&Decl);
83918405

83928406
NamedDecl *ActOnTypeParameter(Scope *S, bool Typename,

clang/lib/Sema/Scope.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ void Scope::setFlags(Scope *parent, unsigned flags) {
3737
FnParent = parent->FnParent;
3838
BlockParent = parent->BlockParent;
3939
TemplateParamParent = parent->TemplateParamParent;
40+
DeclParent = parent->DeclParent;
4041
MSLastManglingParent = parent->MSLastManglingParent;
4142
MSCurManglingNumber = getMSLastManglingNumber();
4243
if ((Flags & (FnScope | ClassScope | BlockScope | TemplateParamScope |
@@ -52,6 +53,7 @@ void Scope::setFlags(Scope *parent, unsigned flags) {
5253
PrototypeIndex = 0;
5354
MSLastManglingParent = FnParent = BlockParent = nullptr;
5455
TemplateParamParent = nullptr;
56+
DeclParent = nullptr;
5557
MSLastManglingNumber = 1;
5658
MSCurManglingNumber = 1;
5759
}
@@ -76,6 +78,7 @@ void Scope::setFlags(Scope *parent, unsigned flags) {
7678
PrototypeDepth++;
7779

7880
if (flags & DeclScope) {
81+
DeclParent = this;
7982
if (flags & FunctionPrototypeScope)
8083
; // Prototype scopes are uninteresting.
8184
else if ((flags & ClassScope) && getParent()->isClassScope())

clang/lib/Sema/SemaDecl.cpp

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6353,12 +6353,6 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
63536353
} else if (DiagnoseUnexpandedParameterPack(NameInfo, UPPC_DeclarationType))
63546354
return nullptr;
63556355

6356-
// The scope passed in may not be a decl scope. Zip up the scope tree until
6357-
// we find one that is.
6358-
while ((S->getFlags() & Scope::DeclScope) == 0 ||
6359-
(S->getFlags() & Scope::TemplateParamScope) != 0)
6360-
S = S->getParent();
6361-
63626356
DeclContext *DC = CurContext;
63636357
if (D.getCXXScopeSpec().isInvalid())
63646358
D.setInvalidType();
@@ -6486,12 +6480,22 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
64866480
RemoveUsingDecls(Previous);
64876481
}
64886482

6489-
if (Previous.isSingleResult() &&
6490-
Previous.getFoundDecl()->isTemplateParameter()) {
6491-
// Maybe we will complain about the shadowed template parameter.
6492-
if (!D.isInvalidType())
6493-
DiagnoseTemplateParameterShadow(D.getIdentifierLoc(),
6494-
Previous.getFoundDecl());
6483+
if (auto *TPD = Previous.getAsSingle<NamedDecl>();
6484+
TPD && TPD->isTemplateParameter()) {
6485+
// Older versions of clang allowed the names of function/variable templates
6486+
// to shadow the names of their template parameters. For the compatibility
6487+
// purposes we detect such cases and issue a default-to-error warning that
6488+
// can be disabled with -Wno-strict-primary-template-shadow.
6489+
if (!D.isInvalidType()) {
6490+
bool AllowForCompatibility = false;
6491+
if (Scope *DeclParent = S->getDeclParent();
6492+
Scope *TemplateParamParent = S->getTemplateParamParent()) {
6493+
AllowForCompatibility = DeclParent->Contains(*TemplateParamParent) &&
6494+
TemplateParamParent->isDeclScope(TPD);
6495+
}
6496+
DiagnoseTemplateParameterShadow(D.getIdentifierLoc(), TPD,
6497+
AllowForCompatibility);
6498+
}
64956499

64966500
// Just pretend that we didn't see the previous declaration.
64976501
Previous.clear();
@@ -6515,6 +6519,9 @@ NamedDecl *Sema::HandleDeclarator(Scope *S, Declarator &D,
65156519
if (getLangOpts().CPlusPlus)
65166520
CheckExtraCXXDefaultArguments(D);
65176521

6522+
/// Get the innermost enclosing declaration scope.
6523+
S = S->getDeclParent();
6524+
65186525
NamedDecl *New;
65196526

65206527
bool AddToScope = true;

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12203,10 +12203,8 @@ Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc,
1220312203
assert(NamespcName && "Invalid NamespcName.");
1220412204
assert(IdentLoc.isValid() && "Invalid NamespceName location.");
1220512205

12206-
// This can only happen along a recovery path.
12207-
while (S->isTemplateParamScope())
12208-
S = S->getParent();
12209-
assert(S->getFlags() & Scope::DeclScope && "Invalid Scope.");
12206+
// Get the innermost enclosing declaration scope.
12207+
S = S->getDeclParent();
1221012208

1221112209
UsingDirectiveDecl *UDir = nullptr;
1221212210
NestedNameSpecifier *Qualifier = nullptr;
@@ -13516,11 +13514,8 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS,
1351613514
SourceLocation UsingLoc, UnqualifiedId &Name,
1351713515
const ParsedAttributesView &AttrList,
1351813516
TypeResult Type, Decl *DeclFromDeclSpec) {
13519-
// Skip up to the relevant declaration scope.
13520-
while (S->isTemplateParamScope())
13521-
S = S->getParent();
13522-
assert((S->getFlags() & Scope::DeclScope) &&
13523-
"got alias-declaration outside of declaration scope");
13517+
// Get the innermost enclosing declaration scope.
13518+
S = S->getDeclParent();
1352413519

1352513520
if (Type.isInvalid())
1352613521
return nullptr;

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -881,20 +881,23 @@ bool Sema::DiagnoseUninstantiableTemplate(SourceLocation PointOfInstantiation,
881881
return true;
882882
}
883883

884-
/// DiagnoseTemplateParameterShadow - Produce a diagnostic complaining
885-
/// that the template parameter 'PrevDecl' is being shadowed by a new
886-
/// declaration at location Loc. Returns true to indicate that this is
887-
/// an error, and false otherwise.
888-
void Sema::DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl) {
884+
void Sema::DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl,
885+
bool SupportedForCompatibility) {
889886
assert(PrevDecl->isTemplateParameter() && "Not a template parameter");
890887

891-
// C++ [temp.local]p4:
892-
// A template-parameter shall not be redeclared within its
893-
// scope (including nested scopes).
888+
// C++23 [temp.local]p6:
889+
// The name of a template-parameter shall not be bound to any following.
890+
// declaration whose locus is contained by the scope to which the
891+
// template-parameter belongs.
894892
//
895-
// Make this a warning when MSVC compatibility is requested.
896-
unsigned DiagId = getLangOpts().MSVCCompat ? diag::ext_template_param_shadow
897-
: diag::err_template_param_shadow;
893+
// When MSVC compatibility is enabled, the diagnostic is always a warning
894+
// by default. Otherwise, it an error unless SupportedForCompatibility is
895+
// true, in which case it is a default-to-error warning.
896+
unsigned DiagId =
897+
getLangOpts().MSVCCompat
898+
? diag::ext_template_param_shadow
899+
: (SupportedForCompatibility ? diag::ext_compat_template_param_shadow
900+
: diag::err_template_param_shadow);
898901
const auto *ND = cast<NamedDecl>(PrevDecl);
899902
Diag(Loc, DiagId) << ND->getDeclName();
900903
NoteTemplateParameterLocation(*ND);
@@ -8502,9 +8505,7 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) {
85028505
return false;
85038506

85048507
// Find the nearest enclosing declaration scope.
8505-
while ((S->getFlags() & Scope::DeclScope) == 0 ||
8506-
(S->getFlags() & Scope::TemplateParamScope) != 0)
8507-
S = S->getParent();
8508+
S = S->getDeclParent();
85088509

85098510
// C++ [temp.pre]p6: [P2096]
85108511
// A template, explicit specialization, or partial specialization shall not
@@ -10619,11 +10620,8 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
1061910620
return true;
1062010621
}
1062110622

10622-
// The scope passed in may not be a decl scope. Zip up the scope tree until
10623-
// we find one that is.
10624-
while ((S->getFlags() & Scope::DeclScope) == 0 ||
10625-
(S->getFlags() & Scope::TemplateParamScope) != 0)
10626-
S = S->getParent();
10623+
// Get the innermost enclosing declaration scope.
10624+
S = S->getDeclParent();
1062710625

1062810626
// Determine the type of the declaration.
1062910627
TypeSourceInfo *T = GetTypeForDeclarator(D);

clang/test/CXX/temp/temp.res/temp.local/p6.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -verify %s -fcxx-exceptions -std=c++1y
1+
// RUN: %clang_cc1 -verify %s -fcxx-exceptions -std=c++1y -Wno-error=strict-primary-template-shadow
22

33
namespace N {}
44

@@ -127,16 +127,30 @@ template<int T> struct Z { // expected-note 16{{declared here}}
127127
template<typename T> // expected-note {{declared here}}
128128
void f(int T) {} // expected-error {{declaration of 'T' shadows template parameter}}
129129

130-
// FIXME: These are ill-formed: a template-parameter shall not have the same name as the template name.
131130
namespace A {
132131
template<typename T> struct T {}; // expected-error{{declaration of 'T' shadows template parameter}}
133132
// expected-note@-1{{template parameter is declared here}}
133+
template<typename T> struct U {
134+
template<typename V> struct V {}; // expected-error{{declaration of 'V' shadows template parameter}}
135+
// expected-note@-1{{template parameter is declared here}}
136+
};
134137
}
135138
namespace B {
136-
template<typename T> void T() {}
139+
template<typename T> void T() {} // expected-warning{{declaration of 'T' shadows template parameter}}
140+
// expected-note@-1{{template parameter is declared here}}
141+
142+
template<typename T> struct U {
143+
template<typename V> void V(); // expected-warning{{declaration of 'V' shadows template parameter}}
144+
// expected-note@-1{{template parameter is declared here}}
145+
};
137146
}
138147
namespace C {
139-
template<typename T> int T;
148+
template<typename T> int T; // expected-warning{{declaration of 'T' shadows template parameter}}
149+
// expected-note@-1{{template parameter is declared here}}
150+
template<typename T> struct U {
151+
template<typename V> static int V; // expected-warning{{declaration of 'V' shadows template parameter}}
152+
// expected-note@-1{{template parameter is declared here}}
153+
};
140154
}
141155

142156
namespace PR28023 {

0 commit comments

Comments
 (0)