Skip to content

Parser: skip primary function templates in Sema instantiation calls#33

Open
Fedr wants to merge 8 commits into
masterfrom
fix/skip-primary-templates-in-sema-calls
Open

Parser: skip primary function templates in Sema instantiation calls#33
Fedr wants to merge 8 commits into
masterfrom
fix/skip-primary-templates-in-sema-calls

Conversation

@Fedr
Copy link
Copy Markdown

@Fedr Fedr commented May 12, 2026

Summary

The visitor unconditionally called Sema::InstantiateDefaultArgument and Sema::InstantiateFunctionDefinition on every FunctionDecl it traversed, including primary function templates that pass through because of allow_uninstantiated_templates. On a primary template both APIs null-deref inside libclang and crash with STATUS_ACCESS_VIOLATION. Skipping the call when decl.isTemplated() is true fixes both crashes — the specific instantiations are visited separately and processed correctly.

Stack traces (clang 22.1.4, MSYS2 mingw-w64-clang-x86_64)

InstantiateDefaultArgument at main.cpp:2838:

#0  clang::FunctionDecl::getNumParams() const                      (this = invalid)
#1  clang::Sema::addInstantiatedParametersToScope
#2  clang::Sema::SubstDefaultArgument                              (ForCallExpr = true)
#3  clang::Sema::InstantiateDefaultArgument
#4  mrbind::ClangAstVisitor_InstTypesAndCollectNewTypes::VisitFunctionDecl  (main.cpp:2838)

InstantiateFunctionDefinition at main.cpp:1072:

#0  clang::FunctionDecl::isDefined(FunctionDecl const*&, bool)     (this = invalid)
#1  clang::Sema::InstantiateFunctionDefinition
#2  mrbind::InstantiateReturnTypeIfNeeded                          (main.cpp:1072)
#3  mrbind::ClangAstVisitor_InstTypesAndCollectNewTypes::VisitFunctionDecl  (main.cpp:2859)

Both inputs the same: PatternFD returned by FunctionDecl::getTemplateInstantiationPattern(/*ForDefinition*/ false) on a primary template is null, and Sema dereferences it without a guard.

Tested clang versions

clang repro without fix with fix
18.1.8 crashes clean
19.1.7 crashes clean
21.1.8 crashes clean
22.1.4 crashes clean

Source-diff of Sema::SubstDefaultArgument, Sema::addInstantiatedParametersToScope, Sema::InstantiateDefaultArgument, FunctionDecl::getTemplateInstantiationPattern across llvmorg-18.1.8 ... llvmorg-22.1.4 is logically identical (only type/RAII renames), so the bug is at least as old as v18.

Standalone clang-tooling reproducer (no mrbind dependency):

// repro_input.cpp
template <typename T> struct Wrap {
    template <typename U = T> void method(int x = sizeof(U)) {}
};
inline void use() { Wrap<int> w; w.method(); }

A RecursiveASTVisitor whose VisitFunctionDecl calls Sema::InstantiateDefaultArgument on every ParmVarDecl with hasUninstantiatedDefaultArg() (no isTemplated() guard) crashes identically inside libclang. Will be filed upstream against LLVM as API hardening (Sema::InstantiateDefaultArgument's documented precondition is only assert(Param->hasUninstantiatedDefaultArg()) — it should also assert FD->isTemplateInstantiation() or fail gracefully on a null pattern).

Verification on real input

Local mrbind build with this fix, parsing #include <boost/multiprecision/cpp_int.hpp> on clang 22.1.4:

  • Without fix: silent crash after 0 lines of output (the original CI signature).
  • With fix: parser progresses past Sema instantiation, reaches a separate cppdecl error on _Complex _Float16 — that's a different issue in a different library, not addressed here.

Test plan

  • CI on this branch is green (mrbind has its own tests in test/).
  • Picked up as a submodule bump in MeshLib PR #6021 (clang 22 toolchain) and observed to clear the STATUS_ACCESS_VIOLATION segfault during Generate C bindings.

… / InstantiateFunctionDefinition calls

When the visitor walks a primary `FunctionDecl` for which `decl.isTemplated()`
is true, calling `Sema::InstantiateDefaultArgument` and
`Sema::InstantiateFunctionDefinition` on it is meaningless — there is no
specialization to substitute into — and on every clang version I tested
(18.1.8, 19.1.7, 21.1.8, 22.1.4) both APIs crash with `STATUS_ACCESS_VIOLATION`:

* `Sema::InstantiateDefaultArgument` -> `SubstDefaultArgument` ->
  `addInstantiatedParametersToScope`: derefs a `nullptr` returned by
  `FunctionDecl::getTemplateInstantiationPattern(/*ForDefinition*/ false)`.

* `Sema::InstantiateFunctionDefinition` -> `FunctionDecl::isDefined()`:
  derefs an invalid pattern decl pointer.

The visitor is allowed to see primary templates via `allow_uninstantiated_templates`,
and the specific instantiations are visited separately and handled correctly,
so just skipping the call on primary templates is the right fix.

Reduces to a standalone clang-tooling reproducer:

  // repro_input.cpp
  template <typename T> struct Wrap {
      template <typename U = T> void method(int x = sizeof(U)) {}
  };
  inline void use() { Wrap<int> w; w.method(); }

  // tool: a RecursiveASTVisitor that calls
  // Sema::InstantiateDefaultArgument on every ParmVarDecl with
  // `hasUninstantiatedDefaultArg()` (no `isTemplated()` guard) crashes
  // identically inside libclang. Bug exists at least back to clang 18.1.8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fedr added a commit to MeshInspector/MeshLib that referenced this pull request May 12, 2026
Picks up the fix that skips primary function templates in
`Sema::InstantiateDefaultArgument` / `Sema::InstantiateFunctionDefinition`
calls. Without the fix, mrbind parsing `<boost/multiprecision/cpp_int.hpp>`
on clang 22 segfaults inside libclang with `STATUS_ACCESS_VIOLATION` on
a null `PatternDecl`. See MeshInspector/mrbind#33 for the full repro and
stack traces.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Install `llvm::sys::PrintStackTraceOnErrorSignal` at the top of `main()`
so a `SIGSEGV` / Windows SEH `STATUS_ACCESS_VIOLATION` from inside libclang
prints a backtrace to stderr instead of silently dying. Diagnostic-only —
no behavior change on the happy path.

With `RelWithDebInfo` builds (already the default per
`install_mrbind_windows_msys2.bat`), mrbind frames carry DWARF source
locations. libclang-cpp / libLLVM frames show function names only,
because msys2 strips DLL debug info.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
adalisk-emikhaylov and others added 2 commits May 13, 2026 05:25
…rgs path

The block's own comment already says this path is only needed for free
function templates and that "something else already instantiates them for
the class member functions". When the path is exercised on a CXXMethodDecl
(in particular a CXXConstructorDecl), `Sema::SubstDecl` on the member
template's pattern null-derefs inside
`TemplateDeclInstantiator::InitMethodInstantiation`.

Captured stack from CI (clang 22.1.4) parsing
`<boost/multiprecision/cpp_int.hpp>` with
`--buggy-substitute-default-template-args`:

  #0 clang::TemplateDeclInstantiator::InitMethodInstantiation
  #1 clang::TemplateDeclInstantiator::VisitCXXMethodDecl
  #2 lambda inside Sema::SubstDecl (runWithSufficientStackSpace)
  #3 clang::StackExhaustionHandler::runWithSufficientStackSpace
  #4 clang::Sema::runWithSufficientStackSpace
  #5 clang::Sema::SubstDecl
  #6 mrbind::ClangAstVisitor_InstTypesAndCollectNewTypes::VisitFunctionDecl
       main.cpp:2815  <- this call site
  #7 clang::RecursiveASTVisitor::TraverseCXXConstructorDecl

Adding `!llvm::isa<clang::CXXMethodDecl>(decl)` to the existing
`decl->isTemplated()` check matches the comment and avoids the crash.
This is consistent with the previous commit, which skipped primary
function templates from `InstantiateDefaultArgument` /
`InstantiateFunctionDefinition` for the same class of latent libclang
bug (public Sema API null-derefs on a primary template's pattern).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@adalisk-emikhaylov adalisk-emikhaylov force-pushed the fix/skip-primary-templates-in-sema-calls branch from 8f5d884 to c75830f Compare May 13, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants