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 &amp; 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 &amp; Member Function Templates: Returned `nullptr`, completely 
losing `SourceRange` and `NestedNameSpecifier` information.
* Member Functions &amp; 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)
&gt; 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

Reply via email to