[clang] [Clang][Sema] Create Local Scope for Variable Template (PR #228280)

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 1 16:52:25 PDT 2026


https://github.com/aokblast created https://github.com/llvm/llvm-project/pull/228280

A variable template should create its own LocalScope, as it should be opaque to other instantiations. This can occur when there are multiple instantiations in the same lexical scope. The correct behavior is that these instantiations should not be chained together.

Assisted-by: Claude # Test ReleaseNote

>From 3878c5f3621585d002622580f55e40d809d80ec2 Mon Sep 17 00:00:00 2001
From: ShengYi Hung <aokblast at FreeBSD.org>
Date: Thu, 1 Oct 2026 18:39:56 -0500
Subject: [PATCH] [Clang][Sema] Create Local Scope for Variable Template

A variable template should create its own LocalScope as its should be
opaque to other instantiation. This can happens when there is any
duplicate instantiation in the same lexical scope. The correct behavior
here is that these instantiation should not be chained together.

Assisted-by: Claude # Test ReleaseNote
---
 clang/docs/ReleaseNotes.md                    |  5 +++
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  4 +++
 .../SemaTemplate/instantiate-var-template.cpp | 34 +++++++++++++++++++
 3 files changed, 43 insertions(+)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index b71758e4b9647..677ff99ec0381 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -767,6 +767,11 @@ features cannot lower the translation-unit ABI level;
   lookup before asserting that the name is not dependent, avoiding an assertion 
   after an earlier diagnostic has caused the declaration to be unavailable. (#GH220525)
 
+- Fixed an assertion failure when the initializer of a variable template
+  specialization with a deduced type is instantiated from inside a lambda in
+  the initializer of another specialization of the same variable template.
+  (#GH134148)
+
 #### Bug Fixes to AST Handling
 
 - Fixed a non-deterministic ordering of unused local typedefs that made
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 7e29de5be2e97..5e72f05c863a0 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -6355,6 +6355,10 @@ VarTemplateSpecializationDecl *Sema::BuildVarTemplateInstantiation(
   if (Inst.isInvalid())
     return nullptr;
 
+  // A variable template specialization is never a local declaration, so it
+  // must not see the locals of whatever instantiation we are currently in.
+  LocalInstantiationScope Local(*this);
+
   // Instantiate the first declaration of the variable template: for a partial
   // specialization of a static data member template, the first declaration may
   // or may not be the declaration in the class; if it's in the class, we want
diff --git a/clang/test/SemaTemplate/instantiate-var-template.cpp b/clang/test/SemaTemplate/instantiate-var-template.cpp
index 50b7219af4bea..bc6dc3d301578 100644
--- a/clang/test/SemaTemplate/instantiate-var-template.cpp
+++ b/clang/test/SemaTemplate/instantiate-var-template.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -verify -std=c++1y %s
+// RUN: %clang_cc1 -verify -std=c++17 %s
 
 namespace PR17846 {
   template <typename T> constexpr T pi = T(3.14);
@@ -58,3 +59,36 @@ namespace GH97881_comment {
     (void)sizeof(g<false>); // expected-note {{in instantiation of variable template specialization 'GH97881_comment::g'}}
   }
 }
+
+#if __cplusplus >= 201703L
+namespace GH134148 {
+  // The initializer of a variable template specialization with an undeduced
+  // type is instantiated eagerly, here from inside the instantiation of the
+  // lambda in another specialization's initializer. The nested instantiation
+  // must get its own local instantiation scope rather than reusing the
+  // lambda's scope, which already holds the instantiations of the lambda's
+  // own locals.
+  template <int N> constexpr auto sum = [] { return sum<N - 1> + N; }();
+  template <> constexpr auto sum<0> = 0;
+  static_assert(sum<3> == 6);
+
+  template <class T> struct Unwrap { using type = T; static constexpr int depth = 0; };
+  template <class T> struct Unwrap<T &> {
+    using type = typename Unwrap<T>::type;
+    static constexpr int depth = Unwrap<T>::depth + 1;
+  };
+
+  template <class T, class... Args>
+  constexpr auto count = [] {
+    if constexpr (Unwrap<T>::depth > 0)
+      return count<typename Unwrap<T>::type, Args...> + 1;
+    else
+      return sizeof...(Args);
+  }();
+  template <class T, int = count<T>> constexpr int use(T &&) { return count<T>; }
+
+  int f();
+  static_assert(use(f) == 1);
+  static_assert(count<int &&> == 0);
+}
+#endif



More information about the cfe-commits mailing list