[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
Wed Apr 30 03:16:07 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/4] 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/4] 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>);
>From 50d40656f62ae98d428db474ba683cbb2e98179f Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Tue, 29 Apr 2025 23:50:39 +0800
Subject: [PATCH 3/4] Introduce `ASTContext::isSameTemplateArgument`, remove
underlines in diagnostic messages, and add test
---
clang/docs/ReleaseNotes.rst | 2 +-
clang/include/clang/AST/ASTContext.h | 5 ++
clang/lib/AST/ASTContext.cpp | 48 +++++++++++++++++++
clang/lib/Sema/SemaTemplate.cpp | 38 +++++----------
clang/lib/Sema/SemaTemplateDeduction.cpp | 24 ++--------
.../cxx1y-variable-templates_in_class.cpp | 20 ++++++++
6 files changed, 88 insertions(+), 49 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9e0fe8a94729b..4c0513f6a849f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -415,7 +415,7 @@ Bug Fixes to C++ Support
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)
+ whose type depends on itself. (#GH51347), (#GH55872)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 3ff9f308f3a5e..99ab20473df92 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -2939,6 +2939,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
TemplateArgument getCanonicalTemplateArgument(const TemplateArgument &Arg)
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/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 1b6b3d06ddc1e..3e43c529cf676 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -7594,6 +7594,54 @@ 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:
+ return true;
+
+ 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 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/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 2212d8eca9c39..94c69e1a18e6a 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4348,23 +4348,6 @@ 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
@@ -4389,16 +4372,20 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
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),
- TemplateArgEqualityComparator{Context})) {
+ IsSameTemplateArg)) {
Diag(TemplateNameLoc,
diag::err_auto_variable_cannot_appear_in_own_initializer)
- << diag::ParsingInitFor::VarTemplate << Var << Var->getType()
- << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc());
+ << diag::ParsingInitFor::VarTemplate << Var << Var->getType();
return true;
}
@@ -4408,12 +4395,11 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
if (ParsingInitForAutoVars.count(Partial) &&
llvm::equal(CTAI.CanonicalConverted,
Partial->getTemplateArgs().asArray(),
- TemplateArgEqualityComparator{Context})) {
+ IsSameTemplateArg)) {
Diag(TemplateNameLoc,
diag::err_auto_variable_cannot_appear_in_own_initializer)
<< diag::ParsingInitFor::VarTemplatePartialSpec << Partial
- << Partial->getType()
- << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc());
+ << Partial->getType();
return true;
}
@@ -4431,14 +4417,12 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
Diag(TemplateNameLoc,
diag::err_auto_variable_cannot_appear_in_own_initializer)
<< diag::ParsingInitFor::VarTemplateExplicitSpec << Spec
- << Spec->getType()
- << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc());
+ << 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()
- << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc());
+ << Spec << Spec->getType();
return true;
}
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 170b9f05002b1..4a23b6efc4da6 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2844,35 +2844,17 @@ static bool isSameTemplateArg(ASTContext &Context,
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::StructuralValue:
+ case TemplateArgument::Expression:
+ return Context.isSameTemplateArgument(X, Y);
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()) {
diff --git a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
index eafadb07b29e1..57a48fac56cd6 100644
--- a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
+++ b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
@@ -412,6 +412,26 @@ 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
>From 781d3c64f1ff47df3e4e7e3af78302814cf82979 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zwuis at outlook.com>
Date: Wed, 30 Apr 2025 18:06:05 +0800
Subject: [PATCH 4/4] Promote `isSameTemplateArg`, and use
`llvm::APSInt::isSameValue`
---
clang/lib/AST/ASTContext.cpp | 5 +-
clang/lib/Sema/SemaTemplateDeduction.cpp | 65 ++----------------------
2 files changed, 6 insertions(+), 64 deletions(-)
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 65716fda0dd89..2ca3f72608c25 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -7648,7 +7648,7 @@ bool ASTContext::isSameTemplateArgument(const TemplateArgument &Arg1,
switch (Arg1.getKind()) {
case TemplateArgument::Null:
- return true;
+ llvm_unreachable("Comparing NULL template argument");
case TemplateArgument::Type:
return hasSameType(Arg1.getAsType(), Arg2.getAsType());
@@ -7666,7 +7666,8 @@ bool ASTContext::isSameTemplateArgument(const TemplateArgument &Arg1,
getCanonicalTemplateName(Arg2.getAsTemplateOrTemplatePattern());
case TemplateArgument::Integral:
- return Arg1.getAsIntegral() == Arg2.getAsIntegral();
+ return llvm::APSInt::isSameValue(Arg1.getAsIntegral(),
+ Arg2.getAsIntegral());
case TemplateArgument::StructuralValue:
return Arg1.structurallyEquals(Arg2);
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index e7721a97a5c0c..57aeaaf182cdb 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -114,27 +114,6 @@ 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 };
@@ -273,7 +252,7 @@ checkDeducedTemplateArguments(ASTContext &Context,
if (Y.getKind() == TemplateArgument::Expression ||
Y.getKind() == TemplateArgument::Declaration ||
(Y.getKind() == TemplateArgument::Integral &&
- hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral())))
+ llvm::APSInt::isSameValue(X.getAsIntegral(), Y.getAsIntegral())))
return X.wasDeducedFromArrayBound() ? Y : X;
// All other combinations are incompatible.
@@ -2574,7 +2553,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
case TemplateArgument::Integral:
if (A.getKind() == TemplateArgument::Integral) {
- if (hasSameExtendedValue(P.getAsIntegral(), A.getAsIntegral()))
+ if (llvm::APSInt::isSameValue(P.getAsIntegral(), A.getAsIntegral()))
return TemplateDeductionResult::Success;
}
Info.FirstArg = P;
@@ -2828,44 +2807,6 @@ 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:
- case TemplateArgument::Declaration:
- case TemplateArgument::NullPtr:
- case TemplateArgument::Template:
- case TemplateArgument::TemplateExpansion:
- case TemplateArgument::StructuralValue:
- case TemplateArgument::Expression:
- return Context.isSameTemplateArgument(X, Y);
-
- case TemplateArgument::Integral:
- return hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral());
-
- 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,
@@ -3331,7 +3272,7 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction(
break;
TemplateArgument PP = P.isPackExpansion() ? P.getPackExpansionPattern() : P,
PA = A.isPackExpansion() ? A.getPackExpansionPattern() : A;
- if (!isSameTemplateArg(S.Context, PP, PA)) {
+ if (!S.Context.isSameTemplateArgument(PP, PA)) {
if (!P.isPackExpansion() && !A.isPackExpansion()) {
Info.Param = makeTemplateParameter(TPL->getParam(
(AsStack.empty() ? As.end() : AsStack.back().begin()) -
More information about the cfe-commits
mailing list