Repository navigation
fix: stop crashing on f():m() inside parameterised functions (#36) - #37
Merged
Merged
Conversation
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 Report❌ Patch coverage is
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. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #36
问题
在带参数的函数体内,把函数调用结果直接作为方法调用的接收者(
Pkg.Fn():get(k))会导致编译器 SIGSEGV(exit 139)。因为是编译期崩溃,哪怕这个函数永远不会被调用也会崩,整个服务起不来,且没有任何 Lua 侧报错。三个 JIT 后端(TCC/GCC/interp)都崩——崩溃发生在 JIT 之前。根因
数学参数发现(
FindMathParamIndices)会遍历带参函数体里的每一个 FunctionCall 节点并交给CheckCallNodeChange,而后者假定被调者是简单变量名:对
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.cppCheckCallNodeChangereturn falsesrc/compile/type_inferencer.cppResolveCallReturnTypereturn T_DYNAMIC,顺带修掉assumed_ret查不到时ret_it->second的 UBsrc/compile/c_gen.cppTryCompileNativeSpecCallExprreturn {},顺带把越界的.at()换成contains检查三处都先用
fc->Name()排除方法调用(obj:m()/a.b())。语义上是保守退化——少做数学特化,不改变行为。验证
test/lua/infer/test_infer_method_call_on_call.lua+TEST(infer, test_infer_method_call_on_call)。test_redis.integration_*(需要真实 Redis),无新增回归。