https://github.com/16bit-ykiko updated 
https://github.com/llvm/llvm-project/pull/197856

>From a527caae1fe28ef5ca27cf38c01c9a8196d1fac0 Mon Sep 17 00:00:00 2001
From: ykiko <[email protected]>
Date: Fri, 15 May 2026 11:13:32 +0800
Subject: [PATCH] [Clang][AST] Fix ExplicitInstantiationDecl accessors
 confusing declared type with class encoding
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Several ExplicitInstantiationDecl accessors (getTypeAsWritten,
getQualifierLoc, getTagKWLoc, getNumTemplateArgs, and the angle-loc
getters) used TypeLoc kind checks to distinguish class template /
nested class instantiations from function / variable template
instantiations. This was incorrect: a variable template's declared
type can be a TagType or TemplateSpecializationType, causing:

- getTypeAsWritten() returning nullptr → crash in DeclPrinter (GH#197797)
- getQualifierLoc() returning the type's qualifier instead of the
  variable's → wrong output (e.g. "ns::var" instead of "var")
- getTagKWLoc() returning the type's elaborated keyword
- getNumTemplateArgs/AngleLocs extracting from the type instead of
  the variable's template arguments

Add a private isClassOrNestedClassSpec() helper that checks
isa<RecordDecl>(getSpecialization()), and guard all TypeSourceInfo
fallback paths with it.

Fixes #197797
---
 clang/include/clang/AST/DeclTemplate.h        |  7 +++
 clang/lib/AST/DeclTemplate.cpp                | 38 +++++++++-------
 clang/lib/Sema/SemaTemplate.cpp               |  5 +--
 .../AST/ast-print-explicit-instantiation.cpp  | 24 ++++++++++
 .../explicit-instantiation-source-info.cpp    | 44 +++++++++++++++++++
 5 files changed, 99 insertions(+), 19 deletions(-)

diff --git a/clang/include/clang/AST/DeclTemplate.h 
b/clang/include/clang/AST/DeclTemplate.h
index 9fb41c87da732..4e9f348f28847 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3449,6 +3449,13 @@ class ExplicitInstantiationDecl final
     return hasTrailingQualifier() ? 1 : 0;
   }
 
+  /// Whether the specialization is a class template or nested class.
+  /// When true, TypeSourceInfo encodes the class itself (qualifier, tag
+  /// keyword, template arguments) rather than a declared type.
+  bool isClassOrNestedClassSpec() const {
+    return isa<RecordDecl>(getSpecialization());
+  }
+
   /// Raw access to the internal TypeSourceInfo.  For class templates this is
   /// a TemplateSpecializationTypeLoc; for nested classes a TagTypeLoc.
   /// Public getTypeAsWritten() returns null for those cases.
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 08e6512a1c74d..0786c3ba56518 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1843,6 +1843,8 @@ ExplicitInstantiationDecl::CreateDeserialized(ASTContext 
&C, GlobalDeclID ID,
 }
 
 SourceLocation ExplicitInstantiationDecl::getTagKWLoc() const {
+  if (!isClassOrNestedClassSpec())
+    return SourceLocation();
   if (auto *TSI = getRawTypeSourceInfo()) {
     if (auto TL = TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>())
       return TL.getElaboratedKeywordLoc();
@@ -1855,8 +1857,9 @@ SourceLocation ExplicitInstantiationDecl::getTagKWLoc() 
const {
 NestedNameSpecifierLoc ExplicitInstantiationDecl::getQualifierLoc() const {
   if (hasTrailingQualifier())
     return *getTrailingObjects<NestedNameSpecifierLoc>();
-  if (auto *TSI = getRawTypeSourceInfo())
-    return TSI->getTypeLoc().getPrefix();
+  if (isClassOrNestedClassSpec())
+    if (auto *TSI = getRawTypeSourceInfo())
+      return TSI->getTypeLoc().getPrefix();
   return NestedNameSpecifierLoc();
 }
 
@@ -1864,20 +1867,21 @@ TypeSourceInfo 
*ExplicitInstantiationDecl::getTypeAsWritten() const {
   auto *TSI = getRawTypeSourceInfo();
   if (!TSI)
     return nullptr;
-  TypeLoc TL = TSI->getTypeLoc();
-  // For class templates and nested classes, the "type" is fully described by
-  // the unified accessors (getQualifierLoc, getTemplateArg, getTagKWLoc).
-  if (TL.getAs<TemplateSpecializationTypeLoc>() || TL.getAs<TagTypeLoc>())
-    return nullptr;
+  if (isClassOrNestedClassSpec()) {
+    TypeLoc TL = TSI->getTypeLoc();
+    if (TL.getAs<TemplateSpecializationTypeLoc>() || TL.getAs<TagTypeLoc>())
+      return nullptr;
+  }
   return TSI;
 }
 
 unsigned ExplicitInstantiationDecl::getNumTemplateArgs() const {
   if (const auto *Args = getTrailingArgsInfo())
     return Args->NumTemplateArgs;
-  if (auto *TSI = getRawTypeSourceInfo())
-    if (auto TL = TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>())
-      return TL.getNumArgs();
+  if (isClassOrNestedClassSpec())
+    if (auto *TSI = getRawTypeSourceInfo())
+      if (auto TL = TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>())
+        return TL.getNumArgs();
   return 0;
 }
 
@@ -1892,18 +1896,20 @@ ExplicitInstantiationDecl::getTemplateArg(unsigned I) 
const {
 SourceLocation ExplicitInstantiationDecl::getTemplateArgsLAngleLoc() const {
   if (const auto *Args = getTrailingArgsInfo())
     return Args->getLAngleLoc();
-  if (auto *TSI = getRawTypeSourceInfo())
-    if (auto TL = TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>())
-      return TL.getLAngleLoc();
+  if (isClassOrNestedClassSpec())
+    if (auto *TSI = getRawTypeSourceInfo())
+      if (auto TL = TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>())
+        return TL.getLAngleLoc();
   return SourceLocation();
 }
 
 SourceLocation ExplicitInstantiationDecl::getTemplateArgsRAngleLoc() const {
   if (const auto *Args = getTrailingArgsInfo())
     return Args->getRAngleLoc();
-  if (auto *TSI = getRawTypeSourceInfo())
-    if (auto TL = TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>())
-      return TL.getRAngleLoc();
+  if (isClassOrNestedClassSpec())
+    if (auto *TSI = getRawTypeSourceInfo())
+      if (auto TL = TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>())
+        return TL.getRAngleLoc();
   return SourceLocation();
 }
 
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 71c2928b22d53..798efe94d82d1 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -10790,6 +10790,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
 
     VarDecl *Prev = Previous.getAsSingle<VarDecl>();
     VarTemplateDecl *PrevTemplate = Previous.getAsSingle<VarTemplateDecl>();
+    const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr;
 
     if (!PrevTemplate) {
       if (!Prev || !Prev->isStaticDataMember()) {
@@ -10858,6 +10859,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
       // Ignore access control bits, we don't need them for redeclaration
       // checking.
       Prev = cast<VarDecl>(Res.get());
+      ArgsAsWritten = ASTTemplateArgumentListInfo::Create(Context, 
TemplateArgs);
     }
 
     // C++0x [temp.explicit]p2:
@@ -10912,9 +10914,6 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
       return true;
     }
 
-    const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr;
-    if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(Prev))
-      ArgsAsWritten = VTSD->getTemplateArgsAsWritten();
     addExplicitInstantiationDecl(
         Context, CurContext, Prev, ExternLoc, TemplateLoc,
         D.getCXXScopeSpec().getWithLocInContext(Context), ArgsAsWritten,
diff --git a/clang/test/AST/ast-print-explicit-instantiation.cpp 
b/clang/test/AST/ast-print-explicit-instantiation.cpp
index 6c794e9575039..40e71881e9fdf 100644
--- a/clang/test/AST/ast-print-explicit-instantiation.cpp
+++ b/clang/test/AST/ast-print-explicit-instantiation.cpp
@@ -25,6 +25,7 @@ template int ns::var<int>;
 // CHECK: extern template float ns::var<float>;
 extern template float ns::var<float>;
 
+
 template <typename T> struct X { struct Inner {}; };
 // CHECK: template struct X<int>::Inner;
 template struct X<int>::Inner;
@@ -58,3 +59,26 @@ void A<T>::B<U>::g(V) {}
 
 // CHECK: template void A<int>::B<double>::g<float>(float);
 template void A<int>::B<double>::g<float>(float);
+
+namespace GH197797 {
+struct S {};
+enum E { X };
+template <typename T> struct Wrap {};
+
+// Variable templates with tag / class-template-specialization declared types.
+template <typename T> T var = T{};
+// CHECK: extern template S var<S>;
+extern template S var<S>;
+// CHECK: extern template E var<E>;
+extern template E var<E>;
+// CHECK: extern template Wrap<int> var<Wrap<int>>;
+extern template Wrap<int> var<Wrap<int>>;
+
+// Variable declared type has a qualifier but the variable itself does not:
+// the type's qualifier must not leak into the variable name.
+template <typename T> T var2 = T{};
+// CHECK: extern template ns::S<int> var2<ns::S<int>>;
+extern template ns::S<int> var2<ns::S<int>>;
+// CHECK: extern template ns::S<float> var2<ns::S<float>>;
+extern template ns::S<float> var2<ns::S<float>>;
+} // namespace GH197797
diff --git a/clang/test/AST/explicit-instantiation-source-info.cpp 
b/clang/test/AST/explicit-instantiation-source-info.cpp
index 3086fd261dd25..b304bb6114946 100644
--- a/clang/test/AST/explicit-instantiation-source-info.cpp
+++ b/clang/test/AST/explicit-instantiation-source-info.cpp
@@ -1,5 +1,9 @@
 // RUN: %clang_cc1 -fsyntax-only -ast-dump %s | FileCheck %s
 
+struct Plain {};
+enum Color { Red };
+template <typename T> struct Wrap {};
+
 namespace ns {
   template <typename T> void foo(T x) {}
   template <typename T> T bar = T{};
@@ -212,3 +216,43 @@ namespace ns {
   // CHECK-NEXT: TemplateArgument <col:21> type 'short'
   // CHECK-NEXT:   BuiltinType {{.*}} 'short'
 }
+
+// GH197797: variable template with tag declared type
+extern template Plain ns::bar<Plain>;
+// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:36> col:8 
explicit_instantiation_declaration extern 'bar'
+// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns'
+// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'Plain'
+// CHECK-NEXT: RecordTypeLoc <col:17> 'Plain'
+// CHECK:      TemplateArgument <col:31> type 'Plain'
+
+// Definition after declaration — template arg locations must be independent.
+template Plain ns::bar<Plain>;
+// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:29> col:1 
explicit_instantiation_definition 'bar'
+// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns'
+// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'Plain'
+// CHECK-NEXT: RecordTypeLoc <col:10> 'Plain'
+// CHECK:      TemplateArgument <col:24> type 'Plain'
+
+// Variable template with enum declared type.
+extern template Color ns::bar<Color>;
+// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:36> col:8 
explicit_instantiation_declaration extern 'bar'
+// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns'
+// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'Color'
+// CHECK-NEXT: EnumTypeLoc <col:17> 'Color'
+// CHECK:      TemplateArgument <col:31> type 'Color'
+
+// Variable template with class template specialization declared type.
+extern template Wrap<int> ns::bar<Wrap<int>>;
+// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:44> col:8 
explicit_instantiation_declaration extern 'bar'
+// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns'
+// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'Wrap<int>'
+// CHECK-NEXT: TemplateSpecializationTypeLoc <col:17, col:25> 'Wrap<int>'
+
+// Variable template: type has qualifier, variable does not — no leaking.
+namespace ns {
+  extern template Wrap<int> bar<Wrap<int>>;
+  // CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:3, col:42> 
col:10 explicit_instantiation_declaration extern 'bar'
+  // CHECK-NOT: NestedNameSpecifier
+  // CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'Wrap<int>'
+  // CHECK-NEXT: TemplateSpecializationTypeLoc <col:19, col:27> 'Wrap<int>'
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to