Skip to content

Fix C++ template call segfault#360

Closed
Smiie-2 wants to merge 1 commit into
DeusData:mainfrom
Smiie-2:fix-template-call-segfault
Closed

Fix C++ template call segfault#360
Smiie-2 wants to merge 1 commit into
DeusData:mainfrom
Smiie-2:fix-template-call-segfault

Conversation

@Smiie-2
Copy link
Copy Markdown

@Smiie-2 Smiie-2 commented May 17, 2026

This fixes a crash in the C/C++ LSP path when resolving nested template function calls with default template arguments. The underlying issue was that unbound template parameters could flow through substitution as
NULL, and call-expression return handling assumed the substituted type was always valid.

Example
MangoHud’s src/hud_elements.cpp was one reproducer, but the fix targets the general case of calls like a nested template function inside another call expression

What changed:

  • Preserved unbound template parameters during type substitution instead of turning them into NULL.
  • Added a guard before unwrapping substituted call return types.
  • Improved template-function lookup in the C++ call resolver path.
  • Tightened pending template-call deduction so it respects the registered formal parameter count.
  • Made C++ template-declaration unwrapping handle nested template declarations more reliably.
  • Added regressions for the general substitution bug and for the nested template-call crash pattern.

Verification:

  • make -f Makefile.cbm cbm
  • make -f Makefile.cbm build/c/test-runner
  • clsp_nocrash_template_function_multi_param_nested_call PASS
  • typerep_substitute_unbound_param_preserved PASS

DeusData pushed a commit that referenced this pull request May 30, 2026
…t args

Root cause: unbound template parameters flowed through cbm_type_substitute
as NULL, and call-expression return handling then dereferenced the NULL
substituted type. Reproduced on MangoHud's src/hud_elements.cpp.

- type_rep: cbm_type_substitute now returns the original param `t` (not
  NULL) when a type arg is unbound, in both the TYPE_PARAM and named-param
  paths — unbound params stay intact instead of poisoning the type.
- c_lsp: guard `if (!ret) return cbm_type_unknown();` before unwrapping
  the substituted return type; guard the substitute result before use; add
  template_function call-node resolution (qualified/scoped/plain names).
- extract_defs: factor template-inner lookup into find_cpp_template_inner_node,
  which recurses into nested template_declarations and also handles
  field_declaration; reuse it from unwrap_template_inner.
- tests: clsp_nocrash_template_function_multi_param_nested_call (the nested
  reproducer) and typerep_substitute_unbound_param_preserved.

Distilled from #360 onto current main. The formal-parameter-count clamp
that overlapped this PR already landed via #322, so only the additive
crash fixes are taken here. Contributes to the C++ stability cluster (#390;
relates to #215/#312).
@DeusData
Copy link
Copy Markdown
Owner

Thank you, @Smiie-2! 🙏 This is a really solid crash fix and the root-cause analysis was excellent — you correctly identified that unbound template parameters were flowing through cbm_type_substitute as NULL and then getting dereferenced in the call-expression return path. The MangoHud hud_elements.cpp reproducer was perfect for isolating it.

I reviewed each change closely:

  • cbm_type_substitute returning the original param t instead of NULL for unbound args (both the TYPE_PARAM and named-param paths) is the right root-cause fix.
  • The if (!ret) return cbm_type_unknown(); guard before return-type unwrapping is the actual NULL-deref backstop.
  • The find_cpp_template_inner_node refactor (recursing into nested template_declarations + handling field_declaration) and the template_function call-node resolution are nice additive improvements.

One note on landing: the formal-parameter-count clamp that your PR also included had meanwhile landed independently via #322, so to avoid a double-fix I took only the additive parts of your change (substitution NULL-preservation, the return guards, the template lookup, and both of your new tests). I distilled it onto current main and credited you as the commit author:

Landed in d895c16. Verified locally: build clean, all 3,619 tests pass — including your clsp_nocrash_template_function_multi_param_nested_call and typerep_substitute_unbound_param_preserved. Together with #322 this should close out the C++ template crash class tracked in #390 (relates to #215/#312). Thank you for the deep fix! 🙏

@DeusData DeusData closed this May 30, 2026
DeusData pushed a commit that referenced this pull request May 30, 2026
- cbm_arena_alloc returns NULL on a NULL arena (both arena.c copies)
  instead of dereferencing it — defense-in-depth against the NULL-arena
  type-allocation path that could crash the LSP type layer.
- discover: add "vendored" to the always-skip directory list.

Distilled from #374, taking the parts not already on main. The PR's
ts_lsp tuple-arena allocation, the C/C++ template formal-count clamp,
and the type_rep unbound-param preservation it also carried all landed
independently in v0.7.0 / via #322 / #360, so only these two
defensive improvements remain. Relates to #390.
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