-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Fix missing initializer for inline static template member with auto caused by delayed template instantiation. #138122
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
base: main
Are you sure you want to change the base?
Conversation
…h auto caused by delayed template instantiation.
@llvm/pr-subscribers-clang Author: 执着 (dty2) ChangesTry to fix missing initializer for inline static template member with auto caused by delayed template instantiation. Bug Fix: Bug 135032 Reproduction Steps: template <typename T> struct B {
template <typename G> inline static auto var = 5;
};
int main() {
int bb = B<int>::var<int>;
return 0;
} GDB Breakpoints for Debugging: #(1)
b Sema::BuildTemplateIdExpr
#(2)
b Sema::CheckVarTemplateId
#(3)
b Sema::CheckVarTemplateId
#(4)
b Sema::BuildVarTemplateInstantiation
#(5)
b TemplateDeclInstantiator::VisitVarTemplateSpecializationDecl
#(6)
b VarTemplateSpecializationDecl::Create
#(7)
b VarTemplateSpecializationDecl::VarTemplateSpecializationDecl
#(8)
b VarDecl::VarDecl
#(6)
b Sema::BuildVariableInstantiation
#(7)
b Sema::InstantiateVariableInitializer
#(3)
b Sema::BuildDeclarationNameExpr
#(4)
b Sema::BuildDeclRefExpr Key Observations: The AST shows VarTemplateSpecializationDecl as int (correct), but GDB reveals its type is actually auto during execution. Example from AST: DeclRefExpr 0x598f651f5a60 <col:9, col:27> 'auto' lvalue VarTemplateSpecialization 0x598f651f5980 'var' 'int' (VarTemplate 0x598f651d1938 'var') Solution: Full diff: https://github.com/llvm/llvm-project/pull/138122.diff 3 Files Affected:
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 76c055d28f091..eb66ec34a956f 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -6027,8 +6027,14 @@ void Sema::BuildVariableInstantiation(
Context.setManglingNumber(NewVar, Context.getManglingNumber(OldVar));
Context.setStaticLocalNumber(NewVar, Context.getStaticLocalNumber(OldVar));
+ bool VarTemplateWithAutoType = false;
+ QualType VarSourceType = OldVar->getTypeSourceInfo()->getType();
+ if (VarSourceType->getAs<AutoType>()) {
+ VarTemplateWithAutoType = true;
+ }
+
// Figure out whether to eagerly instantiate the initializer.
- if (InstantiatingVarTemplate || InstantiatingVarTemplatePartialSpec) {
+ if ((InstantiatingVarTemplate && !VarTemplateWithAutoType) || 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.
diff --git a/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp b/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp
index 812e438f30c9a..b1d8e376b826f 100644
--- a/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp
+++ b/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp
@@ -1,5 +1,13 @@
// RUN: %clang_cc1 -std=c++1z %s -emit-llvm -o - -triple x86_64-linux-gnu | FileCheck %s
+template <typename T> struct B {
+ template <typename G> inline static auto var = 5;
+};
+
+int inlinevartemplate = B<int>::var<int>;
+// CHECK: @_ZN1BIiE3varIiEE = {{.*}}global i32 5{{.*}}comdat
+// CHECK-NOT: @_ZN1BIiE3varIfEE
+
struct Q {
// CHECK: @_ZN1Q1kE = linkonce_odr constant i32 5, comdat
static constexpr int k = 5;
diff --git a/clang/test/SemaTemplate/cxx17-inline-variables.cpp b/clang/test/SemaTemplate/cxx17-inline-variables.cpp
index 7fc0aa8eeeb0c..27067c8e1b5e4 100644
--- a/clang/test/SemaTemplate/cxx17-inline-variables.cpp
+++ b/clang/test/SemaTemplate/cxx17-inline-variables.cpp
@@ -27,3 +27,9 @@ template <typename T> constexpr int A<T>::n = sizeof(A) + sizeof(T);
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);
+
+template <typename T> struct B {
+ template <typename G> inline static auto var = 5;
+};
+
+int b = B<int>::var<int>;
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
…h auto caused by delayed template instantiation.
bool VarTemplateWithAutoType = false; | ||
QualType VarSourceType = OldVar->getTypeSourceInfo()->getType(); | ||
if (VarSourceType->getAs<AutoType>()) { | ||
VarTemplateWithAutoType = true; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bool VarTemplateWithAutoType = false; | |
QualType VarSourceType = OldVar->getTypeSourceInfo()->getType(); | |
if (VarSourceType->getAs<AutoType>()) { | |
VarTemplateWithAutoType = true; | |
} | |
bool VarTemplateWithAutoType = OldVar->getTypeSourceInfo()->getType()->getAs<AutoType>(); |
Can we avoid getTypeSourceInfo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your suggestion is correct, I will adopt it.
Also, can you help me test if this fixes #134526? Thanks |
sure, no problem. |
@zyn0217 #include <iostream>
template <class T>
struct S {
template <class U> static const auto var = T();
template <class U> static const auto foo = var<T>;
};
int main() {
const int p = S<int>::var<int> + S<int>::foo<int> + 1;
std::cout << p;
return 0;
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a release note in clang/docs/ReleaseNotes.rst .
@@ -6027,8 +6027,15 @@ void Sema::BuildVariableInstantiation( | |||
Context.setManglingNumber(NewVar, Context.getManglingNumber(OldVar)); | |||
Context.setStaticLocalNumber(NewVar, Context.getStaticLocalNumber(OldVar)); | |||
|
|||
bool VarTemplateWithAutoType = false; | |||
QualType VarSourceType = OldVar->getTypeSourceInfo()->getType(); | |||
if (VarSourceType->getAs<AutoType>()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use ->isUndeducedType()
to handle this case
template <typename T> struct S { S(T); };
template <typename T> struct B {
template <typename G> inline static S var = 5;
};
?
Update: And we need to change OldVar->
to NewVar->
.
if ((InstantiatingVarTemplate && !VarTemplateWithAutoType) || | ||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that we can just swap these two if
statements. We need to handle partial specializations as well. E.g.
template <typename> struct B {
template <typename, typrname> inline static auto var = 6;
template <typename T> inline static auto var<int, T> = 7;
};
int b = B<int>::var<int, int>;
Try to fix missing initializer for inline static template member with auto caused by delayed template instantiation.
Bug Fix: Bug 135032
Problem Description:
Due to nested templates, when instantiating the outer layer (the template class), the inner layer (the template variable) uses delayed instantiation.
This causes the declaration (VarDecl) of the template variable to retain the type from the original template declaration (i.e., auto), and it loses the initializer.
Later, when instantiating the template variable, its VarTemplateSpecializationDecl type depends on the VarDecl type.
Thus, the VarTemplateSpecializationDecl also has no initializer, and its type remains auto.
Ultimately, when building the reference expression in Sema::BuildDeclarationNameExpr, the expression's type is auto and stays as auto until code generation, triggering llvm_unreachable in CodeGenTypes::ConvertType.
Reproduction Steps:
Test Code:
GDB Breakpoints for Debugging:
Annotations like #(1) mark the call order in a tree structure. For example, two #(3) entries mean Sema::CheckVarTemplateId calls Sema::BuildDeclarationNameExpr after its own execution.
Key Observations:
While debugging, I generated the AST for the test code and noticed a contradiction:
The AST shows VarTemplateSpecializationDecl as int (correct), but GDB reveals its type is actually auto during execution.
Example from AST:
DeclRefExpr 0x598f651f5a60 <col:9, col:27> 'auto' lvalue VarTemplateSpecialization 0x598f651f5980 'var' 'int' (VarTemplate 0x598f651d1938 'var')
The DeclRefExpr has type auto, while its referenced VarTemplateSpecialization claims to be int—a clear inconsistency.
Solution:
Since I noticed that the deduction of auto type is caused by the initializer
I plan to do special processing for template variables of type auto, that is, to prevent their delayed instantiation
so that their initializers will not be lost when the outer template class is instantiated