[clang] [clang-tools-extra] [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 21:24:01 PDT 2026


================
@@ -10827,8 +10865,16 @@ 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(), 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;
----------------
16bit-ykiko wrote:

I tried returning EID from `ActOnExplicitInstantiation`, but it caused 13 test failures. Here are the CI results: https://github.com/llvm/llvm-project/actions/runs/24298212726/artifacts/6390095043

The failures are all access control errors on private members, e.g. explicit instantiation of private member function templates like `template void A::f<int>();`.

The root cause is in `PopParsingDeclaration` (`SemaDeclAttr.cpp`): the Parser uses a delayed diagnostic pool to accumulate access checks during parsing. When `ActOnExplicitInstantiation` looks up `A::f`, an access check is recorded in the pool because `f` is private. At the end of parsing, `PopParsingDeclaration(State, decl)` checks whether `decl` is null — if null, all delayed diagnostics are silently discarded; if non-null, they are emitted. Returning EID (non-null) causes the delayed access checks to fire, even though explicit instantiation of private members is perfectly legal (it happens at namespace scope, outside the access context).

So returning `nullptr` is the correct behavior here — it tells the Parser that there's no "new" declaration to validate.

https://github.com/llvm/llvm-project/pull/191658


More information about the cfe-commits mailing list