[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:32:04 PDT 2026
https://github.com/16bit-ykiko updated https://github.com/llvm/llvm-project/pull/191658
>From 15a609e9782ee3637244f0c6677d19eeda5a7b1b Mon Sep 17 00:00:00 2001
From: ykiko <ykikoykikoykiko at gmail.com>
Date: Sun, 12 Apr 2026 03:02:08 +0800
Subject: [PATCH 1/3] [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:
>From 61c859d6a02a4099aac88bca9bbe8d31a27b1579 Mon Sep 17 00:00:00 2001
From: ykiko <ykikoykikoykiko at gmail.com>
Date: Sun, 12 Apr 2026 03:29:48 +0800
Subject: [PATCH 2/3] clang-format
---
clang/include/clang/AST/ASTNodeTraverser.h | 3 +-
clang/include/clang/AST/DeclTemplate.h | 14 +++---
clang/lib/AST/DeclTemplate.cpp | 9 ++--
clang/lib/Sema/SemaTemplate.cpp | 55 +++++++++++-----------
clang/lib/Tooling/Syntax/BuildTree.cpp | 5 +-
5 files changed, 44 insertions(+), 42 deletions(-)
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index 39a6864bf169e..77a4d5655b196 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -685,7 +685,8 @@ class ASTNodeTraverser
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.
+ // 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(),
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index b470b520531f1..6f148e1cf2e6d 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3440,7 +3440,8 @@ class ExplicitInstantiationDecl : public Decl {
/// arguments were deduced.
const ASTTemplateArgumentListInfo *TemplateArgsAsWritten = nullptr;
- /// Location of the entity name (e.g., 'foo' in 'template void ns::foo<int>(int)').
+ /// Location of the entity name (e.g., 'foo' in 'template void
+ /// ns::foo<int>(int)').
SourceLocation NameLoc;
/// Type source info for the declaration type:
@@ -3455,10 +3456,8 @@ class ExplicitInstantiationDecl : public Decl {
unsigned TSK : 3;
ExplicitInstantiationDecl(DeclContext *DC, SourceRange Range,
- NamedDecl *Specialization,
- SourceLocation ExternLoc,
- SourceLocation TemplateLoc,
- SourceLocation TagKWLoc,
+ NamedDecl *Specialization, SourceLocation ExternLoc,
+ SourceLocation TemplateLoc, SourceLocation TagKWLoc,
NestedNameSpecifierLoc QualifierLoc,
const ASTTemplateArgumentListInfo *ArgsAsWritten,
SourceLocation NameLoc,
@@ -3469,8 +3468,9 @@ class ExplicitInstantiationDecl : public Decl {
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");
+ assert((TSK == TSK_ExplicitInstantiationDeclaration) ==
+ ExternLoc.isValid() &&
+ "ExternLoc should be valid iff TSK is a declaration");
}
ExplicitInstantiationDecl(EmptyShell Empty)
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 76e3de5394d86..ecce1a43d298c 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1792,12 +1792,11 @@ ExplicitInstantiationDecl *ExplicitInstantiationDecl::Create(
NamedDecl *Specialization, SourceLocation ExternLoc,
SourceLocation TemplateLoc, SourceLocation TagKWLoc,
NestedNameSpecifierLoc QualifierLoc,
- const ASTTemplateArgumentListInfo *ArgsAsWritten,
- SourceLocation NameLoc, TypeSourceInfo *TypeAsWritten,
- TemplateSpecializationKind TSK) {
+ 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);
+ DC, Range, Specialization, ExternLoc, TemplateLoc, TagKWLoc, QualifierLoc,
+ ArgsAsWritten, NameLoc, TypeAsWritten, TSK);
}
ExplicitInstantiationDecl *
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 66fc2b6fc81e3..a94f0e2fe3c1e 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -9320,9 +9320,8 @@ 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) {
+ const ASTTemplateArgumentListInfo *ArgsAsWritten, SourceLocation NameLoc,
+ TypeSourceInfo *TypeAsWritten, TemplateSpecializationKind TSK) {
NestedNameSpecifierLoc QualifierLoc;
if (SS.isNotEmpty())
QualifierLoc = SS.getWithLocInContext(Context);
@@ -10438,10 +10437,10 @@ DeclResult Sema::ActOnExplicitInstantiation(
// Set the template specialization kind.
Specialization->setTemplateSpecializationKind(TSK);
- addExplicitInstantiationDecl(
- Context, CurContext, SS, RAngleLoc, Specialization, ExternLoc,
- TemplateLoc, KWLoc, Specialization->getTemplateArgsAsWritten(),
- TemplateNameLoc, nullptr, TSK);
+ addExplicitInstantiationDecl(Context, CurContext, SS, RAngleLoc,
+ Specialization, ExternLoc, TemplateLoc, KWLoc,
+ Specialization->getTemplateArgsAsWritten(),
+ TemplateNameLoc, nullptr, TSK);
return Specialization;
}
@@ -10531,10 +10530,10 @@ DeclResult Sema::ActOnExplicitInstantiation(
Specialization->setTemplateSpecializationKind(TSK);
}
- addExplicitInstantiationDecl(
- Context, CurContext, SS, RAngleLoc, Specialization, ExternLoc,
- TemplateLoc, KWLoc, Specialization->getTemplateArgsAsWritten(),
- TemplateNameLoc, nullptr, TSK);
+ addExplicitInstantiationDecl(Context, CurContext, SS, RAngleLoc,
+ Specialization, ExternLoc, TemplateLoc, KWLoc,
+ Specialization->getTemplateArgsAsWritten(),
+ TemplateNameLoc, nullptr, TSK);
return Specialization;
}
@@ -10651,8 +10650,8 @@ Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation ExternLoc,
MarkVTableUsed(NameLoc, RecordDef, true);
addExplicitInstantiationDecl(Context, CurContext, SS, NameLoc, Record,
- ExternLoc, TemplateLoc, KWLoc, nullptr,
- NameLoc, nullptr, TSK);
+ ExternLoc, TemplateLoc, KWLoc, nullptr, NameLoc,
+ nullptr, TSK);
return TagD;
}
@@ -10874,12 +10873,13 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
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);
+ 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).
+ // unrelated semantic actions (e.g. access checks via
+ // FinalizeDeclaration).
return (Decl *)nullptr;
}
}
@@ -11063,12 +11063,13 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
if (HasExplicitTemplateArgs)
ArgsAsWritten =
ASTTemplateArgumentListInfo::Create(Context, TemplateArgs);
- addExplicitInstantiationDecl(
- Context, CurContext, D.getCXXScopeSpec(),
- D.getSourceRange().getEnd(), Specialization, ExternLoc, TemplateLoc,
- SourceLocation(), ArgsAsWritten, D.getIdentifierLoc(), T, TSK);
+ 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).
+ // unrelated semantic actions (e.g. access checks via
+ // FinalizeDeclaration).
return (Decl *)nullptr;
}
}
@@ -11131,10 +11132,10 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
if (HasExplicitTemplateArgs)
ArgsAsWritten =
ASTTemplateArgumentListInfo::Create(Context, TemplateArgs);
- addExplicitInstantiationDecl(
- Context, CurContext, D.getCXXScopeSpec(),
- D.getSourceRange().getEnd(), Specialization, ExternLoc, TemplateLoc,
- SourceLocation(), ArgsAsWritten, D.getIdentifierLoc(), T, TSK);
+ 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;
diff --git a/clang/lib/Tooling/Syntax/BuildTree.cpp b/clang/lib/Tooling/Syntax/BuildTree.cpp
index cff89b7dfd758..4fd5c009353fa 100644
--- a/clang/lib/Tooling/Syntax/BuildTree.cpp
+++ b/clang/lib/Tooling/Syntax/BuildTree.cpp
@@ -738,8 +738,9 @@ class BuildTreeVisitor : public RecursiveASTVisitor<BuildTreeVisitor> {
}
// 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.
+ // 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;
}
>From bdcc69ce6c8da49f379806e7c750eba9c74f0f2f Mon Sep 17 00:00:00 2001
From: ykiko <ykikoykikoykiko at gmail.com>
Date: Sun, 12 Apr 2026 03:31:51 +0800
Subject: [PATCH 3/3] add ExplicitInstantiation to
getIdentifierNamespaceForKind
---
clang/lib/AST/DeclBase.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 18b6a5b06bab8..2cbdff9da2e68 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -1019,6 +1019,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case ImplicitConceptSpecialization:
case OpenACCDeclare:
case OpenACCRoutine:
+ case ExplicitInstantiation:
// Never looked up by name.
return 0;
}
More information about the cfe-commits
mailing list