Skip to content

fix: stop crashing on f():m() inside parameterised functions (#36) - #37

Merged
esrrhs merged 1 commit into
masterfrom
fix/issue-36-method-call-on-call
Oct 17, 2026
Merged

esrrhs merged 1 commit into
masterfrom
fix/issue-36-method-call-on-call

Conversation

@esrrhs

@esrrhs esrrhs commented Oct 17, 2026

Copy link
Copy Markdown
Owner

Fixes #36

问题

在带参数的函数体内,把函数调用结果直接作为方法调用的接收者(Pkg.Fn():get(k))会导致编译器 SIGSEGV(exit 139)。因为是编译期崩溃,哪怕这个函数永远不会被调用也会崩,整个服务起不来,且没有任何 Lua 侧报错。三个 JIT 后端(TCC/GCC/interp)都崩——崩溃发生在 JIT 之前。

根因

数学参数发现(FindMathParamIndices)会遍历带参函数体里的每一个 FunctionCall 节点并交给 CheckCallNodeChange,而后者假定被调者是简单变量名:

DEBUG_ASSERT(callee_pe && callee_pe->GetPrefixKind() == PrefixExpKind::kVar);
const auto callee_var = std::dynamic_pointer_cast<SyntaxTreeVar>(callee_pe->GetValue());
DEBUG_ASSERT(callee_var && callee_var->GetVarKind() == VarKind::kSimple);
const auto &callee_name = callee_var->GetName();   // <- 崩溃点

对 f():m() 来说被调者是一个 FunctionCall 节点,dynamic_pointer_cast<SyntaxTreeVar> 返回空指针;而 -DNDEBUG 恒开(Debug 构建也是),DEBUG_ASSERT 被编译掉,于是变成裸的空指针解引用(崩溃地址 0x3f = NULL + 0x28)。

「函数带参数」是触发条件,因为 FindMathParamIndices 只对形参非空的函数做数学参数发现——所以 function f() 不崩,function f(a) / function f(...) 都崩。

修复

把三处同样的「拿断言当校验」改成真实提前返回:

文件 函数 修改
src/compile/type_inferencer.cpp CheckCallNodeChange 非简单名调用 → return false
src/compile/type_inferencer.cpp ResolveCallReturnType → return T_DYNAMIC,顺带修掉 assumed_ret 查不到时 ret_it->second 的 UB
src/compile/c_gen.cpp TryCompileNativeSpecCallExpr → return {},顺带把越界的 .at() 换成 contains 检查

三处都先用 fc->Name() 排除方法调用(obj:m() / a.b())。语义上是保守退化——少做数学特化,不改变行为。

验证

  • issue 里的 8 个场景(无参 / 1 参 / 3 参 / 作为语句 / 表构造器内 / 同包函数 / 文件内 local 函数 / 链式)全部通过,TCC(0) / GCC(1) / interp(2) 三后端输出一致。
  • 新增回归用例 test/lua/infer/test_infer_method_call_on_call.lua + TEST(infer, test_infer_method_call_on_call)。
  • infer 全量 222 个测试通过。
  • 全量 1592 个测试:1588 通过,4 个失败均为既有基线失败 test_redis.integration_*(需要真实 Redis),无新增回归。

A method call whose receiver is itself a call expression (`Pkg.Fn():get(k)`)
crashed the compiler with SIGSEGV as soon as the enclosing function had a
parameter list, even if that function was never called. Every JIT backend was
affected because the crash happens before JIT: exit 139 with no Lua-level
message.

Root cause: math-parameter discovery (FindMathParamIndices) walks every
FunctionCall node of a parameterised function and hands each one to
CheckCallNodeChange, which assumed the callee was a simple name:

    DEBUG_ASSERT(callee_pe && callee_pe->GetPrefixKind() == PrefixExpKind::kVar);
    const auto callee_var = std::dynamic_pointer_cast<SyntaxTreeVar>(callee_pe->GetValue());
    DEBUG_ASSERT(callee_var && callee_var->GetVarKind() == VarKind::kSimple);
    const auto &callee_name = callee_var->GetName();

For `f():m()` the callee node is a FunctionCall, so the cast yields nullptr,
and -DNDEBUG is always on (even in Debug builds), so DEBUG_ASSERT is compiled
out and GetName() dereferences nullptr.

Fix: turn the assertions into real early returns in all three places that make
the same assumption -- CheckCallNodeChange (return false), ResolveCallReturnType
(return T_DYNAMIC, which also removes an out-of-range `ret_it->second` UB when
the callee name is absent from assumed_ret) and CGen::TryCompileNativeSpecCallExpr
(return {}, which also replaces an out-of-range `.at()`). All three also reject
method calls up front via `fc->Name()`, which is what obj:m() sets. The result
is a conservative deopt (fewer math specializations), never a behaviour change.

Regression test: test/lua/infer/test_infer_method_call_on_call.lua covers the
crashing shape, the same expression inside arithmetic, and the two-step
workaround, across all three JIT backends.

Full suite: 1592 tests, 1588 pass, 4 failures are the pre-existing
test_redis.integration_* cases that need a live Redis.
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 70.00000% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.05%. Comparing base (2c4bf28) to head (829d7c5).

Files with missing lines Patch % Lines
src/compile/c_gen.cpp 53.84% 6 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #37      +/-   ##
==========================================
- Coverage   88.07%   88.05%   -0.02%     
==========================================
  Files         124      124              
  Lines       25498    25507       +9     
==========================================
+ Hits        22457    22460       +3     
- Misses       3041     3047       +6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@esrrhs
esrrhs merged commit 01abdf2 into master Oct 17, 2026
9 checks passed
@esrrhs
esrrhs deleted the fix/issue-36-method-call-on-call branch October 17, 2026 06:12
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.

[bug] 带参函数内 f():m() 链式方法调用导致编译器 SIGSEGV (exit 139)

2 participants