diff --git a/src/compile/c_gen.cpp b/src/compile/c_gen.cpp index 8ca89f1..345f455 100644 --- a/src/compile/c_gen.cpp +++ b/src/compile/c_gen.cpp @@ -3243,22 +3243,37 @@ std::string CGen::CompileNumericExp(const SyntaxTreeInterfacePtr &exp) { // 可直接参与后续原生算术运算。 std::string CGen::TryCompileNativeSpecCallExpr(const SyntaxTreeInterfacePtr &functioncall_node) { const auto fc = std::dynamic_pointer_cast(functioncall_node); - DEBUG_ASSERT(fc); + if (!fc) { + return {}; + } const auto args_node = fc->Args(); - DEBUG_ASSERT(args_node); + if (!args_node) { + return {}; + } const auto args_ptr = std::dynamic_pointer_cast(args_node); - DEBUG_ASSERT(args_ptr); - if (args_ptr->GetArgsKind() != ArgsKind::kExpList) { + if (!args_ptr || args_ptr->GetArgsKind() != ArgsKind::kExpList) { + return {}; + } + // 同样只支持 callee(args):方法调用/对调用结果再调用/成员调用的被调者不是简单变量名, + // 必须提前返回,否则会在空指针上取名字(Release 下 DEBUG_ASSERT 被编译掉)。 + if (!fc->Name().empty()) { return {}; } const auto callee_pe = std::dynamic_pointer_cast(fc->prefixexp()); - DEBUG_ASSERT(callee_pe && callee_pe->GetPrefixKind() == PrefixExpKind::kVar); + if (!callee_pe || callee_pe->GetPrefixKind() != PrefixExpKind::kVar) { + return {}; + } const auto callee_var = std::dynamic_pointer_cast(callee_pe->GetValue()); - DEBUG_ASSERT(callee_var && callee_var->GetVarKind() == VarKind::kSimple); + if (!callee_var || callee_var->GetVarKind() != VarKind::kSimple) { + return {}; + } if (BindsLocalValue(callee_var.get())) { return {}; } const auto &callee_name = callee_var->GetName(); + if (!ir().math_param_positions.contains(callee_name)) { + return {}; + } const auto explist_ptr = std::dynamic_pointer_cast(args_ptr->Explist()); DEBUG_ASSERT(explist_ptr); const auto &raw_args = explist_ptr->Exps(); diff --git a/src/compile/type_inferencer.cpp b/src/compile/type_inferencer.cpp index 35355fc..b8baee2 100644 --- a/src/compile/type_inferencer.cpp +++ b/src/compile/type_inferencer.cpp @@ -1429,11 +1429,24 @@ bool TypeInferencer::CheckCallNodeChange(const SyntaxTreeInterfacePtr &node, con return false; } const auto fc = std::dynamic_pointer_cast(node); - DEBUG_ASSERT(fc); + if (!fc) { + return false; + } + // 只处理 callee(args) 这种简单直接调用。方法调用 obj:m(args)、对调用结果再调用 + // f():m(args)、成员调用 a.b(args) 的被调者都不是简单变量名,必须提前返回: + // 否则下面的 dynamic_pointer_cast 得到空指针,Release 下 DEBUG_ASSERT 被编译掉, + // 会在空指针上取名字而崩溃(issue #36)。 + if (!fc->Name().empty()) { + return false; + } const auto callee_pe = std::dynamic_pointer_cast(fc->prefixexp()); - DEBUG_ASSERT(callee_pe && callee_pe->GetPrefixKind() == PrefixExpKind::kVar); + if (!callee_pe || callee_pe->GetPrefixKind() != PrefixExpKind::kVar) { + return false; + } const auto callee_var = std::dynamic_pointer_cast(callee_pe->GetValue()); - DEBUG_ASSERT(callee_var && callee_var->GetVarKind() == VarKind::kSimple); + if (!callee_var || callee_var->GetVarKind() != VarKind::kSimple) { + return false; + } const auto &callee_name = callee_var->GetName(); if (const auto math_it = math_param_positions.find(callee_name); math_it != math_param_positions.end()) { const auto args_ptr = std::dynamic_pointer_cast(fc->Args()); @@ -1645,10 +1658,19 @@ InferredType TypeInferencer::ResolveCallReturnType(const std::shared_ptrName().empty()) { + return T_DYNAMIC; + } const auto callee_pe = std::dynamic_pointer_cast(fc->prefixexp()); - DEBUG_ASSERT(callee_pe && callee_pe->GetPrefixKind() == PrefixExpKind::kVar); + if (!callee_pe || callee_pe->GetPrefixKind() != PrefixExpKind::kVar) { + return T_DYNAMIC; + } const auto callee_var = std::dynamic_pointer_cast(callee_pe->GetValue()); - DEBUG_ASSERT(callee_var && callee_var->GetVarKind() == VarKind::kSimple); + if (!callee_var || callee_var->GetVarKind() != VarKind::kSimple) { + return T_DYNAMIC; + } const auto &callee_name = callee_var->GetName(); const auto math_it = tctx.ctx->math_positions->find(callee_name); diff --git a/test/lua/infer/test_infer_method_call_on_call.lua b/test/lua/infer/test_infer_method_call_on_call.lua new file mode 100644 index 0000000..f010a86 --- /dev/null +++ b/test/lua/infer/test_infer_method_call_on_call.lua @@ -0,0 +1,34 @@ +-- Regression (issue #36): a method call whose receiver is itself a call +-- expression (`make():get(k)`) inside a function that takes parameters used to +-- crash the compiler with SIGSEGV, before the function was ever called. +-- +-- Math-parameter discovery walks every FunctionCall node of a parameterised +-- function and used to assume the callee was a simple name: for `make():get(k)` +-- the callee node is a FunctionCall, so the cast to SyntaxTreeVar yielded +-- nullptr and GetName() dereferenced it. +local box = container.map() + +function make() + return box +end + +-- The receiver is a call expression, and the enclosing function has a parameter. +function test(n) + box:set("v", 42) + local x = make():get("v") + return x + n +end + +-- Same shape, but the result flows into an arithmetic expression (numeric +-- specialization path) instead of a plain local binding. +function test_arith(n) + box:set("v", 42) + return make():get("v") * n + 1 +end + +-- A method call on a plain variable receiver must keep working unchanged. +function test_var(n) + box:set("v", 42) + local b = make() + return b:get("v") + n +end diff --git a/test/test_infer.cpp b/test/test_infer.cpp index cced7e5..9f26532 100644 --- a/test/test_infer.cpp +++ b/test/test_infer.cpp @@ -148,6 +148,25 @@ TEST(infer, test_infer_degrade_func_call) { }); } +// Regression (issue #36): a method call on a call result (`make():get(k)`) +// inside a parameterised function used to SIGSEGV during math-parameter +// discovery. It must now compile, and still evaluate like the two-step form. +TEST(infer, test_infer_method_call_on_call) { + const auto code = InferGetCCode("./infer/test_infer_method_call_on_call.lua"); + ASSERT_NE(code.find("FlCallClosure("), std::string::npos); + + InferRunHelper([](State *s, JITType type, bool debug_mode) { + CompileFile(s, "./infer/test_infer_method_call_on_call.lua", {.debug_mode = debug_mode}); + int ret = 0; + Call(s, type, "test", ret, 1); + ASSERT_EQ(ret, 43); + Call(s, type, "test_arith", ret, 2); + ASSERT_EQ(ret, 85); + Call(s, type, "test_var", ret, 3); + ASSERT_EQ(ret, 45); + }); +} + // Mid-way degradation: a parameter-bound for loop. n is a math param. // int 特化:sum/i 都是 int64_t。float 特化:Lua 5.4 仍走整数 for(FlForLimitToInt), // sum 不会退化成 double。