Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions src/compile/c_gen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<SyntaxTreeFunctioncall>(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<SyntaxTreeArgs>(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<SyntaxTreePrefixexp>(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<SyntaxTreeVar>(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<SyntaxTreeExplist>(args_ptr->Explist());
DEBUG_ASSERT(explist_ptr);
const auto &raw_args = explist_ptr->Exps();
Expand Down
32 changes: 27 additions & 5 deletions src/compile/type_inferencer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1429,11 +1429,24 @@ bool TypeInferencer::CheckCallNodeChange(const SyntaxTreeInterfacePtr &node, con
return false;
}
const auto fc = std::dynamic_pointer_cast<SyntaxTreeFunctioncall>(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<SyntaxTreePrefixexp>(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<SyntaxTreeVar>(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<SyntaxTreeArgs>(fc->Args());
Expand Down Expand Up @@ -1645,10 +1658,19 @@ InferredType TypeInferencer::ResolveCallReturnType(const std::shared_ptr<SyntaxT
return T_DYNAMIC;
}
// 提取被调函数名(仅支持简单形式 callee(...),方法调用形式不处理)。
// 这里必须真判断而不能只靠 DEBUG_ASSERT:f():m() 的被调者是一个函数调用节点,
// dynamic_pointer_cast 会得到空指针,Release 下会在空指针上取名字而崩溃。
if (!fc->Name().empty()) {
return T_DYNAMIC;
}
const auto callee_pe = std::dynamic_pointer_cast<SyntaxTreePrefixexp>(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<SyntaxTreeVar>(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);
Expand Down
34 changes: 34 additions & 0 deletions test/lua/infer/test_infer_method_call_on_call.lua
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions test/test_infer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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。
Expand Down
Loading