Skip to content

[Clang] Do not defer variable template instantiation for undeduced types #141009

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 22, 2025

Conversation

zyn0217
Copy link
Contributor

@zyn0217 zyn0217 commented May 22, 2025

The previous approach broke the instantiation convention for templated
substitutions, as we were attempting to instantiate the initializer
even when it was still dependent.

We deferred variable template instantiation until the end of the TU.
However, type deduction requires the initializer immediately,
similar to how constant evaluation does.

Fixes #140773
Fixes #135032
Fixes #134526

Reapplies #138122

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 22, 2025
@llvmbot
Copy link
Member

llvmbot commented May 22, 2025

@llvm/pr-subscribers-clang

Author: Younan Zhang (zyn0217)

Changes

The previous approach broke the instantiation convention for templated
substitutions, as we were attempting to instantiate the initializer
even when it was still dependent.

We deferred variable template instantiation until the end of the TU.
However, type deduction requires the initializer immediately,
similar to how constant evaluation does.

Fixes #140773
Fixes #135032
Fixes #134526


Full diff: https://github.com/llvm/llvm-project/pull/141009.diff

5 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+1-1)
  • (modified) clang/lib/Sema/SemaExpr.cpp (+2-1)
  • (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+3-3)
  • (modified) clang/test/CodeGenCXX/cxx1z-inline-variables.cpp (+1-1)
  • (modified) clang/test/SemaTemplate/cxx17-inline-variables.cpp (+33-3)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f04cb7b91788c..026b585826200 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -728,7 +728,7 @@ Bug Fixes to C++ Support
   in a ``constexpr`` function. (#GH131432)
 - Fixed an incorrect TreeTransform for calls to ``consteval`` functions if a conversion template is present. (#GH137885)
 - Clang now emits a warning when class template argument deduction for alias templates is used in C++17. (#GH133806)
-- Fix missed initializer instantiation bug for variable templates. (#GH138122)
+- Fixed a missed initializer instantiation bug for variable templates. (#GH134526), (#GH138122)
 - Fix a crash when checking the template template parameters of a dependent lambda appearing in an alias declaration.
   (#GH136432), (#GH137014), (#GH138018)
 - Fixed an assertion when trying to constant-fold various builtins when the argument
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index b18e83b605e4f..37566450cd88c 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -20155,9 +20155,10 @@ static void DoMarkVarDeclReferenced(
           Var->setTemplateSpecializationKind(TSK, PointOfInstantiation);
       }
 
-      if (UsableInConstantExpr) {
+      if (UsableInConstantExpr || Var->getType()->isUndeducedType()) {
         // Do not defer instantiations of variables that could be used in a
         // constant expression.
+        // The type deduction also needs a complete initializer.
         SemaRef.runWithSufficientStackSpace(PointOfInstantiation, [&] {
           SemaRef.InstantiateVariableDefinition(PointOfInstantiation, Var);
         });
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index b12085c6f6935..d4f99c1fa16f6 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -6032,11 +6032,11 @@ void Sema::BuildVariableInstantiation(
   Context.setStaticLocalNumber(NewVar, Context.getStaticLocalNumber(OldVar));
 
   // Figure out whether to eagerly instantiate the initializer.
-  if (NewVar->getType()->isUndeducedType()) {
+  if (InstantiatingVarTemplate || InstantiatingVarTemplatePartialSpec) {
+    // 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.
     InstantiateVariableInitializer(NewVar, OldVar, TemplateArgs);
-  } else if (InstantiatingVarTemplate || InstantiatingVarTemplatePartialSpec) {
-    // We're producing a template. Don't instantiate the initializer yet.
   } else if (InstantiatingSpecFromTemplate ||
              (OldVar->isInline() && OldVar->isThisDeclarationADefinition() &&
               !NewVar->isThisDeclarationADefinition())) {
diff --git a/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp b/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp
index 1cb30b178e06f..9b1a6e4647e85 100644
--- a/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp
+++ b/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp
@@ -5,7 +5,7 @@ template <typename T> struct InlineAuto {
 };
 int inlineauto = InlineAuto<int>::var<int>;
 // CHECK: @_ZN10InlineAutoIiE3varIiEE = {{.*}}i32 5{{.*}}comdat
-//
+
 template <typename> struct PartialInlineAuto {
   template <typename, typename> inline static auto var = 6;
   template <typename T> inline static auto var<int, T> = 7;
diff --git a/clang/test/SemaTemplate/cxx17-inline-variables.cpp b/clang/test/SemaTemplate/cxx17-inline-variables.cpp
index 06f1ec17fb7a8..d51588bd492ee 100644
--- a/clang/test/SemaTemplate/cxx17-inline-variables.cpp
+++ b/clang/test/SemaTemplate/cxx17-inline-variables.cpp
@@ -1,5 +1,5 @@
 // RUN: %clang_cc1 -std=c++17 -verify %s
-// expected-no-diagnostics
+
 template<bool> struct DominatorTreeBase {
   static constexpr bool IsPostDominator = true;
 };
@@ -28,6 +28,8 @@ template <typename T> inline constexpr int A<T>::m = sizeof(A) + sizeof(T);
 static_assert(A<int>().f() == 5);
 static_assert(A<int>().g() == 5);
 
+namespace GH135032 {
+
 template <typename T> struct InlineAuto {
   template <typename G> inline static auto var = 5;
 };
@@ -37,5 +39,33 @@ template <typename> struct PartialInlineAuto {
   template <typename T> inline static auto var<int, T> = 7;
 };
 
-int inlineauto = InlineAuto<int>::var<int>;
-int partialinlineauto = PartialInlineAuto<int>::var<int, int>;
+int inline_auto = InlineAuto<int>::var<int>;
+int partial_inline_auto = PartialInlineAuto<int>::var<int, int>;
+
+}
+
+namespace GH140773 {
+template <class T> class ConstString { // #ConstString
+  ConstString(typename T::type) {} // #ConstString-Ctor
+};
+
+template <class = int>
+struct Foo {
+  template <char>
+  static constexpr ConstString kFilename{[] { // #kFileName
+    return 42;
+  }};
+};
+
+// We don't want to instantiate the member template until it's used!
+Foo<> foo;
+
+auto X = Foo<>::kFilename<'a'>;
+// expected-error@#kFileName {{no viable constructor}}
+// expected-note@-2 {{in instantiation of static data member}}
+// expected-note@#ConstString-Ctor {{candidate template ignored}}
+// expected-note@#ConstString-Ctor {{implicit deduction guide}}
+// expected-note@#ConstString {{candidate template ignored}}
+// expected-note@#ConstString {{implicit deduction guide}}
+
+}

The previous approach broke the instantiation convention for templated
substitutions, as we were attempting to instantiate the initializer
even when it was still dependent.

We deferred variable template instantiation until the end of the TU.
However, type deduction requires the initializer immediately,
similar to how constant evaluation does.
@zyn0217 zyn0217 merged commit bf6d24a into llvm:main May 22, 2025
12 checks passed
sivan-shani pushed a commit to sivan-shani/llvm-project that referenced this pull request Jun 3, 2025
…pes (llvm#141009)

The previous approach broke the instantiation convention for templated
substitutions, as we were attempting to instantiate the initializer
even when it was still dependent.

We deferred variable template instantiation until the end of the TU.
However, type deduction requires the initializer immediately,
similar to how constant evaluation does.

Fixes llvm#140773
Fixes llvm#135032
Fixes llvm#134526

Reapplies llvm#138122
ajaden-codes pushed a commit to Jaddyen/llvm-project that referenced this pull request Jun 6, 2025
…pes (llvm#141009)

The previous approach broke the instantiation convention for templated
substitutions, as we were attempting to instantiate the initializer
even when it was still dependent.

We deferred variable template instantiation until the end of the TU.
However, type deduction requires the initializer immediately,
similar to how constant evaluation does.

Fixes llvm#140773
Fixes llvm#135032
Fixes llvm#134526

Reapplies llvm#138122
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
3 participants