feat: struct方法 impl (P1 #9)

- lexer: TOK_IMPL 关键字
- ast: AST_IMPL_BLOCK, AST_METHOD_CALL + AST_PROGRAM impls数组
- parser: impl StructName { fn ... } + p.method() 方法调用
- sema: 方法名mangle(StructName$method), self参数, 类型检查
- codegen: METHOD_CALL→mangled函数调用(recv为第一参数)
- 新增集成测试: 19_struct_method.l

P1 4项全部完成: type alias + enum + array + impl
测试: 145 通过 (41+15+65+24)
This commit is contained in:
2026-06-05 14:30:24 +08:00
parent 2923e7574d
commit 9f6e695ba8
11 changed files with 340 additions and 20 deletions
+106
View File
@@ -1,4 +1,5 @@
#include "sema.h"
#include <stdio.h>
#include <string.h>
// === 类型关系 ===
@@ -345,6 +346,66 @@ static void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena*
break;
}
case AST_METHOD_CALL: {
analyze_expr(node->as.method_call.receiver, scope, errors, a);
const char* recv_struct = node->as.method_call.receiver->type.struct_name;
if (node->as.method_call.receiver->type.kind != TYPE_STRUCT || !recv_struct) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"只有结构体类型支持方法调用");
node->type.kind = TYPE_ERROR; break;
}
// 构造改名后的函数名并查找
char mangled[256];
snprintf(mangled, sizeof(mangled), "%s$%s", recv_struct,
node->as.method_call.method_name);
Symbol* sym = scope_lookup(scope, mangled);
if (!sym || sym->kind != SYM_FUNCTION) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"结构体 '%s' 没有方法 '%s'", recv_struct,
node->as.method_call.method_name);
node->type.kind = TYPE_ERROR; break;
}
// 检查参数数量(用户提供的参数 + 隐含的 self)
if (node->as.method_call.arg_count + 1 != sym->param_count) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"方法 '%s' 需要 %zu 个参数,提供了 %zu 个",
node->as.method_call.method_name,
sym->param_count > 0 ? sym->param_count - 1 : 0,
node->as.method_call.arg_count);
node->type.kind = TYPE_ERROR; break;
}
// 对每个参数进行类型检查(跳过 self 参数,即 sym->param_types[0] 是 self 的类型)
for (size_t i = 0; i < node->as.method_call.arg_count; i++) {
analyze_expr(node->as.method_call.args[i], scope, errors, a);
TypeKind actual = node->as.method_call.args[i]->type.kind;
TypeKind expected = sym->param_types[i + 1];
if (actual != TYPE_ERROR && actual != expected &&
!(expected == TYPE_I64 && actual == TYPE_ENUM)) {
if (expected == TYPE_STRUCT) {
// 结构体类型参数:比较具体类型名
const char* actual_name = node->as.method_call.args[i]->type.struct_name;
const char* expected_name = sym->param_struct_names ? sym->param_struct_names[i + 1] : NULL;
if (!actual_name || !expected_name || strcmp(actual_name, expected_name) != 0) {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"参数 %zu 类型不匹配: 期望 '%s',得到 '%s'",
i + 1,
expected_name ? expected_name : "struct",
actual_name ? actual_name : type_name(actual));
}
} else {
error_add(errors, "<sema>", node->loc.line, node->loc.col,
"参数 %zu 类型不匹配: 期望 '%s',得到 '%s'",
i + 1, type_name(expected), type_name(actual));
}
}
}
node->type.kind = sym->return_type;
if (sym->return_type == TYPE_STRUCT && sym->return_struct_type_name) {
node->type.struct_name = sym->return_struct_type_name;
}
break;
}
default: break;
}
}
@@ -391,6 +452,51 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
fnames, ftypes, fstruct_names,
sd->as.struct_decl.field_count);
}
// 处理 impl 块:将方法名改写为 StructName$methodName
// 并自动添加 self 参数(第一个参数),然后注册为普通函数。
// 同时将改写后的方法追加到程序 functions 数组方便后续 codegen。
{
// 先统计需要新增多少个函数(impl 中的方法总数)
size_t extra_fn = 0;
for (size_t i = 0; i < node->as.program.impl_count; i++) {
AstNode* impl = node->as.program.impls[i];
extra_fn += impl->as.impl_block.method_count;
}
if (extra_fn > 0) {
AstNode** new_fns = (AstNode**)arena_alloc_impl(a,
(node->as.program.fn_count + extra_fn) * sizeof(AstNode*));
memcpy(new_fns, node->as.program.functions,
node->as.program.fn_count * sizeof(AstNode*));
size_t write_pos = node->as.program.fn_count;
for (size_t i = 0; i < node->as.program.impl_count; i++) {
AstNode* impl = node->as.program.impls[i];
const char* st_name = impl->as.impl_block.struct_name;
// 验证目标结构体存在
Symbol* st_sym = scope_lookup_struct(scope, st_name);
if (!st_sym) {
error_add(errors, "<sema>", impl->loc.line, impl->loc.col,
"impl 的目标结构体 '%s' 未定义", st_name);
continue;
}
for (size_t j = 0; j < impl->as.impl_block.method_count; j++) {
AstNode* method = impl->as.impl_block.methods[j];
// 构造改名后的函数名
char mangled[256];
snprintf(mangled, sizeof(mangled), "%s$%s", st_name,
method->as.function.name);
method->as.function.name = arena_strdup_impl(a, mangled,
strlen(mangled));
// 追加到新 functions 数组
new_fns[write_pos++] = method;
}
}
// 更新程序节点
node->as.program.functions = new_fns;
node->as.program.fn_count = node->as.program.fn_count + extra_fn;
}
}
// 第二遍:收集所有函数签名
for (size_t i = 0; i < node->as.program.fn_count; i++) {
AstNode* fn = node->as.program.functions[i];