llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clangir Author: ykiko (16bit-ykiko) <details> <summary>Changes</summary> 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. --- Patch is 48.70 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/191658.diff 25 Files Affected: - (modified) clang/include/clang/AST/ASTNodeTraverser.h (+11) - (modified) clang/include/clang/AST/DeclTemplate.h (+116) - (modified) clang/include/clang/AST/JSONNodeDumper.h (+1) - (modified) clang/include/clang/AST/RecursiveASTVisitor.h (+14-2) - (modified) clang/include/clang/AST/TextNodeDumper.h (+1) - (modified) clang/include/clang/Basic/DeclNodes.td (+1) - (modified) clang/include/clang/Serialization/ASTBitCodes.h (+4-1) - (modified) clang/lib/AST/DeclTemplate.cpp (+20) - (modified) clang/lib/AST/JSONNodeDumper.cpp (+26) - (modified) clang/lib/AST/TextNodeDumper.cpp (+14) - (modified) clang/lib/CIR/CodeGen/CIRGenDecl.cpp (+1) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+1) - (modified) clang/lib/CodeGen/CGDecl.cpp (+1) - (modified) clang/lib/CodeGen/CodeGenModule.cpp (+1) - (modified) clang/lib/Sema/SemaTemplate.cpp (+87-15) - (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+8) - (modified) clang/lib/Serialization/ASTCommon.cpp (+1) - (modified) clang/lib/Serialization/ASTReaderDecl.cpp (+20) - (modified) clang/lib/Serialization/ASTWriterDecl.cpp (+18) - (modified) clang/lib/Tooling/Syntax/BuildTree.cpp (+7) - (modified) clang/test/AST/ast-dump-templates-pattern.cpp (+26-6) - (modified) clang/test/AST/ast-dump-templates.cpp (+50-12) - (added) clang/test/AST/explicit-instantiation-source-info.cpp (+166) - (added) clang/test/SemaTemplate/explicit-instantiation-diag-location.cpp (+31) - (modified) clang/tools/libclang/CIndex.cpp (+2) ``````````diff 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 - // ... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/191658 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
