[clang] [Clang][AST] Introduce `ExplicitInstantiationDecl` to preserve source info and fix diagnostic locations (PR #191658)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Apr 11 12:12:15 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
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
More information about the cfe-commits
mailing list