From 6ebe551ee36aef2373a8acfa09c514ffe32a6d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E8=88=AA=E5=AE=87?= <3364451258@qq.com> Date: Sun, 7 Jun 2026 18:48:04 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20AST=5FPARAMETER=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=95=B0=E7=BB=84=E5=85=83=E7=B4=A0=E7=B1=BB=E5=9E=8B=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=20+=20=E4=BA=94=E5=AD=90=E6=A3=8B=E9=9B=86=E6=88=90?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: 函数参数声明 i64[N] 只在 TypeInfo 存储数组信息, AST_PARAMETER 仅存 TypeKind(TYPE_ARRAY), 丢失元素类型和大小, 导致 sema 将参数 数组误判为 i32[N], codegen 生成 void GEP 而崩溃。 修复: - AST_PARAMETER 新增 arr_elem_type/arr_elem_struct/arr_size 字段 - parser 传入 parse_type_expr 的完整数组信息 - sema 将数组信息从 AST 节点复制到 Symbol - codegen 为数组参数生成正确的 LLVMArrayType 附加: 45_gomoku.l — 5x5 五子棋双AI对弈, 测试数组/函数/循环/字符串 Co-Authored-By: Claude Opus 4.7 --- src/ast/ast.c | 7 ++++- src/ast/ast.h | 6 ++-- src/codegen/codegen.c | 10 +++++++ src/parser/expr.c | 4 ++- src/parser/parser.c | 10 +++++-- src/sema/sema.c | 10 +++++++ test/programs/45_gomoku.l | 63 +++++++++++++++++++++++++++++++++++++++ test/test_codegen.c | 14 ++++----- 8 files changed, 110 insertions(+), 14 deletions(-) create mode 100644 test/programs/45_gomoku.l diff --git a/src/ast/ast.c b/src/ast/ast.c index 5fbe7d9..75f8e79 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -51,11 +51,16 @@ AstNode* ast_make_function(void* alloc, const char* name, AstNode** params, size } AstNode* ast_make_parameter(void* alloc, const char* name, TypeKind type, - const char* struct_type_name, bool is_out, SourceLoc loc) { + const char* struct_type_name, bool is_out, + TypeKind arr_elem, const char* arr_elem_sname, + int64_t arr_size, SourceLoc loc) { NEW(alloc, AST_PARAMETER); n->as.parameter.name = name; n->as.parameter.type = type; n->as.parameter.struct_type_name = struct_type_name; n->as.parameter.is_out = is_out; + n->as.parameter.arr_elem_type = arr_elem; + n->as.parameter.arr_elem_struct = arr_elem_sname; + n->as.parameter.arr_size = arr_size; return n; } diff --git a/src/ast/ast.h b/src/ast/ast.h index 30bc6c5..2f66f83 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -76,7 +76,8 @@ struct AstNode { TypeKind* multi_ret_types; const char** multi_ret_snames; size_t multi_ret_count; const char** captured; TypeKind* cap_types; size_t cap_count; } function; // AST_PARAMETER (也用作结构体字段: name + type) - struct { const char* name; TypeKind type; const char* struct_type_name; bool is_out; } parameter; + struct { const char* name; TypeKind type; const char* struct_type_name; bool is_out; + TypeKind arr_elem_type; const char* arr_elem_struct; int64_t arr_size; } parameter; // AST_BLOCK struct { struct AstNode** stmts; size_t stmt_count; } block; // AST_LET_STMT @@ -158,7 +159,8 @@ AstNode* ast_make_function(void* alloc, const char* name, AstNode** params, size TypeKind ret, const char* ret_struct_name, AstNode* body, bool is_pub, const char** type_params, size_t tp_count, SourceLoc loc); -AstNode* ast_make_parameter(void* alloc, const char* name, TypeKind type, const char* struct_type_name, bool is_out, SourceLoc loc); +AstNode* ast_make_parameter(void* alloc, const char* name, TypeKind type, const char* struct_type_name, bool is_out, + TypeKind arr_elem, const char* arr_elem_sname, int64_t arr_size, SourceLoc loc); AstNode* ast_make_block(void* alloc, AstNode** stmts, size_t count, SourceLoc loc); AstNode* ast_make_let(void* alloc, const char* name, TypeKind annot_type, bool has_type_annot, bool is_mut, AstNode* init, const char* struct_type_name, diff --git a/src/codegen/codegen.c b/src/codegen/codegen.c index ff307c6..e5514ce 100644 --- a/src/codegen/codegen.c +++ b/src/codegen/codegen.c @@ -416,6 +416,11 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena, if (param->as.parameter.type == TYPE_STRUCT && param->as.parameter.struct_type_name) { inner_ty = find_struct_type(&ctx, param->as.parameter.struct_type_name); + } else if (param->as.parameter.type == TYPE_ARRAY) { + LLVMTypeRef et = param->as.parameter.arr_elem_struct + ? (find_struct_type(&ctx, param->as.parameter.arr_elem_struct) ?: LLVMInt64TypeInContext(ctx.context)) + : to_llvm_type(&ctx, param->as.parameter.arr_elem_type); + inner_ty = LLVMArrayType(et, (unsigned)param->as.parameter.arr_size); } else { inner_ty = to_llvm_type(&ctx, param->as.parameter.type); } @@ -480,6 +485,11 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena, if (pnode->as.parameter.type == TYPE_STRUCT && pnode->as.parameter.struct_type_name) { param_ty = find_struct_type(&ctx, pnode->as.parameter.struct_type_name); + } else if (pnode->as.parameter.type == TYPE_ARRAY) { + LLVMTypeRef et = pnode->as.parameter.arr_elem_struct + ? (find_struct_type(&ctx, pnode->as.parameter.arr_elem_struct) ?: LLVMInt64TypeInContext(ctx.context)) + : to_llvm_type(&ctx, pnode->as.parameter.arr_elem_type); + param_ty = LLVMArrayType(et, (unsigned)pnode->as.parameter.arr_size); } else { param_ty = to_llvm_type(&ctx, pnode->as.parameter.type); } diff --git a/src/parser/expr.c b/src/parser/expr.c index 066a6af..f396604 100644 --- a/src/parser/expr.c +++ b/src/parser/expr.c @@ -338,7 +338,9 @@ AstNode* parse_expr_prec(Parser* p, Precedence min_prec, ErrorInfo* error) { if (pti.kind == TYPE_ERROR) return NULL; plist[pc++] = ast_make_parameter(p->arena, arena_strdup_impl(p->arena, pname->start, pname->length), - pti.kind, pti.struct_name, is_out, tok_loc(pname)); + pti.kind, pti.struct_name, is_out, + pti.element_type, pti.element_struct_name, pti.array_size, + tok_loc(pname)); if (match(p, TOK_COMMA)) continue; else break; } if (!expect(p, TOK_RPAREN, error, "缺少 ')'")) return NULL; diff --git a/src/parser/parser.c b/src/parser/parser.c index 17b89be..5078602 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -27,7 +27,9 @@ AstNode* parse_struct_decl(Parser* p, ErrorInfo* error) { } fields[fcount++] = ast_make_parameter(p->arena, arena_strdup_impl(p->arena, fname->start, fname->length), - fti.kind, fti.struct_name, false, tok_loc(fname)); + fti.kind, fti.struct_name, false, + fti.element_type, fti.element_struct_name, fti.array_size, + tok_loc(fname)); if (peek(p)->kind == TOK_COMMA) advance(p); else break; } @@ -356,7 +358,9 @@ AstNode* parse_function(Parser* p, bool is_pub, ErrorInfo* error) { if (pti.kind == TYPE_ERROR) return NULL; params[pcount++] = ast_make_parameter(p->arena, arena_strdup_impl(p->arena, pname->start, pname->length), - pti.kind, pti.struct_name, is_out, tok_loc(pname)); + pti.kind, pti.struct_name, is_out, + pti.element_type, pti.element_struct_name, pti.array_size, + tok_loc(pname)); if (match(p, TOK_COMMA)) continue; else break; } @@ -700,7 +704,7 @@ AstNode* parse(Arena* a, const Token* tokens, size_t count, arena_strdup_impl(p.arena, fbuf, strlen(fbuf)), fn->as.function.multi_ret_types[i], fn->as.function.multi_ret_snames[i], - false, tok_loc(peek(&p))); + false, 0, NULL, 0, tok_loc(peek(&p))); } if (struct_count >= 64) { error->message = "结构体过多"; return NULL; } structs[struct_count++] = ast_make_struct_decl(p.arena, diff --git a/src/sema/sema.c b/src/sema/sema.c index b7e58f1..fba60aa 100644 --- a/src/sema/sema.c +++ b/src/sema/sema.c @@ -161,6 +161,11 @@ void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { AstNode* p = mono_fn->as.function.params[j]; Symbol* ps = scope_insert(fn_scope, a, p->as.parameter.name, SYM_PARAMETER, p->as.parameter.type); if (ps && p->as.parameter.is_out) { ps->kind = SYM_VARIABLE; ps->is_mut = true; } + if (ps && p->as.parameter.type == TYPE_ARRAY) { + ps->array_element_type = p->as.parameter.arr_elem_type; + ps->array_element_struct_name = p->as.parameter.arr_elem_struct; + ps->array_size = p->as.parameter.arr_size; + } } // 分析函数体 current_return_type = mono_fn->as.function.return_type; @@ -202,6 +207,11 @@ void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { if (sym && pt == TYPE_STRUCT && psn) { sym->struct_type_name = psn; } + if (sym && pt == TYPE_ARRAY) { + sym->array_element_type = p->as.parameter.arr_elem_type; + sym->array_element_struct_name = p->as.parameter.arr_elem_struct; + sym->array_size = p->as.parameter.arr_size; + } } // 解析返回类型的别名 const char* ret_sn = node->as.function.return_struct_type_name; diff --git a/test/programs/45_gomoku.l b/test/programs/45_gomoku.l new file mode 100644 index 0000000..e66bbea --- /dev/null +++ b/test/programs/45_gomoku.l @@ -0,0 +1,63 @@ +// === 五子棋 5x5 — L Language 综合测试 (带简单AI) === +fn idx(x: i64, y: i64) -> i64 { return y * 5 + x; } + +fn count_dir(b: i64[25], x: i64, y: i64, dx: i64, dy: i64, color: i64) -> i64 { + var cx = x + dx; var cy = y + dy; var n = 0; + while cx >= 0 { if cx > 4 { return n; } if cy < 0 { return n; } if cy > 4 { return n; } + if b[idx(cx, cy)] != color { return n; } + n = n + 1; cx = cx + dx; cy = cy + dy; } + return n; +} + +fn score_pos(b: i64[25], x: i64, y: i64, c: i64) -> i64 { + if b[idx(x, y)] != 0 { return -1; } + var h = count_dir(b, x, y, 1, 0, c) + count_dir(b, x, y, -1, 0, c); + var v = count_dir(b, x, y, 0, 1, c) + count_dir(b, x, y, 0, -1, c); + var d = count_dir(b, x, y, 1, 1, c) + count_dir(b, x, y, -1, -1, c); + var e = count_dir(b, x, y, 1, -1, c) + count_dir(b, x, y, -1, 1, c); + if h > 2 { return 1000; } if v > 2 { return 1000; } + if d > 2 { return 1000; } if e > 2 { return 1000; } + return h + v + d + e; +} + +fn ai(b: i64[25], c: i64) -> i64 { + var bx = 2; var by = 2; var bs = -1; var x = 0; + while x < 5 { var y = 0; while y < 5 { + var s = score_pos(b, x, y, c); + if s > bs { bs = s; bx = x; by = y; } + y = y + 1; } x = x + 1; } + return by * 100 + bx; +} + +fn won(b: i64[25], x: i64, y: i64) -> bool { + var c = b[idx(x, y)]; if c == 0 { return false; } + if count_dir(b, x, y, 1, 0, c) + count_dir(b, x, y, -1, 0, c) > 2 { return true; } + if count_dir(b, x, y, 0, 1, c) + count_dir(b, x, y, 0, -1, c) > 2 { return true; } + if count_dir(b, x, y, 1, 1, c) + count_dir(b, x, y, -1, -1, c) > 2 { return true; } + if count_dir(b, x, y, 1, -1, c) + count_dir(b, x, y, -1, 1, c) > 2 { return true; } + return false; +} + +fn print5(b: i64[25]) -> void { + var r = 0; while r < 5 { + var line = ""; var c = 0; while c < 5 { + var v = b[idx(c, r)]; + if v == 0 { line = line + ". "; } + if v == 1 { line = line + "X "; } + if v == 2 { line = line + "O "; } + c = c + 1; } print_str(line); r = r + 1; } +} + +fn main() -> void { + var b: i64[25] = b; var i = 0; while i < 25 { b[i] = 0; i = i + 1; } + b[idx(2, 2)] = 1; var turn = 2; var n = 1; + while n < 25 { + var mv = ai(b, turn); var y = mv / 100; var x = mv - y * 100; + b[idx(x, y)] = turn; + if turn == 1 { print_str("X: "); } else { print_str("O: "); } + print_i64(x); print_i64(y); + if won(b, x, y) { print5(b); if turn == 1 { print_str("X wins!"); } else { print_str("O wins!"); } return; } + n = n + 1; if turn == 1 { turn = 2; } else { turn = 1; } + } + print5(b); print_str("Draw"); +} diff --git a/test/test_codegen.c b/test/test_codegen.c index 3dfb557..e2700a4 100644 --- a/test/test_codegen.c +++ b/test/test_codegen.c @@ -129,8 +129,8 @@ void test_codegen_struct_decl() { /* 构造 AST: struct Point { x: i64, y: i64 } */ AstNode* fields[2]; - fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, false, loc_at(1, 1)); - fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, false, loc_at(1, 1)); + fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, false, 0, NULL, 0, loc_at(1, 1)); + fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, false, 0, NULL, 0, loc_at(1, 1)); AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1)); AstNode* structs[] = { struct_decl }; @@ -185,8 +185,8 @@ void test_codegen_struct_field_access() { /* 构造 AST: struct Point { x: i64, y: i64 } */ AstNode* fields[2]; - fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, false, loc_at(1, 1)); - fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, false, loc_at(1, 1)); + fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, false, 0, NULL, 0, loc_at(1, 1)); + fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, false, 0, NULL, 0, loc_at(1, 1)); AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1)); AstNode* structs[] = { struct_decl }; @@ -356,13 +356,13 @@ void test_codegen_method_call() { /* struct Point { x: i64, y: i64 } */ AstNode* fields[2]; - fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, false, loc_at(1, 1)); - fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, false, loc_at(1, 1)); + fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, false, 0, NULL, 0, loc_at(1, 1)); + fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, false, 0, NULL, 0, loc_at(1, 1)); AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1)); AstNode* structs[] = { struct_decl }; /* fn Point$get_x(self: Point) -> i64 { return self.x; } */ - AstNode* self_param = ast_make_parameter(&a, "self", TYPE_STRUCT, "Point", false, loc_at(1, 1)); + AstNode* self_param = ast_make_parameter(&a, "self", TYPE_STRUCT, "Point", false, 0, NULL, 0, loc_at(1, 1)); AstNode* params[] = { self_param }; AstNode* self_ident = ast_make_ident(&a, "self", loc_at(1, 1)); self_ident->type.kind = TYPE_STRUCT;