Skip to content

Commit 91f1830

Browse files
authored
[Clang][Sema] Handle invalid variable template specialization whose type depends on itself (#134522)
1 parent 5936c02 commit 91f1830

10 files changed

+170
-85
lines changed

clang/docs/ReleaseNotes.rst

+2
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,8 @@ Bug Fixes to C++ Support
658658
referred to a reference to an incomplete type. (#GH129397)
659659
- Fixed a crash when a cast involved a parenthesized aggregate initialization in dependent context. (#GH72880)
660660
- Fixed a crash when forming an invalid function type in a dependent context. (#GH138657) (#GH115725) (#GH68852)
661+
- No longer crashes when instantiating invalid variable template specialization
662+
whose type depends on itself. (#GH51347), (#GH55872)
661663

662664
Bug Fixes to AST Handling
663665
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/ASTContext.h

+5
Original file line numberDiff line numberDiff line change
@@ -2970,6 +2970,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
29702970
TemplateTemplateParmDecl *insertCanonicalTemplateTemplateParmDeclInternal(
29712971
TemplateTemplateParmDecl *CanonTTP) const;
29722972

2973+
/// Determine whether the given template arguments \p Arg1 and \p Arg2 are
2974+
/// equivalent.
2975+
bool isSameTemplateArgument(const TemplateArgument &Arg1,
2976+
const TemplateArgument &Arg2) const;
2977+
29732978
/// Type Query functions. If the type is an instance of the specified class,
29742979
/// return the Type pointer for the underlying maximally pretty type. This
29752980
/// is a member of ASTContext because this may need to do some amount of

clang/include/clang/Basic/DiagnosticSemaKinds.td

+11-3
Original file line numberDiff line numberDiff line change
@@ -2526,9 +2526,14 @@ def note_implicit_deduction_guide : Note<"implicit deduction guide declared as '
25262526
def warn_cxx98_compat_auto_type_specifier : Warning<
25272527
"'auto' type specifier is incompatible with C++98">,
25282528
InGroup<CXX98Compat>, DefaultIgnore;
2529-
def err_auto_variable_cannot_appear_in_own_initializer : Error<
2530-
"variable %0 declared with deduced type %1 "
2531-
"cannot appear in its own initializer">;
2529+
def err_auto_variable_cannot_appear_in_own_initializer
2530+
: Error<
2531+
"%enum_select<ParsingInitFor>{%Var{variable}|"
2532+
"%VarTemplate{variable template}|"
2533+
"%VarTemplatePartialSpec{variable template partial specialization}|"
2534+
"%VarTemplateExplicitSpec{variable template explicit "
2535+
"specialization}}0 %1 "
2536+
"declared with deduced type %2 cannot appear in its own initializer">;
25322537
def err_binding_cannot_appear_in_own_initializer : Error<
25332538
"binding %0 cannot appear in the initializer of its own "
25342539
"decomposition declaration">;
@@ -5302,6 +5307,9 @@ def err_template_member_noparams : Error<
53025307
"extraneous 'template<>' in declaration of member %0">;
53035308
def err_template_tag_noparams : Error<
53045309
"extraneous 'template<>' in declaration of %0 %1">;
5310+
def err_var_template_spec_type_depends_on_self : Error<
5311+
"the type of variable template specialization %0 declared with deduced type "
5312+
"%1 depends on itself">;
53055313

53065314
def warn_unqualified_call_to_std_cast_function : Warning<
53075315
"unqualified call to '%0'">, InGroup<DiagGroup<"unqualified-std-cast-call">>;

clang/lib/AST/ASTContext.cpp

+49
Original file line numberDiff line numberDiff line change
@@ -7660,6 +7660,55 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const {
76607660
llvm_unreachable("Unhandled template argument kind");
76617661
}
76627662

7663+
bool ASTContext::isSameTemplateArgument(const TemplateArgument &Arg1,
7664+
const TemplateArgument &Arg2) const {
7665+
if (Arg1.getKind() != Arg2.getKind())
7666+
return false;
7667+
7668+
switch (Arg1.getKind()) {
7669+
case TemplateArgument::Null:
7670+
llvm_unreachable("Comparing NULL template argument");
7671+
7672+
case TemplateArgument::Type:
7673+
return hasSameType(Arg1.getAsType(), Arg2.getAsType());
7674+
7675+
case TemplateArgument::Declaration:
7676+
return Arg1.getAsDecl()->getUnderlyingDecl()->getCanonicalDecl() ==
7677+
Arg2.getAsDecl()->getUnderlyingDecl()->getCanonicalDecl();
7678+
7679+
case TemplateArgument::NullPtr:
7680+
return hasSameType(Arg1.getNullPtrType(), Arg2.getNullPtrType());
7681+
7682+
case TemplateArgument::Template:
7683+
case TemplateArgument::TemplateExpansion:
7684+
return getCanonicalTemplateName(Arg1.getAsTemplateOrTemplatePattern()) ==
7685+
getCanonicalTemplateName(Arg2.getAsTemplateOrTemplatePattern());
7686+
7687+
case TemplateArgument::Integral:
7688+
return llvm::APSInt::isSameValue(Arg1.getAsIntegral(),
7689+
Arg2.getAsIntegral());
7690+
7691+
case TemplateArgument::StructuralValue:
7692+
return Arg1.structurallyEquals(Arg2);
7693+
7694+
case TemplateArgument::Expression: {
7695+
llvm::FoldingSetNodeID ID1, ID2;
7696+
Arg1.getAsExpr()->Profile(ID1, *this, /*Canonical=*/true);
7697+
Arg2.getAsExpr()->Profile(ID2, *this, /*Canonical=*/true);
7698+
return ID1 == ID2;
7699+
}
7700+
7701+
case TemplateArgument::Pack:
7702+
return llvm::equal(
7703+
Arg1.getPackAsArray(), Arg2.getPackAsArray(),
7704+
[&](const TemplateArgument &Arg1, const TemplateArgument &Arg2) {
7705+
return isSameTemplateArgument(Arg1, Arg2);
7706+
});
7707+
}
7708+
7709+
llvm_unreachable("Unhandled template argument kind");
7710+
}
7711+
76637712
NestedNameSpecifier *
76647713
ASTContext::getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS) const {
76657714
if (!NNS)

clang/lib/Sema/SemaExpr.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
251251
<< D->getDeclName();
252252
} else {
253253
Diag(Loc, diag::err_auto_variable_cannot_appear_in_own_initializer)
254-
<< D->getDeclName() << cast<VarDecl>(D)->getType();
254+
<< diag::ParsingInitFor::Var << D->getDeclName()
255+
<< cast<VarDecl>(D)->getType();
255256
}
256257
return true;
257258
}

clang/lib/Sema/SemaTemplate.cpp

+50-1
Original file line numberDiff line numberDiff line change
@@ -4372,15 +4372,64 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
43724372
// Produce a placeholder value if the specialization is dependent.
43734373
if (Template->getDeclContext()->isDependentContext() ||
43744374
TemplateSpecializationType::anyDependentTemplateArguments(
4375-
TemplateArgs, CTAI.CanonicalConverted))
4375+
TemplateArgs, CTAI.CanonicalConverted)) {
4376+
if (ParsingInitForAutoVars.empty())
4377+
return DeclResult();
4378+
4379+
auto IsSameTemplateArg = [&](const TemplateArgument &Arg1,
4380+
const TemplateArgument &Arg2) {
4381+
return Context.isSameTemplateArgument(Arg1, Arg2);
4382+
};
4383+
4384+
if (VarDecl *Var = Template->getTemplatedDecl();
4385+
ParsingInitForAutoVars.count(Var) &&
4386+
llvm::equal(
4387+
CTAI.CanonicalConverted,
4388+
Template->getTemplateParameters()->getInjectedTemplateArgs(Context),
4389+
IsSameTemplateArg)) {
4390+
Diag(TemplateNameLoc,
4391+
diag::err_auto_variable_cannot_appear_in_own_initializer)
4392+
<< diag::ParsingInitFor::VarTemplate << Var << Var->getType();
4393+
return true;
4394+
}
4395+
4396+
SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs;
4397+
Template->getPartialSpecializations(PartialSpecs);
4398+
for (VarTemplatePartialSpecializationDecl *Partial : PartialSpecs)
4399+
if (ParsingInitForAutoVars.count(Partial) &&
4400+
llvm::equal(CTAI.CanonicalConverted,
4401+
Partial->getTemplateArgs().asArray(),
4402+
IsSameTemplateArg)) {
4403+
Diag(TemplateNameLoc,
4404+
diag::err_auto_variable_cannot_appear_in_own_initializer)
4405+
<< diag::ParsingInitFor::VarTemplatePartialSpec << Partial
4406+
<< Partial->getType();
4407+
return true;
4408+
}
4409+
43764410
return DeclResult();
4411+
}
43774412

43784413
// Find the variable template specialization declaration that
43794414
// corresponds to these arguments.
43804415
void *InsertPos = nullptr;
43814416
if (VarTemplateSpecializationDecl *Spec =
43824417
Template->findSpecialization(CTAI.CanonicalConverted, InsertPos)) {
43834418
checkSpecializationReachability(TemplateNameLoc, Spec);
4419+
if (Spec->getType()->isUndeducedType()) {
4420+
if (ParsingInitForAutoVars.count(Spec))
4421+
Diag(TemplateNameLoc,
4422+
diag::err_auto_variable_cannot_appear_in_own_initializer)
4423+
<< diag::ParsingInitFor::VarTemplateExplicitSpec << Spec
4424+
<< Spec->getType();
4425+
else
4426+
// We are substituting the initializer of this variable template
4427+
// specialization.
4428+
Diag(TemplateNameLoc, diag::err_var_template_spec_type_depends_on_self)
4429+
<< Spec << Spec->getType();
4430+
4431+
return true;
4432+
}
43844433
// If we already have a variable template specialization, return it.
43854434
return Spec;
43864435
}

clang/lib/Sema/SemaTemplateDeduction.cpp

+3-80
Original file line numberDiff line numberDiff line change
@@ -114,27 +114,6 @@ namespace clang {
114114
using namespace clang;
115115
using namespace sema;
116116

117-
/// Compare two APSInts, extending and switching the sign as
118-
/// necessary to compare their values regardless of underlying type.
119-
static bool hasSameExtendedValue(llvm::APSInt X, llvm::APSInt Y) {
120-
if (Y.getBitWidth() > X.getBitWidth())
121-
X = X.extend(Y.getBitWidth());
122-
else if (Y.getBitWidth() < X.getBitWidth())
123-
Y = Y.extend(X.getBitWidth());
124-
125-
// If there is a signedness mismatch, correct it.
126-
if (X.isSigned() != Y.isSigned()) {
127-
// If the signed value is negative, then the values cannot be the same.
128-
if ((Y.isSigned() && Y.isNegative()) || (X.isSigned() && X.isNegative()))
129-
return false;
130-
131-
Y.setIsSigned(true);
132-
X.setIsSigned(true);
133-
}
134-
135-
return X == Y;
136-
}
137-
138117
/// The kind of PartialOrdering we're performing template argument deduction
139118
/// for (C++11 [temp.deduct.partial]).
140119
enum class PartialOrderingKind { None, NonCall, Call };
@@ -273,7 +252,7 @@ checkDeducedTemplateArguments(ASTContext &Context,
273252
if (Y.getKind() == TemplateArgument::Expression ||
274253
Y.getKind() == TemplateArgument::Declaration ||
275254
(Y.getKind() == TemplateArgument::Integral &&
276-
hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral())))
255+
llvm::APSInt::isSameValue(X.getAsIntegral(), Y.getAsIntegral())))
277256
return X.wasDeducedFromArrayBound() ? Y : X;
278257

279258
// All other combinations are incompatible.
@@ -2574,7 +2553,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
25742553

25752554
case TemplateArgument::Integral:
25762555
if (A.getKind() == TemplateArgument::Integral) {
2577-
if (hasSameExtendedValue(P.getAsIntegral(), A.getAsIntegral()))
2556+
if (llvm::APSInt::isSameValue(P.getAsIntegral(), A.getAsIntegral()))
25782557
return TemplateDeductionResult::Success;
25792558
}
25802559
Info.FirstArg = P;
@@ -2828,62 +2807,6 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
28282807
/*HasDeducedAnyParam=*/nullptr);
28292808
}
28302809

2831-
/// Determine whether two template arguments are the same.
2832-
static bool isSameTemplateArg(ASTContext &Context, const TemplateArgument &X,
2833-
const TemplateArgument &Y) {
2834-
if (X.getKind() != Y.getKind())
2835-
return false;
2836-
2837-
switch (X.getKind()) {
2838-
case TemplateArgument::Null:
2839-
llvm_unreachable("Comparing NULL template argument");
2840-
2841-
case TemplateArgument::Type:
2842-
return Context.getCanonicalType(X.getAsType()) ==
2843-
Context.getCanonicalType(Y.getAsType());
2844-
2845-
case TemplateArgument::Declaration:
2846-
return isSameDeclaration(X.getAsDecl(), Y.getAsDecl());
2847-
2848-
case TemplateArgument::NullPtr:
2849-
return Context.hasSameType(X.getNullPtrType(), Y.getNullPtrType());
2850-
2851-
case TemplateArgument::Template:
2852-
case TemplateArgument::TemplateExpansion:
2853-
return Context.getCanonicalTemplateName(
2854-
X.getAsTemplateOrTemplatePattern()).getAsVoidPointer() ==
2855-
Context.getCanonicalTemplateName(
2856-
Y.getAsTemplateOrTemplatePattern()).getAsVoidPointer();
2857-
2858-
case TemplateArgument::Integral:
2859-
return hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral());
2860-
2861-
case TemplateArgument::StructuralValue:
2862-
return X.structurallyEquals(Y);
2863-
2864-
case TemplateArgument::Expression: {
2865-
llvm::FoldingSetNodeID XID, YID;
2866-
X.getAsExpr()->Profile(XID, Context, true);
2867-
Y.getAsExpr()->Profile(YID, Context, true);
2868-
return XID == YID;
2869-
}
2870-
2871-
case TemplateArgument::Pack: {
2872-
unsigned PackIterationSize = X.pack_size();
2873-
if (X.pack_size() != Y.pack_size())
2874-
return false;
2875-
ArrayRef<TemplateArgument> XP = X.pack_elements();
2876-
ArrayRef<TemplateArgument> YP = Y.pack_elements();
2877-
for (unsigned i = 0; i < PackIterationSize; ++i)
2878-
if (!isSameTemplateArg(Context, XP[i], YP[i]))
2879-
return false;
2880-
return true;
2881-
}
2882-
}
2883-
2884-
llvm_unreachable("Invalid TemplateArgument Kind!");
2885-
}
2886-
28872810
TemplateArgumentLoc
28882811
Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
28892812
QualType NTTPType, SourceLocation Loc,
@@ -3349,7 +3272,7 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction(
33493272
break;
33503273
TemplateArgument PP = P.isPackExpansion() ? P.getPackExpansionPattern() : P,
33513274
PA = A.isPackExpansion() ? A.getPackExpansionPattern() : A;
3352-
if (!isSameTemplateArg(S.Context, PP, PA)) {
3275+
if (!S.Context.isSameTemplateArgument(PP, PA)) {
33533276
if (!P.isPackExpansion() && !A.isPackExpansion()) {
33543277
Info.Param = makeTemplateParameter(TPL->getParam(
33553278
(AsStack.empty() ? As.end() : AsStack.back().begin()) -

clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,26 @@ namespace dependent_static_var_template {
412412
}
413413

414414
int cf() { return F<int>(); }
415+
416+
#ifdef CPP1Y
417+
namespace GH55872 {
418+
struct s {
419+
template<typename T>
420+
static CONST auto f = [] { return T::template g<s>; };
421+
// expected-note@-1 {{in instantiation of static data member 'dependent_static_var_template::GH55872::t::g' requested here}}
422+
// expected-note@-2 {{while substituting into a lambda expression here}}
423+
};
424+
425+
struct t {
426+
template<typename T>
427+
static CONST auto g = [] { return T::template f<t>; };
428+
// expected-error@-1 {{the type of variable template specialization 'f<dependent_static_var_template::GH55872::t>' declared with deduced type 'const auto' depends on itself}}
429+
// expected-note@-2 {{while substituting into a lambda expression here}}
430+
};
431+
432+
void test() { s::f<t>()(); } // expected-note {{in instantiation of static data member 'dependent_static_var_template::GH55872::s::f' requested here}}
433+
}
434+
#endif
415435
}
416436

417437
#ifndef PRECXX11

clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -492,4 +492,21 @@ static_assert(C<int, 0,1,2,3,4>::VALUEARRAY[3] == 3, "");
492492
static_assert(C<int, 0,1,2,3,4>::VALUEARRAY[0] == 0, "");
493493

494494
}
495+
496+
namespace appear_in_its_own_init {
497+
template <class T>
498+
auto GH51347 = GH51347<T>; // expected-error {{variable template 'GH51347' declared with deduced type 'auto' cannot appear in its own initializer}}
499+
500+
template <class T, class... Ts>
501+
auto a = [] {
502+
using U = T;
503+
a<U, Ts...>; // expected-error {{variable template 'a' declared with deduced type 'auto' cannot appear in its own initializer}}
504+
};
505+
506+
template <int...> int b;
507+
template <int I>
508+
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}}
509+
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}}
510+
}
511+
495512
#endif

clang/test/SemaTemplate/instantiate-var-template.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,14 @@ namespace InvalidInsertPos {
4747
template<> int v<int, 0>;
4848
int k = v<int, 500>;
4949
}
50+
51+
namespace GH97881_comment {
52+
template <bool B>
53+
auto g = sizeof(g<!B>);
54+
// expected-error@-1 {{the type of variable template specialization 'g<false>'}}
55+
// expected-note@-2 {{in instantiation of variable template specialization 'GH97881_comment::g'}}
56+
57+
void test() {
58+
(void)sizeof(g<false>); // expected-note {{in instantiation of variable template specialization 'GH97881_comment::g'}}
59+
}
60+
}

0 commit comments

Comments
 (0)