https://github.com/16bit-ykiko created https://github.com/llvm/llvm-project/pull/191658
This is the initial fix of https://github.com/llvm/llvm-project/issues/191442. Following the discussion here https://github.com/llvm/llvm-project/issues/115418#issuecomment-2467017012. ### Description This PR introduces a new AST node, `ExplicitInstantiationDecl`, to systematically fix the long-standing issue of missing or incorrect source location information for explicit template instantiations (Issue #191442). As a direct result of this new architecture, it also resolves Issue #21133 by ensuring that duplicate explicit instantiation diagnostics point to the actual lexical occurrence rather than the implicit point of instantiation. #### Background & The Problem Historically, Clang's AST lacked a dedicated node to represent the lexical occurrence of an explicit instantiation statement. Instead, `Sema` tried to shoehorn this information into existing specialization nodes (e.g., `FunctionDecl`, `VarTemplateSpecializationDecl`) or simply returned `nullptr`. This resulted in fragmented behavior across the seven instantiable entity types: * Function & Member Function Templates: Returned `nullptr`, completely losing `SourceRange` and `NestedNameSpecifier` information. * Member Functions & Static Data Members: Mutated existing nodes in-place. Consequently, multiple `template` or `extern template` declarations in the same file would overwrite each other's source locations. * Variable Templates: Suffered from `dyn_cast` bugs and dropped NNS information. #### Design Trade-offs Evaluated Before settling on the current design, I evaluated a mixed redeclaration-chain approach (similar to how explicit *specializations* are handled, creating new `FunctionDecl` nodes and stitching them into the redecl chain). However, this approach had significant flaws: 1. Inconsistency: It couldn't be cleanly applied to member functions or static data members due to `DeclContext` constraints (e.g., a member function shouldn't lexically reside in a namespace `DeclContext`, but placing it in the class context would pollute member lookup). 2. Fragility: It required bypassing standard `FoldingSet` mechanisms (`setFunctionTemplateSpecialization`). 3. Lookup Pollution: Injecting new `NamedDecl` nodes purely for instantiations risked breaking downstream `ASTMatcher`s and altering name lookup behavior. #### The Chosen Architecture: `ExplicitInstantiationDecl` To avoid these pitfalls, this PR introduces `ExplicitInstantiationDecl` as a **purely lexical annotation node**. **Key Design Characteristics:** 1. Inherits from `Decl`, not `NamedDecl`: This is the most crucial design choice. Much like `StaticAssertDecl` or `FriendDecl`, this node lives in a `DeclContext` (making it traversable by `RecursiveASTVisitor` and visible in AST dumps) but remains completely invisible to C++ name lookup. It does not interfere with overload resolution or lookup tables. 2. Unified Representation: A single node type now covers all seven entity types. It holds a pointer (`Specialization`) to the underlying instantiated declaration, unifying how functions, variables, classes, and members are handled. 3. Lexical Fidelity: The node resides in the enclosing namespace or Translation Unit where the explicit instantiation was actually written, perfectly preserving the `SourceRange`, `NestedNameSpecifierLoc`, and the exact locations of the `template` and `extern` keywords. - Fix #21040 - Fix #52659 - Fix #115418 - Fix #14230 - Fix #21133 (Diagnostic Locations) > Because every explicit instantiation is now explicitly recorded in its > `DeclContext`, `DiagLocForExplicitInstantiation` no longer relies on the > often-inaccurate `PointOfInstantiation` (which frequently points to implicit > instantiation sites). Instead, it searches the `DeclContext` hierarchy to > find the corresponding `ExplicitInstantiationDecl`. As a result, the `note: > previous explicit instantiation is here` diagnostic now correctly points to > the exact lexical location. >From 15a609e9782ee3637244f0c6677d19eeda5a7b1b Mon Sep 17 00:00:00 2001 From: ykiko <[email protected]> Date: Sun, 12 Apr 2026 03:02:08 +0800 Subject: [PATCH] [clang] Add ExplicitInstantiationDecl to preserve source info for explicit template instantiations --- clang/include/clang/AST/ASTNodeTraverser.h | 11 ++ clang/include/clang/AST/DeclTemplate.h | 116 ++++++++++++ clang/include/clang/AST/JSONNodeDumper.h | 1 + clang/include/clang/AST/RecursiveASTVisitor.h | 16 +- clang/include/clang/AST/TextNodeDumper.h | 1 + clang/include/clang/Basic/DeclNodes.td | 1 + .../include/clang/Serialization/ASTBitCodes.h | 5 +- clang/lib/AST/DeclTemplate.cpp | 20 +++ clang/lib/AST/JSONNodeDumper.cpp | 26 +++ clang/lib/AST/TextNodeDumper.cpp | 14 ++ clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 1 + clang/lib/CIR/CodeGen/CIRGenModule.cpp | 1 + clang/lib/CodeGen/CGDecl.cpp | 1 + clang/lib/CodeGen/CodeGenModule.cpp | 1 + clang/lib/Sema/SemaTemplate.cpp | 102 +++++++++-- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 8 + clang/lib/Serialization/ASTCommon.cpp | 1 + clang/lib/Serialization/ASTReaderDecl.cpp | 20 +++ clang/lib/Serialization/ASTWriterDecl.cpp | 18 ++ clang/lib/Tooling/Syntax/BuildTree.cpp | 7 + clang/test/AST/ast-dump-templates-pattern.cpp | 32 +++- clang/test/AST/ast-dump-templates.cpp | 62 +++++-- .../explicit-instantiation-source-info.cpp | 166 ++++++++++++++++++ .../explicit-instantiation-diag-location.cpp | 31 ++++ clang/tools/libclang/CIndex.cpp | 2 + 25 files changed, 628 insertions(+), 36 deletions(-) create mode 100644 clang/test/AST/explicit-instantiation-source-info.cpp create mode 100644 clang/test/SemaTemplate/explicit-instantiation-diag-location.cpp diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index 3be24ff868c2d..39a6864bf169e 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -683,6 +683,17 @@ class ASTNodeTraverser Visit(D->getMessage()); } + void VisitExplicitInstantiationDecl(const ExplicitInstantiationDecl *D) { + // The specialization is already elsewhere in the AST; don't re-traverse it. + // Traverse source-location sub-nodes: template arguments and type-as-written. + if (const auto *ArgsAsWritten = D->getTemplateArgsAsWritten()) + for (unsigned I = 0, E = ArgsAsWritten->NumTemplateArgs; I != E; ++I) + Visit((*ArgsAsWritten)[I].getArgument(), + (*ArgsAsWritten)[I].getSourceRange()); + if (TypeSourceInfo *TSI = D->getTypeAsWritten()) + Visit(TSI->getTypeLoc()); + } + void VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { dumpTemplateDecl(D); } diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index a4a1bb9c13c79..b470b520531f1 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -3405,6 +3405,122 @@ getReplacedTemplateParameter(Decl *D, unsigned Index); /// If we have an implicit instantiation, adjust 'D' to refer to template. const Decl &adjustDeclToTemplate(const Decl &D); +/// Represents an explicit instantiation of a template entity in source code. +/// +/// This node records source location information for an explicit instantiation +/// statement. It does not participate in name lookup (inherits from Decl, not +/// NamedDecl), and does not affect code generation. The underlying +/// specialization decl (FunctionDecl, VarDecl, CXXRecordDecl, etc.) continues +/// to handle all semantic and codegen responsibilities. +/// +/// \code +/// template void ns::foo<int>(int); // function template +/// extern template struct ns::S<int>; // class template (extern) +/// template int ns::bar<int>; // variable template +/// template void ns::S<int>::method(int); // member function +/// \endcode +class ExplicitInstantiationDecl : public Decl { + /// The underlying specialization being explicitly instantiated. + NamedDecl *Specialization = nullptr; + + /// The source range of the entire explicit instantiation statement. + SourceRange Range; + + /// Location of the 'extern' keyword (invalid if not extern template). + SourceLocation ExternLoc; + + /// Location of the struct/class/union keyword (for class template and + /// nested class instantiations; invalid otherwise). + SourceLocation TagKWLoc; + + /// Nested name specifier with source locations (e.g., ns::S<int>::). + NestedNameSpecifierLoc QualifierLoc; + + /// Template arguments as written (e.g., <int>). Null if the template + /// arguments were deduced. + const ASTTemplateArgumentListInfo *TemplateArgsAsWritten = nullptr; + + /// Location of the entity name (e.g., 'foo' in 'template void ns::foo<int>(int)'). + SourceLocation NameLoc; + + /// Type source info for the declaration type: + /// - Function templates / member functions: FunctionProtoTypeLoc with + /// return type location and parameter type locations. + /// - Variable templates / static data members: the declared type. + /// - Class templates / nested classes: null. + TypeSourceInfo *TypeAsWritten = nullptr; + + /// Whether this is a declaration (extern template) or definition (template). + LLVM_PREFERRED_TYPE(TemplateSpecializationKind) + unsigned TSK : 3; + + ExplicitInstantiationDecl(DeclContext *DC, SourceRange Range, + NamedDecl *Specialization, + SourceLocation ExternLoc, + SourceLocation TemplateLoc, + SourceLocation TagKWLoc, + NestedNameSpecifierLoc QualifierLoc, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + SourceLocation NameLoc, + TypeSourceInfo *TypeAsWritten, + TemplateSpecializationKind TSK) + : Decl(ExplicitInstantiation, DC, TemplateLoc), + Specialization(Specialization), Range(Range), ExternLoc(ExternLoc), + TagKWLoc(TagKWLoc), QualifierLoc(QualifierLoc), + TemplateArgsAsWritten(ArgsAsWritten), NameLoc(NameLoc), + TypeAsWritten(TypeAsWritten), TSK(TSK) { + assert((TSK == TSK_ExplicitInstantiationDeclaration) == ExternLoc.isValid() + && "ExternLoc should be valid iff TSK is a declaration"); + } + + ExplicitInstantiationDecl(EmptyShell Empty) + : Decl(ExplicitInstantiation, Empty), TSK(TSK_Undeclared) {} + + virtual void anchor(); + +public: + friend class ASTDeclReader; + friend class ASTDeclWriter; + + static ExplicitInstantiationDecl * + Create(ASTContext &C, DeclContext *DC, SourceRange Range, + NamedDecl *Specialization, SourceLocation ExternLoc, + SourceLocation TemplateLoc, SourceLocation TagKWLoc, + NestedNameSpecifierLoc QualifierLoc, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + SourceLocation NameLoc, TypeSourceInfo *TypeAsWritten, + TemplateSpecializationKind TSK); + + static ExplicitInstantiationDecl *CreateDeserialized(ASTContext &C, + GlobalDeclID ID); + + NamedDecl *getSpecialization() const { return Specialization; } + + SourceRange getSourceRange() const override LLVM_READONLY { return Range; } + + SourceLocation getExternLoc() const { return ExternLoc; } + SourceLocation getTemplateLoc() const { return getLocation(); } + SourceLocation getTagKWLoc() const { return TagKWLoc; } + SourceLocation getNameLoc() const { return NameLoc; } + + NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; } + + const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const { + return TemplateArgsAsWritten; + } + + TypeSourceInfo *getTypeAsWritten() const { return TypeAsWritten; } + + TemplateSpecializationKind getTemplateSpecializationKind() const { + return static_cast<TemplateSpecializationKind>(TSK); + } + + bool isExternTemplate() const { return ExternLoc.isValid(); } + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == ExplicitInstantiation; } +}; + } // namespace clang #endif // LLVM_CLANG_AST_DECLTEMPLATE_H diff --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h index 69dbdbbdb3ecd..4e8d1649bbf8b 100644 --- a/clang/include/clang/AST/JSONNodeDumper.h +++ b/clang/include/clang/AST/JSONNodeDumper.h @@ -268,6 +268,7 @@ class JSONNodeDumper void VisitLinkageSpecDecl(const LinkageSpecDecl *LSD); void VisitAccessSpecDecl(const AccessSpecDecl *ASD); void VisitFriendDecl(const FriendDecl *FD); + void VisitExplicitInstantiationDecl(const ExplicitInstantiationDecl *D); void VisitObjCIvarDecl(const ObjCIvarDecl *D); void VisitObjCMethodDecl(const ObjCMethodDecl *D); diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index ce6ad723191e0..37bcfb5b2ed76 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1748,6 +1748,16 @@ DEF_TRAVERSE_DECL(StaticAssertDecl, { TRY_TO(TraverseStmt(D->getMessage())); }) +DEF_TRAVERSE_DECL(ExplicitInstantiationDecl, { + if (D->getQualifierLoc()) + TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); + if (const auto *ArgsAsWritten = D->getTemplateArgsAsWritten()) + for (unsigned I = 0, E = ArgsAsWritten->NumTemplateArgs; I != E; ++I) + TRY_TO(TraverseTemplateArgumentLoc((*ArgsAsWritten)[I])); + if (TypeSourceInfo *TSI = D->getTypeAsWritten()) + TRY_TO(TraverseTypeLoc(TSI->getTypeLoc())); +}) + DEF_TRAVERSE_DECL(TranslationUnitDecl, { // Code in an unnamed namespace shows up automatically in // decls_begin()/decls_end(). Thus we don't need to recurse on @@ -2027,8 +2037,10 @@ bool RecursiveASTVisitor<Derived>::TraverseTemplateInstantiations( TRY_TO(TraverseDecl(RD)); break; - // FIXME: For now traverse explicit instantiations here. Change that - // once they are represented as dedicated nodes in the AST. + // Unlike class/variable template specializations, function template + // specializations are not independent children of the DeclContext — + // they are only reachable via FunctionTemplateDecl::specializations(). + // We must traverse them here so visitors can see the instantiated body. case TSK_ExplicitInstantiationDeclaration: case TSK_ExplicitInstantiationDefinition: TRY_TO(TraverseDecl(RD)); diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 32e83ebb5c8eb..6b7ac6fac8111 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -395,6 +395,7 @@ class TextNodeDumper void VisitLinkageSpecDecl(const LinkageSpecDecl *D); void VisitAccessSpecDecl(const AccessSpecDecl *D); void VisitFriendDecl(const FriendDecl *D); + void VisitExplicitInstantiationDecl(const ExplicitInstantiationDecl *D); void VisitObjCIvarDecl(const ObjCIvarDecl *D); void VisitObjCMethodDecl(const ObjCMethodDecl *D); void VisitObjCTypeParamDecl(const ObjCTypeParamDecl *D); diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index 04311055bb600..ffb58b43812dc 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -101,6 +101,7 @@ def AccessSpec : DeclNode<Decl>; def Friend : DeclNode<Decl>; def FriendTemplate : DeclNode<Decl>; def StaticAssert : DeclNode<Decl>; +def ExplicitInstantiation : DeclNode<Decl>; def Block : DeclNode<Decl, "blocks">, DeclContext; def OutlinedFunction : DeclNode<Decl>, DeclContext; def Captured : DeclNode<Decl>, DeclContext; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 783cd82895a90..499bd772ae126 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1543,7 +1543,10 @@ enum DeclCode { // An OpenACCRoutineDecl record. DECL_OPENACC_ROUTINE, - DECL_LAST = DECL_OPENACC_ROUTINE + /// An ExplicitInstantiationDecl record. + DECL_EXPLICIT_INSTANTIATION, + + DECL_LAST = DECL_EXPLICIT_INSTANTIATION }; /// Record codes for each kind of statement or expression. diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 99d02fdc99e92..76e3de5394d86 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -1784,3 +1784,23 @@ const Decl &clang::adjustDeclToTemplate(const Decl &D) { // FIXME: Adjust alias templates? return D; } + +void ExplicitInstantiationDecl::anchor() {} + +ExplicitInstantiationDecl *ExplicitInstantiationDecl::Create( + ASTContext &C, DeclContext *DC, SourceRange Range, + NamedDecl *Specialization, SourceLocation ExternLoc, + SourceLocation TemplateLoc, SourceLocation TagKWLoc, + NestedNameSpecifierLoc QualifierLoc, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + SourceLocation NameLoc, TypeSourceInfo *TypeAsWritten, + TemplateSpecializationKind TSK) { + return new (C, DC) ExplicitInstantiationDecl( + DC, Range, Specialization, ExternLoc, TemplateLoc, TagKWLoc, + QualifierLoc, ArgsAsWritten, NameLoc, TypeAsWritten, TSK); +} + +ExplicitInstantiationDecl * +ExplicitInstantiationDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) { + return new (C, ID) ExplicitInstantiationDecl(EmptyShell()); +} diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index 3138f95e6a83b..8373dd8e373e0 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -1119,6 +1119,32 @@ void JSONNodeDumper::VisitAccessSpecDecl(const AccessSpecDecl *ASD) { JOS.attribute("access", createAccessSpecifier(ASD->getAccess())); } +void JSONNodeDumper::VisitExplicitInstantiationDecl( + const ExplicitInstantiationDecl *D) { + attributeOnlyIfTrue("isExternTemplate", D->isExternTemplate()); + if (D->getSpecialization()) + JOS.attribute("specializationDeclId", + createPointerRepresentation(D->getSpecialization())); + switch (D->getTemplateSpecializationKind()) { + case TSK_Undeclared: + break; + case TSK_ImplicitInstantiation: + JOS.attribute("templateSpecializationKind", "implicit_instantiation"); + break; + case TSK_ExplicitSpecialization: + JOS.attribute("templateSpecializationKind", "explicit_specialization"); + break; + case TSK_ExplicitInstantiationDeclaration: + JOS.attribute("templateSpecializationKind", + "explicit_instantiation_declaration"); + break; + case TSK_ExplicitInstantiationDefinition: + JOS.attribute("templateSpecializationKind", + "explicit_instantiation_definition"); + break; + } +} + void JSONNodeDumper::VisitFriendDecl(const FriendDecl *FD) { if (const TypeSourceInfo *T = FD->getFriendType()) JOS.attribute("type", createQualType(T->getType())); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 250ec8b666e05..108d77527f276 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -2936,6 +2936,20 @@ void TextNodeDumper::VisitAccessSpecDecl(const AccessSpecDecl *D) { dumpAccessSpecifier(D->getAccess()); } +void TextNodeDumper::VisitExplicitInstantiationDecl( + const ExplicitInstantiationDecl *D) { + dumpTemplateSpecializationKind(D->getTemplateSpecializationKind()); + if (D->isExternTemplate()) + OS << " extern"; + OS << " template"; + if (D->getQualifierLoc()) + dumpNestedNameSpecifier(D->getQualifierLoc().getNestedNameSpecifier()); + if (const NamedDecl *Spec = D->getSpecialization()) { + OS << " '" << Spec->getDeclName() << "'"; + dumpDeclRef(Spec); + } +} + void TextNodeDumper::VisitFriendDecl(const FriendDecl *D) { if (TypeSourceInfo *T = D->getFriendType()) dumpType(T->getType()); diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index b96b822609c10..3e2d4035fef15 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -831,6 +831,7 @@ void CIRGenFunction::emitDecl(const Decl &d, bool evaluateConditionDecl) { case Decl::Function: // void X(); case Decl::EnumConstant: // enum ? { X = ? } + case Decl::ExplicitInstantiation: case Decl::StaticAssert: // static_assert(X, ""); [C++0x] case Decl::Label: // __label__ x; case Decl::Import: diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 2037b92e2b5d1..abe5834af46ee 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -2031,6 +2031,7 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) { case Decl::Concept: case Decl::CXXDeductionGuide: case Decl::Empty: + case Decl::ExplicitInstantiation: case Decl::FunctionTemplate: case Decl::StaticAssert: case Decl::TypeAliasTemplate: diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 748362105cb02..419b3c477e7b2 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -125,6 +125,7 @@ void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) { case Decl::Function: // void X(); case Decl::EnumConstant: // enum ? { X = ? } case Decl::StaticAssert: // static_assert(X, ""); [C++0x] + case Decl::ExplicitInstantiation: case Decl::Label: // __label__ x; case Decl::Import: case Decl::MSGuid: // __declspec(uuid("...")) diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 68a403e02968d..6a1dcfd55f4f4 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -7777,6 +7777,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { break; case Decl::StaticAssert: + case Decl::ExplicitInstantiation: // Nothing to do. break; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index c436b7018a2bd..66fc2b6fc81e3 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -9314,10 +9314,41 @@ static void StripImplicitInstantiation(NamedDecl *D, bool MinGW) { FD->setInlineSpecified(false); } +/// Create an ExplicitInstantiationDecl to record source-location info for an +/// explicit template instantiation statement, and add it to \p CurContext. +static void addExplicitInstantiationDecl( + ASTContext &Context, DeclContext *CurContext, const CXXScopeSpec &SS, + SourceLocation EndLoc, NamedDecl *Spec, SourceLocation ExternLoc, + SourceLocation TemplateLoc, SourceLocation TagKWLoc, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + SourceLocation NameLoc, TypeSourceInfo *TypeAsWritten, + TemplateSpecializationKind TSK) { + NestedNameSpecifierLoc QualifierLoc; + if (SS.isNotEmpty()) + QualifierLoc = SS.getWithLocInContext(Context); + SourceLocation BeginLoc = ExternLoc.isValid() ? ExternLoc : TemplateLoc; + auto *EID = ExplicitInstantiationDecl::Create( + Context, CurContext, SourceRange(BeginLoc, EndLoc), Spec, ExternLoc, + TemplateLoc, TagKWLoc, QualifierLoc, ArgsAsWritten, NameLoc, + TypeAsWritten, TSK); + CurContext->addDecl(EID); +} + /// Compute the diagnostic location for an explicit instantiation // declaration or definition. static SourceLocation DiagLocForExplicitInstantiation( NamedDecl* D, SourceLocation PointOfInstantiation) { + // Search for an ExplicitInstantiationDecl that references D. Per + // [temp.explicit], explicit instantiations must appear in an enclosing + // namespace of the template, so the EID is in D's DeclContext or an ancestor. + for (DeclContext *DC = D->getDeclContext(); DC; DC = DC->getParent()) { + for (auto *Decl : DC->decls()) { + if (auto *EID = dyn_cast<ExplicitInstantiationDecl>(Decl)) + if (EID->getSpecialization() == D) + return EID->getTemplateLoc(); + } + } + // Explicit instantiations following a specialization have no effect and // hence no PointOfInstantiation. In that case, walk decl backwards // until a valid name loc is found. @@ -10406,6 +10437,11 @@ DeclResult Sema::ActOnExplicitInstantiation( if (HasNoEffect) { // Set the template specialization kind. Specialization->setTemplateSpecializationKind(TSK); + + addExplicitInstantiationDecl( + Context, CurContext, SS, RAngleLoc, Specialization, ExternLoc, + TemplateLoc, KWLoc, Specialization->getTemplateArgsAsWritten(), + TemplateNameLoc, nullptr, TSK); return Specialization; } @@ -10495,6 +10531,10 @@ DeclResult Sema::ActOnExplicitInstantiation( Specialization->setTemplateSpecializationKind(TSK); } + addExplicitInstantiationDecl( + Context, CurContext, SS, RAngleLoc, Specialization, ExternLoc, + TemplateLoc, KWLoc, Specialization->getTemplateArgsAsWritten(), + TemplateNameLoc, nullptr, TSK); return Specialization; } @@ -10569,8 +10609,12 @@ Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation ExternLoc, MSInfo->getPointOfInstantiation(), HasNoEffect)) return true; - if (HasNoEffect) + if (HasNoEffect) { + addExplicitInstantiationDecl(Context, CurContext, SS, NameLoc, Record, + ExternLoc, TemplateLoc, KWLoc, nullptr, + NameLoc, nullptr, TSK); return TagD; + } } CXXRecordDecl *RecordDef @@ -10606,10 +10650,9 @@ Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation ExternLoc, if (TSK == TSK_ExplicitInstantiationDefinition) MarkVTableUsed(NameLoc, RecordDef, true); - // FIXME: We don't have any representation for explicit instantiations of - // member classes. Such a representation is not needed for compilation, but it - // should be available for clients that want to see all of the declarations in - // the source code. + addExplicitInstantiationDecl(Context, CurContext, SS, NameLoc, Record, + ExternLoc, TemplateLoc, KWLoc, nullptr, + NameLoc, nullptr, TSK); return TagD; } @@ -10803,7 +10846,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, if (!HasNoEffect) { // Instantiate static data member or variable template. Prev->setTemplateSpecializationKind(TSK, D.getIdentifierLoc()); - if (auto *VTSD = dyn_cast<VarTemplatePartialSpecializationDecl>(Prev)) { + if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(Prev)) { VTSD->setExternKeywordLoc(ExternLoc); VTSD->setTemplateKeywordLoc(TemplateLoc); } @@ -10827,8 +10870,18 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, return true; } - // FIXME: Create an ExplicitInstantiation node? - return (Decl*) nullptr; + { + const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr; + if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(Prev)) + ArgsAsWritten = VTSD->getTemplateArgsAsWritten(); + addExplicitInstantiationDecl( + Context, CurContext, D.getCXXScopeSpec(), + D.getSourceRange().getEnd(), Prev, ExternLoc, TemplateLoc, + SourceLocation(), ArgsAsWritten, D.getIdentifierLoc(), T, TSK); + // Don't return the EID to the Parser — doing so would trigger + // unrelated semantic actions (e.g. access checks via FinalizeDeclaration). + return (Decl *)nullptr; + } } // If the declarator is a template-id, translate the parser's template @@ -11005,10 +11058,19 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, HasNoEffect)) return true; - // FIXME: We may still want to build some representation of this - // explicit specialization. - if (HasNoEffect) - return (Decl*) nullptr; + if (HasNoEffect) { + const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr; + if (HasExplicitTemplateArgs) + ArgsAsWritten = + ASTTemplateArgumentListInfo::Create(Context, TemplateArgs); + addExplicitInstantiationDecl( + Context, CurContext, D.getCXXScopeSpec(), + D.getSourceRange().getEnd(), Specialization, ExternLoc, TemplateLoc, + SourceLocation(), ArgsAsWritten, D.getIdentifierLoc(), T, TSK); + // Don't return the EID to the Parser — doing so would trigger + // unrelated semantic actions (e.g. access checks via FinalizeDeclaration). + return (Decl *)nullptr; + } } // HACK: libc++ has a bug where it attempts to explicitly instantiate the @@ -11036,7 +11098,6 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, TSK = TSK_ExplicitInstantiationDeclaration; Specialization->setTemplateSpecializationKind(TSK, D.getIdentifierLoc()); - if (Specialization->isDefined()) { // Let the ASTConsumer know that this function has been explicitly // instantiated now, and its linkage might have changed. @@ -11065,8 +11126,19 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, : Specialization->getInstantiatedFromMemberFunction(), D.getIdentifierLoc(), D.getCXXScopeSpec().isSet(), TSK); - // FIXME: Create some kind of ExplicitInstantiationDecl here. - return (Decl*) nullptr; + { + const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr; + if (HasExplicitTemplateArgs) + ArgsAsWritten = + ASTTemplateArgumentListInfo::Create(Context, TemplateArgs); + addExplicitInstantiationDecl( + Context, CurContext, D.getCXXScopeSpec(), + D.getSourceRange().getEnd(), Specialization, ExternLoc, TemplateLoc, + SourceLocation(), ArgsAsWritten, D.getIdentifierLoc(), T, TSK); + // Don't return the EID to the Parser — doing so would trigger + // unrelated semantic actions (e.g. access checks via FinalizeDeclaration). + return (Decl *)nullptr; + } } TypeResult Sema::ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK, diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 09c2482168ab7..f307e813ff27a 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2101,6 +2101,14 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) { InstantiatedMessageExpr.get(), D->getRParenLoc(), D->isFailed()); } +Decl *TemplateDeclInstantiator::VisitExplicitInstantiationDecl( + ExplicitInstantiationDecl *D) { + // ExplicitInstantiationDecl is a source-info-only node and should not + // appear inside a template pattern. Nothing to instantiate. + llvm_unreachable("ExplicitInstantiationDecl should not be instantiated"); + return nullptr; +} + Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { EnumDecl *PrevDecl = nullptr; if (EnumDecl *PatternPrev = getPreviousDeclForInstantiation(D)) { diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index 69db02f2efc40..089da7e082317 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -436,6 +436,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { case Decl::Friend: case Decl::FriendTemplate: case Decl::StaticAssert: + case Decl::ExplicitInstantiation: case Decl::Block: case Decl::OutlinedFunction: case Decl::Captured: diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 9033ea55bc5e2..eb13e15b92f14 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -405,6 +405,7 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> { void VisitFriendDecl(FriendDecl *D); void VisitFriendTemplateDecl(FriendTemplateDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); + void VisitExplicitInstantiationDecl(ExplicitInstantiationDecl *D); void VisitBlockDecl(BlockDecl *BD); void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D); void VisitCapturedDecl(CapturedDecl *CD); @@ -2785,6 +2786,22 @@ void ASTDeclReader::VisitStaticAssertDecl(StaticAssertDecl *D) { D->RParenLoc = readSourceLocation(); } +void ASTDeclReader::VisitExplicitInstantiationDecl( + ExplicitInstantiationDecl *D) { + VisitDecl(D); + D->Specialization = readDeclAs<NamedDecl>(); + D->Range = readSourceRange(); + D->ExternLoc = readSourceLocation(); + D->TagKWLoc = readSourceLocation(); + D->QualifierLoc = Record.readNestedNameSpecifierLoc(); + bool HasArgsAsWritten = Record.readInt(); + if (HasArgsAsWritten) + D->TemplateArgsAsWritten = Record.readASTTemplateArgumentListInfo(); + D->NameLoc = readSourceLocation(); + D->TypeAsWritten = readTypeSourceInfo(); + D->TSK = Record.readInt(); +} + void ASTDeclReader::VisitEmptyDecl(EmptyDecl *D) { VisitDecl(D); } @@ -4107,6 +4124,9 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) { case DECL_STATIC_ASSERT: D = StaticAssertDecl::CreateDeserialized(Context, ID); break; + case DECL_EXPLICIT_INSTANTIATION: + D = ExplicitInstantiationDecl::CreateDeserialized(Context, ID); + break; case DECL_OBJC_METHOD: D = ObjCMethodDecl::CreateDeserialized(Context, ID); break; diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index e415ac1e47862..42610a1985f60 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -144,6 +144,7 @@ namespace clang { void VisitFriendDecl(FriendDecl *D); void VisitFriendTemplateDecl(FriendTemplateDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); + void VisitExplicitInstantiationDecl(ExplicitInstantiationDecl *D); void VisitBlockDecl(BlockDecl *D); void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D); void VisitCapturedDecl(CapturedDecl *D); @@ -2176,6 +2177,23 @@ void ASTDeclWriter::VisitStaticAssertDecl(StaticAssertDecl *D) { Code = serialization::DECL_STATIC_ASSERT; } +void ASTDeclWriter::VisitExplicitInstantiationDecl( + ExplicitInstantiationDecl *D) { + VisitDecl(D); + Record.AddDeclRef(D->getSpecialization()); + Record.AddSourceRange(D->getSourceRange()); + Record.AddSourceLocation(D->getExternLoc()); + Record.AddSourceLocation(D->getTagKWLoc()); + Record.AddNestedNameSpecifierLoc(D->getQualifierLoc()); + Record.push_back(D->getTemplateArgsAsWritten() != nullptr); + if (D->getTemplateArgsAsWritten()) + Record.AddASTTemplateArgumentListInfo(D->getTemplateArgsAsWritten()); + Record.AddSourceLocation(D->getNameLoc()); + Record.AddTypeSourceInfo(D->getTypeAsWritten()); + Record.push_back(D->getTemplateSpecializationKind()); + Code = serialization::DECL_EXPLICIT_INSTANTIATION; +} + /// Emit the DeclContext part of a declaration context decl. void ASTDeclWriter::VisitDeclContext(DeclContext *DC) { static_assert(DeclContext::NumDeclContextBits == 13, diff --git a/clang/lib/Tooling/Syntax/BuildTree.cpp b/clang/lib/Tooling/Syntax/BuildTree.cpp index 9d49d72dea69b..cff89b7dfd758 100644 --- a/clang/lib/Tooling/Syntax/BuildTree.cpp +++ b/clang/lib/Tooling/Syntax/BuildTree.cpp @@ -737,6 +737,13 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> { return true; } + // ExplicitInstantiationDecl is an auxiliary AST node that records source + // info. The syntax tree is already built by TraverseClassTemplateSpecializationDecl + // or by the parser for function/variable templates, so skip this node. + bool TraverseExplicitInstantiationDecl(ExplicitInstantiationDecl *) { + return true; + } + bool WalkUpFromTemplateDecl(TemplateDecl *S) { foldTemplateDeclaration( Builder.getDeclarationRange(S), diff --git a/clang/test/AST/ast-dump-templates-pattern.cpp b/clang/test/AST/ast-dump-templates-pattern.cpp index 624a9b1c7a94c..2b8efaeed43ed 100644 --- a/clang/test/AST/ast-dump-templates-pattern.cpp +++ b/clang/test/AST/ast-dump-templates-pattern.cpp @@ -18,7 +18,11 @@ namespace TestClassRedecl { // CHECK: |-ClassTemplateDecl {{.+}} <line:[[@LINE-6]]:{{.+}} A // CHECK: | |-CXXRecordDecl 0x[[TestClassRedecl_D2:[^ ]+]] prev 0x[[TestClassRedecl_D1]] {{.+}} struct A // CHECK: | `-ClassTemplateSpecialization 0x[[TestClassRedecl_S]] 'A' -// CHECK: `-ClassTemplateSpecializationDecl 0x[[TestClassRedecl_S]] <line:[[@LINE-8]]:{{.+}} struct A definition instantiated_from 0x[[TestClassRedecl_D1]] explicit_instantiation_definition +// CHECK: |-ClassTemplateSpecializationDecl 0x[[TestClassRedecl_S]] <line:[[@LINE-8]]:{{.+}} struct A definition instantiated_from 0x[[TestClassRedecl_D1]] explicit_instantiation_definition +// CHECK: `-ExplicitInstantiationDecl {{.+}} <line:[[@LINE-9]]:{{.+}} template 'A' +// CHECK: |-ClassTemplateSpecialization 0x[[TestClassRedecl_S]] 'A' +// CHECK: `-TemplateArgument {{.+}} type 'int' +// CHECK: `-BuiltinType {{.+}} 'int' } namespace TestFunctionRedecl { @@ -29,9 +33,15 @@ namespace TestFunctionRedecl { // CHECK: |-FunctionTemplateDecl 0x[[TestFunctionRedecl_T1:[^ ]+]] <line:[[@LINE-4]]:{{.+}} f // CHECK: | |-FunctionDecl 0x[[TestFunctionRedecl_D1:[^ ]+]] {{.+}} f 'void ()' // CHECK: | `-FunctionDecl 0x[[TestFunctionRedecl_S1:[^ ]+]] {{.+}} f 'void ()' explicit_instantiation_definition instantiated_from 0x[[TestFunctionRedecl_D1]] -// CHECK: `-FunctionTemplateDecl 0x[[TestFunctionRedecl_T2:[^ ]+]] prev 0x[[TestFunctionRedecl_T1]] <line:[[@LINE-6]]:{{.+}} f -// CHECK: |-FunctionDecl 0x[[TestFunctionRedecl_D2:[^ ]+]] prev 0x[[TestFunctionRedecl_D1]] {{.+}} f 'void ()' -// CHECK: `-Function 0x[[TestFunctionRedecl_S1]] 'f' 'void ()' +// CHECK: |-FunctionTemplateDecl 0x[[TestFunctionRedecl_T2:[^ ]+]] prev 0x[[TestFunctionRedecl_T1]] <line:[[@LINE-6]]:{{.+}} f +// CHECK: | |-FunctionDecl 0x[[TestFunctionRedecl_D2:[^ ]+]] prev 0x[[TestFunctionRedecl_D1]] {{.+}} f 'void ()' +// CHECK: | `-Function 0x[[TestFunctionRedecl_S1]] 'f' 'void ()' +// CHECK: `-ExplicitInstantiationDecl {{.+}} <line:[[@LINE-8]]:{{.+}} template 'f' +// CHECK: |-Function 0x[[TestFunctionRedecl_S1]] 'f' 'void ()' +// CHECK: |-TemplateArgument {{.+}} type 'int' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: `-FunctionProtoTypeLoc {{.+}} 'void ()' cdecl +// CHECK: `-BuiltinTypeLoc {{.+}} 'void' } // FIXME: Bogus instantiated_from self-reference. @@ -46,7 +56,12 @@ namespace TestVariableRedecl { // CHECK: |-VarTemplateDecl 0x[[TestVariableRedecl_T2:[^ ]+]] prev 0x[[TestVariableRedecl_T1]] <line:[[@LINE-6]]:{{.+}} a // CHECK: | |-VarDecl 0x[[TestVariableRedecl_D2:[^ ]+]] prev 0x[[TestVariableRedecl_D1]] {{.+}} a 'T' extern instantiated_from 0x[[TestVariableRedecl_D1]] // CHECK: | `-VarTemplateSpecialization 0x[[TestVariableRedecl_S1]] 'a' 'int' -// CHECK: `-VarTemplateSpecializationDecl 0x[[TestVariableRedecl_S1]] {{.+}} a 'int' explicit_instantiation_definition cinit instantiated_from 0x[[TestVariableRedecl_D1]] +// CHECK: |-VarTemplateSpecializationDecl 0x[[TestVariableRedecl_S1]] {{.+}} a 'int' explicit_instantiation_definition cinit instantiated_from 0x[[TestVariableRedecl_D1]] +// CHECK: `-ExplicitInstantiationDecl {{.+}} <line:[[@LINE-9]]:{{.+}} template 'a' +// CHECK: |-VarTemplateSpecialization 0x[[TestVariableRedecl_S1]] 'a' 'int' +// CHECK: |-TemplateArgument {{.+}} type 'int' +// CHECK: | `-BuiltinType {{.+}} 'int' +// CHECK: `-BuiltinTypeLoc {{.+}} 'int' } namespace TestNestedClassRedecl { @@ -70,5 +85,10 @@ namespace TestNestedClassRedecl { // CHECK: | `-ClassTemplateSpecialization 0x[[TestNestedClassRedecl_A_S1]] 'A' // CHECK: |-ClassTemplateDecl 0x{{.+}} parent 0x[[TestNestedClassRedecl_A_D1]] prev 0x[[TestNestedClassRedecl_B_T1]] <line:[[@LINE-14]]:{{.+}} B // CHECK: | `-CXXRecordDecl 0x[[TestNestedClassRedecl_B_D2:[^ ]+]] parent 0x[[TestNestedClassRedecl_A_D1]] prev 0x[[TestNestedClassRedecl_B_D1]] {{.+}} struct B definition -// CHECK: `-ClassTemplateSpecializationDecl 0x[[TestNestedClassRedecl_B_S1]] parent 0x[[TestNestedClassRedecl_A_S1]] <line:[[@LINE-15]]:{{.+}} struct B definition instantiated_from 0x[[TestNestedClassRedecl_B_D2]] explicit_instantiation_definition +// CHECK: |-ClassTemplateSpecializationDecl 0x[[TestNestedClassRedecl_B_S1]] parent 0x[[TestNestedClassRedecl_A_S1]] <line:[[@LINE-15]]:{{.+}} struct B definition instantiated_from 0x[[TestNestedClassRedecl_B_D2]] explicit_instantiation_definition +// CHECK: `-ExplicitInstantiationDecl {{.+}} <line:[[@LINE-16]]:{{.+}} template 'B' +// CHECK: |-NestedNameSpecifier TypeSpec {{.+}} +// CHECK: |-ClassTemplateSpecialization 0x[[TestNestedClassRedecl_B_S1]] 'B' +// CHECK: `-TemplateArgument {{.+}} type 'char' +// CHECK: `-BuiltinType {{.+}} 'char' } diff --git a/clang/test/AST/ast-dump-templates.cpp b/clang/test/AST/ast-dump-templates.cpp index 8cf9b6a29e332..fac806ffa9f37 100644 --- a/clang/test/AST/ast-dump-templates.cpp +++ b/clang/test/AST/ast-dump-templates.cpp @@ -5032,12 +5032,31 @@ namespace TestAbbreviatedTemplateDecls { // JSON-NEXT: "TemplateInstantiationPattern": "0x{{.*}}" // JSON-NEXT: } // JSON-NEXT: ] -// JSON-NEXT: } -// JSON-NEXT: ] -// JSON-NEXT: }, -// JSON-NEXT: { -// JSON-NEXT: "id": "0x{{.*}}", -// JSON-NEXT: "kind": "NamespaceDecl", +// JSON-NEXT: }, +// JSON-NEXT: { +// JSON-NEXT: "id": "0x{{.*}}", +// JSON-NEXT: "kind": "ExplicitInstantiationDecl", +// JSON-NEXT: "loc": { +// JSON-NEXT: "offset": {{[0-9]+}}, +// JSON-NEXT: "line": 102, +// JSON-NEXT: "col": 1, +// JSON-NEXT: "tokLen": 8 +// JSON-NEXT: }, +// JSON-NEXT: "range": { +// JSON-NEXT: "begin": { +// JSON-NEXT: "offset": {{[0-9]+}}, +// JSON-NEXT: "col": 1, +// JSON-NEXT: "tokLen": 8 +// JSON-NEXT: }, +// JSON-NEXT: "end": { +// JSON-NEXT: "offset": {{[0-9]+}}, +// JSON-NEXT: "col": 30, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: "specializationDeclId": "0x{{.*}}", +// JSON-NEXT: "templateSpecializationKind": "explicit_instantiation_definition", +// JSON: "kind": "NamespaceDecl", // JSON-NEXT: "loc": { // JSON-NEXT: "offset": 3310, // JSON-NEXT: "line": 105, @@ -6317,12 +6336,31 @@ namespace TestAbbreviatedTemplateDecls { // JSON-NEXT: "tagUsed": "struct" // JSON-NEXT: } // JSON-NEXT: ] -// JSON-NEXT: } -// JSON-NEXT: ] -// JSON-NEXT: }, -// JSON-NEXT: { -// JSON-NEXT: "id": "0x{{.*}}", -// JSON-NEXT: "kind": "NamespaceDecl", +// JSON-NEXT: }, +// JSON-NEXT: { +// JSON-NEXT: "id": "0x{{.*}}", +// JSON-NEXT: "kind": "ExplicitInstantiationDecl", +// JSON-NEXT: "loc": { +// JSON-NEXT: "offset": {{[0-9]+}}, +// JSON-NEXT: "line": 133, +// JSON-NEXT: "col": 3, +// JSON-NEXT: "tokLen": 8 +// JSON-NEXT: }, +// JSON-NEXT: "range": { +// JSON-NEXT: "begin": { +// JSON-NEXT: "offset": {{[0-9]+}}, +// JSON-NEXT: "col": 3, +// JSON-NEXT: "tokLen": 8 +// JSON-NEXT: }, +// JSON-NEXT: "end": { +// JSON-NEXT: "offset": {{[0-9]+}}, +// JSON-NEXT: "col": 22, +// JSON-NEXT: "tokLen": 1 +// JSON-NEXT: } +// JSON-NEXT: }, +// JSON-NEXT: "specializationDeclId": "0x{{.*}}", +// JSON-NEXT: "templateSpecializationKind": "explicit_instantiation_definition", +// JSON: "kind": "NamespaceDecl", // JSON-NEXT: "loc": { // JSON-NEXT: "offset": 4584, // JSON-NEXT: "line": 142, diff --git a/clang/test/AST/explicit-instantiation-source-info.cpp b/clang/test/AST/explicit-instantiation-source-info.cpp new file mode 100644 index 0000000000000..0903ba140cb9e --- /dev/null +++ b/clang/test/AST/explicit-instantiation-source-info.cpp @@ -0,0 +1,166 @@ +// RUN: %clang_cc1 -fsyntax-only -ast-dump %s | FileCheck %s + +namespace ns { + template <typename T> void foo(T x) {} + template <typename T> T bar = T{}; + template <typename T> struct S { + void method(T x) {} + static T sval; + template <typename U> void tmpl(U u) {} + struct Inner { T val; }; + }; + template <typename T> T S<T>::sval = T{}; +} + +// (a) function template +template void ns::foo<int>(int); +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:31> col:1 explicit_instantiation_definition template 'foo' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: Function {{.*}} 'foo' 'void (int)' +// CHECK-NEXT: TemplateArgument <col:23> type 'int' +// CHECK-NEXT: BuiltinType {{.*}} 'int' +// CHECK-NEXT: FunctionProtoTypeLoc <col:10, col:31> 'void (int)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} <col:28> col:31 'int' +// CHECK-NEXT: BuiltinTypeLoc <col:28> 'int' +// CHECK-NEXT: BuiltinTypeLoc <col:10> 'void' + +// (b) variable template +template int ns::bar<int>; +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:25> col:1 explicit_instantiation_definition template 'bar' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'int' +// CHECK-NEXT: TemplateArgument <col:22> type 'int' +// CHECK-NEXT: BuiltinType {{.*}} 'int' +// CHECK-NEXT: BuiltinTypeLoc <col:10> 'int' + +// (c) class template +template struct ns::S<int>; +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:26> col:1 explicit_instantiation_definition template 'S' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: ClassTemplateSpecialization {{.*}} 'S' +// CHECK-NEXT: TemplateArgument <col:23> type 'int' +// CHECK-NEXT: BuiltinType {{.*}} 'int' + +// (d) member function +template void ns::S<long>::method(long); +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:39> col:1 explicit_instantiation_definition template 'method' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S<long>' +// CHECK-NEXT: CXXMethod {{.*}} 'method' 'void (long)' +// CHECK-NEXT: FunctionProtoTypeLoc <col:10, col:39> 'void (long)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} <col:35> col:39 'long' +// CHECK-NEXT: BuiltinTypeLoc <col:35> 'long' +// CHECK-NEXT: BuiltinTypeLoc <col:10> 'void' + +// (e) static data member +template long ns::S<long>::sval; +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:28> col:1 explicit_instantiation_definition template 'sval' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S<long>' +// CHECK-NEXT: Var {{.*}} 'sval' 'long' +// CHECK-NEXT: BuiltinTypeLoc <col:10> 'long' + +// (f) member function template +template void ns::S<long>::tmpl<double>(double); +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:47> col:1 explicit_instantiation_definition template 'tmpl' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S<long>' +// CHECK-NEXT: CXXMethod {{.*}} 'tmpl' 'void (double)' +// CHECK-NEXT: TemplateArgument <col:33> type 'double' +// CHECK-NEXT: BuiltinType {{.*}} 'double' +// CHECK-NEXT: FunctionProtoTypeLoc <col:10, col:47> 'void (double)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} <col:41> col:47 'double' +// CHECK-NEXT: BuiltinTypeLoc <col:41> 'double' +// CHECK-NEXT: BuiltinTypeLoc <col:10> 'void' + +// (g) nested class (no template args or type) +template struct ns::S<long>::Inner; +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:30> col:1 explicit_instantiation_definition template 'Inner' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S<long>' +// CHECK-NEXT: CXXRecord {{.*}} 'Inner' + +// extern template variants +extern template void ns::foo<float>(float); +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:42> col:8 explicit_instantiation_declaration extern template 'foo' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: Function {{.*}} 'foo' 'void (float)' +// CHECK-NEXT: TemplateArgument <col:30> type 'float' +// CHECK-NEXT: BuiltinType {{.*}} 'float' +// CHECK-NEXT: FunctionProtoTypeLoc <col:17, col:42> 'void (float)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} <col:37> col:42 'float' +// CHECK-NEXT: BuiltinTypeLoc <col:37> 'float' +// CHECK-NEXT: BuiltinTypeLoc <col:17> 'void' + +extern template struct ns::S<float>; +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:35> col:8 explicit_instantiation_declaration extern template 'S' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: ClassTemplateSpecialization {{.*}} 'S' +// CHECK-NEXT: TemplateArgument <col:30> type 'float' +// CHECK-NEXT: BuiltinType {{.*}} 'float' + +// extern template: variable template +extern template double ns::bar<double>; +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:38> col:8 explicit_instantiation_declaration extern template 'bar' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'double' +// CHECK-NEXT: TemplateArgument <col:32> type 'double' +// CHECK-NEXT: BuiltinType {{.*}} 'double' +// CHECK-NEXT: BuiltinTypeLoc <col:17> 'double' + +// extern template: member function +extern template void ns::S<double>::method(double); +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:50> col:8 explicit_instantiation_declaration extern template 'method' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S<double>' +// CHECK-NEXT: CXXMethod {{.*}} 'method' 'void (double)' +// CHECK-NEXT: FunctionProtoTypeLoc <col:17, col:50> 'void (double)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} <col:44> col:50 'double' +// CHECK-NEXT: BuiltinTypeLoc <col:44> 'double' +// CHECK-NEXT: BuiltinTypeLoc <col:17> 'void' + +// extern template: static data member +extern template double ns::S<double>::sval; +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:39> col:8 explicit_instantiation_declaration extern template 'sval' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S<double>' +// CHECK-NEXT: Var {{.*}} 'sval' 'double' +// CHECK-NEXT: BuiltinTypeLoc <col:17> 'double' + +// extern template: member function template +extern template void ns::S<double>::tmpl<float>(float); +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:54> col:8 explicit_instantiation_declaration extern template 'tmpl' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S<double>' +// CHECK-NEXT: CXXMethod {{.*}} 'tmpl' 'void (float)' +// CHECK-NEXT: TemplateArgument <col:42> type 'float' +// CHECK-NEXT: BuiltinType {{.*}} 'float' +// CHECK-NEXT: FunctionProtoTypeLoc <col:17, col:54> 'void (float)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} <col:49> col:54 'float' +// CHECK-NEXT: BuiltinTypeLoc <col:49> 'float' +// CHECK-NEXT: BuiltinTypeLoc <col:17> 'void' + +// extern template: nested class (no template args or type) +extern template struct ns::S<double>::Inner; +// CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:1, col:39> col:8 explicit_instantiation_declaration extern template 'Inner' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S<double>' +// CHECK-NEXT: CXXRecord {{.*}} 'Inner' + +// Same-namespace explicit instantiation (no cross-namespace qualifier) +namespace ns { + template void foo<short>(short); + // CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:3, col:33> col:3 explicit_instantiation_definition template 'foo' + // CHECK-NEXT: Function {{.*}} 'foo' 'void (short)' + // CHECK-NEXT: TemplateArgument <col:21> type 'short' + // CHECK-NEXT: BuiltinType {{.*}} 'short' + // CHECK-NEXT: FunctionProtoTypeLoc <col:12, col:33> 'void (short)' cdecl + // CHECK-NEXT: ParmVarDecl {{.*}} <col:28> col:33 'short' + // CHECK-NEXT: BuiltinTypeLoc <col:28> 'short' + // CHECK-NEXT: BuiltinTypeLoc <col:12> 'void' + + template short bar<short>; + // CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:3, col:27> col:3 explicit_instantiation_definition template 'bar' + // CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'short' + // CHECK-NEXT: TemplateArgument <col:22> type 'short' + // CHECK-NEXT: BuiltinType {{.*}} 'short' + // CHECK-NEXT: BuiltinTypeLoc <col:12> 'short' + + template struct S<short>; + // CHECK: ExplicitInstantiationDecl {{.*}} <line:[[@LINE-1]]:3, col:26> col:3 explicit_instantiation_definition template 'S' + // CHECK-NEXT: ClassTemplateSpecialization {{.*}} 'S' + // CHECK-NEXT: TemplateArgument <col:21> type 'short' + // CHECK-NEXT: BuiltinType {{.*}} 'short' +} diff --git a/clang/test/SemaTemplate/explicit-instantiation-diag-location.cpp b/clang/test/SemaTemplate/explicit-instantiation-diag-location.cpp new file mode 100644 index 0000000000000..b8d22efaac7df --- /dev/null +++ b/clang/test/SemaTemplate/explicit-instantiation-diag-location.cpp @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// Verify that the "previous explicit instantiation" note points to the first +// explicit instantiation statement, not to the implicit instantiation site. +// This is a regression test for https://github.com/llvm/llvm-project/issues/21133 + +// Function template with implicit instantiation before explicit. +template <typename T> void f() {} +void use_f() { f<int>(); } +template void f<int>(); // expected-note{{previous explicit instantiation is here}} +template void f<int>(); // expected-error{{duplicate explicit instantiation of 'f<int>'}} + +// Class template with implicit instantiation before explicit. +template <typename T> struct S {}; +void use_S(S<int>) {} +template struct S<int>; // expected-note{{previous explicit instantiation is here}} +template struct S<int>; // expected-error{{duplicate explicit instantiation of 'S<int>'}} + +// Cross-namespace: template in ns, explicit instantiation at global scope. +namespace ns { + template <typename T> void g() {} +} +void use_g() { ns::g<double>(); } +template void ns::g<double>(); // expected-note{{previous explicit instantiation is here}} +template void ns::g<double>(); // expected-error{{duplicate explicit instantiation of 'g<double>'}} + +// Variable template with implicit instantiation before explicit. +template <typename T> T var = T{}; +int use_var = var<int>; +template int var<int>; // expected-note{{previous explicit instantiation is here}} +template int var<int>; // expected-error{{duplicate explicit instantiation of 'var<int>'}} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index 3ee37ed2dfc27..b3516456822a6 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -7257,6 +7257,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::FileScopeAsm: case Decl::TopLevelStmt: case Decl::StaticAssert: + case Decl::ExplicitInstantiation: case Decl::Block: case Decl::OutlinedFunction: case Decl::Captured: @@ -8837,6 +8838,7 @@ static CXLanguageKind getDeclLanguage(const Decl *D) { case Decl::NamespaceAlias: case Decl::NonTypeTemplateParm: case Decl::StaticAssert: + case Decl::ExplicitInstantiation: case Decl::TemplateTemplateParm: case Decl::TemplateTypeParm: case Decl::UnresolvedUsingTypename: _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
