[clang] Revert "[Clang][Sema] Handle invalid variable template specialization whose type depends on itself (#134522)" (PR #139539)
Yanzuo Liu via cfe-commits
cfe-commits at lists.llvm.org
Mon May 12 05:03:12 PDT 2025
https://github.com/zwuis created https://github.com/llvm/llvm-project/pull/139539
#134522 triggers compilation error with libstdc++. See #139067 or <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120190>.
This reverts commit 91f1830cb69cab96201adba6378d64095e251f95.
>From 4878337bb6b2d6152fc53160ba4e515d2dadc266 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Mon, 12 May 2025 19:47:46 +0800
Subject: [PATCH] Revert "[Clang][Sema] Handle invalid variable template
specialization whose type depends on itself (#134522)"
This reverts commit 91f1830cb69cab96201adba6378d64095e251f95.
---
clang/docs/ReleaseNotes.rst | 2 -
clang/include/clang/AST/ASTContext.h | 5 --
.../clang/Basic/DiagnosticSemaKinds.td | 14 +---
clang/lib/AST/ASTContext.cpp | 49 -----------
clang/lib/Sema/SemaExpr.cpp | 3 +-
clang/lib/Sema/SemaTemplate.cpp | 51 +-----------
clang/lib/Sema/SemaTemplateDeduction.cpp | 83 ++++++++++++++++++-
.../cxx1y-variable-templates_in_class.cpp | 20 -----
.../cxx1y-variable-templates_top_level.cpp | 17 ----
.../SemaTemplate/instantiate-var-template.cpp | 11 ---
10 files changed, 85 insertions(+), 170 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e362ec595a3bb..d0645f3cbc6a5 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -684,8 +684,6 @@ Bug Fixes to C++ Support
- Fixed an assertion when trying to constant-fold various builtins when the argument
referred to a reference to an incomplete type. (#GH129397)
- Fixed a crash when a cast involved a parenthesized aggregate initialization in dependent context. (#GH72880)
-- No longer crashes when instantiating invalid variable template specialization
- whose type depends on itself. (#GH51347), (#GH55872)
- Improved parser recovery of invalid requirement expressions. In turn, this
fixes crashes from follow-on processing of the invalid requirement. (#GH138820)
- Fixed the handling of pack indexing types in the constraints of a member function redeclaration. (#GH138255)
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 1fdc488a76507..555afc315b704 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -2971,11 +2971,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
TemplateTemplateParmDecl *insertCanonicalTemplateTemplateParmDeclInternal(
TemplateTemplateParmDecl *CanonTTP) const;
- /// Determine whether the given template arguments \p Arg1 and \p Arg2 are
- /// equivalent.
- bool isSameTemplateArgument(const TemplateArgument &Arg1,
- const TemplateArgument &Arg2) const;
-
/// Type Query functions. If the type is an instance of the specified class,
/// return the Type pointer for the underlying maximally pretty type. This
/// is a member of ASTContext because this may need to do some amount of
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index ca47cf62324f3..4751acfbb0456 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2531,14 +2531,9 @@ 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<
- "%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_auto_variable_cannot_appear_in_own_initializer : Error<
+ "variable %0 declared with deduced type %1 "
+ "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">;
@@ -5312,9 +5307,6 @@ 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/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index c58cd2c93fb60..6b8685ae99256 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -7666,55 +7666,6 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const {
llvm_unreachable("Unhandled template argument kind");
}
-bool ASTContext::isSameTemplateArgument(const TemplateArgument &Arg1,
- const TemplateArgument &Arg2) const {
- if (Arg1.getKind() != Arg2.getKind())
- return false;
-
- switch (Arg1.getKind()) {
- case TemplateArgument::Null:
- llvm_unreachable("Comparing NULL template argument");
-
- case TemplateArgument::Type:
- return hasSameType(Arg1.getAsType(), Arg2.getAsType());
-
- case TemplateArgument::Declaration:
- return Arg1.getAsDecl()->getUnderlyingDecl()->getCanonicalDecl() ==
- Arg2.getAsDecl()->getUnderlyingDecl()->getCanonicalDecl();
-
- case TemplateArgument::NullPtr:
- return hasSameType(Arg1.getNullPtrType(), Arg2.getNullPtrType());
-
- case TemplateArgument::Template:
- case TemplateArgument::TemplateExpansion:
- return getCanonicalTemplateName(Arg1.getAsTemplateOrTemplatePattern()) ==
- getCanonicalTemplateName(Arg2.getAsTemplateOrTemplatePattern());
-
- case TemplateArgument::Integral:
- return llvm::APSInt::isSameValue(Arg1.getAsIntegral(),
- Arg2.getAsIntegral());
-
- case TemplateArgument::StructuralValue:
- return Arg1.structurallyEquals(Arg2);
-
- case TemplateArgument::Expression: {
- llvm::FoldingSetNodeID ID1, ID2;
- Arg1.getAsExpr()->Profile(ID1, *this, /*Canonical=*/true);
- Arg2.getAsExpr()->Profile(ID2, *this, /*Canonical=*/true);
- return ID1 == ID2;
- }
-
- case TemplateArgument::Pack:
- return llvm::equal(
- Arg1.getPackAsArray(), Arg2.getPackAsArray(),
- [&](const TemplateArgument &Arg1, const TemplateArgument &Arg2) {
- return isSameTemplateArgument(Arg1, Arg2);
- });
- }
-
- llvm_unreachable("Unhandled template argument kind");
-}
-
NestedNameSpecifier *
ASTContext::getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS) const {
if (!NNS)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 2c81f7c583eb6..4af9b6f0a1f56 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -251,8 +251,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
<< D->getDeclName();
} else {
Diag(Loc, diag::err_auto_variable_cannot_appear_in_own_initializer)
- << diag::ParsingInitFor::Var << D->getDeclName()
- << cast<VarDecl>(D)->getType();
+ << D->getDeclName() << cast<VarDecl>(D)->getType();
}
return true;
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 7940340064eda..8ccfdb80e1ce5 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4373,43 +4373,8 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
// Produce a placeholder value if the specialization is dependent.
if (Template->getDeclContext()->isDependentContext() ||
TemplateSpecializationType::anyDependentTemplateArguments(
- TemplateArgs, CTAI.CanonicalConverted)) {
- if (ParsingInitForAutoVars.empty())
- return DeclResult();
-
- auto IsSameTemplateArg = [&](const TemplateArgument &Arg1,
- const TemplateArgument &Arg2) {
- return Context.isSameTemplateArgument(Arg1, Arg2);
- };
-
- if (VarDecl *Var = Template->getTemplatedDecl();
- ParsingInitForAutoVars.count(Var) &&
- llvm::equal(
- CTAI.CanonicalConverted,
- Template->getTemplateParameters()->getInjectedTemplateArgs(Context),
- IsSameTemplateArg)) {
- Diag(TemplateNameLoc,
- diag::err_auto_variable_cannot_appear_in_own_initializer)
- << diag::ParsingInitFor::VarTemplate << Var << Var->getType();
- return true;
- }
-
- SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs;
- Template->getPartialSpecializations(PartialSpecs);
- for (VarTemplatePartialSpecializationDecl *Partial : PartialSpecs)
- if (ParsingInitForAutoVars.count(Partial) &&
- llvm::equal(CTAI.CanonicalConverted,
- Partial->getTemplateArgs().asArray(),
- IsSameTemplateArg)) {
- Diag(TemplateNameLoc,
- diag::err_auto_variable_cannot_appear_in_own_initializer)
- << diag::ParsingInitFor::VarTemplatePartialSpec << Partial
- << Partial->getType();
- return true;
- }
-
+ TemplateArgs, CTAI.CanonicalConverted))
return DeclResult();
- }
// Find the variable template specialization declaration that
// corresponds to these arguments.
@@ -4417,20 +4382,6 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
if (VarTemplateSpecializationDecl *Spec =
Template->findSpecialization(CTAI.CanonicalConverted, InsertPos)) {
checkSpecializationReachability(TemplateNameLoc, Spec);
- if (Spec->getType()->isUndeducedType()) {
- if (ParsingInitForAutoVars.count(Spec))
- Diag(TemplateNameLoc,
- diag::err_auto_variable_cannot_appear_in_own_initializer)
- << diag::ParsingInitFor::VarTemplateExplicitSpec << Spec
- << Spec->getType();
- 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();
-
- return true;
- }
// If we already have a variable template specialization, return it.
return Spec;
}
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 5dc06ebc2a235..b3505bad281f2 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -114,6 +114,27 @@ namespace clang {
using namespace clang;
using namespace sema;
+/// Compare two APSInts, extending and switching the sign as
+/// necessary to compare their values regardless of underlying type.
+static bool hasSameExtendedValue(llvm::APSInt X, llvm::APSInt Y) {
+ if (Y.getBitWidth() > X.getBitWidth())
+ X = X.extend(Y.getBitWidth());
+ else if (Y.getBitWidth() < X.getBitWidth())
+ Y = Y.extend(X.getBitWidth());
+
+ // If there is a signedness mismatch, correct it.
+ if (X.isSigned() != Y.isSigned()) {
+ // If the signed value is negative, then the values cannot be the same.
+ if ((Y.isSigned() && Y.isNegative()) || (X.isSigned() && X.isNegative()))
+ return false;
+
+ Y.setIsSigned(true);
+ X.setIsSigned(true);
+ }
+
+ return X == Y;
+}
+
/// The kind of PartialOrdering we're performing template argument deduction
/// for (C++11 [temp.deduct.partial]).
enum class PartialOrderingKind { None, NonCall, Call };
@@ -252,7 +273,7 @@ checkDeducedTemplateArguments(ASTContext &Context,
if (Y.getKind() == TemplateArgument::Expression ||
Y.getKind() == TemplateArgument::Declaration ||
(Y.getKind() == TemplateArgument::Integral &&
- llvm::APSInt::isSameValue(X.getAsIntegral(), Y.getAsIntegral())))
+ hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral())))
return X.wasDeducedFromArrayBound() ? Y : X;
// All other combinations are incompatible.
@@ -2553,7 +2574,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
case TemplateArgument::Integral:
if (A.getKind() == TemplateArgument::Integral) {
- if (llvm::APSInt::isSameValue(P.getAsIntegral(), A.getAsIntegral()))
+ if (hasSameExtendedValue(P.getAsIntegral(), A.getAsIntegral()))
return TemplateDeductionResult::Success;
}
Info.FirstArg = P;
@@ -2807,6 +2828,62 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
/*HasDeducedAnyParam=*/nullptr);
}
+/// Determine whether two template arguments are the same.
+static bool isSameTemplateArg(ASTContext &Context, const TemplateArgument &X,
+ const TemplateArgument &Y) {
+ if (X.getKind() != Y.getKind())
+ return false;
+
+ switch (X.getKind()) {
+ case TemplateArgument::Null:
+ llvm_unreachable("Comparing NULL template argument");
+
+ case TemplateArgument::Type:
+ return Context.getCanonicalType(X.getAsType()) ==
+ Context.getCanonicalType(Y.getAsType());
+
+ case TemplateArgument::Declaration:
+ return isSameDeclaration(X.getAsDecl(), Y.getAsDecl());
+
+ case TemplateArgument::NullPtr:
+ return Context.hasSameType(X.getNullPtrType(), Y.getNullPtrType());
+
+ case TemplateArgument::Template:
+ case TemplateArgument::TemplateExpansion:
+ return Context.getCanonicalTemplateName(
+ X.getAsTemplateOrTemplatePattern()).getAsVoidPointer() ==
+ Context.getCanonicalTemplateName(
+ Y.getAsTemplateOrTemplatePattern()).getAsVoidPointer();
+
+ case TemplateArgument::Integral:
+ return hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral());
+
+ case TemplateArgument::StructuralValue:
+ return X.structurallyEquals(Y);
+
+ case TemplateArgument::Expression: {
+ llvm::FoldingSetNodeID XID, YID;
+ X.getAsExpr()->Profile(XID, Context, true);
+ Y.getAsExpr()->Profile(YID, Context, true);
+ return XID == YID;
+ }
+
+ case TemplateArgument::Pack: {
+ unsigned PackIterationSize = X.pack_size();
+ if (X.pack_size() != Y.pack_size())
+ return false;
+ ArrayRef<TemplateArgument> XP = X.pack_elements();
+ ArrayRef<TemplateArgument> YP = Y.pack_elements();
+ for (unsigned i = 0; i < PackIterationSize; ++i)
+ if (!isSameTemplateArg(Context, XP[i], YP[i]))
+ return false;
+ return true;
+ }
+ }
+
+ llvm_unreachable("Invalid TemplateArgument Kind!");
+}
+
TemplateArgumentLoc
Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
QualType NTTPType, SourceLocation Loc,
@@ -3272,7 +3349,7 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction(
break;
TemplateArgument PP = P.isPackExpansion() ? P.getPackExpansionPattern() : P,
PA = A.isPackExpansion() ? A.getPackExpansionPattern() : A;
- if (!S.Context.isSameTemplateArgument(PP, PA)) {
+ if (!isSameTemplateArg(S.Context, PP, PA)) {
if (!P.isPackExpansion() && !A.isPackExpansion()) {
Info.Param = makeTemplateParameter(TPL->getParam(
(AsStack.empty() ? As.end() : AsStack.back().begin()) -
diff --git a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
index 57a48fac56cd6..eafadb07b29e1 100644
--- a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
+++ b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
@@ -412,26 +412,6 @@ namespace dependent_static_var_template {
}
int cf() { return F<int>(); }
-
-#ifdef CPP1Y
- namespace GH55872 {
- struct s {
- template<typename T>
- static CONST auto f = [] { return T::template g<s>; };
- // expected-note at -1 {{in instantiation of static data member 'dependent_static_var_template::GH55872::t::g' requested here}}
- // expected-note at -2 {{while substituting into a lambda expression here}}
- };
-
- struct t {
- template<typename T>
- static CONST auto g = [] { return T::template f<t>; };
- // expected-error at -1 {{the type of variable template specialization 'f<dependent_static_var_template::GH55872::t>' declared with deduced type 'const auto' depends on itself}}
- // expected-note at -2 {{while substituting into a lambda expression here}}
- };
-
- void test() { s::f<t>()(); } // expected-note {{in instantiation of static data member 'dependent_static_var_template::GH55872::s::f' requested here}}
- }
-#endif
}
#ifndef PRECXX11
diff --git a/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp b/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
index 1fe0ce9aabf29..6fc2032ee7fb4 100644
--- a/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
+++ b/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
@@ -492,21 +492,4 @@ 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 50b7219af4bea..60d3bd3b59f53 100644
--- a/clang/test/SemaTemplate/instantiate-var-template.cpp
+++ b/clang/test/SemaTemplate/instantiate-var-template.cpp
@@ -47,14 +47,3 @@ namespace InvalidInsertPos {
template<> int v<int, 0>;
int k = v<int, 500>;
}
-
-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'}}
- }
-}
More information about the cfe-commits
mailing list