Files
llvm-project/clang/lib/Serialization/ASTWriterDecl.cpp
ykiko fb02433749 [Clang][AST] Introduce ExplicitInstantiationDecl to preserve source info and fix diagnostic locations (#191658)
This is the initial fix of
https://github.com/llvm/llvm-project/issues/191442. Following the
discussion here
https://github.com/llvm/llvm-project/issues/115418#issuecomment-2467017012.

- Fix #21040
- Fix #52659
- Fix #115418
- Fix #14230
- Fix #21133

### 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.

#### 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.

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.

Assisted-by: Claude Code (Anthropic) — used for test writing and
checking test results
2026-04-22 16:21:27 -03:00

123 KiB