From acfbbeee8bab2a00be052b69f54c0ef074ad9710 Mon Sep 17 00:00:00 2001 From: zzy <2450266535@qq.com> Date: Thu, 17 Sep 2026 13:37:59 +0800 Subject: [PATCH] =?UTF-8?q?stage1=20=E6=94=AF=E6=8C=81=E6=9B=B4=E5=A4=9A?= =?UTF-8?q?=E8=AF=AD=E6=B3=95=20=E4=BF=AE=E5=A4=8D=E6=9B=B4=E5=A4=9A?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20=E6=9A=82=E6=97=B6stage1=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E7=BB=93=E6=9D=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gramma.pest | 2 +- nob.c | 15 ++ stage1/spl_ast.c | 3 + stage1/spl_ast.h | 2 +- stage1/spl_ast2ir.c | 356 +++++++++++++++++++++++++--- stage1/spl_ir.h | 3 +- stage1/spl_ir2vm.c | 24 +- stage1/spl_lang_type.c | 6 +- stage1/spl_parser.c | 59 +++-- stage1/spl_sema.c | 511 ++++++++++++++++++++++++++++++++--------- stage1/spl_type.c | 4 +- 11 files changed, 802 insertions(+), 183 deletions(-) diff --git a/gramma.pest b/gramma.pest index 0e65b10..55669ab 100644 --- a/gramma.pest +++ b/gramma.pest @@ -32,7 +32,7 @@ VarDecl = { DeclHeader ~ KEYWORD_var ~ VarBody } ConstDecl = { DeclHeader ~ KEYWORD_const ~ VarBody } VarBody = { Pattern ~ (COLON ~ TypeExpr)? ~ (EQUAL ~ Expr)? } -TypeDecl = { DeclHeader ~ KEYWORD_type ~ IDENTIFIER ~ EQUAL ~ TypeExpr } +TypeDecl = { DeclHeader ~ KEYWORD_type ~ Pattern ~ EQUAL ~ TypeExpr } MemberDecl = { DeclHeader ~ IDENTIFIER ~ (COLON ~ TypeExpr)? ~ (EQUAL ~ Expr)? } diff --git a/nob.c b/nob.c index 80cb757..9b8ddb2 100644 --- a/nob.c +++ b/nob.c @@ -272,6 +272,21 @@ static bool test_once_with_dump(const char *fname, void *user_data) { return nob_cmd_run(&cmd); } +static bool is_stage_name(const char *s) { + return strcmp(s, "stage1") == 0 || strcmp(s, "stage2") == 0 || strcmp(s, "diff") == 0; +} + +static bool is_dump_mode(const char *s) { + return strcmp(s, "lex") == 0 || strcmp(s, "ast") == 0 || strcmp(s, "sema") == 0 || + strcmp(s, "ir") == 0 || strcmp(s, "vm") == 0; +} + +static const char *stage_dir_of(const char *stage) { + if (strcmp(stage, "stage2") == 0) + return "./stage2/"; + return "./tests/stage1/"; +} + typedef bool (*test_fn_t)(const char *fname, void *user_data); static bool run_test(Nob_Files *files, test_fn_t test_once, void *user_data) { const char *test_folder = "./tests/stage1/"; diff --git a/stage1/spl_ast.c b/stage1/spl_ast.c index 413eafe..ee7fb1f 100644 --- a/stage1/spl_ast.c +++ b/stage1/spl_ast.c @@ -134,6 +134,9 @@ static void dump_node(spl_ast_t *ast, spl_ast_node_ref_t node_ref, dump_stack_t break; case SPL_AST_TYPE_DECL: + dump_vec(ast, node->type_decl.attr_list, stack); + if (node->type_decl.pattern) + dump_node(ast, node->type_decl.pattern, stack, false); if (node->type_decl.type_expr) dump_node(ast, node->type_decl.type_expr, stack, true); break; diff --git a/stage1/spl_ast.h b/stage1/spl_ast.h index d417a65..18a4a43 100644 --- a/stage1/spl_ast.h +++ b/stage1/spl_ast.h @@ -184,7 +184,7 @@ struct spl_ast_node { struct { spl_ast_node_ref_vec_t attr_list; - const char *name; + spl_ast_node_ref_t pattern; spl_ast_node_ref_t type_expr; } type_decl; diff --git a/stage1/spl_ast2ir.c b/stage1/spl_ast2ir.c index 98e5b94..2aa0b62 100644 --- a/stage1/spl_ast2ir.c +++ b/stage1/spl_ast2ir.c @@ -29,6 +29,12 @@ static spl_ir_node_ref_t emit_expr(spl_ast2ir_t *a, spl_ast_node_ref_t ref); static spl_ir_node_ref_t emit_lvalue(spl_ast2ir_t *a, spl_ast_node_ref_t ref); static void emit_stmt(spl_ast2ir_t *a, spl_ast_node_ref_t ref); static void emit_decl(spl_ast2ir_t *a, spl_ast_node_ref_t ref); +static spl_ir_node_ref_t emit_var_decl(spl_ast2ir_t *a, spl_ast_node_t *n); +static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref, + spl_ir_node_ref_t src_ptr, spl_type_id_t tid); +static spl_ir_node_ref_t emit_match_check(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref, + spl_type_id_t scrut_ir, spl_ir_node_ref_t scrut_slot); + static void emit_fn_reserve(spl_ast2ir_t *a, spl_ast_node_t *n); static void emit_fn_body(spl_ast2ir_t *a, spl_ast_node_t *n); static void emit_container_reserve(spl_ast2ir_t *a, spl_ast_node_ref_t ref); @@ -111,6 +117,181 @@ static spl_ir_node_ref_t emit_zero_value(spl_ast2ir_t *a, spl_type_id_t tid) { } } +/* 把 pattern 里的 ident 绑到 src_ptr 指向内存的对应位置。 + - PATTERN_IDENT: def2ir_map[def_id] = src_ptr + - PATTERN_DOT: src_ptr 指向 enum,payload 在 mem_field(tid, src_ptr, vidx+1) + - PATTERN_TUPLE / PATTERN_AGGREGATE: 递归 mem_field + UB 语义:不做 tag 检查,调用者保证变体正确。 +*/ +static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref, + spl_ir_node_ref_t src_ptr, spl_type_id_t tid) { + if (!pat_ref || !src_ptr) + return; + spl_ast_node_t *pat = spl_ast_node(a->ast, pat_ref); + if (!pat) + return; + + switch (pat->kind) { + case SPL_AST_PATTERN_IDENT: { + if (pat->resolved_def_id) + map_put(a->def2ir_map, pat->resolved_def_id, src_ptr); + return; + } + + case SPL_AST_EXPR_INT_LIT: + case SPL_AST_EXPR_FLOAT_LIT: + case SPL_AST_EXPR_CHAR_LIT: + case SPL_AST_EXPR_STRING_LIT: + case SPL_AST_EXPR_BOOL_LIT: + case SPL_AST_EXPR_NULL_LIT: + return; /* 字面量不绑定 */ + + case SPL_AST_PATTERN_UNDERSCORE: + return; + + case SPL_AST_PATTERN_LIST: + for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i) + emit_pattern_bind(a, vec_at(pat->pattern_list.patterns, i), src_ptr, tid); + return; + + case SPL_AST_PATTERN_TUPLE: { + spl_type_node_t *tn = spl_type_node(&a->sema->type->ir, tid); + if (!tn || tn->kind != SPL_TYPE_STRUCT) + return; + usize n = vec_size(pat->pattern_tuple.patterns); + usize nf = vec_size(tn->agg_type.variants); + if (n > nf) + return; + for (usize i = 0; i < n; ++i) { + spl_type_id_t ft = vec_at(tn->agg_type.variants, i).type_id; + spl_ir_node_ref_t fp = spl_ir_builder_mem_field(a->ir, tid, src_ptr, i); + emit_pattern_bind(a, vec_at(pat->pattern_tuple.patterns, i), fp, ft); + } + return; + } + + case SPL_AST_PATTERN_DOT: { + spl_type_node_t *tn = spl_type_node(&a->sema->type->ir, tid); + if (!tn || tn->kind != SPL_TYPE_ENUM) + return; + usize vidx = pat->resolved_def_id; + if (vidx == 0) + return; + vidx -= 1; + if (vidx >= vec_size(tn->agg_type.variants)) + return; + spl_type_id_t payload_ir = vec_at(tn->agg_type.variants, vidx).type_id; + spl_ir_node_ref_t pptr = spl_ir_builder_mem_field(a->ir, tid, src_ptr, vidx + 1); + if (pat->pattern_dot.sub_pattern) + emit_pattern_bind(a, pat->pattern_dot.sub_pattern, pptr, payload_ir); + return; + } + + case SPL_AST_PATTERN_AGGREGATE: { + spl_type_node_t *tn = spl_type_node(&a->sema->type->ir, tid); + if (!tn || (tn->kind != SPL_TYPE_STRUCT && tn->kind != SPL_TYPE_UNION)) + return; + for (usize i = 0; i < vec_size(pat->pattern_aggregate.fields); ++i) { + spl_ast_node_t *f = spl_ast_node(a->ast, vec_at(pat->pattern_aggregate.fields, i)); + if (!f || f->kind != SPL_AST_PATTERN_FIELD) + continue; + usize idx = f->resolved_def_id; + if (idx >= vec_size(tn->agg_type.variants)) + continue; + spl_type_id_t ft = vec_at(tn->agg_type.variants, idx).type_id; + spl_ir_node_ref_t fp = spl_ir_builder_mem_field(a->ir, tid, src_ptr, idx); + if (f->pattern_field.pattern) + emit_pattern_bind(a, f->pattern_field.pattern, fp, ft); + } + return; + } + + default: + return; + } +} + +/* 计算 pattern 对 scrutinee 的匹配条件。 + 返回 0 表示无条件命中(IDENT / UNDERSCORE),否则返回 bool 节点。 */ +static spl_ir_node_ref_t emit_match_check(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref, + spl_type_id_t scrut_ir, spl_ir_node_ref_t scrut_slot) { + spl_ast_node_t *pat = spl_ast_node(a->ast, pat_ref); + if (!pat) + return 0; + + switch (pat->kind) { + case SPL_AST_PATTERN_UNDERSCORE: + case SPL_AST_PATTERN_IDENT: + return 0; + + case SPL_AST_PATTERN_LIST: { + spl_lang_type_id_t bt = spl_lang_make_bool(a->sema->type); + spl_type_id_t bt_ir = to_ir_type(a, bt); + spl_ir_node_ref_t combined = 0; + for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i) { + spl_ir_node_ref_t c = + emit_match_check(a, vec_at(pat->pattern_list.patterns, i), scrut_ir, scrut_slot); + if (!c) + return 0; + combined = combined ? spl_ir_builder_arith_or(a->ir, bt_ir, combined, c) : c; + } + return combined; + } + + case SPL_AST_PATTERN_DOT: { + spl_type_node_t *tn = spl_type_node(&a->sema->type->ir, scrut_ir); + if (!tn || tn->kind != SPL_TYPE_ENUM) + return 0; + usize vidx = pat->resolved_def_id; + if (vidx == 0) + return 0; + vidx -= 1; + spl_type_id_t tag_ir = tn->agg_type.tag_type; + spl_ir_node_ref_t tag_ptr = + spl_ir_builder_mem_field(a->ir, scrut_ir, scrut_slot, SPL_ENUM_TAG_FIELD_IDX); + spl_ir_node_ref_t tag_val = spl_ir_builder_mem_load(a->ir, tag_ir, tag_ptr); + spl_ir_node_ref_t vidx_const = spl_ir_builder_type_const_int(a->ir, tag_ir, vidx); + return spl_ir_builder_cmp_eq(a->ir, tag_ir, tag_val, vidx_const); + } + + case SPL_AST_EXPR_INT_LIT: + case SPL_AST_EXPR_FLOAT_LIT: + case SPL_AST_EXPR_CHAR_LIT: + case SPL_AST_EXPR_STRING_LIT: + case SPL_AST_EXPR_BOOL_LIT: + case SPL_AST_EXPR_NULL_LIT: { + spl_ir_node_ref_t val = emit_expr(a, pat_ref); + if (!val) + return 0; + spl_ir_node_ref_t loaded = spl_ir_builder_mem_load(a->ir, scrut_ir, scrut_slot); + return spl_ir_builder_cmp_eq(a->ir, scrut_ir, loaded, val); + } + + default: + /* tuple / aggregate pattern 暂不作为 match 条件 */ + return 0; + } +} + +/* 分配 slot → store init 值 → bind pattern。 + 返回指向变量内存的指针(*tid),调用者可用于读取初值。 */ +static spl_ir_node_ref_t emit_var_decl(spl_ast2ir_t *a, spl_ast_node_t *n) { + spl_type_id_t var_ir = node_ir(a, n); + if (!var_ir) + return 0; + + spl_ir_node_ref_t slot = spl_ir_builder_mem_alloca(a->ir, var_ir, const_usize(a, 1)); + + if (n->var_const_decl.init_expr) { + spl_ir_node_ref_t v = emit_expr(a, n->var_const_decl.init_expr); + if (v) + spl_ir_builder_mem_store(a->ir, var_ir, slot, v); + } + + emit_pattern_bind(a, n->var_const_decl.pattern, slot, var_ir); + return slot; +} + static void copy_attrs_to_ir_node(spl_ast2ir_t *a, spl_ast_node_t *n, spl_ir_node_ref_t ir_ref) { if (!ir_ref || !n || vec_size(n->attrs) == 0) return; @@ -843,7 +1024,6 @@ static spl_ir_node_ref_t emit_expr_raw(spl_ast2ir_t *a, spl_ast_node_ref_t ref) item->struct_init_item.name); spl_type_id_t tag_ir = tn->agg_type.tag_type; - spl_type_id_t payload_ir = vec_at(tn->agg_type.variants, vidx).type_id; spl_ir_node_ref_t eptr = spl_ir_builder_mem_alloca(a->ir, t, const_usize(a, 1)); @@ -852,11 +1032,14 @@ static spl_ir_node_ref_t emit_expr_raw(spl_ast2ir_t *a, spl_ast_node_ref_t ref) spl_ir_node_ref_t tag_val = spl_ir_builder_type_const_int(a->ir, tag_ir, vidx); spl_ir_builder_mem_store(a->ir, tag_ir, tptr, tag_val); - spl_ir_node_ref_t pptr = spl_ir_builder_mem_field(a->ir, t, eptr, vidx + 1); - spl_ir_node_ref_t payload = emit_expr(a, item->struct_init_item.expr); - if (!payload) - Panic("enum payload emit failed"); - spl_ir_builder_mem_store(a->ir, payload_ir, pptr, payload); + if (item->struct_init_item.expr) { + spl_type_id_t payload_ir = vec_at(tn->agg_type.variants, vidx).type_id; + spl_ir_node_ref_t pptr = spl_ir_builder_mem_field(a->ir, t, eptr, vidx + 1); + spl_ir_node_ref_t payload = emit_expr(a, item->struct_init_item.expr); + if (!payload) + Panic("enum payload emit failed"); + spl_ir_builder_mem_store(a->ir, payload_ir, pptr, payload); + } return eptr; } @@ -931,9 +1114,117 @@ static spl_ir_node_ref_t emit_expr_raw(spl_ast2ir_t *a, spl_ast_node_ref_t ref) case SPL_AST_EXPR_IF: case SPL_AST_EXPR_IFVAR: return emit_if_like(a, ref); + case SPL_AST_EXPR_MATCH: { + spl_ast_node_ref_t matched_ref = n->match_expr.matched_expr; + spl_ast_node_t *matched_node = spl_ast_node(a->ast, matched_ref); + spl_lang_type_id_t matched_lang = node_lang(a, matched_node); + spl_type_id_t matched_ir = to_ir_type(a, matched_lang); + if (!matched_ir) + return 0; + + spl_ir_node_ref_t scrut_slot = + spl_ir_builder_mem_alloca(a->ir, matched_ir, const_usize(a, 1)); + spl_ir_node_ref_t scrut_val = emit_expr(a, matched_ref); + if (!scrut_val) + return 0; + spl_ir_builder_mem_store(a->ir, matched_ir, scrut_slot, scrut_val); + + usize narms = vec_size(n->match_expr.arms); + spl_ir_node_ref_vec_t arm_branches; + spl_ir_block_vec_t cond_blks; + vec_init(arm_branches); + vec_init(cond_blks); + + for (usize i = 0; i < narms; ++i) { + vec_push(cond_blks, a->ir->current_block); + + spl_ast_node_t *arm = spl_ast_node(a->ast, vec_at(n->match_expr.arms, i)); + if (arm && arm->kind == SPL_AST_MATCH_ARM) { + spl_ir_node_ref_t cond = + emit_match_check(a, arm->match_arm.pattern, matched_ir, scrut_slot); + if (cond) { + spl_ir_node_ref_t br = spl_ir_builder_control_br(a->ir, cond); + vec_push(arm_branches, br); + } else { + spl_ir_node_ref_t j = spl_ir_builder_control_jmp(a->ir); + vec_push(arm_branches, j); + } + } else { + spl_ir_node_ref_t j = spl_ir_builder_control_jmp(a->ir); + vec_push(arm_branches, j); + } + + spl_ir_builder_block_end(a->ir); + if (i + 1 < narms) + spl_ir_builder_block_begin(a->ir); + } + + usize no_match_blk = spl_ir_builder_block_begin(a->ir); + spl_ir_node_ref_t no_match_jmp = spl_ir_builder_control_jmp(a->ir); + spl_ir_builder_block_end(a->ir); + + spl_ir_node_ref_vec_t body_jmps; + spl_ir_block_vec_t body_blks; + vec_init(body_jmps); + vec_init(body_blks); + + for (usize i = 0; i < narms; ++i) { + usize blk = spl_ir_builder_block_begin(a->ir); + vec_push(body_blks, blk); + + spl_ast_node_t *arm = spl_ast_node(a->ast, vec_at(n->match_expr.arms, i)); + if (arm && arm->kind == SPL_AST_MATCH_ARM) { + emit_pattern_bind(a, arm->match_arm.pattern, scrut_slot, matched_ir); + if (arm->match_arm.body) + emit_expr(a, arm->match_arm.body); + } + + spl_ir_node_ref_t j = 0; + if (!a->ir->terminated) + j = spl_ir_builder_control_jmp(a->ir); + vec_push(body_jmps, j); + spl_ir_builder_block_end(a->ir); + } + + usize end_blk = spl_ir_builder_block_begin(a->ir); + + for (usize i = 0; i < narms; ++i) { + spl_ir_node_ref_t branch = vec_at(arm_branches, i); + if (!branch) + continue; + spl_ir_node_t *bn = spl_ir_node(&a->ir->ir, a->ir->current_fn, branch); + if (!bn) + continue; + usize body_blk = vec_at(body_blks, i); + if (bn->kind == SPL_IR_CONTROL_BR) { + usize next_blk = (i + 1 < narms) ? vec_at(cond_blks, i + 1) : no_match_blk; + spl_ir_builder_control_br_fixup(a->ir, branch, body_blk, next_blk); + } else if (bn->kind == SPL_IR_CONTROL_JMP) { + spl_ir_builder_control_jmp_fixup(a->ir, branch, body_blk); + } + } + + for (usize i = 0; i < narms; ++i) { + spl_ir_node_ref_t j = vec_at(body_jmps, i); + if (j) + spl_ir_builder_control_jmp_fixup(a->ir, j, end_blk); + } + spl_ir_builder_control_jmp_fixup(a->ir, no_match_jmp, end_blk); + + vec_free(arm_branches); + vec_free(cond_blks); + vec_free(body_jmps); + vec_free(body_blks); + + return 0; + } case SPL_AST_EXPR_WHILE: case SPL_AST_EXPR_LOOP: return emit_loop_like(a, ref); + case SPL_AST_EXPR_FOR: + spl_diag_fatal(a->diag, SPL_DIAG_KIND_UNHANDLED_NODE, &n->dbg, + "for is not implemented yet"); + return 0; case SPL_AST_EXPR_SLICE: { spl_ast_node_t *obj = spl_ast_node(a->ast, n->slice.obj); spl_lang_type_id_t obj_lang = node_lang(a, obj); @@ -1041,13 +1332,12 @@ static spl_ir_node_ref_t emit_if_like(spl_ast2ir_t *a, spl_ast_node_ref_t ref) { spl_ast_node_ref_t else_ref = 0; if (is_ifvar) { - emit_decl(a, n->ifvar_expr.var_decl); spl_ast_node_t *vd = spl_ast_node(a->ast, n->ifvar_expr.var_decl); - if (!vd || !vd->var_const_decl.init_expr) - Panic("ifvar requires initializer"); - spl_ir_node_ref_t slot = 0; - if (!map_get(a->def2ir_map, vd->resolved_def_id, &slot)) - Panic("ifvar var slot not found"); + if (!vd) + Panic("ifvar missing var_decl"); + spl_ir_node_ref_t slot = emit_var_decl(a, vd); + if (!slot) + Panic("ifvar var_decl emit failed"); cond = spl_ir_builder_mem_load(a->ir, node_ir(a, vd), slot); then_ref = n->ifvar_expr.then_block; else_ref = n->ifvar_expr.else_branch; @@ -1172,7 +1462,7 @@ static spl_ir_node_ref_t emit_loop_like(spl_ast2ir_t *a, spl_ast_node_ref_t ref) for (usize i = 0; i < vec_size(top->breaks); ++i) spl_ir_builder_control_jmp_fixup(a->ir, vec_at(top->breaks, i), after_blk); vec_free(top->breaks); - vec_pop(a->loop_stack); + (void)vec_pop(a->loop_stack); if (!result_slot) return 0; @@ -1309,32 +1599,11 @@ static void emit_decl(spl_ast2ir_t *a, spl_ast_node_ref_t ref) { switch (n->kind) { case SPL_AST_VAR_DECL: - case SPL_AST_CONST_DECL: { + case SPL_AST_CONST_DECL: if (!a->ir->current_fn) return; - if (!n->resolved_def_id) - return; - - spl_ast_node_t *pat = spl_ast_node(a->ast, n->var_const_decl.pattern); - if (!pat || pat->kind != SPL_AST_PATTERN_IDENT) - return; - - spl_lang_type_id_t var_lang = spl_def_get_lang_id(a->sema->type, n->resolved_def_id); - spl_type_id_t var_ir = to_ir_type(a, var_lang); - if (!var_ir) - return; - - spl_ir_node_ref_t one = const_usize(a, 1); - spl_ir_node_ref_t slot = spl_ir_builder_mem_alloca(a->ir, var_ir, one); - map_put(a->def2ir_map, n->resolved_def_id, slot); - - if (n->var_const_decl.init_expr) { - spl_ir_node_ref_t v = emit_expr(a, n->var_const_decl.init_expr); - if (v) - spl_ir_builder_mem_store(a->ir, var_ir, slot, v); - } + emit_var_decl(a, n); return; - } default: return; } @@ -1344,6 +1613,13 @@ static void emit_fn_reserve(spl_ast2ir_t *a, spl_ast_node_t *n) { if (!n || !n->resolved_def_id) return; + spl_ir_node_ref_t existing = 0; + if (map_get(a->def2ir_map, n->resolved_def_id, &existing)) { + spl_diag_error(a->diag, SPL_DIAG_KIND_INTERNAL, &n->dbg, + "fn `%s` reserved twice (def#%zu);", n->fn_decl.name, n->resolved_def_id); + return; + } + spl_lang_type_id_t fn_lang = spl_def_get_lang_id(a->sema->type, n->resolved_def_id); spl_lang_type_node_t *fn_ln = spl_lang_type_node(a->sema->type, fn_lang); if (!fn_ln || fn_ln->kind != SPL_LANG_TYPE_FN) @@ -1438,7 +1714,7 @@ static void emit_fn_body(spl_ast2ir_t *a, spl_ast_node_t *n) { while (vec_size(a->loop_stack) > loop_base) { spl_loop_ctx_t *top = &vec_at(a->loop_stack, vec_size(a->loop_stack) - 1); vec_free(top->breaks); - vec_pop(a->loop_stack); + (void)vec_pop(a->loop_stack); } a->cur_ret_lang = saved_ret; @@ -1500,6 +1776,9 @@ static void emit_container_reserve(spl_ast2ir_t *a, spl_ast_node_ref_t ref) { emit_fn_reserve(a, d); break; case SPL_AST_TYPE_DECL: { + spl_ast_node_t *tpat = spl_ast_node(a->ast, d->type_decl.pattern); + if (!tpat || tpat->kind != SPL_AST_PATTERN_IDENT) + break; spl_ast_node_t *te = spl_ast_node(a->ast, d->type_decl.type_expr); if (te && te->kind == SPL_AST_TYPE_AGGREGATE) emit_aggregate_fns_reserve(a, te); @@ -1536,6 +1815,9 @@ static void emit_container_body(spl_ast2ir_t *a, spl_ast_node_ref_t ref) { emit_fn_body(a, d); break; case SPL_AST_TYPE_DECL: { + spl_ast_node_t *tpat = spl_ast_node(a->ast, d->type_decl.pattern); + if (!tpat || tpat->kind != SPL_AST_PATTERN_IDENT) + break; spl_ast_node_t *te = spl_ast_node(a->ast, d->type_decl.type_expr); if (te && te->kind == SPL_AST_TYPE_AGGREGATE) emit_aggregate_fns_body(a, te); diff --git a/stage1/spl_ir.h b/stage1/spl_ir.h index c111378..63bda0f 100644 --- a/stage1/spl_ir.h +++ b/stage1/spl_ir.h @@ -2,11 +2,10 @@ #define __SPL_IR_H__ #include "../stage0/include/utils.h" +#include "spl_attr.h" #include "spl_dbg.h" #include "spl_type.h" -#include "spl_attr.h" - /* clang-format off */ #define SPL_IR_FN_TABLE \ X(arith.add, V0, SPL_IR_ARITH_ADD) \ diff --git a/stage1/spl_ir2vm.c b/stage1/spl_ir2vm.c index bd383ea..8f2873c 100644 --- a/stage1/spl_ir2vm.c +++ b/stage1/spl_ir2vm.c @@ -24,13 +24,17 @@ static usize ir_type_size(spl_ir2vm_t *ctx, spl_type_id_t tid) { return ir_type_size(ctx, tn->array_type.element) * tn->array_type.len; case SPL_TYPE_ENUM: { usize tag_sz = ir_type_size(ctx, tn->agg_type.tag_type); - usize max_payload = 0; + usize payload_align = 1; + usize payload_size = 0; for (usize i = 0; i < vec_size(tn->agg_type.variants); ++i) { usize vs = ir_type_size(ctx, vec_at(tn->agg_type.variants, i).type_id); - if (vs > max_payload) - max_payload = vs; + if (vs > payload_align) + payload_align = vs; + if (vs > payload_size) + payload_size = vs; } - return align_up(tag_sz, 8) + max_payload; + usize payload_off = align_up(tag_sz, payload_align); + return align_up(payload_off + payload_size, payload_align); } case SPL_TYPE_STRUCT: case SPL_TYPE_UNION: { @@ -102,6 +106,18 @@ static usize ir_agg_field_off(spl_ir2vm_t *ctx, spl_type_id_t tid, usize idx) { } if (tn->kind == SPL_TYPE_ARRAY) return idx * ir_type_size(ctx, tn->array_type.element); + if (tn->kind == SPL_TYPE_ENUM) { + if (idx == 0) + return 0; + usize tag_sz = ir_type_size(ctx, tn->agg_type.tag_type); + usize payload_align = 1; + for (usize i = 0; i < vec_size(tn->agg_type.variants); ++i) { + usize vs = ir_type_size(ctx, vec_at(tn->agg_type.variants, i).type_id); + if (vs > payload_align) + payload_align = vs; + } + return align_up(tag_sz, payload_align); + } return 0; } diff --git a/stage1/spl_lang_type.c b/stage1/spl_lang_type.c index ff34dae..b0ad50e 100644 --- a/stage1/spl_lang_type.c +++ b/stage1/spl_lang_type.c @@ -371,6 +371,8 @@ spl_lang_type_id_t spl_lang_make_fn(spl_lang_type_t *type, spl_lang_type_id_t re spl_lang_type_id_t spl_lang_make_aggregate(spl_lang_type_t *type, spl_lang_type_kind_t kind, const char *name, const spl_lang_type_id_t *members_lang, usize nmembers) { + (void)name; // TODO + spl_type_node_kind_t tk; switch (kind) { case SPL_LANG_TYPE_STRUCT: @@ -662,7 +664,7 @@ static void spl_lang_type_dump_rec(spl_lang_type_t *type, spl_lang_type_id_t id, spl_lang_type_node_t *n = spl_lang_type_node(type, id); if (!n) { printf("none"); - vec_pop(*stack); + (void)vec_pop(*stack); return; } switch (n->kind) { @@ -748,7 +750,7 @@ static void spl_lang_type_dump_rec(spl_lang_type_t *type, spl_lang_type_id_t id, break; } - vec_pop(*stack); + (void)vec_pop(*stack); } void spl_lang_type_dump(spl_lang_type_t *type, spl_lang_type_id_t id) { diff --git a/stage1/spl_parser.c b/stage1/spl_parser.c index 0ab1766..f06af9a 100644 --- a/stage1/spl_parser.c +++ b/stage1/spl_parser.c @@ -252,6 +252,30 @@ static int can_start_statement(spl_tok_type_t t) { } } +static int is_builtin_type_kw(spl_tok_type_t t) { + switch (t) { + // case KW_I8: + // case KW_U8: + // case KW_I16: + // case KW_U16: + // case KW_I32: + // case KW_U32: + // case KW_I64: + // case KW_U64: + // case KW_F32: + // case KW_F64: + // case KW_ISIZE: + // case KW_USIZE: + case KW_BOOL: + // case KW_VOID: + // case KW_PTR: + case KW_ANY: + return 1; + default: + return 0; + } +} + static spl_ast_node_ref_t parse_expr(spl_parser_t *p); static spl_ast_node_ref_vec_t parse_expr_list(spl_parser_t *p); static spl_ast_node_ref_t parse_block(spl_parser_t *p); @@ -662,13 +686,12 @@ static spl_ast_node_ref_t parse_type_decl(spl_parser_t *p, spl_ast_node_ref_vec_ return 0; advance(p); - const spl_tok_t *name = peek(p); - if (!name || name->type != TOK_IDENT) { - PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, name ? name : kw, "expected type name"); + spl_ast_node_ref_t pattern = parse_pattern(p); + if (!pattern) { + PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, peek(p), "expected pattern after 'type'"); vec_free(tag_list); return 0; } - advance(p); if (!expect(p, TOK_ASSIGN, "'=' in type declaration")) { vec_free(tag_list); @@ -678,9 +701,11 @@ static spl_ast_node_ref_t parse_type_decl(spl_parser_t *p, spl_ast_node_ref_vec_ spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_DECL, kw); spl_ast_node_t *td = node_at(p->ast, ref); MOVE_VEC(td->type_decl.attr_list, tag_list); - SET_STR_FIELD(p->ast, td->type_decl.name, name); + td->type_decl.pattern = pattern; + spl_ast_node_ref_t type_expr = parse_type_expr(p); - node_at(p->ast, ref)->type_decl.type_expr = type_expr; + td = node_at(p->ast, ref); /* parse_type_expr 可能 push node_buckets,重新取 */ + td->type_decl.type_expr = type_expr; const spl_tok_t *t = peek(p); if (t && (t->type == TOK_COMMA || t->type == TOK_SEMICOLON)) @@ -909,7 +934,8 @@ static spl_ast_node_ref_t parse_type_base(spl_parser_t *p) { return ref; } - if (bt->type == TOK_IDENT || bt->type == KW_BOOL) { + /* 内建类型 keyword + 普通标识符类型路径 */ + if (bt->type == TOK_IDENT || is_builtin_type_kw(bt->type)) { advance(p); spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_PATH, bt); spl_ast_node_t *tp = node_at(p->ast, ref); @@ -932,15 +958,6 @@ static spl_ast_node_ref_t parse_type_base(spl_parser_t *p) { if (bt->type == TOK_AT || bt->type == TOK_SHARP) return parse_type_comptime_call(p); - if (bt->type == KW_ANY) { - advance(p); - spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_PATH, bt); - spl_ast_node_t *tp = node_at(p->ast, ref); - SET_STR_FIELD(p->ast, tp->type_path.name, bt); - vec_init(tp->type_path.generic_args); - return ref; - } - PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, bt, "unrecognized type"); return 0; } @@ -1950,8 +1967,6 @@ static spl_ast_node_ref_t parse_match_expr(spl_parser_t *p) { if (!expect(p, TOK_RIGHT_FAT_ARROW, "'=>' in match arm")) break; - if (!expect(p, TOK_L_BRACE, "'{' in match arm body")) - break; spl_ast_node_ref_t body = parse_block(p); vec_push(patterns, pat); @@ -2164,8 +2179,12 @@ static spl_ast_node_ref_t parse_block(spl_parser_t *p) { block = node_at(p->ast, ref); if (stmt) vec_push(block->block.stmts, stmt); + else + break; } else if (can_start_expr(t->type)) { spl_ast_node_ref_t expr = parse_expr(p); + if (!expr) + break; block = node_at(p->ast, ref); t = peek(p); if (t && t->type == TOK_SEMICOLON) { @@ -2218,12 +2237,10 @@ static void skip_to_sync(spl_parser_t *p) { const spl_tok_t *t = peek(p); if (!t || t->type == TOK_EOF) return; - if (t->type == TOK_SEMICOLON) { + if (t->type == TOK_SEMICOLON || t->type == TOK_R_BRACE) { advance(p); return; } - if (t->type == TOK_R_BRACE) - return; advance(p); } } diff --git a/stage1/spl_sema.c b/stage1/spl_sema.c index eee4d0e..16bd725 100644 --- a/stage1/spl_sema.c +++ b/stage1/spl_sema.c @@ -97,11 +97,14 @@ static spl_lang_type_id_t sema_intrin_call(spl_sema_t *sema, spl_ast_node_t *n, static spl_lang_type_id_t sema_intrin_field(spl_sema_t *sema, spl_ast_node_t *n, spl_intrin_t in); static spl_lang_type_id_t sema_eval_decl(spl_sema_t *sema, spl_def_id_t def_id); static spl_lang_type_id_t sema_infer_fn_signature(spl_sema_t *sema, spl_ast_node_t *n); - -static bool sema_is_infer(spl_sema_t *sema, spl_lang_type_id_t tid) { - spl_lang_type_node_t *n = spl_lang_type_node(sema->type, tid); - return n && n->kind == SPL_LANG_TYPE_INFER; -} +static void sema_bind_pattern(spl_sema_t *sema, spl_ast_node_ref_t pat_ref, + spl_lang_type_id_t matched_tid); +static void sema_do_type_unpack(spl_sema_t *sema, spl_ast_node_t *decl, spl_ast_node_t *pat, + spl_ast_node_ref_t type_expr_ref); +static void sema_type_unpack_walk(spl_sema_t *sema, spl_ast_node_t *pat, + spl_lang_type_id_t src_lang); +static void sema_type_unpack_one(spl_sema_t *sema, spl_ast_node_t *pat, const char *name, + spl_lang_type_id_t src_lang); static spl_lang_type_id_t sema_lang_from_ir(spl_sema_t *sema, spl_type_id_t ir) { for (usize i = 1; i < vec_size(sema->type->lang_table); ++i) { @@ -112,6 +115,15 @@ static spl_lang_type_id_t sema_lang_from_ir(spl_sema_t *sema, spl_type_id_t ir) return 0; } +static spl_lang_type_kind_t sema_lang_kind(spl_sema_t *sema, spl_lang_type_id_t tid) { + spl_lang_type_node_t *n = spl_lang_type_node(sema->type, tid); + return n ? n->kind : SPL_LANG_TYPE_ERROR; +} + +static bool sema_is_infer(spl_sema_t *sema, spl_lang_type_id_t tid) { + return sema_lang_kind(sema, tid) == SPL_LANG_TYPE_INFER; +} + static void sema_type_to_str(spl_sema_t *sema, spl_lang_type_id_t tid, char *buf, usize cap) { if (!buf || cap == 0) return; @@ -311,19 +323,34 @@ static spl_lang_type_id_t sema_build_aggregate(spl_sema_t *sema, spl_ast_node_t .ast_node_ref = mref}; vec_push(members, m); } else if (mn->kind == SPL_AST_FN_DECL) { - spl_def_id_t fn_def = - spl_def_alloc(sema->type, SPL_DEF_FN, mn->fn_decl.name, mn->dbg, sema->ast, mref); - mn->resolved_def_id = fn_def; + spl_def_id_t fn_def = mn->resolved_def_id; + if (!fn_def) { + fn_def = spl_def_alloc(sema->type, SPL_DEF_FN, mn->fn_decl.name, mn->dbg, sema->ast, + mref); + mn->resolved_def_id = fn_def; + } spl_def_node_t *fn_d = spl_def_node(sema->type, fn_def); if (fn_d) fn_d->owner_def = owner_def_id; spl_lang_method_t meth = {.name = mn->fn_decl.name, .def_id = fn_def}; vec_push(methods, meth); } else if (mn->kind == SPL_AST_TYPE_DECL) { - spl_def_id_t tdef = mn->resolved_def_id; - if (!tdef) + spl_ast_node_t *tpat = spl_ast_node(sema->ast, mn->type_decl.pattern); + if (!tpat || tpat->kind != SPL_AST_PATTERN_IDENT) continue; - spl_lang_method_t meth = {.name = mn->type_decl.name, .def_id = tdef}; + const char *tname = tpat->pattern_ident.name; + + spl_def_id_t tdef = mn->resolved_def_id; + if (!tdef) { + spl_ast_node_kind_t tek = + mn->type_decl.type_expr ? spl_ast_node(sema->ast, mn->type_decl.type_expr)->kind + : 0; + spl_def_node_kind_t dk = + (tek == SPL_AST_TYPE_AGGREGATE) ? SPL_DEF_AGG : SPL_DEF_SCALAR; + tdef = spl_def_alloc(sema->type, dk, tname, mn->dbg, sema->ast, mref); + mn->resolved_def_id = tdef; + } + spl_lang_method_t meth = {.name = tname, .def_id = tdef}; vec_push(methods, meth); } } @@ -402,15 +429,19 @@ static spl_lang_type_id_t sema_build_container_aggregate(spl_sema_t *sema, spl_a spl_def_id_t tdef = d->resolved_def_id; if (!tdef) continue; - spl_lang_method_t meth = {.name = d->type_decl.name, .def_id = tdef}; + spl_ast_node_t *tpat = spl_ast_node(sema->ast, d->type_decl.pattern); + if (!tpat || tpat->kind != SPL_AST_PATTERN_IDENT) + continue; + spl_lang_method_t meth = {.name = tpat->pattern_ident.name, .def_id = tdef}; vec_push(methods, meth); } else if (d->kind == SPL_AST_VAR_DECL || d->kind == SPL_AST_CONST_DECL) { - spl_def_id_t vdef = d->resolved_def_id; - if (!vdef) - continue; + /* def_id 现在挂在 pattern 上,不在 VAR_DECL 节点上 */ spl_ast_node_t *pat = spl_ast_node(sema->ast, d->var_const_decl.pattern); if (!pat || pat->kind != SPL_AST_PATTERN_IDENT) continue; + spl_def_id_t vdef = pat->resolved_def_id; + if (!vdef) + continue; spl_lang_method_t meth = {.name = pat->pattern_ident.name, .def_id = vdef}; vec_push(methods, meth); } @@ -550,6 +581,9 @@ static spl_lang_type_id_t sema_eval_decl(spl_sema_t *sema, spl_def_id_t def_id) if (n) { switch (n->kind) { case SPL_AST_TYPE_DECL: { + spl_ast_node_t *pat = spl_ast_node(sema->ast, n->type_decl.pattern); + if (!pat || pat->kind != SPL_AST_PATTERN_IDENT) + break; if (n->type_decl.type_expr) { spl_ast_node_t *te = spl_ast_node(sema->ast, n->type_decl.type_expr); if (te && te->kind == SPL_AST_TYPE_AGGREGATE) @@ -628,21 +662,28 @@ static void sema_collect_names(spl_sema_t *sema, spl_ast_node_ref_t ref) { break; } case SPL_AST_TYPE_DECL: { - spl_symbol_t sym = {.name = n->type_decl.name, .kind = SPL_SYMBOL_KIND_TYPE}; + spl_ast_node_t *pat = spl_ast_node(sema->ast, n->type_decl.pattern); + if (!pat) + return; + if (pat->kind != SPL_AST_PATTERN_IDENT) + return; /* 解包由 sema_collect_types 处理 */ + + const char *name = pat->pattern_ident.name; spl_ast_node_t *te = spl_ast_node(sema->ast, n->type_decl.type_expr); if (!te) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, - "type declaration `%s` missing type expression", n->type_decl.name); + "type declaration `%s` missing type expression", name); return; } + spl_symbol_t sym = {.name = name, .kind = SPL_SYMBOL_KIND_TYPE}; if (te->kind == SPL_AST_TYPE_AGGREGATE) - sym.node = spl_def_alloc(sema->type, SPL_DEF_AGG, sym.name, te->dbg, sema->ast, ref); + sym.node = spl_def_alloc(sema->type, SPL_DEF_AGG, name, te->dbg, sema->ast, ref); else - sym.node = spl_def_alloc(sema->type, SPL_DEF_SCALAR, sym.name, te->dbg, sema->ast, ref); + sym.node = spl_def_alloc(sema->type, SPL_DEF_SCALAR, name, te->dbg, sema->ast, ref); n->resolved_def_id = sym.node; if (!spl_scope_insert(sema->scope, sema->scope->current_scope, sym)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_DUPLICATE_SYMBOL, &n->dbg, - "duplicate symbol `%s`", sym.name); + "duplicate symbol `%s`", name); } break; } @@ -659,18 +700,20 @@ static void sema_collect_names(spl_sema_t *sema, spl_ast_node_ref_t ref) { case SPL_AST_VAR_DECL: case SPL_AST_CONST_DECL: { spl_ast_node_t *pat = spl_ast_node(sema->ast, n->var_const_decl.pattern); - if (pat && pat->kind == SPL_AST_PATTERN_IDENT) { - const char *name = pat->pattern_ident.name; - spl_symbol_t sym = {.name = name, .kind = SPL_SYMBOL_KIND_VAR}; - sym.node = spl_def_alloc(sema->type, SPL_DEF_VAR, name, n->dbg, sema->ast, ref); - n->resolved_def_id = sym.node; - if (!spl_scope_insert(sema->scope, sema->scope->current_scope, sym)) { - spl_diag_error(sema->diag, SPL_DIAG_KIND_DUPLICATE_SYMBOL, &n->dbg, - "duplicate symbol `%s`", name); - } - } else { + if (!pat || pat->kind != SPL_AST_PATTERN_IDENT) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, - "unsupported variable pattern (only simple identifier)"); + "container-level var only supports simple identifier; " + "move destructuring into a function body"); + break; + } + const char *name = pat->pattern_ident.name; + spl_symbol_t sym = {.name = name, .kind = SPL_SYMBOL_KIND_VAR}; + sym.node = spl_def_alloc(sema->type, SPL_DEF_VAR, name, n->dbg, sema->ast, ref); + /* def_id 挂在 pattern 的 ident 上,不在 VAR_DECL 节点上 */ + pat->resolved_def_id = sym.node; + if (!spl_scope_insert(sema->scope, sema->scope->current_scope, sym)) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_DUPLICATE_SYMBOL, &n->dbg, + "duplicate symbol `%s`", name); } break; } @@ -692,8 +735,17 @@ static void sema_collect_types(spl_sema_t *sema, spl_ast_node_ref_t ref) { for (usize i = 0; i < vec_size(n->container_members.declarations); ++i) { spl_ast_node_ref_t dref = vec_at(n->container_members.declarations, i); spl_ast_node_t *d = spl_ast_node(sema->ast, dref); - if (d && d->kind == SPL_AST_TYPE_DECL && d->resolved_def_id) - sema_eval_decl(sema, d->resolved_def_id); + if (!d || d->kind != SPL_AST_TYPE_DECL) + continue; + spl_ast_node_t *pat = spl_ast_node(sema->ast, d->type_decl.pattern); + if (!pat) + continue; + if (pat->kind == SPL_AST_PATTERN_IDENT) { + if (d->resolved_def_id) + sema_eval_decl(sema, d->resolved_def_id); + } else { + sema_do_type_unpack(sema, d, pat, d->type_decl.type_expr); + } } break; case SPL_AST_TOP_LEVEL_BLOCK: @@ -833,6 +885,23 @@ static spl_lang_type_id_t sema_block_body(spl_sema_t *sema, spl_ast_node_t *n, return r; } +static bool sema_pattern_is_literal(spl_ast_node_kind_t k) { + switch (k) { + case SPL_AST_EXPR_INT_LIT: + case SPL_AST_EXPR_FLOAT_LIT: + case SPL_AST_EXPR_CHAR_LIT: + case SPL_AST_EXPR_STRING_LIT: + case SPL_AST_EXPR_BOOL_LIT: + case SPL_AST_EXPR_NULL_LIT: + return true; + default: + return false; + } +} + +/* 把 matched_tid 的绑定按 pattern 结构插进当前 scope。 + 调用前必须已经 push_scope。 + 约定:PATTERN_DOT 用 resolved_def_id = variant_idx + 1 保存变体位置。 */ static void sema_bind_pattern(spl_sema_t *sema, spl_ast_node_ref_t pat_ref, spl_lang_type_id_t matched_tid) { if (!pat_ref) @@ -841,9 +910,24 @@ static void sema_bind_pattern(spl_sema_t *sema, spl_ast_node_ref_t pat_ref, if (!pat) return; + if (sema_pattern_is_literal(pat->kind)) { + spl_lang_type_id_t lt = sema_infer(sema, pat_ref); + if (lt && matched_tid) + sema_coerce(sema, pat_ref, matched_tid); + return; + } + switch (pat->kind) { case SPL_AST_PATTERN_IDENT: { const char *name = pat->pattern_ident.name; + if (!name || strcmp(name, "_") == 0) + return; + /* 容器级 var 已在 collect_names 阶段分配 def_id,这里只回填 lang。 */ + if (pat->resolved_def_id) { + spl_def_resolve(sema->type, pat->resolved_def_id, matched_tid); + pat->resolved_lang_tid = matched_tid; + return; + } spl_symbol_t sym = {.name = name, .kind = SPL_SYMBOL_KIND_VAR}; sym.node = spl_def_alloc(sema->type, SPL_DEF_VAR, name, pat->dbg, sema->ast, pat_ref); spl_def_resolve(sema->type, sym.node, matched_tid); @@ -859,11 +943,17 @@ static void sema_bind_pattern(spl_sema_t *sema, spl_ast_node_ref_t pat_ref, case SPL_AST_PATTERN_UNDERSCORE: return; + case SPL_AST_PATTERN_LIST: { + for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i) + sema_bind_pattern(sema, vec_at(pat->pattern_list.patterns, i), matched_tid); + return; + } + case SPL_AST_PATTERN_TUPLE: { spl_lang_type_node_t *tn = spl_lang_type_node(sema->type, matched_tid); if (!tn || tn->kind != SPL_LANG_TYPE_TUPLE) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg, - "tuple pattern requires a tuple value"); + "tuple pattern requires tuple value"); return; } usize natoms = vec_size(pat->pattern_tuple.patterns); @@ -873,7 +963,6 @@ static void sema_bind_pattern(spl_sema_t *sema, spl_ast_node_ref_t pat_ref, "tuple pattern arity mismatch (expected %zu, got %zu)", nelems, natoms); return; } - /* 递归 bind 会 push lang_table,tn 会悬垂;先把元素类型拷到本地 */ spl_lang_type_id_vec_t elem_tids; vec_init(elem_tids); for (usize i = 0; i < nelems; ++i) @@ -885,13 +974,245 @@ static void sema_bind_pattern(spl_sema_t *sema, spl_ast_node_ref_t pat_ref, return; } - case SPL_AST_PATTERN_DOT: - case SPL_AST_PATTERN_AGGREGATE: - default: - spl_diag_error(sema->diag, SPL_DIAG_KIND_UNHANDLED_NODE, &pat->dbg, - "pattern kind %d not yet supported", (int)pat->kind); + case SPL_AST_PATTERN_DOT: { + spl_lang_type_node_t *mn = spl_lang_type_node(sema->type, matched_tid); + if (!mn || mn->kind != SPL_LANG_TYPE_ENUM) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg, + "variant pattern requires enum value"); + return; + } + spl_type_id_t agg_ir = spl_lang_type_get_ir_type(sema->type, matched_tid); + usize idx = 0; + if (!spl_type_agg_member_idx(&sema->type->ir, agg_ir, pat->pattern_dot.name, &idx)) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_NO_FIELD, &pat->dbg, "no variant `%s` in enum", + pat->pattern_dot.name); + return; + } + spl_type_node_t *irn = spl_type_node(&sema->type->ir, agg_ir); + if (!irn || idx >= vec_size(irn->agg_type.variants)) + return; + spl_lang_type_id_t payload_lang = + sema_lang_from_ir(sema, vec_at(irn->agg_type.variants, idx).type_id); + pat->resolved_def_id = idx + 1; + pat->resolved_lang_tid = matched_tid; + if (pat->pattern_dot.sub_pattern && payload_lang) + sema_bind_pattern(sema, pat->pattern_dot.sub_pattern, payload_lang); return; } + + case SPL_AST_PATTERN_AGGREGATE: { + spl_lang_type_node_t *an = spl_lang_type_node(sema->type, matched_tid); + if (!an || (an->kind != SPL_LANG_TYPE_STRUCT && an->kind != SPL_LANG_TYPE_UNION && + an->kind != SPL_LANG_TYPE_ENUM)) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg, + "aggregate pattern requires struct or enum value"); + return; + } + spl_type_id_t agg_ir = spl_lang_type_get_ir_type(sema->type, matched_tid); + usize nfields = vec_size(pat->pattern_aggregate.fields); + if (an->kind == SPL_LANG_TYPE_ENUM && nfields != 1) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg, + "enum pattern must have exactly one variant"); + return; + } + for (usize i = 0; i < nfields; ++i) { + spl_ast_node_t *f = spl_ast_node(sema->ast, vec_at(pat->pattern_aggregate.fields, i)); + if (!f || f->kind != SPL_AST_PATTERN_FIELD) + continue; + usize idx = 0; + if (!spl_type_agg_member_idx(&sema->type->ir, agg_ir, f->pattern_field.name, &idx)) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_NO_FIELD, &f->dbg, "no field `%s`", + f->pattern_field.name); + continue; + } + spl_type_node_t *irn = spl_type_node(&sema->type->ir, agg_ir); + if (!irn || idx >= vec_size(irn->agg_type.variants)) + continue; + spl_lang_type_id_t ft = + sema_lang_from_ir(sema, vec_at(irn->agg_type.variants, idx).type_id); + f->resolved_def_id = idx; + if (f->pattern_field.pattern && ft) + sema_bind_pattern(sema, f->pattern_field.pattern, ft); + } + pat->resolved_lang_tid = matched_tid; + return; + } + + default: + spl_diag_error(sema->diag, SPL_DIAG_KIND_UNHANDLED_NODE, &pat->dbg, + "pattern kind %d not supported", (int)pat->kind); + return; + } +} + +/* type 解包:把容器 struct 的 methods 里的名字引入当前 scope。 + 语法层由 pattern 承载,语义层在这里分派。 + 约束: + - 源类型必须是 LANG_TYPE_STRUCT 且 agg_type.variants 为空 + (即 @import 返回的容器 struct,不是普通带字段的 struct) + - PATTERN_DOT 不允许有 sub_pattern(编译期没有 payload 概念) + - PATTERN_FIELD 不允许带 sub pattern(同上) + - 不做递归导出,显式即所得 */ +static void sema_type_unpack_one(spl_sema_t *sema, spl_ast_node_t *pat, const char *name, + spl_lang_type_id_t src_lang) { + if (!name) + return; + spl_lang_type_node_t *src = spl_lang_type_node(sema->type, src_lang); + if (!src) + return; + + for (usize i = 0; i < vec_size(src->methods); ++i) { + spl_lang_method_t *m = &vec_at(src->methods, i); + if (strcmp(m->name, name) != 0) + continue; + + spl_def_node_t *d = spl_def_node(sema->type, m->def_id); + spl_symbol_kind_t k = SPL_SYMBOL_KIND_VAR; + if (d) { + switch (d->kind) { + case SPL_DEF_FN: + k = SPL_SYMBOL_KIND_FN; + break; + case SPL_DEF_AGG: + case SPL_DEF_SCALAR: + case SPL_DEF_DISTINCT: + case SPL_DEF_ALIAS: + case SPL_DEF_BUILTIN: + k = SPL_SYMBOL_KIND_TYPE; + break; + default: + k = SPL_SYMBOL_KIND_VAR; + break; + } + } + + spl_symbol_t sym = {.name = name, .kind = k, .node = m->def_id}; + if (!spl_scope_insert(sema->scope, sema->scope->current_scope, sym)) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_DUPLICATE_SYMBOL, &pat->dbg, + "type unpack: duplicate symbol `%s`", name); + } + pat->resolved_def_id = m->def_id; + return; + } + + spl_diag_error(sema->diag, SPL_DIAG_KIND_NO_FIELD, &pat->dbg, + "type unpack: no symbol `%s` in source container", name); +} + +static void sema_type_unpack_walk(spl_sema_t *sema, spl_ast_node_t *pat, + spl_lang_type_id_t src_lang) { + if (!pat) + return; + switch (pat->kind) { + case SPL_AST_PATTERN_UNDERSCORE: + return; + + case SPL_AST_PATTERN_DOT: + if (pat->pattern_dot.sub_pattern) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg, + "type unpack: payload pattern not allowed in compile-time unpack"); + return; + } + sema_type_unpack_one(sema, pat, pat->pattern_dot.name, src_lang); + return; + + case SPL_AST_PATTERN_AGGREGATE: + for (usize i = 0; i < vec_size(pat->pattern_aggregate.fields); ++i) { + spl_ast_node_t *f = spl_ast_node(sema->ast, vec_at(pat->pattern_aggregate.fields, i)); + if (!f || f->kind != SPL_AST_PATTERN_FIELD) + continue; + if (f->pattern_field.pattern) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &f->dbg, + "type unpack: field pattern not allowed"); + continue; + } + sema_type_unpack_one(sema, f, f->pattern_field.name, src_lang); + } + return; + + case SPL_AST_PATTERN_LIST: + for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i) { + spl_ast_node_t *sub = spl_ast_node(sema->ast, vec_at(pat->pattern_list.patterns, i)); + sema_type_unpack_walk(sema, sub, src_lang); + } + return; + + case SPL_AST_PATTERN_IDENT: + /* collect_names 已分派,不应到这里 */ + return; + + default: + spl_diag_error(sema->diag, SPL_DIAG_KIND_UNHANDLED_NODE, &pat->dbg, + "type unpack: unsupported pattern kind %d", (int)pat->kind); + return; + } +} + +static void sema_do_type_unpack(spl_sema_t *sema, spl_ast_node_t *decl, spl_ast_node_t *pat, + spl_ast_node_ref_t type_expr_ref) { + if (!type_expr_ref) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &decl->dbg, + "type unpack missing source type expression"); + return; + } + + spl_lang_type_id_t src_lang = sema_infer(sema, type_expr_ref); + if (!src_lang) + return; + + spl_lang_type_node_t *src = spl_lang_type_node(sema->type, src_lang); + if (!src) + return; + if (src->kind != SPL_LANG_TYPE_STRUCT) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &decl->dbg, + "type unpack source must be a container struct"); + return; + } + + spl_type_id_t src_ir = spl_lang_type_get_ir_type(sema->type, src_lang); + spl_type_node_t *irn = spl_type_node(&sema->type->ir, src_ir); + if (!irn) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &decl->dbg, + "type unpack source IR unresolved"); + return; + } + if (vec_size(irn->agg_type.variants) != 0) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &decl->dbg, + "type unpack source has fields; only container struct allowed"); + return; + } + + sema_type_unpack_walk(sema, pat, src_lang); +} + +/* var / const / if-var 共用的初始化推导 + bind。 + 当前 scope 必须已经是 bind 目标 scope(调用者负责 push)。 */ +static spl_lang_type_id_t sema_var_body(spl_sema_t *sema, spl_ast_node_t *n) { + spl_lang_type_id_t decl_tid = 0; + if (n->var_const_decl.type) + decl_tid = sema_infer(sema, n->var_const_decl.type); + + if (!n->var_const_decl.init_expr) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_VAR_UNINITIALIZED, &n->dbg, + "variable must be initialized"); + return 0; + } + + if (decl_tid) { + if (!sema_coerce(sema, n->var_const_decl.init_expr, decl_tid)) + return 0; + } else { + decl_tid = sema_infer(sema, n->var_const_decl.init_expr); + if (!decl_tid || sema_is_infer(sema, decl_tid)) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_CANNOT_INFER, &n->dbg, + "cannot infer variable type"); + return 0; + } + } + + sema_bind_pattern(sema, n->var_const_decl.pattern, decl_tid); + n->resolved_lang_tid = decl_tid; + return decl_tid; } static spl_lang_type_id_t sema_coerce(spl_sema_t *sema, spl_ast_node_ref_t ref, @@ -1842,9 +2163,15 @@ static spl_lang_type_id_t sema_infer(spl_sema_t *sema, spl_ast_node_ref_t ref) { } case SPL_AST_EXPR_IFVAR: { - sema_infer(sema, n->ifvar_expr.var_decl); + if (!n->ifvar_expr.var_decl) + return 0; + spl_ast_node_t *vd = spl_ast_node(sema->ast, n->ifvar_expr.var_decl); + sema_push_scope(sema); + sema_var_body(sema, vd); if (n->ifvar_expr.then_block) sema_infer(sema, n->ifvar_expr.then_block); + sema_pop_scope(sema); + /* else 在 pattern scope 之外:绑定名在 else 不可见 */ if (n->ifvar_expr.else_branch) sema_infer(sema, n->ifvar_expr.else_branch); return 0; @@ -1865,7 +2192,7 @@ static spl_lang_type_id_t sema_infer(spl_sema_t *sema, spl_ast_node_ref_t ref) { if (n->while_expr.else_branch) sema_infer(sema, n->while_expr.else_branch); - vec_pop(sema->label_stack); + (void)vec_pop(sema->label_stack); return 0; } case SPL_AST_EXPR_LOOP: { @@ -1880,7 +2207,7 @@ static spl_lang_type_id_t sema_infer(spl_sema_t *sema, spl_ast_node_ref_t ref) { if (n->loop_expr.body) sema_infer(sema, n->loop_expr.body); - vec_pop(sema->label_stack); + (void)vec_pop(sema->label_stack); return 0; } case SPL_AST_EXPR_FOR: { @@ -1891,7 +2218,6 @@ static spl_lang_type_id_t sema_infer(spl_sema_t *sema, spl_ast_node_ref_t ref) { lbl = ln->label.name; } vec_push(sema->label_stack, lbl); - sema_push_scope(sema); usize nsrc = vec_size(n->for_expr.iter_exprs); @@ -1900,49 +2226,47 @@ static spl_lang_type_id_t sema_infer(spl_sema_t *sema, spl_ast_node_ref_t ref) { if (pat && pat->kind == SPL_AST_PATTERN_LIST) { usize natoms = vec_size(pat->pattern_list.patterns); - for (usize i = 0; i < nsrc; ++i) { - spl_lang_type_id_t container_tid = - sema_infer(sema, vec_at(n->for_expr.iter_exprs, i)); - spl_lang_type_id_t elem_tid = sema_for_elem_type(sema, container_tid); - if (i < natoms) - sema_bind_pattern(sema, vec_at(pat->pattern_list.patterns, i), elem_tid); - } - if (natoms > nsrc) { + if (natoms != nsrc) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg, - "for: pattern has %zu atoms but only %zu sources", natoms, nsrc); + "for: %zu pattern atoms but %zu sources", natoms, nsrc); + } + for (usize i = 0; i < nsrc && i < natoms; ++i) { + spl_lang_type_id_t ct = sema_infer(sema, vec_at(n->for_expr.iter_exprs, i)); + spl_lang_type_id_t et = sema_for_elem_type(sema, ct); + sema_bind_pattern(sema, vec_at(pat->pattern_list.patterns, i), et); } } else { - for (usize i = 0; i < nsrc; ++i) { - spl_lang_type_id_t container_tid = - sema_infer(sema, vec_at(n->for_expr.iter_exprs, i)); - if (i == 0 && pat_ref) { - spl_lang_type_id_t elem_tid = sema_for_elem_type(sema, container_tid); - sema_bind_pattern(sema, pat_ref, elem_tid); - } - } - if (nsrc > 1 && pat_ref) { - spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg, + if (nsrc > 1) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "for: multiple sources require pattern list"); } + for (usize i = 0; i < nsrc; ++i) { + spl_lang_type_id_t ct = sema_infer(sema, vec_at(n->for_expr.iter_exprs, i)); + if (i == 0) + sema_bind_pattern(sema, pat_ref, sema_for_elem_type(sema, ct)); + } } if (n->for_expr.body) sema_infer(sema, n->for_expr.body); if (n->for_expr.else_branch) sema_infer(sema, n->for_expr.else_branch); - sema_pop_scope(sema); - vec_pop(sema->label_stack); + sema_pop_scope(sema); + (void)vec_pop(sema->label_stack); return 0; } case SPL_AST_EXPR_MATCH: { - sema_infer(sema, n->match_expr.matched_expr); + spl_lang_type_id_t matched = sema_infer(sema, n->match_expr.matched_expr); for (usize i = 0; i < vec_size(n->match_expr.arms); ++i) { spl_ast_node_t *arm = spl_ast_node(sema->ast, vec_at(n->match_expr.arms, i)); - if (arm && arm->kind == SPL_AST_MATCH_ARM) { - sema_infer(sema, arm->match_arm.pattern); + if (!arm || arm->kind != SPL_AST_MATCH_ARM) + continue; + sema_push_scope(sema); + sema_bind_pattern(sema, arm->match_arm.pattern, matched); + if (arm->match_arm.body) sema_infer(sema, arm->match_arm.body); - } + sema_pop_scope(sema); } return 0; } @@ -2114,6 +2438,9 @@ static spl_lang_type_id_t sema_infer(spl_sema_t *sema, spl_ast_node_ref_t ref) { } case SPL_AST_TYPE_DECL: { + spl_ast_node_t *pat = spl_ast_node(sema->ast, n->type_decl.pattern); + if (!pat || pat->kind != SPL_AST_PATTERN_IDENT) + return 0; if (!n->type_decl.type_expr) break; spl_def_node_t *def = @@ -2139,50 +2466,8 @@ static spl_lang_type_id_t sema_infer(spl_sema_t *sema, spl_ast_node_ref_t ref) { } case SPL_AST_VAR_DECL: - case SPL_AST_CONST_DECL: { - spl_ast_node_t *pat = spl_ast_node(sema->ast, n->var_const_decl.pattern); - if (!pat || pat->kind != SPL_AST_PATTERN_IDENT) { - spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, - "unsupported variable pattern"); - return 0; - } - const char *name = pat->pattern_ident.name; - - if (n->resolved_def_id == 0) { - spl_symbol_t sym = {.name = name, .kind = SPL_SYMBOL_KIND_VAR}; - sym.node = spl_def_alloc(sema->type, SPL_DEF_VAR, name, n->dbg, sema->ast, ref); - n->resolved_def_id = sym.node; - if (!spl_scope_insert(sema->scope, sema->scope->current_scope, sym)) { - spl_diag_error(sema->diag, SPL_DIAG_KIND_DUPLICATE_SYMBOL, &n->dbg, - "duplicate symbol `%s`", name); - } - } - - spl_lang_type_id_t decl_tid = 0; - if (n->var_const_decl.type) - decl_tid = sema_infer(sema, n->var_const_decl.type); - - if (!n->var_const_decl.init_expr) { - spl_diag_error(sema->diag, SPL_DIAG_KIND_VAR_UNINITIALIZED, &n->dbg, - "variable `%s` must be initialized", name); - return 0; - } - - if (decl_tid) { - sema_coerce(sema, n->var_const_decl.init_expr, decl_tid); - } else { - decl_tid = sema_infer(sema, n->var_const_decl.init_expr); - if (!decl_tid || sema_is_infer(sema, decl_tid)) { - spl_diag_error(sema->diag, SPL_DIAG_KIND_CANNOT_INFER, &n->dbg, - "cannot infer type of `%s`", name); - return 0; - } - } - - spl_def_resolve(sema->type, n->resolved_def_id, decl_tid); - n->resolved_lang_tid = decl_tid; - return decl_tid; - } + case SPL_AST_CONST_DECL: + return sema_var_body(sema, n); case SPL_AST_MEMBER_DECL: if (n->member_decl.type_expr) diff --git a/stage1/spl_type.c b/stage1/spl_type.c index fa5eafe..5dceb29 100644 --- a/stage1/spl_type.c +++ b/stage1/spl_type.c @@ -221,7 +221,7 @@ static void spl_type_pure_dump_rec(spl_type_t *type, spl_type_id_t id, spl_type_ spl_type_node_t *n = spl_type_node(type, id); if (!n) { printf("none"); - vec_pop(*stack); + (void)vec_pop(*stack); return; } switch (n->kind) { @@ -301,7 +301,7 @@ static void spl_type_pure_dump_rec(spl_type_t *type, spl_type_id_t id, spl_type_ break; } - vec_pop(*stack); + (void)vec_pop(*stack); } void spl_type_pure_dump(spl_type_t *type, spl_type_id_t id) {