diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index 5e9463d54747d..d184e03355077 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -686,9 +686,11 @@ class ASTNodeTraverser void VisitExplicitInstantiationDecl(const ExplicitInstantiationDecl *D) { if (TypeSourceInfo *TSI = D->getTypeAsWritten()) Visit(TSI->getTypeLoc()); - for (unsigned I = 0, E = D->getNumTemplateArgs(); I != E; ++I) { - TemplateArgumentLoc Loc = D->getTemplateArg(I); - Visit(Loc.getArgument(), Loc.getSourceRange()); + if (auto NumArgs = D->getNumTemplateArgs()) { + for (unsigned I = 0; I != *NumArgs; ++I) { + TemplateArgumentLoc Loc = D->getTemplateArg(I); + Visit(Loc.getArgument(), Loc.getSourceRange()); + } } } diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 9fb41c87da732..a254b171f6900 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 getClassTypeLoc() const { + if (!isa(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,19 @@ 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; + /// Returns the number of explicit template arguments, or std::nullopt if + /// this entity has no template argument list (e.g., nested classes). + std::optional 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/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index b5be0910194bd..febdf715698d9 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1765,8 +1765,9 @@ DEF_TRAVERSE_DECL(ExplicitInstantiationDecl, { TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); if (TypeSourceInfo *TSI = D->getTypeAsWritten()) TRY_TO(TraverseTypeLoc(TSI->getTypeLoc())); - for (unsigned I = 0, E = D->getNumTemplateArgs(); I != E; ++I) - TRY_TO(TraverseTemplateArgumentLoc(D->getTemplateArg(I))); + if (auto NumArgs = D->getNumTemplateArgs()) + for (unsigned I = 0; I != *NumArgs; ++I) + TRY_TO(TraverseTemplateArgumentLoc(D->getTemplateArg(I))); }) DEF_TRAVERSE_DECL(TranslationUnitDecl, { diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 7e4c1100dbe32..95591d3a09431 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -1346,9 +1346,9 @@ void DeclPrinter::VisitExplicitInstantiationDecl(ExplicitInstantiationDecl *D) { if (D->getQualifierLoc()) D->getQualifierLoc().getNestedNameSpecifier().print(NameOS, Policy); Spec->printName(NameOS, Policy); - if (unsigned NumArgs = D->getNumTemplateArgs()) { + if (auto NumArgs = D->getNumTemplateArgs(); NumArgs && *NumArgs > 0) { SmallVector Args; - for (unsigned I = 0; I < NumArgs; ++I) + for (unsigned I = 0; I < *NumArgs; ++I) Args.push_back(D->getTemplateArg(I)); printTemplateArgumentList(NameOS, Args, Policy); } diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 08e6512a1c74d..66b8372562e9d 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()) - return TL.getElaboratedKeywordLoc(); - if (auto TL = TSI->getTypeLoc().getAs()) - return TL.getElaboratedKeywordLoc(); + if (auto TL = getClassTypeLoc()) { + if (auto TST = TL->getAs()) + return TST.getElaboratedKeywordLoc(); + if (auto Tag = TL->getAs()) + return Tag.getElaboratedKeywordLoc(); } return SourceLocation(); } @@ -1855,56 +1855,53 @@ SourceLocation ExplicitInstantiationDecl::getTagKWLoc() const { NestedNameSpecifierLoc ExplicitInstantiationDecl::getQualifierLoc() const { if (hasTrailingQualifier()) return *getTrailingObjects(); - 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() || TL.getAs()) - return nullptr; - return TSI; + return getRawTypeSourceInfo(); } -unsigned ExplicitInstantiationDecl::getNumTemplateArgs() const { +std::optional ExplicitInstantiationDecl::getNumTemplateArgs() const { if (const auto *Args = getTrailingArgsInfo()) return Args->NumTemplateArgs; - if (auto *TSI = getRawTypeSourceInfo()) - if (auto TL = TSI->getTypeLoc().getAs()) - return TL.getNumArgs(); - return 0; + if (auto TL = getClassTypeLoc()) + if (auto TST = TL->getAs()) + return TST.getNumArgs(); + return std::nullopt; } TemplateArgumentLoc ExplicitInstantiationDecl::getTemplateArg(unsigned I) const { if (const auto *Args = getTrailingArgsInfo()) return (*Args)[I]; - auto *TSI = getRawTypeSourceInfo(); - return TSI->getTypeLoc().castAs().getArgLoc(I); + if (auto TL = getClassTypeLoc()) + if (auto TST = TL->getAs()) + return TST.getArgLoc(I); + llvm_unreachable("template arguments not found in trailing args or TypeLoc"); } SourceLocation ExplicitInstantiationDecl::getTemplateArgsLAngleLoc() const { if (const auto *Args = getTrailingArgsInfo()) return Args->getLAngleLoc(); - if (auto *TSI = getRawTypeSourceInfo()) - if (auto TL = TSI->getTypeLoc().getAs()) - return TL.getLAngleLoc(); - return SourceLocation(); + if (auto TL = getClassTypeLoc()) + if (auto TST = TL->getAs()) + return TST.getLAngleLoc(); + llvm_unreachable("template arguments not found in trailing args or TypeLoc"); } SourceLocation ExplicitInstantiationDecl::getTemplateArgsRAngleLoc() const { if (const auto *Args = getTrailingArgsInfo()) return Args->getRAngleLoc(); - if (auto *TSI = getRawTypeSourceInfo()) - if (auto TL = TSI->getTypeLoc().getAs()) - return TL.getRAngleLoc(); - return SourceLocation(); + if (auto TL = getClassTypeLoc()) + if (auto TST = TL->getAs()) + return TST.getRAngleLoc(); + llvm_unreachable("template arguments not found in trailing args or TypeLoc"); } SourceLocation ExplicitInstantiationDecl::getEndLoc() const { @@ -1914,8 +1911,12 @@ SourceLocation ExplicitInstantiationDecl::getEndLoc() const { if (TSI->getType().hasPostfixDeclaratorSyntax()) return TSI->getTypeLoc().getEndLoc(); // Otherwise, template args RAngleLoc or NameLoc. - SourceLocation RAngle = getTemplateArgsRAngleLoc(); - return RAngle.isValid() ? RAngle : NameLoc; + if (getNumTemplateArgs()) { + SourceLocation RAngle = getTemplateArgsRAngleLoc(); + if (RAngle.isValid()) + return RAngle; + } + return NameLoc; } SourceRange ExplicitInstantiationDecl::getSourceRange() const { 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(); VarTemplateDecl *PrevTemplate = Previous.getAsSingle(); + 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(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(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..9c8ec810220e8 100644 --- a/clang/test/AST/ast-print-explicit-instantiation.cpp +++ b/clang/test/AST/ast-print-explicit-instantiation.cpp @@ -20,24 +20,43 @@ template void bar(T) {} // CHECK: template void bar(int); template void bar(int); +// empty <> (args deduced) +// CHECK: template void ns::foo(double); +template void ns::foo<>(double); + // CHECK: template int ns::var; template int ns::var; // CHECK: extern template float ns::var; extern template float ns::var; +namespace ns { +// CHECK: template struct S; +template struct S; +// CHECK: template void foo(short); +template void foo(short); +// CHECK: template short var; +template short var; +} + template struct X { struct Inner {}; }; // CHECK: template struct X::Inner; template struct X::Inner; +// CHECK: extern template struct X::Inner; +extern template struct X::Inner; template struct Outer { void method(); template void f(U); template static U var; template struct Inner {}; + static T sval; + static T arr[1]; }; template void Outer::method() {} template template void Outer::f(U) {} template template U Outer::var = U{}; +template T Outer::sval = T{}; +template T Outer::arr[1] = {}; // CHECK: template void Outer::method(); template void Outer::method(); @@ -47,6 +66,21 @@ template void Outer::f(double); template double Outer::var; // CHECK: template struct Outer::Inner; template struct Outer::Inner; +// CHECK: template int Outer::sval; +template int Outer::sval; +// CHECK: template int Outer::arr[1]; +template int Outer::arr[1]; + +// CHECK: extern template void Outer::method(); +extern template void Outer::method(); +// CHECK: extern template void Outer::f(double); +extern template void Outer::f(double); +// CHECK: extern template double Outer::var; +extern template double Outer::var; +// CHECK: extern template struct Outer::Inner; +extern template struct Outer::Inner; +// CHECK: extern template int Outer::sval; +extern template int Outer::sval; template struct A { template struct B { @@ -58,3 +92,34 @@ void A::B::g(V) {} // CHECK: template void A::B::g(float); template void A::B::g(float); +// CHECK: extern template void A::B::g(int); +extern template void A::B::g(int); + +namespace GH197797 { +struct S {}; +enum E { X }; +template struct Wrap {}; + +template T var = T{}; +// CHECK: extern template S var; +extern template S var; +// CHECK: extern template E var; +extern template E var; +// CHECK: extern template Wrap var>; +extern template Wrap var>; + +template T var2 = T{}; +// CHECK: extern template ns::S var2>; +extern template ns::S var2>; +// CHECK: extern template ns::S var2>; +extern template ns::S var2>; +} // namespace GH197797 + +// empty <> (default template arguments) +template struct Defaulted {}; +// CHECK: template struct Defaulted; +template struct Defaulted<>; + +template T defaulted_var = T{}; +// CHECK: template int defaulted_var; +template int defaulted_var<>; diff --git a/clang/test/AST/explicit-instantiation-source-info.cpp b/clang/test/AST/explicit-instantiation-source-info.cpp index 3086fd261dd25..648a83a365842 100644 --- a/clang/test/AST/explicit-instantiation-source-info.cpp +++ b/clang/test/AST/explicit-instantiation-source-info.cpp @@ -1,5 +1,11 @@ // RUN: %clang_cc1 -fsyntax-only -ast-dump %s | FileCheck %s +struct Plain {}; +enum Color { Red }; +template struct Wrap {}; +template struct Defaulted {}; +template T defaulted_var = T{}; + namespace ns { template void foo(T x) {} template T bar = T{}; @@ -158,6 +164,43 @@ extern template struct ns::S::Inner; // CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' // CHECK-NEXT: CXXRecord {{.*}} 'Inner' +// extern template: member variable template +extern template double ns::S::mvar; +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'mvar' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'mvar' 'double' +// CHECK-NEXT: BuiltinTypeLoc 'double' +// CHECK-NEXT: TemplateArgument type 'double' +// CHECK-NEXT: BuiltinType {{.*}} 'double' + +// extern template: member class template +extern template struct ns::S::Nested; +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'Nested' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: ClassTemplateSpecialization {{.*}} 'Nested' +// CHECK-NEXT: TemplateArgument type 'double' +// CHECK-NEXT: BuiltinType {{.*}} 'double' + +// extern template: static data member (array) +extern template double ns::S::arr[1]; +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'arr' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: Var {{.*}} 'arr' 'double[1]' +// CHECK-NEXT: ConstantArrayTypeLoc 'double[1]' 1 +// CHECK-NEXT: BuiltinTypeLoc 'double' + +// extern template: deeply nested +extern template void ns::A::B::deep(double); +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'deep' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::A::B' +// CHECK-NEXT: CXXMethod {{.*}} 'deep' 'void (double)' +// CHECK-NEXT: FunctionProtoTypeLoc 'void (double)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} col:63 'double' +// CHECK-NEXT: BuiltinTypeLoc 'double' +// CHECK-NEXT: BuiltinTypeLoc 'void' +// CHECK-NEXT: TemplateArgument type 'double' +// CHECK-NEXT: BuiltinType {{.*}} 'double' + // member variable template template double ns::S::mvar; // CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'mvar' @@ -212,3 +255,67 @@ namespace ns { // CHECK-NEXT: TemplateArgument type 'short' // CHECK-NEXT: BuiltinType {{.*}} 'short' } + +// empty <> (args deduced) +template void ns::foo<>(double); +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'foo' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: Function {{.*}} 'foo' 'void (double)' +// CHECK-NEXT: FunctionProtoTypeLoc 'void (double)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} col:31 'double' +// CHECK-NEXT: BuiltinTypeLoc 'double' +// CHECK-NEXT: BuiltinTypeLoc 'void' + +template void ns::S::tmpl<>(float); +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'tmpl' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: CXXMethod {{.*}} 'tmpl' 'void (float)' +// CHECK-NEXT: FunctionProtoTypeLoc 'void (float)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} col:40 'float' +// CHECK-NEXT: BuiltinTypeLoc 'float' +// CHECK-NEXT: BuiltinTypeLoc 'void' + +// empty <> (default template arguments) +template struct Defaulted<>; +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'Defaulted' +// CHECK-NEXT: ClassTemplateSpecialization {{.*}} 'Defaulted' + +template int defaulted_var<>; +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'defaulted_var' +// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'defaulted_var' 'int' +// CHECK-NEXT: BuiltinTypeLoc 'int' + +extern template Plain ns::bar; +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'bar' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'Plain' +// CHECK-NEXT: RecordTypeLoc 'Plain' +// CHECK: TemplateArgument type 'Plain' + +template Plain ns::bar; +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'bar' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'Plain' +// CHECK-NEXT: RecordTypeLoc 'Plain' +// CHECK: TemplateArgument type 'Plain' + +extern template Color ns::bar; +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'bar' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'Color' +// CHECK-NEXT: EnumTypeLoc 'Color' +// CHECK: TemplateArgument type 'Color' + +extern template Wrap ns::bar>; +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'bar' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'Wrap' +// CHECK-NEXT: TemplateSpecializationTypeLoc 'Wrap' + +namespace ns { + extern template Wrap bar>; + // CHECK: ExplicitInstantiationDecl {{.*}} col:10 explicit_instantiation_declaration extern 'bar' + // CHECK-NOT: NestedNameSpecifier + // CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'Wrap' + // CHECK-NEXT: TemplateSpecializationTypeLoc 'Wrap' +}