https://github.com/akash-manna-sky updated https://github.com/llvm/llvm-project/pull/217709
>From cb00d11cb959de73ecdb282b5ab3502f0cf10da2 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Thu, 20 Aug 2026 23:05:31 +0530 Subject: [PATCH 1/2] [clang] Fix bogus "deduced type depends on itself" for variable template specializations A variable template specialization with a deduced type first named in a context that doesn't require its definition (e.g. decltype(obj<0>)) can be left with an undeduced type: for a member variable template of a class template the initializer lives on the in-class pattern, and DoMarkVarDeclReferenced never instantiated the definition because its undeduced-type clause excluded constexpr variables. On a later reference, CheckVarTemplateId found the existing undeduced specialization and wrongly assumed it was substituting its own initializer, rejecting valid code. Emit the error only when the specialization's initializer instantiation is really in progress (tracked via InstantiatingSpecializations), make the eager initializer instantiation in BuildVariableInstantiation register a RecursiveInstGuard so genuine (also indirect) cycles are still caught, and drop the !UsableInConstantExpr restriction so any reference to an undeduced specialization instantiates its definition, matching GCC for decltype. Fixes #214477 --- clang/docs/ReleaseNotes.md | 5 ++ clang/lib/Sema/SemaExpr.cpp | 3 +- clang/lib/Sema/SemaTemplate.cpp | 15 +++- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 2 + clang/test/SemaTemplate/GH214477.cpp | 76 +++++++++++++++++++ 5 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 clang/test/SemaTemplate/GH214477.cpp diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 8c9467ca7b742..0ac80a68f46ef 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -528,6 +528,11 @@ features cannot lower the translation-unit ABI level; parameter that follows a parameter pack (e.g. `template <typename... T> S::S(T..., int = 10) {}`). (#GH216211) +- Fixed a bogus "deduced type depends on itself" error when a variable template + specialization with a deduced type was first named in a context that did not + require its definition (such as ``decltype``) and then referenced again; + Clang now instantiates the definition to deduce the type instead. (#GH214477) + #### Bug Fixes to AST Handling - Fixed a non-deterministic ordering of unused local typedefs that made diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index da76bbf3c35f0..a5a7398aee15f 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -20715,8 +20715,7 @@ static void DoMarkVarDeclReferenced( bool NeedDefinition = OdrUse == OdrUseContext::Used || NeededForConstantEvaluation || - (TSK != clang::TSK_Undeclared && !UsableInConstantExpr && - Var->getType()->isUndeducedType()); + (TSK != clang::TSK_Undeclared && Var->getType()->isUndeducedType()); assert(!isa<VarTemplatePartialSpecializationDecl>(Var) && "Can't instantiate a partial template specialization."); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 1fde412749cc9..ac7c789fe74e6 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4658,18 +4658,25 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc, Template->findSpecialization(CTAI.CanonicalConverted, InsertPos)) { checkSpecializationReachability(TemplateNameLoc, Spec); if (Spec->getType()->isUndeducedType()) { - if (ParsingInitForAutoVars.count(Spec)) + if (ParsingInitForAutoVars.count(Spec)) { Diag(TemplateNameLoc, diag::err_auto_variable_cannot_appear_in_own_initializer) << diag::ParsingInitFor::VarTemplateExplicitSpec << Spec << Spec->getType(); - else + return true; + } + if (InstantiatingSpecializations.contains( + {Spec->getCanonicalDecl(), + unsigned(RecursiveInstGuard::Kind::Template)})) { // 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; + return true; + } + // Otherwise, the specialization was declared at a point where its type + // was not needed, so its initializer has not been instantiated yet; the + // type will be deduced when the definition is instantiated. } // If we already have a variable template specialization, return it. return Spec; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index ff29ae27a3b66..fab93559a3f6c 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -6523,6 +6523,8 @@ void Sema::BuildVariableInstantiation( // We're producing a template. Don't instantiate the initializer yet. } else if (NewVar->getType()->isUndeducedType()) { // We need the type to complete the declaration of the variable. + RecursiveInstGuard AlreadyInstantiating(*this, NewVar, + RecursiveInstGuard::Kind::Template); InstantiateVariableInitializer(NewVar, OldVar, TemplateArgs); } else if (InstantiatingSpecFromTemplate || (OldVar->isInline() && OldVar->isThisDeclarationADefinition() && diff --git a/clang/test/SemaTemplate/GH214477.cpp b/clang/test/SemaTemplate/GH214477.cpp new file mode 100644 index 0000000000000..76f8e9a2c51e8 --- /dev/null +++ b/clang/test/SemaTemplate/GH214477.cpp @@ -0,0 +1,76 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++26 -verify %s + +namespace GH214477 { + +struct dummy {}; +template <typename A> struct real { A a; }; + +template <typename V> struct Tests { + template <int> static constexpr dummy obj = {}; + + const int reg = [] { + using X = decltype(obj<0>); + static_assert(__is_same(X, const real<int>)); + (void)&obj<0>; + return 0; + }(); + + template <int Tmp> + requires(Tmp == 0) + static constexpr real obj<Tmp> = {0}; +}; + +void test() { (void)Tests<int>{}; } + +template <typename V> struct Tests2 { + template <int> static constexpr dummy obj = {}; + + void f() { + using X = decltype(obj<0>); + static_assert(__is_same(X, const real<int>)); + (void)&obj<0>; + } + + template <int Tmp> + requires(Tmp == 0) + static constexpr real obj<Tmp> = {0}; +}; + +void test2() { Tests2<int>{}.f(); } + +template <typename V> struct Tests3 { + template <int> static constexpr dummy obj = {}; + + const int reg = [] { + (void)&obj<0>; + using X = decltype(obj<0>); + static_assert(__is_same(X, const real<int>)); + return 0; + }(); + + template <int Tmp> + requires(Tmp == 0) + static constexpr real obj<Tmp> = {0}; +}; + +void test3() { (void)Tests3<int>{}; } + +} // namespace GH214477 + +namespace deduced_type_cycle { + +template <int> constexpr auto a = 0; +template <int> constexpr auto b = 0; + +template <int N> + requires(N == 0) +constexpr auto a<N> = b<N>; // expected-note {{in instantiation of variable template specialization 'deduced_type_cycle::b<N>' requested here}} + +template <int N> + requires(N == 0) +constexpr auto b<N> = a<N>; // expected-error {{the type of variable template specialization 'a<0>' declared with deduced type 'const auto' depends on itself}} + +auto y = a<0>; // expected-note {{in instantiation of variable template specialization 'deduced_type_cycle::a<N>' requested here}} + +} // namespace deduced_type_cycle >From 34b9e3302aaa6585746aa9d48de9f55da5e8c95b Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Fri, 21 Aug 2026 09:14:09 +0530 Subject: [PATCH 2/2] [clang] Simplify comment in SemaTemplate.cpp regarding variable template specialization instantiation --- clang/lib/Sema/SemaTemplate.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index ac7c789fe74e6..9b844a8da1403 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4674,9 +4674,8 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc, << Spec << Spec->getType(); return true; } - // Otherwise, the specialization was declared at a point where its type - // was not needed, so its initializer has not been instantiated yet; the - // type will be deduced when the definition is instantiated. + // Not yet deduced; the type is completed when the definition is + // instantiated. } // If we already have a variable template specialization, return it. return Spec; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
