https://github.com/16bit-ykiko updated https://github.com/llvm/llvm-project/pull/197856
>From 96e8c1d67b64498e1ef77f1448ae267ae298ffeb 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 | 6 +- .../AST/ast-print-explicit-instantiation.cpp | 56 +++++++++++++ .../explicit-instantiation-source-info.cpp | 81 +++++++++++++++++++ 5 files changed, 183 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..707a700c9244f 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,8 @@ 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 +10915,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
