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

>From b174a985348759ab0dee487afcf8f3a90293d9a4 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

---
 clang/include/clang/AST/DeclTemplate.h        | 38 ++++-----
 clang/lib/AST/DeclTemplate.cpp                | 50 ++++++------
 clang/lib/Sema/SemaTemplate.cpp               |  5 +-
 .../AST/ast-print-explicit-instantiation.cpp  | 56 +++++++++++++
 .../explicit-instantiation-source-info.cpp    | 81 +++++++++++++++++++
 5 files changed, 182 insertions(+), 48 deletions(-)

diff --git a/clang/include/clang/AST/DeclTemplate.h 
b/clang/include/clang/AST/DeclTemplate.h
index 9fb41c87da732..efc9021797148 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3449,9 +3449,19 @@ class ExplicitInstantiationDecl final
     return hasTrailingQualifier() ? 1 : 0;
   }
 
-  /// 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.
+  /// For class templates / nested classes, returns the TypeLoc encoding the
+  /// entity (TemplateSpecializationTypeLoc or TagTypeLoc).  For function /
+  /// variable templates — where TypeSourceInfo holds the declared type
+  /// rather than the entity — returns std::nullopt.
+  std::optional<TypeLoc> getClassTypeLoc() const {
+    if (!isa<RecordDecl>(getSpecialization()))
+      return std::nullopt;
+    if (auto *TSI = TypeAndFlags.getPointer())
+      return TSI->getTypeLoc();
+    return std::nullopt;
+  }
+
+  /// Raw TypeSourceInfo pointer, needed by the serializer.
   TypeSourceInfo *getRawTypeSourceInfo() const {
     return TypeAndFlags.getPointer();
   }
@@ -3493,13 +3503,10 @@ class ExplicitInstantiationDecl final
   SourceLocation getTemplateLoc() const { return getLocation(); }
   SourceLocation getNameLoc() const { return NameLoc; }
 
-  /// For class templates / nested classes, the tag keyword location is
-  /// stored inside TypeSourceInfo; otherwise returns an invalid location.
+  /// The tag keyword (struct/class/union) location for class templates /
+  /// nested classes; invalid for function / variable templates.
   SourceLocation getTagKWLoc() const;
 
-  /// Whether the qualifier is stored as a trailing object (function / variable
-  /// templates) rather than inside TypeSourceInfo (class templates / nested
-  /// classes).
   bool hasTrailingQualifier() const {
     return TypeAndFlags.getInt() & HasQualifierFlag;
   }
@@ -3508,24 +3515,17 @@ class ExplicitInstantiationDecl final
   }
 
   /// Returns the qualifier regardless of where it is stored.
-  /// For class templates / nested classes, it is extracted from TypeSourceInfo
-  /// (TemplateSpecializationTypeLoc or TagTypeLoc).
-  /// For function / variable templates, it comes from a trailing object.
+  /// For class templates / nested classes, extracted from the class TypeLoc;
+  /// for function / variable templates, from a trailing object.
   NestedNameSpecifierLoc getQualifierLoc() const;
 
-  /// Number of explicit template arguments, regardless of storage.
-  /// For class templates they come from TemplateSpecializationTypeLoc;
-  /// for function / variable templates from trailing
-  /// ASTTemplateArgumentListInfo.
   unsigned getNumTemplateArgs() const;
   TemplateArgumentLoc getTemplateArg(unsigned I) const;
   SourceLocation getTemplateArgsLAngleLoc() const;
   SourceLocation getTemplateArgsRAngleLoc() const;
 
-  /// For function / variable templates, returns the declared type (return type
-  /// or variable type).  For class templates and nested classes returns null —
-  /// the qualifier, tag keyword, and template arguments are accessible via
-  /// getQualifierLoc(), getTagKWLoc(), and getTemplateArg().
+  /// The declared type (return type or variable type) for function / variable
+  /// templates.  Null for class templates and nested classes.
   TypeSourceInfo *getTypeAsWritten() const;
 
   TemplateSpecializationKind getTemplateSpecializationKind() const {
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 08e6512a1c74d..e1e3088cdf1e5 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1843,11 +1843,11 @@ 
ExplicitInstantiationDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
 }
 
 SourceLocation ExplicitInstantiationDecl::getTagKWLoc() const {
-  if (auto *TSI = getRawTypeSourceInfo()) {
-    if (auto TL = TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>())
-      return TL.getElaboratedKeywordLoc();
-    if (auto TL = TSI->getTypeLoc().getAs<TagTypeLoc>())
-      return TL.getElaboratedKeywordLoc();
+  if (auto TL = getClassTypeLoc()) {
+    if (auto TST = TL->getAs<TemplateSpecializationTypeLoc>())
+      return TST.getElaboratedKeywordLoc();
+    if (auto Tag = TL->getAs<TagTypeLoc>())
+      return Tag.getElaboratedKeywordLoc();
   }
   return SourceLocation();
 }
@@ -1855,29 +1855,24 @@ SourceLocation ExplicitInstantiationDecl::getTagKWLoc() 
const {
 NestedNameSpecifierLoc ExplicitInstantiationDecl::getQualifierLoc() const {
   if (hasTrailingQualifier())
     return *getTrailingObjects<NestedNameSpecifierLoc>();
-  if (auto *TSI = getRawTypeSourceInfo())
-    return TSI->getTypeLoc().getPrefix();
+  if (auto TL = getClassTypeLoc())
+    return TL->getPrefix();
   return NestedNameSpecifierLoc();
 }
 
 TypeSourceInfo *ExplicitInstantiationDecl::getTypeAsWritten() const {
-  auto *TSI = getRawTypeSourceInfo();
-  if (!TSI)
+  // For class-like entities, TSI encodes the class itself, not a declared 
type.
+  if (getClassTypeLoc())
     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;
-  return TSI;
+  return getRawTypeSourceInfo();
 }
 
 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 (auto TL = getClassTypeLoc())
+    if (auto TST = TL->getAs<TemplateSpecializationTypeLoc>())
+      return TST.getNumArgs();
   return 0;
 }
 
@@ -1885,25 +1880,28 @@ TemplateArgumentLoc
 ExplicitInstantiationDecl::getTemplateArg(unsigned I) const {
   if (const auto *Args = getTrailingArgsInfo())
     return (*Args)[I];
-  auto *TSI = getRawTypeSourceInfo();
-  return 
TSI->getTypeLoc().castAs<TemplateSpecializationTypeLoc>().getArgLoc(I);
+  // Must be a class template — args live in the TemplateSpecializationTypeLoc.
+  return getRawTypeSourceInfo()
+      ->getTypeLoc()
+      .castAs<TemplateSpecializationTypeLoc>()
+      .getArgLoc(I);
 }
 
 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 (auto TL = getClassTypeLoc())
+    if (auto TST = TL->getAs<TemplateSpecializationTypeLoc>())
+      return TST.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 (auto TL = getClassTypeLoc())
+    if (auto TST = TL->getAs<TemplateSpecializationTypeLoc>())
+      return TST.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..a8a49bac0df15 100644
--- a/clang/test/AST/ast-print-explicit-instantiation.cpp
+++ b/clang/test/AST/ast-print-explicit-instantiation.cpp
@@ -25,19 +25,35 @@ template int ns::var<int>;
 // CHECK: extern template float ns::var<float>;
 extern template float ns::var<float>;
 
+namespace ns {
+// CHECK: template struct S<short>;
+template struct S<short>;
+// CHECK: template void foo<short>(short);
+template void foo<short>(short);
+// CHECK: template short var<short>;
+template short var<short>;
+}
+
+
 template <typename T> struct X { struct Inner {}; };
 // CHECK: template struct X<int>::Inner;
 template struct X<int>::Inner;
+// CHECK: extern template struct X<float>::Inner;
+extern template struct X<float>::Inner;
 
 template <typename T> struct Outer {
   void method();
   template <typename U> void f(U);
   template <typename U> static U var;
   template <typename U> struct Inner {};
+  static T sval;
+  static T arr[1];
 };
 template <typename T> void Outer<T>::method() {}
 template <typename T> template <typename U> void Outer<T>::f(U) {}
 template <typename T> template <typename U> U Outer<T>::var = U{};
+template <typename T> T Outer<T>::sval = T{};
+template <typename T> T Outer<T>::arr[1] = {};
 
 // CHECK: template void Outer<int>::method();
 template void Outer<int>::method();
@@ -47,6 +63,21 @@ template void Outer<int>::f<double>(double);
 template double Outer<int>::var<double>;
 // CHECK: template struct Outer<int>::Inner<double>;
 template struct Outer<int>::Inner<double>;
+// CHECK: template int Outer<int>::sval;
+template int Outer<int>::sval;
+// CHECK: template int Outer<int>::arr[1];
+template int Outer<int>::arr[1];
+
+// CHECK: extern template void Outer<float>::method();
+extern template void Outer<float>::method();
+// CHECK: extern template void Outer<float>::f<double>(double);
+extern template void Outer<float>::f<double>(double);
+// CHECK: extern template double Outer<float>::var<double>;
+extern template double Outer<float>::var<double>;
+// CHECK: extern template struct Outer<float>::Inner<double>;
+extern template struct Outer<float>::Inner<double>;
+// CHECK: extern template int Outer<float>::sval;
+extern template int Outer<float>::sval;
 
 template <typename T> struct A {
   template <typename U> struct B {
@@ -58,3 +89,28 @@ 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);
+// CHECK: extern template void A<float>::B<double>::g<int>(int);
+extern template void A<float>::B<double>::g<int>(int);
+
+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..cc9aa6c5054a6 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{};
@@ -158,6 +162,43 @@ extern template struct ns::S<double>::Inner;
 // CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S<double>'
 // CHECK-NEXT: CXXRecord {{.*}} 'Inner'
 
+// extern template: member variable template
+extern template double ns::S<double>::mvar<double>;
+// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:50> col:8 
explicit_instantiation_declaration extern 'mvar'
+// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S<double>'
+// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'mvar' 'double'
+// CHECK-NEXT: BuiltinTypeLoc <col:17> 'double'
+// CHECK-NEXT: TemplateArgument <col:44> type 'double'
+// CHECK-NEXT:   BuiltinType {{.*}} 'double'
+
+// extern template: member class template
+extern template struct ns::S<double>::Nested<double>;
+// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:52> col:8 
explicit_instantiation_declaration extern 'Nested'
+// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S<double>'
+// CHECK-NEXT: ClassTemplateSpecialization {{.*}} 'Nested'
+// CHECK-NEXT: TemplateArgument <col:46> type 'double'
+// CHECK-NEXT:   BuiltinType {{.*}} 'double'
+
+// extern template: static data member (array)
+extern template double ns::S<double>::arr[1];
+// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:44> col:8 
explicit_instantiation_declaration extern 'arr'
+// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S<double>'
+// CHECK-NEXT: Var {{.*}} 'arr' 'double[1]'
+// CHECK-NEXT: ConstantArrayTypeLoc <col:17, col:44> 'double[1]' 1
+// CHECK-NEXT:   BuiltinTypeLoc <col:17> 'double'
+
+// extern template: deeply nested
+extern template void ns::A<int>::B<float>::deep<double>(double);
+// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:63> col:8 
explicit_instantiation_declaration extern 'deep'
+// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::A<int>::B<float>'
+// CHECK-NEXT: CXXMethod {{.*}} 'deep' 'void (double)'
+// CHECK-NEXT: FunctionProtoTypeLoc <col:17, col:63> 'void (double)' cdecl
+// CHECK-NEXT:   ParmVarDecl {{.*}} <col:57> col:63 'double'
+// CHECK-NEXT:     BuiltinTypeLoc <col:57> 'double'
+// CHECK-NEXT:   BuiltinTypeLoc <col:17> 'void'
+// CHECK-NEXT: TemplateArgument <col:49> type 'double'
+// CHECK-NEXT:   BuiltinType {{.*}} 'double'
+
 // member variable template
 template double ns::S<long>::mvar<double>;
 // CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:41> col:1 
explicit_instantiation_definition 'mvar'
@@ -212,3 +253,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