[clang] [Clang][Sema] Handle invalid variable template specialization whose type depends on itself (PR #134522)
Yanzuo Liu via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 28 08:03:47 PDT 2025
https://github.com/zwuis updated https://github.com/llvm/llvm-project/pull/134522
>From ccab3dc1f18ffeda9acb07c0bd5f80f65cc788b9 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Sun, 6 Apr 2025 15:06:56 +0800
Subject: [PATCH 1/2] Handle invalid variable template specialization whose
type depends on itself
---
clang/docs/ReleaseNotes.rst | 2 ++
.../include/clang/Basic/DiagnosticSemaKinds.td | 3 +++
clang/lib/Sema/SemaTemplate.cpp | 7 +++++++
.../SemaTemplate/instantiate-var-template.cpp | 18 ++++++++++++++++++
4 files changed, 30 insertions(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5217e04b5e83f..9e0fe8a94729b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -414,6 +414,8 @@ Bug Fixes to C++ Support
- Clang now issues an error when placement new is used to modify a const-qualified variable
in a ``constexpr`` function. (#GH131432)
- Clang now emits a warning when class template argument deduction for alias templates is used in C++17. (#GH133806)
+- No longer crashes when instantiating invalid variable template specialization
+ whose type depends on itself. (#GH51347)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 393bfecf9a36b..0c1da40dba388 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5259,6 +5259,9 @@ def err_template_member_noparams : Error<
"extraneous 'template<>' in declaration of member %0">;
def err_template_tag_noparams : Error<
"extraneous 'template<>' in declaration of %0 %1">;
+def err_var_template_spec_type_depends_on_self : Error<
+ "the type of variable template specialization %0 declared with deduced type "
+ "%1 depends on itself">;
def warn_unqualified_call_to_std_cast_function : Warning<
"unqualified call to '%0'">, InGroup<DiagGroup<"unqualified-std-cast-call">>;
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 153f44f8ec67a..ce54dbbb3b9fe 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4377,6 +4377,13 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
if (VarTemplateSpecializationDecl *Spec =
Template->findSpecialization(CTAI.CanonicalConverted, InsertPos)) {
checkSpecializationReachability(TemplateNameLoc, Spec);
+ if (Spec->getType()->isUndeducedType()) {
+ // We are substituting the initializer of this variable template
+ // specialization.
+ Diag(TemplateNameLoc, diag::err_var_template_spec_type_depends_on_self)
+ << Spec << Spec->getType();
+ return true;
+ }
// If we already have a variable template specialization, return it.
return Spec;
}
diff --git a/clang/test/SemaTemplate/instantiate-var-template.cpp b/clang/test/SemaTemplate/instantiate-var-template.cpp
index 60d3bd3b59f53..34a1b9710814b 100644
--- a/clang/test/SemaTemplate/instantiate-var-template.cpp
+++ b/clang/test/SemaTemplate/instantiate-var-template.cpp
@@ -47,3 +47,21 @@ namespace InvalidInsertPos {
template<> int v<int, 0>;
int k = v<int, 500>;
}
+
+namespace GH51347 {
+ template <typename T>
+ auto p = p<T>; // expected-error {{the type of variable template specialization 'p<int>'}}
+
+ auto x = p<int>; // expected-note {{in instantiation of variable template specialization 'GH51347::p'}}
+}
+
+namespace GH97881_comment {
+ template <bool B>
+ auto g = sizeof(g<!B>);
+ // expected-error at -1 {{the type of variable template specialization 'g<false>'}}
+ // expected-note at -2 {{in instantiation of variable template specialization 'GH97881_comment::g'}}
+
+ void test() {
+ (void)sizeof(g<false>); // expected-note {{in instantiation of variable template specialization 'GH97881_comment::g'}}
+ }
+}
>From c23525e5ff672e679b76717033205e84d1c70c02 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Mon, 28 Apr 2025 23:03:20 +0800
Subject: [PATCH 2/2] Recognize things like current instantiation
---
.../clang/Basic/DiagnosticSemaKinds.td | 11 ++-
clang/lib/Sema/SemaExpr.cpp | 3 +-
clang/lib/Sema/SemaTemplate.cpp | 68 +++++++++++++++++--
.../cxx1y-variable-templates_top_level.cpp | 17 +++++
.../SemaTemplate/instantiate-var-template.cpp | 7 --
5 files changed, 90 insertions(+), 16 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 0c1da40dba388..5afd824fad4f1 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2495,9 +2495,14 @@ def note_implicit_deduction_guide : Note<"implicit deduction guide declared as '
def warn_cxx98_compat_auto_type_specifier : Warning<
"'auto' type specifier is incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;
-def err_auto_variable_cannot_appear_in_own_initializer : Error<
- "variable %0 declared with deduced type %1 "
- "cannot appear in its own initializer">;
+def err_auto_variable_cannot_appear_in_own_initializer
+ : Error<
+ "%enum_select<ParsingInitFor>{%Var{variable}|"
+ "%VarTemplate{variable template}|"
+ "%VarTemplatePartialSpec{variable template partial specialization}|"
+ "%VarTemplateExplicitSpec{variable template explicit "
+ "specialization}}0 %1 "
+ "declared with deduced type %2 cannot appear in its own initializer">;
def err_binding_cannot_appear_in_own_initializer : Error<
"binding %0 cannot appear in the initializer of its own "
"decomposition declaration">;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index e7f418ae6802e..82b581d7d1dc2 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -251,7 +251,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
<< D->getDeclName();
} else {
Diag(Loc, diag::err_auto_variable_cannot_appear_in_own_initializer)
- << D->getDeclName() << cast<VarDecl>(D)->getType();
+ << diag::ParsingInitFor::Var << D->getDeclName()
+ << cast<VarDecl>(D)->getType();
}
return true;
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index ce54dbbb3b9fe..2212d8eca9c39 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4348,6 +4348,23 @@ struct PartialSpecMatchResult {
VarTemplatePartialSpecializationDecl *Partial;
TemplateArgumentList *Args;
};
+
+struct TemplateArgEqualityComparator {
+ const ASTContext &Context;
+
+ bool operator()(const TemplateArgument &Canonical,
+ const TemplateArgument &Unknown) const {
+ llvm::FoldingSetNodeID ID1, ID2;
+ Canonical.Profile(ID1, Context);
+ Context.getCanonicalTemplateArgument(Unknown).Profile(ID2, Context);
+#ifndef NDEBUG
+ llvm::FoldingSetNodeID ID3;
+ Context.getCanonicalTemplateArgument(Canonical).Profile(ID3, Context);
+ assert(ID1 == ID3);
+#endif
+ return ID1 == ID2;
+ }
+};
} // end anonymous namespace
DeclResult
@@ -4368,8 +4385,40 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
// Produce a placeholder value if the specialization is dependent.
if (Template->getDeclContext()->isDependentContext() ||
TemplateSpecializationType::anyDependentTemplateArguments(
- TemplateArgs, CTAI.CanonicalConverted))
+ TemplateArgs, CTAI.CanonicalConverted)) {
+ if (ParsingInitForAutoVars.empty())
+ return DeclResult();
+
+ if (VarDecl *Var = Template->getTemplatedDecl();
+ ParsingInitForAutoVars.count(Var) &&
+ llvm::equal(
+ CTAI.CanonicalConverted,
+ Template->getTemplateParameters()->getInjectedTemplateArgs(Context),
+ TemplateArgEqualityComparator{Context})) {
+ Diag(TemplateNameLoc,
+ diag::err_auto_variable_cannot_appear_in_own_initializer)
+ << diag::ParsingInitFor::VarTemplate << Var << Var->getType()
+ << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc());
+ return true;
+ }
+
+ SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs;
+ Template->getPartialSpecializations(PartialSpecs);
+ for (VarTemplatePartialSpecializationDecl *Partial : PartialSpecs)
+ if (ParsingInitForAutoVars.count(Partial) &&
+ llvm::equal(CTAI.CanonicalConverted,
+ Partial->getTemplateArgs().asArray(),
+ TemplateArgEqualityComparator{Context})) {
+ Diag(TemplateNameLoc,
+ diag::err_auto_variable_cannot_appear_in_own_initializer)
+ << diag::ParsingInitFor::VarTemplatePartialSpec << Partial
+ << Partial->getType()
+ << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc());
+ return true;
+ }
+
return DeclResult();
+ }
// Find the variable template specialization declaration that
// corresponds to these arguments.
@@ -4378,10 +4427,19 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
Template->findSpecialization(CTAI.CanonicalConverted, InsertPos)) {
checkSpecializationReachability(TemplateNameLoc, Spec);
if (Spec->getType()->isUndeducedType()) {
- // We are substituting the initializer of this variable template
- // specialization.
- Diag(TemplateNameLoc, diag::err_var_template_spec_type_depends_on_self)
- << Spec << Spec->getType();
+ if (ParsingInitForAutoVars.count(Spec))
+ Diag(TemplateNameLoc,
+ diag::err_auto_variable_cannot_appear_in_own_initializer)
+ << diag::ParsingInitFor::VarTemplateExplicitSpec << Spec
+ << Spec->getType()
+ << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc());
+ else
+ // We are substituting the initializer of this variable template
+ // specialization.
+ Diag(TemplateNameLoc, diag::err_var_template_spec_type_depends_on_self)
+ << Spec << Spec->getType()
+ << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc());
+
return true;
}
// If we already have a variable template specialization, return it.
diff --git a/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp b/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
index 6fc2032ee7fb4..1fe0ce9aabf29 100644
--- a/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
+++ b/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
@@ -492,4 +492,21 @@ static_assert(C<int, 0,1,2,3,4>::VALUEARRAY[3] == 3, "");
static_assert(C<int, 0,1,2,3,4>::VALUEARRAY[0] == 0, "");
}
+
+namespace appear_in_its_own_init {
+template <class T>
+auto GH51347 = GH51347<T>; // expected-error {{variable template 'GH51347' declared with deduced type 'auto' cannot appear in its own initializer}}
+
+template <class T, class... Ts>
+auto a = [] {
+ using U = T;
+ a<U, Ts...>; // expected-error {{variable template 'a' declared with deduced type 'auto' cannot appear in its own initializer}}
+};
+
+template <int...> int b;
+template <int I>
+auto b<I, I * 2, 5> = b<I, I * 2, 5l>; // expected-error {{variable template partial specialization 'b<I, I * 2, 5>' declared with deduced type 'auto' cannot appear in its own initializer}}
+template <> auto b<0, 0, 0> = b<0, 0, 0>; // expected-error {{variable template explicit specialization 'b<0, 0, 0>' declared with deduced type 'auto' cannot appear in its own initializer}}
+}
+
#endif
diff --git a/clang/test/SemaTemplate/instantiate-var-template.cpp b/clang/test/SemaTemplate/instantiate-var-template.cpp
index 34a1b9710814b..50b7219af4bea 100644
--- a/clang/test/SemaTemplate/instantiate-var-template.cpp
+++ b/clang/test/SemaTemplate/instantiate-var-template.cpp
@@ -48,13 +48,6 @@ namespace InvalidInsertPos {
int k = v<int, 500>;
}
-namespace GH51347 {
- template <typename T>
- auto p = p<T>; // expected-error {{the type of variable template specialization 'p<int>'}}
-
- auto x = p<int>; // expected-note {{in instantiation of variable template specialization 'GH51347::p'}}
-}
-
namespace GH97881_comment {
template <bool B>
auto g = sizeof(g<!B>);
More information about the cfe-commits
mailing list