#include "spl_sema.h" #include "spl_intrin.h" #include "spl_lang_type.h" #include "spl_type.h" static spl_ast_node_ref_vec_t *node_attr_list(spl_ast_node_t *n) { switch (n->kind) { case SPL_AST_FN_DECL: return &n->fn_decl.tag_list; case SPL_AST_VAR_DECL: case SPL_AST_CONST_DECL: return &n->var_const_decl.attr_list; case SPL_AST_TYPE_DECL: return &n->type_decl.attr_list; case SPL_AST_PARAM_DECL: return &n->param_decl.attr_list; case SPL_AST_MEMBER_DECL: return &n->member_decl.attr_list; case SPL_AST_TOP_LEVEL_BLOCK: return &n->top_level_block.tag_list; default: return NULL; } } static void sema_resolve_attrs(spl_sema_t *sema, spl_ast_node_ref_vec_t *raw, spl_attr_id_vec_t *out) { for (usize i = 0; i < vec_size(*raw); ++i) { spl_ast_node_ref_t aref = vec_at(*raw, i); spl_ast_node_t *a = spl_ast_node(sema->ast, aref); if (!a || (a->kind != SPL_AST_TAG_ITEM && a->kind != SPL_AST_EXTERNAL_TAG_ITEM)) continue; spl_attr_info_t *info = spl_attr_lookup(sema->attr, NULL, a->tag_item.name); if (!info) { spl_diag_error(sema->diag, SPL_DIAG_KIND_UNKNOWN_ATTR, &a->dbg, "unknown attribute `%s`", a->tag_item.name); continue; } usize nargs = vec_size(a->tag_item.args); if ((int)nargs < info->min_args || (info->max_args >= 0 && (int)nargs > info->max_args)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_UNKNOWN_ATTR, &a->dbg, "attribute `%s` expects %d..%d args, got %zu", info->name, info->min_args, info->max_args, nargs); continue; } spl_attr_arg_vec_t args; vec_init(args); int bad = 0; for (usize j = 0; j < nargs; ++j) { spl_ast_node_t *arg = spl_ast_node(sema->ast, vec_at(a->tag_item.args, j)); spl_attr_arg_t oa = {0}; if (!arg) { bad = 1; break; } switch (arg->kind) { case SPL_AST_EXPR_IDENT: oa.kind = SPL_ATTR_ARG_STR; oa.value.s = arg->ident.name; break; case SPL_AST_EXPR_STRING_LIT: oa.kind = SPL_ATTR_ARG_STR; oa.value.s = arg->string_lit.data; break; case SPL_AST_EXPR_INT_LIT: oa.kind = SPL_ATTR_ARG_INT; oa.value.i = arg->int_lit.bits; break; default: spl_diag_error(sema->diag, SPL_DIAG_KIND_UNKNOWN_ATTR, &a->dbg, "attribute arg #%zu has unsupported form", j); bad = 1; break; } if (bad) break; vec_push(args, oa); } if (bad) { vec_free(args); continue; } spl_attr_id_t id = spl_attr_alloc(sema->attr, info, args); vec_push(*out, id); } } static spl_lang_type_id_t sema_infer(spl_sema_t *sema, spl_ast_node_ref_t ref); static spl_lang_type_id_t sema_coerce(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_lang_type_id_t expected); static spl_lang_type_id_t sema_intrin_call(spl_sema_t *sema, spl_ast_node_t *n, spl_intrin_t in, spl_ast_node_ref_vec_t *args); 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 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) { spl_lang_type_node_t *ln = &vec_at(sema->type->lang_table, i); if (ln->ir_type_id == ir) return i; } 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; if (tid == 0) { snprintf(buf, cap, ""); return; } spl_lang_type_node_t *n = spl_lang_type_node(sema->type, tid); if (!n) { snprintf(buf, cap, "", tid); return; } spl_type_node_t *ir = spl_type_node(&sema->type->ir, n->ir_type_id); switch (n->kind) { case SPL_LANG_TYPE_INT: if (ir && ir->int_type.bits == 0) snprintf(buf, cap, "comptime_int(#%zu)", tid); else if (ir) snprintf(buf, cap, "%s%zu(#%zu)", ir->int_type.is_signed ? "i" : "u", ir->int_type.bits, tid); else snprintf(buf, cap, "int?(#%zu)", tid); break; case SPL_LANG_TYPE_FLOAT: if (ir && ir->float_type.bits == 0) snprintf(buf, cap, "comptime_float(#%zu)", tid); else if (ir) snprintf(buf, cap, "f%zu(#%zu)", ir->float_type.bits, tid); else snprintf(buf, cap, "float?(#%zu)", tid); break; case SPL_LANG_TYPE_BOOL: snprintf(buf, cap, "bool(#%zu)", tid); break; case SPL_LANG_TYPE_UNIT: snprintf(buf, cap, "unit(#%zu)", tid); break; case SPL_LANG_TYPE_NEVER: snprintf(buf, cap, "never(#%zu)", tid); break; case SPL_LANG_TYPE_ANY: snprintf(buf, cap, "any(#%zu)", tid); break; case SPL_LANG_TYPE_INFER: snprintf(buf, cap, "infer(#%zu)", tid); break; case SPL_LANG_TYPE_ERROR: snprintf(buf, cap, "error(#%zu)", tid); break; case SPL_LANG_TYPE_PTR: snprintf(buf, cap, "ptr(#%zu)", tid); break; case SPL_LANG_TYPE_ARRAY: snprintf(buf, cap, "array(#%zu)", tid); break; case SPL_LANG_TYPE_SLICE: snprintf(buf, cap, "slice(#%zu)", tid); break; case SPL_LANG_TYPE_TUPLE: snprintf(buf, cap, "tuple(#%zu)", tid); break; case SPL_LANG_TYPE_RANGE: snprintf(buf, cap, "range(#%zu)", tid); break; case SPL_LANG_TYPE_FN: snprintf(buf, cap, "fn(#%zu)", tid); break; case SPL_LANG_TYPE_VARARGS: snprintf(buf, cap, "varargs(#%zu)", tid); break; default: snprintf(buf, cap, "langkind=%d ir=%zu(#%zu)", (int)n->kind, n->ir_type_id, tid); break; } } static inline void sema_push_scope(spl_sema_t *sema) { spl_scope_id_t new_scope = spl_scope_alloc(sema->scope); sema->scope->current_scope = new_scope; } static inline void sema_pop_scope(spl_sema_t *sema) { spl_scope_id_t cur = sema->scope->current_scope; if (cur == 0 || cur >= vec_size(sema->scope->scopes)) Panic("sema_pop_scope: bad current_scope %zu", cur); sema->scope->current_scope = vec_at(sema->scope->scopes, cur).parent; } static spl_lang_type_id_t sema_for_elem_type(spl_sema_t *sema, spl_lang_type_id_t lang_tid) { spl_lang_type_node_t *n = spl_lang_type_node(sema->type, lang_tid); if (!n) return 0; switch (n->kind) { case SPL_LANG_TYPE_ARRAY: case SPL_LANG_TYPE_SLICE: case SPL_LANG_TYPE_RANGE: case SPL_LANG_TYPE_PTR: return n->extra.elem; default: return 0; } } static spl_lang_type_id_t sema_infer_fn_signature(spl_sema_t *sema, spl_ast_node_t *n) { if (!n || n->resolved_def_id == 0) return 0; spl_def_node_t *def = spl_def_node(sema->type, n->resolved_def_id); if (def && def->lang_id != 0) return def->lang_id; spl_lang_type_id_vec_t param_types; vec_init(param_types); for (usize i = 0; i < vec_size(n->fn_decl.params); ++i) { spl_ast_node_t *pd = spl_ast_node(sema->ast, vec_at(n->fn_decl.params, i)); if (pd && pd->kind == SPL_AST_PARAM_DECL && pd->param_decl.type_expr) vec_push(param_types, sema_infer(sema, pd->param_decl.type_expr)); else vec_push(param_types, spl_lang_make_varargs(sema->type)); } spl_lang_type_id_t ret_tid = sema_infer(sema, n->fn_decl.ret_type); if (ret_tid == 0) ret_tid = spl_lang_make_unit(sema->type); spl_lang_type_id_t fn_tid = spl_lang_make_fn( sema->type, ret_tid, vec_unsafe_get_data(param_types), vec_size(param_types)); vec_free(param_types); spl_def_resolve(sema->type, n->resolved_def_id, fn_tid); return fn_tid; } /* 构建聚合类型:reserve IR 槽位作为身份锚点,让 *Node / []Node 自引用能拿到 稳定 pointee;遍历成员后再 define 填内容。 */ static spl_lang_type_id_t sema_build_aggregate(spl_sema_t *sema, spl_ast_node_t *te, spl_def_id_t owner_def_id) { spl_type_node_kind_t tk; if (strcmp(te->type_aggregate.kind_name, "struct") == 0) tk = SPL_TYPE_STRUCT; else if (strcmp(te->type_aggregate.kind_name, "union") == 0) tk = SPL_TYPE_UNION; else if (strcmp(te->type_aggregate.kind_name, "enum") == 0) tk = SPL_TYPE_ENUM; else tk = SPL_TYPE_STRUCT; spl_lang_type_kind_t lk; switch (tk) { case SPL_TYPE_STRUCT: lk = SPL_LANG_TYPE_STRUCT; break; case SPL_TYPE_UNION: lk = SPL_LANG_TYPE_UNION; break; case SPL_TYPE_ENUM: lk = SPL_LANG_TYPE_ENUM; break; case SPL_TYPE_SHAPE: lk = SPL_LANG_TYPE_SHAPE; break; default: lk = SPL_LANG_TYPE_STRUCT; break; } spl_type_id_t agg_ir = spl_lang_reserve_ir(sema->type); spl_lang_type_node_t ln = {0}; ln.kind = lk; ln.ir_type_id = agg_ir; vec_init(ln.methods); spl_lang_type_id_t agg_lang = spl_lang_node_push(sema->type, &ln); if (owner_def_id) { spl_def_node_t *d = spl_def_node(sema->type, owner_def_id); if (d) d->lang_id = agg_lang; } spl_lang_type_id_t saved_agg = sema->current_aggregate_lang; sema->current_aggregate_lang = agg_lang; /* 聚合体自成命名空间:成员类型可引用同体声明的嵌套类型(裸名)。 */ sema_push_scope(sema); spl_type_member_vec_t members; vec_init(members); spl_lang_method_vec_t methods; vec_init(methods); /* 第一遍:登记嵌套类型/函数的 def,挂进 methods 与聚合作用域。成员类型可能引用它们, 必须早于成员类型推断,否则限定名(经 methods)与裸名(经作用域)都解析不到。 */ for (usize i = 0; i < vec_size(te->type_aggregate.members); ++i) { spl_ast_node_ref_t mref = vec_at(te->type_aggregate.members, i); spl_ast_node_t *mn = spl_ast_node(sema->ast, mref); if (!mn) continue; if (mn->kind == SPL_AST_FN_DECL) { 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); spl_symbol_t fsym = {.name = mn->fn_decl.name, .kind = SPL_SYMBOL_KIND_FN}; fsym.node = fn_def; spl_scope_insert(sema->scope, sema->scope->current_scope, fsym); } else if (mn->kind == SPL_AST_TYPE_DECL) { spl_ast_node_t *tpat = spl_ast_node(sema->ast, mn->type_decl.pattern); if (!tpat || tpat->kind != SPL_AST_PATTERN_IDENT) continue; 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); spl_symbol_t tsym = {.name = tname, .kind = SPL_SYMBOL_KIND_TYPE}; tsym.node = tdef; spl_scope_insert(sema->scope, sema->scope->current_scope, tsym); } } spl_lang_type_node_t *lslot = spl_lang_type_node(sema->type, agg_lang); if (lslot) { vec_free(lslot->methods); lslot->methods = methods; vec_init(methods); } /* 第二遍:成员(字段/变体)类型推断;此时嵌套名字已可解析。 */ for (usize i = 0; i < vec_size(te->type_aggregate.members); ++i) { spl_ast_node_ref_t mref = vec_at(te->type_aggregate.members, i); spl_ast_node_t *mn = spl_ast_node(sema->ast, mref); if (!mn) continue; if (mn->kind == SPL_AST_MEMBER_DECL) { spl_lang_type_id_t mtid = 0; if (mn->member_decl.type_expr) mtid = sema_infer(sema, mn->member_decl.type_expr); spl_type_member_t m = {.name = mn->member_decl.name, .type_id = spl_lang_type_get_ir_type(sema->type, mtid), .ast_node_ref = mref}; vec_push(members, m); } } sema_pop_scope(sema); sema->current_aggregate_lang = saved_agg; spl_type_node_t tn = {0}; tn.kind = tk; tn.layout.mode = SPL_TYPE_LAYOUT_AUTO; if (tk == SPL_TYPE_ENUM) tn.agg_type.tag_type = spl_type_builder_int(&sema->type->ir, 32, 1); tn.agg_type.variants = members; vec_init(members); spl_lang_define_ir(sema->type, agg_ir, &tn); return agg_lang; } /* 文件 container 建成匿名 struct:FN_DECL / TYPE_DECL / VAR_DECL / CONST_DECL 进 methods(复用为 names),MEMBER_DECL 进字段。 */ static spl_lang_type_id_t sema_build_container_aggregate(spl_sema_t *sema, spl_ast_node_t *n, spl_def_id_t owner_def_id) { spl_type_id_t agg_ir = spl_lang_reserve_ir(sema->type); spl_lang_type_node_t ln = {0}; ln.kind = SPL_LANG_TYPE_STRUCT; ln.ir_type_id = agg_ir; vec_init(ln.methods); spl_lang_type_id_t agg_lang = spl_lang_node_push(sema->type, &ln); if (owner_def_id) { spl_def_node_t *d = spl_def_node(sema->type, owner_def_id); if (d) d->lang_id = agg_lang; } spl_lang_type_id_t saved_agg = sema->current_aggregate_lang; sema->current_aggregate_lang = agg_lang; spl_type_member_vec_t members; vec_init(members); spl_lang_method_vec_t methods; vec_init(methods); 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) continue; if (d->kind == SPL_AST_MEMBER_DECL) { spl_lang_type_id_t mtid = 0; if (d->member_decl.type_expr) mtid = sema_infer(sema, d->member_decl.type_expr); spl_type_member_t m = {.name = d->member_decl.name, .type_id = spl_lang_type_get_ir_type(sema->type, mtid), .ast_node_ref = dref}; vec_push(members, m); } else if (d->kind == SPL_AST_FN_DECL) { spl_def_id_t fn_def = d->resolved_def_id; if (!fn_def) continue; 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 = d->fn_decl.name, .def_id = fn_def}; vec_push(methods, meth); } else if (d->kind == SPL_AST_TYPE_DECL) { spl_def_id_t tdef = d->resolved_def_id; if (!tdef) continue; 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) { /* 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); } } sema->current_aggregate_lang = saved_agg; spl_type_node_t tn = {0}; tn.kind = SPL_TYPE_STRUCT; tn.layout.mode = SPL_TYPE_LAYOUT_AUTO; tn.agg_type.variants = members; vec_init(members); spl_lang_define_ir(sema->type, agg_ir, &tn); spl_lang_type_node_t *lslot = spl_lang_type_node(sema->type, agg_lang); if (lslot) { vec_free(lslot->methods); lslot->methods = methods; vec_init(methods); } return agg_lang; } static spl_lang_type_id_t sema_import(spl_sema_t *sema, spl_ast_node_t *n, spl_ast_node_ref_vec_t *args) { if (!sema->loader || !sema->loader->load) { spl_diag_error(sema->diag, SPL_DIAG_KIND_INTERNAL, &n->dbg, "@import: no module loader configured"); return 0; } if (vec_size(*args) != 1) { spl_diag_error(sema->diag, SPL_DIAG_KIND_INTRIN_ARG_COUNT, &n->dbg, "@import expects 1 argument"); return 0; } spl_ast_node_t *arg = spl_ast_node(sema->ast, vec_at(*args, 0)); if (!arg || arg->kind != SPL_AST_EXPR_STRING_LIT || !arg->string_lit.data) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "@import expects a string literal"); return 0; } const char *path = arg->string_lit.data; spl_ast_t *mod_ast = sema->loader->load(sema->loader->user, n->dbg.fname, path, sema->diag); if (!mod_ast || !mod_ast->root) return 0; spl_ast_node_t *mod_root = spl_ast_node(mod_ast, mod_ast->root); if (!mod_root || mod_root->kind != SPL_AST_ROOT) return 0; spl_ast_node_ref_t cm_ref = mod_root->root.members; if (!cm_ref) return 0; spl_ast_node_t *cm = spl_ast_node(mod_ast, cm_ref); if (cm->resolved_def_id) { spl_def_node_t *d = spl_def_node(sema->type, cm->resolved_def_id); if (d && d->lang_id) return d->lang_id; } spl_ast_t *saved_ast = sema->ast; spl_scope_id_t saved_root = sema->scope->root_scope; spl_scope_id_t saved_cur = sema->scope->current_scope; spl_lang_type_id_t saved_agg = sema->current_aggregate_lang; sema->ast = mod_ast; sema->scope->root_scope = 0; sema->scope->current_scope = 0; sema->current_aggregate_lang = 0; spl_sema_run_module(sema, cm_ref); spl_lang_type_id_t t = 0; spl_ast_node_t *cm_after = spl_ast_node(mod_ast, cm_ref); if (cm_after->resolved_def_id) { spl_def_node_t *d = spl_def_node(sema->type, cm_after->resolved_def_id); if (d) t = d->lang_id; } sema->ast = saved_ast; sema->scope->root_scope = saved_root; sema->scope->current_scope = saved_cur; sema->current_aggregate_lang = saved_agg; return t; } static spl_lang_type_id_t sema_eval_decl(spl_sema_t *sema, spl_def_id_t def_id) { spl_def_node_t *def = spl_def_node(sema->type, def_id); if (!def) return 0; if (def->state == SPL_DECL_RESOLVED || def->state == SPL_DECL_ERROR) return def->lang_id; /* RESOLVING 中途但 lang_id 已被 reserve 阶段回填:直接返回。 A ↔ B 相互引用时 B 引用 A 就走这条短路。 */ if (def->lang_id != 0) { if (def->state == SPL_DECL_RESOLVING) return def->lang_id; def->state = SPL_DECL_RESOLVED; return def->lang_id; } if (def->state == SPL_DECL_RESOLVING) { spl_diag_error(sema->diag, SPL_DIAG_KIND_CIRCULAR_DEP, &def->dbg_node, "circular dependency on `%s`", def->name ? def->name : "?"); def->state = SPL_DECL_ERROR; return 0; } /* 提前取所有 def 字段:sema_infer_* / sema_build_* 会 push def_table,def 悬垂。 */ spl_ast_t *owner_ast = def->owner_ast; spl_def_id_t owner_def_id = def->owner_def; usize ast_node_ref = def->ast_node_ref; spl_dbg_node_t dbg_node = def->dbg_node; if (!owner_ast) { spl_diag_error(sema->diag, SPL_DIAG_KIND_INTERNAL, &dbg_node, "def#%zu has no owner_ast", def_id); spl_def_node_t *d2 = spl_def_node(sema->type, def_id); if (d2) d2->state = SPL_DECL_ERROR; return 0; } spl_ast_t *saved_ast = sema->ast; sema->ast = owner_ast; def = spl_def_node(sema->type, def_id); if (def) def->state = SPL_DECL_RESOLVING; spl_ast_node_t *n = spl_ast_node(sema->ast, ast_node_ref); spl_lang_type_id_t result = 0; 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) result = sema_build_aggregate(sema, te, def_id); else result = sema_infer(sema, n->type_decl.type_expr); } n->resolved_lang_tid = result; break; } case SPL_AST_FN_DECL: { spl_lang_type_id_t saved_agg = sema->current_aggregate_lang; if (owner_def_id) { spl_def_node_t *owner = spl_def_node(sema->type, owner_def_id); if (owner && owner->lang_id != 0) sema->current_aggregate_lang = owner->lang_id; } result = sema_infer_fn_signature(sema, n); sema->current_aggregate_lang = saved_agg; break; } case SPL_AST_VAR_DECL: case SPL_AST_CONST_DECL: { if (n->var_const_decl.type) result = sema_infer(sema, n->var_const_decl.type); else if (n->var_const_decl.init_expr) result = sema_infer(sema, n->var_const_decl.init_expr); break; } case SPL_AST_CONTAINER_MEMBERS: result = sema_build_container_aggregate(sema, n, def_id); n->resolved_lang_tid = result; break; default: break; } } spl_def_node_t *final_def = spl_def_node(sema->type, def_id); if (final_def) { /* reserve 阶段已回填 lang_id 时,即使 result==0(构建失败)也不清掉身份, 后续引用拿到 ERROR 状态节点而非报环。 */ if (result != 0) final_def->lang_id = result; final_def->state = result ? SPL_DECL_RESOLVED : SPL_DECL_ERROR; } sema->ast = saved_ast; return result; } static void sema_collect_names(spl_sema_t *sema, spl_ast_node_ref_t ref) { spl_ast_node_t *n = spl_ast_node(sema->ast, ref); if (!n) return; switch (n->kind) { case SPL_AST_CONTAINER_MEMBERS: { if (sema->scope->root_scope == 0) { spl_scope_id_t root = spl_scope_alloc(sema->scope); sema->scope->root_scope = root; } sema->scope->current_scope = sema->scope->root_scope; spl_scope_id_t mod_scope = spl_scope_alloc(sema->scope); sema->scope->current_scope = mod_scope; spl_symbol_t file_sym = {.name = "$file", .kind = SPL_SYMBOL_KIND_TYPE}; file_sym.node = spl_def_alloc(sema->type, SPL_DEF_AGG, "$file", n->dbg, sema->ast, ref); n->resolved_def_id = file_sym.node; spl_scope_insert(sema->scope, mod_scope, file_sym); for (usize i = 0; i < vec_size(n->container_members.declarations); ++i) { sema_collect_names(sema, vec_at(n->container_members.declarations, i)); } break; } case SPL_AST_TYPE_DECL: { 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", 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, name, te->dbg, sema->ast, ref); else 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`", name); } break; } case SPL_AST_FN_DECL: { spl_symbol_t sym = {.name = n->fn_decl.name, .kind = SPL_SYMBOL_KIND_FN}; sym.node = spl_def_alloc(sema->type, SPL_DEF_FN, sym.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`", sym.name); } break; } 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, "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; } case SPL_AST_TOP_LEVEL_BLOCK: if (n->top_level_block.block) sema_collect_names(sema, n->top_level_block.block); break; default: break; } } static void sema_collect_types(spl_sema_t *sema, spl_ast_node_ref_t ref) { spl_ast_node_t *n = spl_ast_node(sema->ast, ref); if (!n) return; switch (n->kind) { case SPL_AST_CONTAINER_MEMBERS: 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) 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); } } break; case SPL_AST_TOP_LEVEL_BLOCK: if (n->top_level_block.block) sema_collect_types(sema, n->top_level_block.block); break; default: break; } } static void sema_collect_signatures(spl_sema_t *sema) { usize n = vec_size(sema->type->def_table); for (usize i = 1; i < n; ++i) { spl_def_node_t *d = &vec_at(sema->type->def_table, i); /* Nested imports share def_table, but each module has its own scope. * Resolve other modules' signatures only when processing their AST. */ if (d->owner_ast == sema->ast && d->kind == SPL_DEF_FN && d->state == SPL_DECL_UNRESOLVED) sema_eval_decl(sema, i); } } static int sema_def_is_type(const spl_def_node_t *def) { if (!def) return 0; switch (def->kind) { case SPL_DEF_SCALAR: case SPL_DEF_AGG: case SPL_DEF_DISTINCT: case SPL_DEF_ALIAS: case SPL_DEF_BUILTIN: return 1; default: return 0; } } /* 解包声明本身不会在 collect_names 阶段建立符号,因此必须在构建模块根 * 聚合之前处理。否则根字段无法引用通过 `type .{ ... } = source` 导入的类型。 */ static void sema_collect_type_unpacks(spl_sema_t *sema, spl_ast_node_ref_t ref) { spl_ast_node_t *n = spl_ast_node(sema->ast, ref); if (!n) return; switch (n->kind) { case SPL_AST_CONTAINER_MEMBERS: 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) continue; spl_ast_node_t *pat = spl_ast_node(sema->ast, d->type_decl.pattern); if (pat && pat->kind != SPL_AST_PATTERN_IDENT) sema_do_type_unpack(sema, d, pat, d->type_decl.type_expr); } break; case SPL_AST_TOP_LEVEL_BLOCK: if (n->top_level_block.block) sema_collect_type_unpacks(sema, n->top_level_block.block); break; default: break; } } /* @sizeof/@bitsizeof/@alignof 的参数在语法层仍是普通 Expr。 intrinsic 的类型上下文在这里解释表达式形态,不要求 parser 识别 intrinsic。 */ static spl_lang_type_id_t sema_infer_type_context(spl_sema_t *sema, spl_ast_node_ref_t ref) { spl_ast_node_t *n = spl_ast_node(sema->ast, ref); if (!n) return 0; switch (n->kind) { case SPL_AST_EXPR_IDENT: { spl_lang_type_id_t builtin = spl_type_lookup_builtin(sema->type, n->ident.name); if (builtin) { n->resolved_lang_tid = builtin; return builtin; } spl_symbol_t symbol; if (!spl_scope_find(sema->scope, n->ident.name, &symbol) || symbol.kind != SPL_SYMBOL_KIND_TYPE) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "`%s` is not a type", n->ident.name); return 0; } spl_def_node_t *def = spl_def_node(sema->type, symbol.node); if (!sema_def_is_type(def)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "`%s` is not a type", n->ident.name); return 0; } n->resolved_def_id = symbol.node; spl_lang_type_id_t result = sema_eval_decl(sema, symbol.node); n->resolved_lang_tid = result; return result; } case SPL_AST_EXPR_FIELD: { spl_lang_type_id_t owner_id = sema_infer_type_context(sema, n->field.obj); spl_lang_type_node_t *owner = spl_lang_type_node(sema->type, owner_id); if (!owner) return 0; for (usize i = 0; i < vec_size(owner->methods); ++i) { spl_lang_method_t *member = &vec_at(owner->methods, i); if (strcmp(member->name, n->field.field) != 0) continue; spl_def_node_t *def = spl_def_node(sema->type, member->def_id); if (!sema_def_is_type(def)) break; n->resolved_def_id = member->def_id; spl_lang_type_id_t result = sema_eval_decl(sema, member->def_id); n->resolved_lang_tid = result; return result; } spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "`%s` is not a type member", n->field.field); return 0; } case SPL_AST_EXPR_UNARY: if (n->unary.op == SPL_UNARY_OP_DEREF) { spl_lang_type_id_t pointee = sema_infer_type_context(sema, n->unary.operand); if (!pointee) return 0; spl_lang_type_id_t result = spl_lang_make_ptr(sema->type, pointee); n->resolved_lang_tid = result; return result; } break; case SPL_AST_EXPR_TUPLE: { spl_lang_type_id_vec_t elements; vec_init(elements); for (usize i = 0; i < vec_size(n->tuple.elems); ++i) { spl_lang_type_id_t element = sema_infer_type_context(sema, vec_at(n->tuple.elems, i)); if (!element) { vec_free(elements); return 0; } vec_push(elements, element); } spl_lang_type_id_t result = spl_lang_make_tuple(sema->type, vec_unsafe_get_data(elements), vec_size(elements)); vec_free(elements); n->resolved_lang_tid = result; return result; } case SPL_AST_EXPR_COMPTIME_CALL: { spl_lang_type_id_t result = sema_infer(sema, ref); n->resolved_lang_tid = result; return result; } default: break; } spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "intrinsic argument is not a type expression"); return 0; } static spl_lang_type_id_t sema_intrin_call(spl_sema_t *sema, spl_ast_node_t *n, spl_intrin_t in, spl_ast_node_ref_vec_t *args) { const spl_intrin_info_t *info = spl_intrin_info(in); usize nargs = vec_size(*args); if ((int)nargs < info->min_args || (info->max_args >= 0 && (int)nargs > info->max_args)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_INTRIN_ARG_COUNT, &n->dbg, "`%s` expects %d..%d args, got %zu", info->name, info->min_args, info->max_args, nargs); return 0; } switch (in) { case SPL_INTRIN_BITSIZEOF: case SPL_INTRIN_SIZEOF: case SPL_INTRIN_ALIGNOF: { if (!sema_infer_type_context(sema, vec_at(*args, 0))) return 0; return spl_lang_make_int(sema->type, sizeof(void *) * 8, 0); } case SPL_INTRIN_ASSERT: { spl_lang_type_id_t bt = spl_lang_make_bool(sema->type); sema_coerce(sema, vec_at(*args, 0), bt); if (nargs == 2) sema_infer(sema, vec_at(*args, 1)); return spl_lang_make_unit(sema->type); } case SPL_INTRIN_INT_CAST: case SPL_INTRIN_TRUNCATE: case SPL_INTRIN_BIT_CAST: case SPL_INTRIN_FLOAT_CAST: case SPL_INTRIN_FLOAT2INT: case SPL_INTRIN_INT2FLOAT: case SPL_INTRIN_INT2PTR: case SPL_INTRIN_PTR_CAST: { spl_lang_type_id_t src_lang = sema_infer(sema, vec_at(*args, 0)); if (!src_lang) return 0; spl_lang_type_id_t dst_lang = sema_infer(sema, vec_at(*args, 1)); if (!dst_lang) return 0; spl_type_id_t ft = spl_lang_type_get_ir_type(sema->type, src_lang); spl_type_id_t tt = spl_lang_type_get_ir_type(sema->type, dst_lang); spl_intrin_t expected = spl_intrin_lookup_cast(&sema->type->ir, ft, tt); if (expected == SPL_INTRIN_NONE) { spl_diag_error(sema->diag, SPL_DIAG_KIND_CAST_INVALID, &n->dbg, "`%s`: incompatible types", info->name); return 0; } n->resolved_lang_tid = dst_lang; return dst_lang; } case SPL_INTRIN_BOOL2INT: case SPL_INTRIN_PTR2INT: { sema_infer(sema, vec_at(*args, 0)); if (nargs >= 2) { spl_lang_type_id_t dst = sema_infer(sema, vec_at(*args, 1)); if (!dst) return 0; n->resolved_lang_tid = dst; return dst; } spl_lang_type_id_t r = spl_lang_make_int(sema->type, sizeof(void *) * 8, 0); n->resolved_lang_tid = r; return r; } case SPL_INTRIN_ENUM2INT: { spl_lang_type_id_t src = sema_infer(sema, vec_at(*args, 0)); if (!src) return 0; spl_lang_type_node_t *sn = spl_lang_type_node(sema->type, src); if (!sn || sn->kind != SPL_LANG_TYPE_ENUM) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "@enum2int requires an enum operand"); return 0; } spl_type_node_t *sir = spl_type_node(&sema->type->ir, sn->ir_type_id); if (!sir || !sir->agg_type.tag_type) { spl_diag_error(sema->diag, SPL_DIAG_KIND_INTERNAL, &n->dbg, "@enum2int: enum has no tag type"); return 0; } spl_lang_type_id_t tag = sema_lang_from_ir(sema, sir->agg_type.tag_type); n->resolved_lang_tid = tag; return tag; } case SPL_INTRIN_FIELD_COUNT: { if (!sema_infer_type_context(sema, vec_at(*args, 0))) return 0; return spl_lang_make_int(sema->type, sizeof(void *) * 8, 0); } case SPL_INTRIN_OFFSETOF: { spl_lang_type_id_t agg_lang = sema_infer_type_context(sema, vec_at(*args, 0)); if (!agg_lang) return 0; spl_type_id_t agg_ir = spl_lang_type_get_ir_type(sema->type, agg_lang); spl_ast_node_t *fnode = spl_ast_node(sema->ast, vec_at(*args, 1)); if (!fnode) return 0; if (fnode->kind == SPL_AST_EXPR_IDENT) { usize idx = 0; if (!spl_type_agg_member_idx(&sema->type->ir, agg_ir, fnode->ident.name, &idx)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_NO_FIELD, &n->dbg, "no field `%s`", fnode->ident.name); return 0; } } else if (fnode->kind != SPL_AST_EXPR_INT_LIT) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "@offsetof expects a field name or integer index"); return 0; } return spl_lang_make_int(sema->type, sizeof(void *) * 8, 0); } case SPL_INTRIN_FILE_NAME: case SPL_INTRIN_FN_NAME: { spl_lang_type_id_t u8 = spl_lang_make_int(sema->type, 8, 0); spl_lang_type_id_t slice = spl_lang_make_slice(sema->type, u8); n->resolved_lang_tid = slice; return slice; } case SPL_INTRIN_LINE: case SPL_INTRIN_COL: return spl_lang_make_int(sema->type, sizeof(void *) * 8, 0); default: UNREACHABLE(); } return 0; } static spl_lang_type_id_t sema_intrin_field(spl_sema_t *sema, spl_ast_node_t *n, spl_intrin_t in) { const spl_intrin_info_t *info = spl_intrin_info(in); switch (info->result) { case SPL_INTRIN_RESULT_USIZE: return spl_lang_make_int(sema->type, sizeof(void *) * 8, 0); case SPL_INTRIN_RESULT_BOOL: return spl_lang_make_bool(sema->type); case SPL_INTRIN_RESULT_UNIT: return spl_lang_make_unit(sema->type); default: UNREACHABLE(); } (void)n; return 0; } static spl_lang_type_id_t sema_block_body(spl_sema_t *sema, spl_ast_node_t *n, spl_lang_type_id_t expected) { spl_ast_node_ref_t tail = n->block.tail_expr; sema_push_scope(sema); for (usize i = 0; i < vec_size(n->block.stmts); ++i) sema_infer(sema, vec_at(n->block.stmts, i)); spl_lang_type_id_t r = 0; if (tail) { if (expected) { r = sema_coerce(sema, tail, expected); if (!r) { sema_pop_scope(sema); return 0; } } else { r = sema_infer(sema, tail); } } else { r = spl_lang_make_unit(sema->type); } sema_pop_scope(sema); n->resolved_lang_tid = r; 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) return; spl_ast_node_t *pat = spl_ast_node(sema->ast, 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); if (!spl_scope_insert(sema->scope, sema->scope->current_scope, sym)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_DUPLICATE_SYMBOL, &pat->dbg, "duplicate symbol `%s`", name); } pat->resolved_def_id = sym.node; pat->resolved_lang_tid = matched_tid; return; } case SPL_AST_PATTERN_UNDERSCORE: return; case SPL_AST_PATTERN_REF: { if (!matched_tid) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg, "ref pattern requires a concrete type"); return; } spl_lang_type_id_t ptr = spl_lang_make_ptr(sema->type, matched_tid); sema_bind_pattern(sema, pat->pattern_ref.pat, ptr); pat->resolved_lang_tid = ptr; 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 tuple value"); return; } usize natoms = vec_size(pat->pattern_tuple.patterns); usize nelems = vec_size(tn->extra.elems); if (natoms != nelems) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg, "tuple pattern arity mismatch (expected %zu, got %zu)", nelems, natoms); return; } spl_lang_type_id_vec_t elem_tids; vec_init(elem_tids); for (usize i = 0; i < nelems; ++i) vec_push(elem_tids, vec_at(tn->extra.elems, i)); for (usize i = 0; i < natoms; ++i) sema_bind_pattern(sema, vec_at(pat->pattern_tuple.patterns, i), vec_at(elem_tids, i)); vec_free(elem_tids); pat->resolved_lang_tid = matched_tid; return; } 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;实例字段不会被导入 - 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; } 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, spl_lang_type_id_t expected) { if (!ref || !expected) return 0; spl_ast_node_t *n = spl_ast_node(sema->ast, ref); if (!n) return 0; spl_lang_type_id_t already = n->resolved_lang_tid; if (already == expected) return expected; if (already != 0) { spl_lang_type_node_t *an = spl_lang_type_node(sema->type, already); spl_type_node_t *air = an ? spl_type_node(&sema->type->ir, an->ir_type_id) : NULL; bool a_rebindable = (an && an->kind == SPL_LANG_TYPE_INFER) || (an && an->kind == SPL_LANG_TYPE_INT && air && air->int_type.bits == 0) || (an && an->kind == SPL_LANG_TYPE_FLOAT && air && air->float_type.bits == 0); if (!a_rebindable) { if (spl_lang_type_is_subtype(sema->type, already, expected)) { n->expected_lang_tid = expected; return expected; } char ebuf[128], gbuf[128]; sema_type_to_str(sema, expected, ebuf, sizeof(ebuf)); sema_type_to_str(sema, already, gbuf, sizeof(gbuf)); spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "type mismatch: node kind=%d already typed as %s, expected %s", (int)n->kind, gbuf, ebuf); return 0; } n->resolved_lang_tid = 0; } switch (n->kind) { case SPL_AST_EXPR_UNARY: { if (n->unary.op == SPL_UNARY_OP_ADDRESS || n->unary.op == SPL_UNARY_OP_DEREF) break; if (!sema_coerce(sema, n->unary.operand, expected)) return 0; n->resolved_lang_tid = expected; return expected; } case SPL_AST_EXPR_BINARY: { spl_binary_op_t op = n->binary.op; if (op == SPL_OP_ASSIGN || (op >= SPL_OP_ASSIGN_ADD && op <= SPL_OP_ASSIGN_RSHIFT)) { spl_lang_type_id_t lt = sema_infer(sema, n->binary.left); if (!lt) return 0; if (!sema_coerce(sema, n->binary.right, lt)) return 0; n->resolved_lang_tid = lt; return lt; } if (op == SPL_OP_BOOL_OR || op == SPL_OP_BOOL_AND) { spl_lang_type_id_t bt = spl_lang_make_bool(sema->type); if (!sema_coerce(sema, n->binary.left, bt)) return 0; if (!sema_coerce(sema, n->binary.right, bt)) return 0; n->resolved_lang_tid = bt; return bt; } if (op == SPL_OP_CMP_EQ || op == SPL_OP_CMP_NE || op == SPL_OP_CMP_LT || op == SPL_OP_CMP_LE || op == SPL_OP_CMP_GT || op == SPL_OP_CMP_GE) { spl_lang_type_id_t lt = sema_infer(sema, n->binary.left); spl_lang_type_id_t rt = sema_infer(sema, n->binary.right); if (!lt || !rt) return 0; spl_lang_type_id_t common = (lt == rt) ? lt : spl_lang_type_join(sema->type, lt, rt); if (!common) { char lbuf[128], rbuf[128]; sema_type_to_str(sema, lt, lbuf, sizeof(lbuf)); sema_type_to_str(sema, rt, rbuf, sizeof(rbuf)); spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "comparison operands type mismatch: lhs=%s rhs=%s", lbuf, rbuf); return 0; } sema_coerce(sema, n->binary.left, common); sema_coerce(sema, n->binary.right, common); spl_lang_type_id_t b = spl_lang_make_bool(sema->type); n->resolved_lang_tid = b; return b; } if (op == SPL_OP_ADD || op == SPL_OP_SUB) { spl_lang_type_node_t *en = spl_lang_type_node(sema->type, expected); if (en && en->kind == SPL_LANG_TYPE_PTR) { spl_lang_type_id_t lt = sema_infer(sema, n->binary.left); spl_lang_type_node_t *ln = spl_lang_type_node(sema->type, lt); if (ln && ln->kind == SPL_LANG_TYPE_PTR) { spl_lang_type_id_t rt = sema_infer(sema, n->binary.right); spl_lang_type_node_t *rn = spl_lang_type_node(sema->type, rt); if (!rn || rn->kind != SPL_LANG_TYPE_INT) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "pointer offset must be integer"); return 0; } spl_type_node_t *rn_ir = spl_type_node(&sema->type->ir, rn->ir_type_id); if (rn_ir && rn_ir->int_type.bits == 0) { spl_lang_type_id_t it = spl_lang_make_int(sema->type, sizeof(void *) * 8, 1); if (!sema_coerce(sema, n->binary.right, it)) return 0; } n->resolved_lang_tid = expected; return expected; } } } if (!sema_coerce(sema, n->binary.left, expected)) return 0; if (!sema_coerce(sema, n->binary.right, expected)) return 0; n->resolved_lang_tid = expected; return expected; } case SPL_AST_EXPR_ARRAY_LIT: { spl_lang_type_node_t *en = spl_lang_type_node(sema->type, expected); if (en && en->kind == SPL_LANG_TYPE_ARRAY) { spl_lang_type_id_t et = en->extra.elem; for (usize i = 0; i < vec_size(n->array_literal.elems); ++i) if (!sema_coerce(sema, vec_at(n->array_literal.elems, i), et)) return 0; n->resolved_lang_tid = expected; return expected; } /* [N]T -> []T:数组字面量在需要切片处隐式物化为切片。 */ if (en && en->kind == SPL_LANG_TYPE_SLICE) { spl_lang_type_id_t et = en->extra.elem; for (usize i = 0; i < vec_size(n->array_literal.elems); ++i) if (!sema_coerce(sema, vec_at(n->array_literal.elems, i), et)) return 0; n->resolved_lang_tid = expected; return expected; } break; } case SPL_AST_EXPR_TUPLE: { spl_lang_type_node_t *en = spl_lang_type_node(sema->type, expected); if (en && en->kind == SPL_LANG_TYPE_TUPLE) { usize nelems = vec_size(n->tuple.elems); usize ntypes = vec_size(en->extra.elems); if (nelems != ntypes) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "tuple arity mismatch (expected %zu, got %zu)", ntypes, nelems); return 0; } for (usize i = 0; i < nelems; ++i) { spl_lang_type_id_t et = vec_at(en->extra.elems, i); if (!sema_coerce(sema, vec_at(n->tuple.elems, i), et)) return 0; } n->resolved_lang_tid = expected; return expected; } break; } case SPL_AST_EXPR_AGGREGATE_LIT: { spl_lang_type_node_t *en = spl_lang_type_node(sema->type, expected); if (!en || (en->kind != SPL_LANG_TYPE_STRUCT && en->kind != SPL_LANG_TYPE_UNION && en->kind != SPL_LANG_TYPE_ENUM && en->kind != SPL_LANG_TYPE_SLICE)) break; if (n->aggregate_literal.name) { spl_symbol_t sym; if (!spl_scope_find(sema->scope, n->aggregate_literal.name, &sym)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_UNDEFINED_SYMBOL, &n->dbg, "unknown type `%s`", n->aggregate_literal.name); return 0; } spl_def_node_t *def = spl_def_node(sema->type, sym.node); if (!def || def->lang_id != expected) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "aggregate `%s` does not match expected type", n->aggregate_literal.name); return 0; } } spl_type_id_t agg_ir = spl_lang_type_get_ir_type(sema->type, expected); /* 先把每个字段的 ir type_id 拷到本地:循环里 sema_coerce 会 push type_table,irn 会悬垂。 */ spl_type_id_vec_t field_ir_ids; vec_init(field_ir_ids); { spl_type_node_t *irn = spl_type_node(&sema->type->ir, agg_ir); if (!irn) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "aggregate type unresolved"); return 0; } for (usize i = 0; i < vec_size(irn->agg_type.variants); i++) vec_push(field_ir_ids, vec_at(irn->agg_type.variants, i).type_id); } if (en->kind == SPL_LANG_TYPE_ENUM) { if (vec_size(n->aggregate_literal.fields) != 1) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "enum literal must have exactly one variant"); vec_free(field_ir_ids); return 0; } spl_ast_node_t *item = spl_ast_node(sema->ast, vec_at(n->aggregate_literal.fields, 0)); if (!item || item->kind != SPL_AST_STRUCT_INIT_ITEM) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "invalid enum literal"); vec_free(field_ir_ids); return 0; } usize idx = 0; if (!spl_type_agg_member_idx(&sema->type->ir, agg_ir, item->struct_init_item.name, &idx)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_NO_FIELD, &item->dbg, "no variant `%s`", item->struct_init_item.name); vec_free(field_ir_ids); return 0; } if (idx >= vec_size(field_ir_ids)) { vec_free(field_ir_ids); return 0; } usize field_tid_ir = vec_at(field_ir_ids, idx); spl_lang_type_id_t ft = sema_lang_from_ir(sema, field_tid_ir); if (ft && item->struct_init_item.expr) { if (!sema_coerce(sema, item->struct_init_item.expr, ft)) { vec_free(field_ir_ids); return 0; } } vec_free(field_ir_ids); n->resolved_lang_tid = expected; return expected; } for (usize i = 0; i < vec_size(n->aggregate_literal.fields); ++i) { spl_ast_node_t *item = spl_ast_node(sema->ast, vec_at(n->aggregate_literal.fields, i)); if (!item || item->kind != SPL_AST_STRUCT_INIT_ITEM) continue; usize idx = 0; if (!spl_type_agg_member_idx(&sema->type->ir, agg_ir, item->struct_init_item.name, &idx)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_NO_FIELD, &item->dbg, "no field `%s`", item->struct_init_item.name); continue; } if (idx >= vec_size(field_ir_ids)) continue; usize field_tid_ir = vec_at(field_ir_ids, idx); spl_lang_type_id_t ft = sema_lang_from_ir(sema, field_tid_ir); if (ft && item->struct_init_item.expr) { if (!sema_coerce(sema, item->struct_init_item.expr, ft)) { vec_free(field_ir_ids); return 0; } } } vec_free(field_ir_ids); n->resolved_lang_tid = expected; return expected; } case SPL_AST_EXPR_IF: { spl_lang_type_id_t bt = spl_lang_make_bool(sema->type); sema_coerce(sema, n->if_expr.cond, bt); if (n->if_expr.then_block) sema_coerce(sema, n->if_expr.then_block, expected); if (n->if_expr.else_branch) sema_coerce(sema, n->if_expr.else_branch, expected); n->resolved_lang_tid = expected; return expected; } case SPL_AST_EXPR_LOOP: { spl_lang_type_id_t saved = sema->current_loop_break_tid; sema->current_loop_break_tid = expected; sema_infer(sema, ref); sema->current_loop_break_tid = saved; n->resolved_lang_tid = expected; return expected; } case SPL_AST_EXPR_BLOCK: return sema_block_body(sema, n, expected); default: break; } spl_lang_type_id_t from = sema_infer(sema, ref); if (!from) return 0; if (from == expected) return expected; spl_lang_type_node_t *from_n = spl_lang_type_node(sema->type, from); spl_lang_type_node_t *to_n = spl_lang_type_node(sema->type, expected); spl_type_node_t *from_ir = from_n ? spl_type_node(&sema->type->ir, from_n->ir_type_id) : NULL; spl_type_node_t *to_ir = to_n ? spl_type_node(&sema->type->ir, to_n->ir_type_id) : NULL; bool from_comptime_int = from_n && from_n->kind == SPL_LANG_TYPE_INT && from_ir && from_ir->int_type.bits == 0; bool from_comptime_float = from_n && from_n->kind == SPL_LANG_TYPE_FLOAT && from_ir && from_ir->float_type.bits == 0; if (from_comptime_int && to_n && to_n->kind == SPL_LANG_TYPE_INT) { uint32_t bits = to_ir ? (uint32_t)to_ir->int_type.bits : 0; if (n->kind == SPL_AST_EXPR_INT_LIT && bits != 0 && !spl_lang_mpa_int_fits(sema->type, expected, n->int_lit)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_FITS_OVERFLOW, &n->dbg, "comptime value does not fit target type"); return 0; } n->resolved_lang_tid = expected; return expected; } if (from_comptime_float && to_n && to_n->kind == SPL_LANG_TYPE_FLOAT) { n->resolved_lang_tid = expected; return expected; } if (spl_lang_type_is_subtype(sema->type, from, expected)) { n->expected_lang_tid = expected; return expected; } char ebuf[128], gbuf[128]; sema_type_to_str(sema, expected, ebuf, sizeof(ebuf)); sema_type_to_str(sema, from, gbuf, sizeof(gbuf)); spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "type mismatch: expect %s, got %s (node kind=%d)", ebuf, gbuf, (int)n->kind); return 0; } static spl_lang_type_id_t sema_infer(spl_sema_t *sema, spl_ast_node_ref_t ref) { if (ref == 0) return 0; spl_ast_node_t *n = spl_ast_node(sema->ast, ref); if (!n) return 0; spl_ast_node_ref_vec_t *raw = node_attr_list(n); if (raw && vec_size(*raw) > 0) sema_resolve_attrs(sema, raw, &n->attrs); switch (n->kind) { case SPL_AST_EXPR_INT_LIT: { spl_lang_type_id_t t = spl_lang_make_int(sema->type, 0, 1); n->resolved_lang_tid = t; return t; } case SPL_AST_EXPR_FLOAT_LIT: { spl_lang_type_id_t t = spl_lang_make_float(sema->type, 0); n->resolved_lang_tid = t; return t; } case SPL_AST_EXPR_CHAR_LIT: { spl_lang_type_id_t t = spl_lang_make_int(sema->type, 8, 0); n->resolved_lang_tid = t; return t; } case SPL_AST_EXPR_STRING_LIT: { spl_lang_type_id_t u8 = spl_lang_make_int(sema->type, 8, 0); spl_lang_type_id_t s = spl_lang_make_slice(sema->type, u8); n->resolved_lang_tid = s; return s; } case SPL_AST_EXPR_BOOL_LIT: { spl_lang_type_id_t t = spl_lang_make_bool(sema->type); n->resolved_lang_tid = t; return t; } case SPL_AST_EXPR_NULL_LIT: { spl_lang_type_id_t nv = spl_lang_make_never(sema->type); spl_lang_type_id_t p = spl_lang_make_ptr(sema->type, nv); n->resolved_lang_tid = p; return p; } case SPL_AST_EXPR_UNDEFINED_LIT: { spl_lang_type_id_t t = spl_lang_make_never(sema->type); n->resolved_lang_tid = t; return t; } case SPL_AST_EXPR_IDENT: { const char *name = n->ident.name; spl_symbol_t sym; if (!spl_scope_find(sema->scope, name, &sym)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_UNDEFINED_SYMBOL, &n->dbg, "undefined identifier `%s`", name); return 0; } if (sym.node == 0) { spl_diag_error(sema->diag, SPL_DIAG_KIND_UNDEFINED_SYMBOL, &n->dbg, "identifier `%s` has no definition", name); return 0; } n->resolved_def_id = sym.node; spl_lang_type_id_t t = sema_eval_decl(sema, sym.node); n->resolved_lang_tid = t; return t; } case SPL_AST_EXPR_UNARY: { spl_lang_type_id_t op = sema_infer(sema, n->unary.operand); if (op == 0) return 0; spl_lang_type_node_t *tn = spl_lang_type_node(sema->type, op); if (!tn) return 0; switch (n->unary.op) { case SPL_UNARY_OP_MINUS: if (tn->kind != SPL_LANG_TYPE_INT && tn->kind != SPL_LANG_TYPE_FLOAT) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "unary minus requires numeric operand"); return 0; } n->resolved_lang_tid = op; return op; case SPL_UNARY_OP_BIT_NOT: if (tn->kind != SPL_LANG_TYPE_INT) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "bitwise not requires integer operand"); return 0; } n->resolved_lang_tid = op; return op; case SPL_UNARY_OP_NOT: { spl_lang_type_id_t bt = spl_lang_make_bool(sema->type); sema_coerce(sema, n->unary.operand, bt); n->resolved_lang_tid = bt; return bt; } case SPL_UNARY_OP_ADDRESS: { spl_ast_node_t *operand = spl_ast_node(sema->ast, n->unary.operand); if (operand && operand->kind == SPL_AST_EXPR_IDENT) { spl_def_node_t *def = spl_def_node(sema->type, operand->resolved_def_id); if (def && def->kind == SPL_DEF_FN) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "cannot take address of a function: the function name " "itself is a function pointer"); return 0; } } spl_lang_type_id_t p = spl_lang_make_ptr(sema->type, op); n->resolved_lang_tid = p; return p; } case SPL_UNARY_OP_DEREF: { if (tn->kind != SPL_LANG_TYPE_PTR) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "deref requires pointer operand"); return 0; } spl_lang_type_id_t elem = tn->extra.elem; n->resolved_lang_tid = elem; return elem; } default: UNREACHABLE(); } } case SPL_AST_EXPR_BINARY: { spl_binary_op_t op = n->binary.op; if (op == SPL_OP_ASSIGN || (op >= SPL_OP_ASSIGN_ADD && op <= SPL_OP_ASSIGN_RSHIFT)) { spl_lang_type_id_t lt = sema_infer(sema, n->binary.left); if (!lt) return 0; sema_coerce(sema, n->binary.right, lt); n->resolved_lang_tid = lt; return lt; } if (op == SPL_OP_BOOL_OR || op == SPL_OP_BOOL_AND) { spl_lang_type_id_t bt = spl_lang_make_bool(sema->type); sema_coerce(sema, n->binary.left, bt); sema_coerce(sema, n->binary.right, bt); n->resolved_lang_tid = bt; return bt; } if (op == SPL_OP_CMP_EQ || op == SPL_OP_CMP_NE || op == SPL_OP_CMP_LT || op == SPL_OP_CMP_LE || op == SPL_OP_CMP_GT || op == SPL_OP_CMP_GE) { spl_lang_type_id_t lt = sema_infer(sema, n->binary.left); spl_lang_type_id_t rt = sema_infer(sema, n->binary.right); if (!lt || !rt) return 0; spl_lang_type_id_t common = (lt == rt) ? lt : spl_lang_type_join(sema->type, lt, rt); if (!common) { char lbuf[128], rbuf[128]; sema_type_to_str(sema, lt, lbuf, sizeof(lbuf)); sema_type_to_str(sema, rt, rbuf, sizeof(rbuf)); spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "comparison operands type mismatch: lhs=%s rhs=%s", lbuf, rbuf); return 0; } sema_coerce(sema, n->binary.left, common); sema_coerce(sema, n->binary.right, common); spl_lang_type_id_t b = spl_lang_make_bool(sema->type); n->resolved_lang_tid = b; return b; } if (op == SPL_OP_RANGE) { sema_infer(sema, n->binary.left); sema_infer(sema, n->binary.right); spl_lang_type_id_t i64 = spl_lang_make_int(sema->type, 64, 1); spl_lang_type_id_t r = spl_lang_make_range(sema->type, i64); n->resolved_lang_tid = r; return r; } if (op == SPL_OP_LSHIFT || op == SPL_OP_RSHIFT) { spl_lang_type_id_t lt = sema_infer(sema, n->binary.left); if (!lt) return 0; spl_lang_type_node_t *ln = spl_lang_type_node(sema->type, lt); if (!ln || ln->kind != SPL_LANG_TYPE_INT) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "shift requires integer operands"); return 0; } sema_infer(sema, n->binary.right); n->resolved_lang_tid = lt; return lt; } spl_lang_type_id_t lt = sema_infer(sema, n->binary.left); spl_lang_type_id_t rt = sema_infer(sema, n->binary.right); if (!lt || !rt) return 0; spl_lang_type_node_t *ln = spl_lang_type_node(sema->type, lt); spl_lang_type_node_t *rn = spl_lang_type_node(sema->type, rt); if (ln && ln->kind == SPL_LANG_TYPE_PTR && rn && rn->kind == SPL_LANG_TYPE_INT && (op == SPL_OP_ADD || op == SPL_OP_SUB)) { spl_type_node_t *rn_ir = spl_type_node(&sema->type->ir, rn->ir_type_id); if (rn_ir && rn_ir->int_type.bits == 0) { spl_lang_type_id_t it = spl_lang_make_int(sema->type, sizeof(void *) * 8, 1); sema_coerce(sema, n->binary.right, it); } n->resolved_lang_tid = lt; return lt; } spl_lang_type_id_t common = (lt == rt) ? lt : spl_lang_type_join(sema->type, lt, rt); if (!common) { char lbuf[128], rbuf[128]; sema_type_to_str(sema, lt, lbuf, sizeof(lbuf)); sema_type_to_str(sema, rt, rbuf, sizeof(rbuf)); spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "operand types cannot be joined: lhs=%s rhs=%s", lbuf, rbuf); return 0; } sema_coerce(sema, n->binary.left, common); sema_coerce(sema, n->binary.right, common); n->resolved_lang_tid = common; return common; } case SPL_AST_EXPR_CALL: { spl_ast_node_t *callee = spl_ast_node(sema->ast, n->call.callee); if (callee && callee->kind == SPL_AST_EXPR_IDENT) { spl_intrin_t in = spl_intrin_lookup_call(callee->ident.name); if (in != SPL_INTRIN_NONE) { spl_lang_type_id_t t = sema_intrin_call(sema, n, in, &n->call.args); n->resolved_lang_tid = t; return t; } } spl_lang_type_id_t callee_tid = sema_infer(sema, n->call.callee); spl_lang_type_node_t *fn = spl_lang_type_node(sema->type, callee_tid); if (!fn || fn->kind != SPL_LANG_TYPE_FN) { spl_diag_error(sema->diag, SPL_DIAG_KIND_NOT_A_FUNCTION, &n->dbg, "callee is not a function"); return 0; } bool has_varargs = false; usize fixed = vec_size(fn->extra.fn.params); if (fixed > 0) { spl_lang_type_id_t last = vec_at(fn->extra.fn.params, fixed - 1); spl_lang_type_node_t *ln = spl_lang_type_node(sema->type, last); if (ln && ln->kind == SPL_LANG_TYPE_VARARGS) { has_varargs = true; fixed--; } } usize nargs = vec_size(n->call.args); if (has_varargs) { if (nargs < fixed) { spl_diag_error(sema->diag, SPL_DIAG_KIND_ARG_COUNT_MISMATCH, &n->dbg, "too few arguments (expected at least %zu)", fixed); } } else if (nargs != fixed) { spl_diag_error(sema->diag, SPL_DIAG_KIND_ARG_COUNT_MISMATCH, &n->dbg, "argument count mismatch (expected %zu, got %zu)", fixed, nargs); } for (usize i = 0; i < nargs && i < fixed; ++i) { spl_lang_type_node_t *fn2 = spl_lang_type_node(sema->type, callee_tid); if (!fn2) return 0; spl_lang_type_id_t pt = vec_at(fn2->extra.fn.params, i); if (!sema_coerce(sema, vec_at(n->call.args, i), pt)) return 0; } if (has_varargs) { for (usize i = fixed; i < nargs; ++i) { if (!sema_infer(sema, vec_at(n->call.args, i))) return 0; } } spl_lang_type_id_t ret = fn->extra.fn.ret; n->resolved_lang_tid = ret; return ret; } case SPL_AST_EXPR_FIELD: { spl_lang_type_id_t obj_tid = sema_infer(sema, n->field.obj); spl_lang_type_node_t *obj = spl_lang_type_node(sema->type, obj_tid); if (!obj) return 0; /* 限定枚举变体 Type.Variant:基对象是聚合类型名且类型为 enum。 */ spl_ast_node_t *objn = spl_ast_node(sema->ast, n->field.obj); if (objn && objn->kind == SPL_AST_EXPR_IDENT) { spl_def_node_t *od = spl_def_node(sema->type, objn->resolved_def_id); if (od && od->kind == SPL_DEF_AGG && obj->kind == SPL_LANG_TYPE_ENUM) { spl_type_id_t eir = spl_lang_type_get_ir_type(sema->type, obj_tid); usize vidx = 0; if (eir && spl_type_agg_member_idx(&sema->type->ir, eir, n->field.field, &vidx)) { n->resolved_lang_tid = obj_tid; return obj_tid; } } } spl_intrin_t in = spl_intrin_lookup_field(obj->kind, n->field.field); if (in != SPL_INTRIN_NONE) { spl_lang_type_id_t t = sema_intrin_field(sema, n, in); n->resolved_lang_tid = t; return t; } spl_lang_type_id_t agg_lang = obj_tid; if (obj->kind == SPL_LANG_TYPE_PTR) agg_lang = obj->extra.elem; spl_lang_type_node_t *agg = spl_lang_type_node(sema->type, agg_lang); if (!agg) { spl_diag_error(sema->diag, SPL_DIAG_KIND_NO_FIELD, &n->dbg, "field access on invalid type"); return 0; } spl_type_id_t agg_ir = spl_lang_type_get_ir_type(sema->type, agg_lang); if (agg_ir) { const spl_type_member_t *m = spl_type_agg_member(&sema->type->ir, agg_ir, n->field.field); if (m) { spl_lang_type_id_t ft = sema_lang_from_ir(sema, m->type_id); if (!ft) { spl_diag_error(sema->diag, SPL_DIAG_KIND_NO_FIELD, &n->dbg, "cannot resolve field type"); return 0; } n->resolved_lang_tid = ft; return ft; } } const char *fname = n->field.field; usize nmethods = vec_size(agg->methods); for (usize i = 0; i < nmethods; ++i) { spl_lang_type_node_t *agg2 = spl_lang_type_node(sema->type, agg_lang); if (!agg2) break; spl_lang_method_t *meth = &vec_at(agg2->methods, i); if (strcmp(meth->name, fname) != 0) continue; spl_def_id_t did = meth->def_id; n->resolved_def_id = did; spl_lang_type_id_t t = sema_eval_decl(sema, did); n->resolved_lang_tid = t; return t; } spl_diag_error(sema->diag, SPL_DIAG_KIND_NO_FIELD, &n->dbg, "no field `%s`", n->field.field); return 0; } case SPL_AST_EXPR_INDEX: { spl_lang_type_id_t obj_tid = sema_infer(sema, n->index.obj); sema_infer(sema, n->index.index); spl_lang_type_node_t *obj = spl_lang_type_node(sema->type, obj_tid); if (!obj) return 0; spl_lang_type_id_t elem = 0; switch (obj->kind) { case SPL_LANG_TYPE_ARRAY: case SPL_LANG_TYPE_SLICE: case SPL_LANG_TYPE_RANGE: case SPL_LANG_TYPE_PTR: elem = obj->extra.elem; break; case SPL_LANG_TYPE_TUPLE: { spl_ast_node_t *idx_node = spl_ast_node(sema->ast, n->index.index); if (!idx_node || idx_node->kind != SPL_AST_EXPR_INT_LIT) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TUPLE_INDEX_OOB, &n->dbg, "tuple index must be an integer literal"); return 0; } isize i = (isize)idx_node->int_lit.bits; if (i < 0 || (usize)i >= vec_size(obj->extra.elems)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TUPLE_INDEX_OOB, &n->dbg, "tuple index out of range"); return 0; } elem = vec_at(obj->extra.elems, (usize)i); break; } default: spl_diag_error(sema->diag, SPL_DIAG_KIND_INDEX_BASE_INVALID, &n->dbg, "index base must be array/slice/ptr/range/tuple"); return 0; } n->resolved_lang_tid = elem; return elem; } case SPL_AST_EXPR_SLICE: { spl_lang_type_id_t obj_tid = sema_infer(sema, n->slice.obj); if (n->slice.begin) sema_infer(sema, n->slice.begin); if (n->slice.end) sema_infer(sema, n->slice.end); spl_lang_type_node_t *obj = spl_lang_type_node(sema->type, obj_tid); if (!obj) return 0; spl_lang_type_id_t elem = 0; if (obj->kind == SPL_LANG_TYPE_ARRAY || obj->kind == SPL_LANG_TYPE_PTR || obj->kind == SPL_LANG_TYPE_SLICE) { elem = obj->extra.elem; } else { spl_diag_error(sema->diag, SPL_DIAG_KIND_INDEX_BASE_INVALID, &n->dbg, "slice base must be array/ptr/slice"); return 0; } spl_lang_type_id_t s = spl_lang_make_slice(sema->type, elem); n->resolved_lang_tid = s; return s; } case SPL_AST_EXPR_CAST: { spl_lang_type_id_t dst_lang = sema_infer(sema, n->cast.type); if (!dst_lang) return 0; spl_lang_type_id_t src_lang = sema_infer(sema, n->cast.expr); if (!src_lang) return 0; spl_lang_type_node_t *sn = spl_lang_type_node(sema->type, src_lang); spl_type_node_t *sir = sn ? spl_type_node(&sema->type->ir, sn->ir_type_id) : NULL; bool src_is_literal_int = sn && sn->kind == SPL_LANG_TYPE_INT && sir && sir->int_type.bits == 0; bool src_is_literal_float = sn && sn->kind == SPL_LANG_TYPE_FLOAT && sir && sir->float_type.bits == 0; if (src_is_literal_int || src_is_literal_float) { spl_ast_node_t *src_node = spl_ast_node(sema->ast, n->cast.expr); if (src_node && (src_node->kind == SPL_AST_EXPR_INT_LIT || src_node->kind == SPL_AST_EXPR_FLOAT_LIT)) { src_node->resolved_lang_tid = dst_lang; } else if (!sema_coerce(sema, n->cast.expr, dst_lang)) { return 0; } n->resolved_lang_tid = dst_lang; return dst_lang; } spl_type_id_t ft = spl_lang_type_get_ir_type(sema->type, src_lang); spl_type_id_t tt = spl_lang_type_get_ir_type(sema->type, dst_lang); if (!ft || !tt) { spl_diag_error(sema->diag, SPL_DIAG_KIND_CAST_INVALID, &n->dbg, "as: unresolved source or target type"); return 0; } spl_intrin_t in = spl_intrin_lookup_cast(&sema->type->ir, ft, tt); if (in == SPL_INTRIN_NONE) { spl_diag_error(sema->diag, SPL_DIAG_KIND_CAST_INVALID, &n->dbg, "as: invalid conversion (not supported by as)"); return 0; } n->resolved_lang_tid = dst_lang; return dst_lang; } case SPL_AST_EXPR_TUPLE: { spl_lang_type_id_vec_t es; vec_init(es); for (usize i = 0; i < vec_size(n->tuple.elems); ++i) vec_push(es, sema_infer(sema, vec_at(n->tuple.elems, i))); spl_lang_type_id_t t = spl_lang_make_tuple(sema->type, vec_unsafe_get_data(es), vec_size(es)); vec_free(es); n->resolved_lang_tid = t; return t; } case SPL_AST_EXPR_ARRAY_LIT: { spl_lang_type_id_t elem_t = 0; if (n->array_literal.elem_type) elem_t = sema_infer(sema, n->array_literal.elem_type); usize nelems = vec_size(n->array_literal.elems); for (usize i = 0; i < nelems; ++i) { spl_lang_type_id_t et = sema_infer(sema, vec_at(n->array_literal.elems, i)); if (!et) return 0; if (elem_t == 0) { elem_t = et; } else { spl_lang_type_id_t c = (elem_t == et) ? elem_t : spl_lang_type_join(sema->type, elem_t, et); if (c) elem_t = c; } } if (elem_t == 0) elem_t = spl_lang_make_infer(sema->type); usize len = n->array_literal.is_underscore_len ? nelems : n->array_literal.has_len ? (usize)n->array_literal.len : nelems; spl_lang_type_id_t arr = spl_lang_make_array(sema->type, elem_t, len); n->resolved_lang_tid = arr; return arr; } case SPL_AST_EXPR_AGGREGATE_LIT: { const char *type_name = n->aggregate_literal.name; if (!type_name) { spl_lang_type_id_t inf = spl_lang_make_infer(sema->type); n->resolved_lang_tid = inf; return inf; } spl_symbol_t sym; if (!spl_scope_find(sema->scope, type_name, &sym)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_UNDEFINED_SYMBOL, &n->dbg, "unknown type `%s`", type_name); return 0; } spl_def_node_t *def = spl_def_node(sema->type, sym.node); if (!def || !def->lang_id) { spl_diag_error(sema->diag, SPL_DIAG_KIND_UNDEFINED_SYMBOL, &n->dbg, "type `%s` not resolved", type_name); return 0; } spl_lang_type_id_t agg = def->lang_id; spl_lang_type_node_t *an = spl_lang_type_node(sema->type, agg); 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, &n->dbg, "`%s` is not a struct/union/enum", type_name); return 0; } if (an->kind == SPL_LANG_TYPE_ENUM && vec_size(n->aggregate_literal.fields) != 1) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, "enum literal must have exactly one variant"); return 0; } spl_type_id_t agg_ir = spl_lang_type_get_ir_type(sema->type, agg); /* 循环里 sema_coerce 会 push type_table,irn 会悬垂。 先把每个字段的 ir type_id 拷到本地。 */ spl_type_id_vec_t field_ir_ids; vec_init(field_ir_ids); { spl_type_node_t *irn = spl_type_node(&sema->type->ir, agg_ir); if (!irn) { n->resolved_lang_tid = agg; return agg; } for (usize i = 0; i < vec_size(irn->agg_type.variants); i++) vec_push(field_ir_ids, vec_at(irn->agg_type.variants, i).type_id); } for (usize i = 0; i < vec_size(n->aggregate_literal.fields); ++i) { spl_ast_node_t *item = spl_ast_node(sema->ast, vec_at(n->aggregate_literal.fields, i)); if (!item || item->kind != SPL_AST_STRUCT_INIT_ITEM) continue; usize idx = 0; if (!spl_type_agg_member_idx(&sema->type->ir, agg_ir, item->struct_init_item.name, &idx)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_NO_FIELD, &item->dbg, "no field `%s`", item->struct_init_item.name); continue; } if (idx >= vec_size(field_ir_ids)) continue; usize field_tid_ir = vec_at(field_ir_ids, idx); spl_lang_type_id_t ft = sema_lang_from_ir(sema, field_tid_ir); if (ft && item->struct_init_item.expr) sema_coerce(sema, item->struct_init_item.expr, ft); } vec_free(field_ir_ids); n->resolved_lang_tid = agg; return agg; } case SPL_AST_EXPR_COMPTIME_CALL: { if (n->comptime_call.name && strcmp(n->comptime_call.name, "import") == 0) { spl_lang_type_id_t t = sema_import(sema, n, &n->comptime_call.args); n->resolved_lang_tid = t; return t; } if (n->comptime_call.name && strcmp(n->comptime_call.name, "self") == 0) { if (sema->current_aggregate_lang == 0) { spl_diag_error(sema->diag, SPL_DIAG_KIND_UNDEFINED_SYMBOL, &n->dbg, "`@self` outside of aggregate"); return 0; } n->resolved_lang_tid = sema->current_aggregate_lang; return sema->current_aggregate_lang; } spl_intrin_t in = spl_intrin_lookup_call(n->comptime_call.name); if (in == SPL_INTRIN_NONE) { spl_diag_error(sema->diag, SPL_DIAG_KIND_UNKNOWN_INTRIN, &n->dbg, "unknown comptime call `@%s`", n->comptime_call.name); return 0; } spl_lang_type_id_t t = sema_intrin_call(sema, n, in, &n->comptime_call.args); n->resolved_lang_tid = t; return t; } case SPL_AST_EXPR_BLOCK: return sema_block_body(sema, n, 0); case SPL_AST_EXPR_IF: { spl_lang_type_id_t bt = spl_lang_make_bool(sema->type); sema_coerce(sema, n->if_expr.cond, bt); spl_lang_type_id_t then_t = n->if_expr.then_block ? sema_infer(sema, n->if_expr.then_block) : 0; spl_lang_type_id_t else_t = n->if_expr.else_branch ? sema_infer(sema, n->if_expr.else_branch) : 0; spl_lang_type_id_t result = 0; if (then_t && else_t) result = (then_t == else_t) ? then_t : spl_lang_type_join(sema->type, then_t, else_t); else result = then_t ? then_t : else_t; n->resolved_lang_tid = result; return result; } case SPL_AST_EXPR_IFVAR: { 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; } case SPL_AST_EXPR_WHILE: { const char *lbl = NULL; if (n->while_expr.label) { spl_ast_node_t *ln = spl_ast_node(sema->ast, n->while_expr.label); if (ln && ln->kind == SPL_AST_LABEL) lbl = ln->label.name; } vec_push(sema->label_stack, lbl); spl_lang_type_id_t bt = spl_lang_make_bool(sema->type); sema_coerce(sema, n->while_expr.cond, bt); if (n->while_expr.body) sema_infer(sema, n->while_expr.body); if (n->while_expr.else_branch) sema_infer(sema, n->while_expr.else_branch); (void)vec_pop(sema->label_stack); return 0; } case SPL_AST_EXPR_LOOP: { const char *lbl = NULL; if (n->loop_expr.label) { spl_ast_node_t *ln = spl_ast_node(sema->ast, n->loop_expr.label); if (ln && ln->kind == SPL_AST_LABEL) lbl = ln->label.name; } vec_push(sema->label_stack, lbl); if (n->loop_expr.body) sema_infer(sema, n->loop_expr.body); (void)vec_pop(sema->label_stack); return 0; } case SPL_AST_EXPR_FOR: { const char *lbl = NULL; if (n->for_expr.label) { spl_ast_node_t *ln = spl_ast_node(sema->ast, n->for_expr.label); if (ln && ln->kind == SPL_AST_LABEL) lbl = ln->label.name; } vec_push(sema->label_stack, lbl); sema_push_scope(sema); usize nsrc = vec_size(n->for_expr.iter_exprs); spl_ast_node_ref_t pat_ref = n->for_expr.pattern; spl_ast_node_t *pat = pat_ref ? spl_ast_node(sema->ast, pat_ref) : NULL; if (pat && pat->kind == SPL_AST_PATTERN_LIST) { usize natoms = vec_size(pat->pattern_list.patterns); if (natoms != nsrc) { spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg, "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 { 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); (void)vec_pop(sema->label_stack); return 0; } case SPL_AST_EXPR_MATCH: { 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) 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; } case SPL_AST_EXPR_FN_TYPE: { spl_lang_type_id_vec_t ps; vec_init(ps); for (usize i = 0; i < vec_size(n->fn_type.params); ++i) vec_push(ps, sema_infer(sema, vec_at(n->fn_type.params, i))); spl_lang_type_id_t ret = sema_infer(sema, n->fn_type.ret_type); spl_lang_type_id_t fn = spl_lang_make_fn(sema->type, ret, vec_unsafe_get_data(ps), vec_size(ps)); vec_free(ps); n->resolved_lang_tid = fn; return fn; } case SPL_AST_TYPE_PATH: { const char *name = n->type_path.name; spl_lang_type_id_t b = spl_type_lookup_builtin(sema->type, name); if (b) { n->resolved_lang_tid = b; return b; } spl_symbol_t sym; if (!spl_scope_find(sema->scope, name, &sym)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_UNDEFINED_SYMBOL, &n->dbg, "type `%s` not found", name); return 0; } spl_lang_type_id_t t = sema_eval_decl(sema, sym.node); n->resolved_lang_tid = t; return t; } case SPL_AST_TYPE_POINTER: { spl_lang_type_id_t p = sema_infer(sema, n->type_pointer.pointee); spl_lang_type_id_t r = spl_lang_make_ptr(sema->type, p); n->resolved_lang_tid = r; return r; } case SPL_AST_TYPE_ARRAY: { spl_lang_type_id_t e = sema_infer(sema, n->type_array.elem_type); usize len = n->type_array.has_len ? (usize)n->type_array.len : 0; spl_lang_type_id_t r = spl_lang_make_array(sema->type, e, len); n->resolved_lang_tid = r; return r; } case SPL_AST_TYPE_SLICE: { spl_lang_type_id_t e = sema_infer(sema, n->type_slice.elem_type); spl_lang_type_id_t r = spl_lang_make_slice(sema->type, e); n->resolved_lang_tid = r; return r; } case SPL_AST_TYPE_FN: { spl_lang_type_id_vec_t ps; vec_init(ps); for (usize i = 0; i < vec_size(n->type_fn.params); ++i) vec_push(ps, sema_infer(sema, vec_at(n->type_fn.params, i))); spl_lang_type_id_t ret = sema_infer(sema, n->type_fn.ret_type); spl_lang_type_id_t r = spl_lang_make_fn(sema->type, ret, vec_unsafe_get_data(ps), vec_size(ps)); vec_free(ps); n->resolved_lang_tid = r; return r; } case SPL_AST_TYPE_PAREN: { usize c = vec_size(n->type_paren.types); if (c == 0) { spl_lang_type_id_t u = spl_lang_make_unit(sema->type); n->resolved_lang_tid = u; return u; } if (c == 1) { spl_lang_type_id_t t = sema_infer(sema, vec_at(n->type_paren.types, 0)); n->resolved_lang_tid = t; return t; } spl_lang_type_id_vec_t es; vec_init(es); for (usize i = 0; i < c; ++i) vec_push(es, sema_infer(sema, vec_at(n->type_paren.types, i))); spl_lang_type_id_t t = spl_lang_make_tuple(sema->type, vec_unsafe_get_data(es), vec_size(es)); vec_free(es); n->resolved_lang_tid = t; return t; } case SPL_AST_TYPE_AGGREGATE: return sema_build_aggregate(sema, n, 0); case SPL_AST_TYPE_COMPTIME_CALL: { const char *name = n->type_comptime_call.name; if (name && strcmp(name, "self") == 0) { if (sema->current_aggregate_lang == 0) { spl_diag_error(sema->diag, SPL_DIAG_KIND_UNDEFINED_SYMBOL, &n->dbg, "`@self` outside of aggregate"); return 0; } n->resolved_lang_tid = sema->current_aggregate_lang; return sema->current_aggregate_lang; } if (name && strcmp(name, "import") == 0) { spl_lang_type_id_t t = sema_import(sema, n, &n->type_comptime_call.args); n->resolved_lang_tid = t; return t; } return 0; } case SPL_AST_CONTAINER_MEMBERS: for (usize i = 0; i < vec_size(n->container_members.declarations); ++i) sema_infer(sema, vec_at(n->container_members.declarations, i)); return 0; case SPL_AST_FN_DECL: { if (n->resolved_def_id == 0) Panic("fn `%s` not collected", n->fn_decl.name); spl_def_node_t *def = spl_def_node(sema->type, n->resolved_def_id); if (!def) return 0; spl_def_id_t owner_def_id = def->owner_def; spl_lang_type_id_t fn_tid = def->lang_id; if (fn_tid == 0) fn_tid = sema_eval_decl(sema, n->resolved_def_id); spl_lang_type_node_t *fn_ln = spl_lang_type_node(sema->type, fn_tid); if (!fn_ln || fn_ln->kind != SPL_LANG_TYPE_FN) return 0; spl_lang_type_id_t ret_tid = fn_ln->extra.fn.ret; if (!n->fn_decl.body) return fn_tid; spl_lang_type_id_t saved_agg = sema->current_aggregate_lang; if (owner_def_id) { spl_def_node_t *owner = spl_def_node(sema->type, owner_def_id); if (owner && owner->lang_id != 0) sema->current_aggregate_lang = owner->lang_id; } sema_push_scope(sema); for (usize i = 0; i < vec_size(n->fn_decl.params); ++i) { spl_ast_node_t *pd = spl_ast_node(sema->ast, vec_at(n->fn_decl.params, i)); if (pd && pd->kind == SPL_AST_PARAM_DECL) { spl_symbol_t psym = {.name = pd->param_decl.name, .kind = SPL_SYMBOL_KIND_VAR}; psym.node = spl_def_alloc(sema->type, SPL_DEF_VAR, psym.name, pd->dbg, sema->ast, vec_at(n->fn_decl.params, i)); spl_lang_type_id_t p_tid = sema_infer(sema, pd->param_decl.type_expr); spl_def_node_t *pdef = spl_def_node(sema->type, psym.node); if (pdef) { pdef->lang_id = p_tid; pdef->state = p_tid ? SPL_DECL_RESOLVED : SPL_DECL_ERROR; } pd->resolved_def_id = psym.node; if (!spl_scope_insert(sema->scope, sema->scope->current_scope, psym)) { spl_diag_error(sema->diag, SPL_DIAG_KIND_DUPLICATE_SYMBOL, &pd->dbg, "duplicate symbol `%s`", psym.name); } } } spl_lang_type_id_t old_ret = sema->current_fn_ret_tid; sema->current_fn_ret_tid = ret_tid; sema_infer(sema, n->fn_decl.body); sema->current_fn_ret_tid = old_ret; sema_pop_scope(sema); sema->current_aggregate_lang = saved_agg; return fn_tid; } 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 = n->resolved_def_id ? spl_def_node(sema->type, n->resolved_def_id) : NULL; if (!def) break; spl_lang_type_id_t lang = def->lang_id; if (lang == 0) lang = sema_eval_decl(sema, n->resolved_def_id); if (lang == 0) break; spl_ast_node_t *te = spl_ast_node(sema->ast, n->type_decl.type_expr); if (te && te->kind == SPL_AST_TYPE_AGGREGATE) { for (usize i = 0; i < vec_size(te->type_aggregate.members); ++i) { spl_ast_node_ref_t mref = vec_at(te->type_aggregate.members, i); spl_ast_node_t *mn = spl_ast_node(sema->ast, mref); if (mn && mn->kind == SPL_AST_FN_DECL) sema_infer(sema, mref); } } return lang; } case SPL_AST_VAR_DECL: case SPL_AST_CONST_DECL: return sema_var_body(sema, n); case SPL_AST_MEMBER_DECL: if (n->member_decl.type_expr) return sema_infer(sema, n->member_decl.type_expr); return 0; case SPL_AST_PARAM_DECL: if (n->param_decl.type_expr) return sema_infer(sema, n->param_decl.type_expr); return 0; case SPL_AST_STATEMENT_RET: { if (n->statement_ret.expr) { if (sema->current_fn_ret_tid) sema_coerce(sema, n->statement_ret.expr, sema->current_fn_ret_tid); else sema_infer(sema, n->statement_ret.expr); } else if (sema->current_fn_ret_tid) { spl_lang_type_id_t u = spl_lang_make_unit(sema->type); if (sema->current_fn_ret_tid != u) { spl_diag_error(sema->diag, SPL_DIAG_KIND_MUST_RETURN_VALUE, &n->dbg, "function must return a value"); } } return 0; } case SPL_AST_STATEMENT_BREAK: { const char *lbl = NULL; if (n->statement_break.label) { spl_ast_node_t *ln = spl_ast_node(sema->ast, n->statement_break.label); if (ln && ln->kind == SPL_AST_LABEL) lbl = ln->label.name; } if (vec_size(sema->label_stack) == 0) { spl_diag_error(sema->diag, SPL_DIAG_KIND_BREAK_OUTSIDE_LOOP, &n->dbg, "`break` outside of loop"); return 0; } if (lbl) { bool found = false; for (usize i = vec_size(sema->label_stack); i > 0; --i) { const char *cur = vec_at(sema->label_stack, i - 1); if (cur && strcmp(cur, lbl) == 0) { found = true; break; } } if (!found) { spl_diag_error(sema->diag, SPL_DIAG_KIND_UNDEFINED_LABEL, &n->dbg, "undefined label `$%s`", lbl); } } if (n->statement_break.expr) { if (sema->current_loop_break_tid != 0) { sema_coerce(sema, n->statement_break.expr, sema->current_loop_break_tid); } else { sema_infer(sema, n->statement_break.expr); } } return 0; } case SPL_AST_STATEMENT_CONTINUE: { const char *lbl = NULL; if (n->statement_continue.label) { spl_ast_node_t *ln = spl_ast_node(sema->ast, n->statement_continue.label); if (ln && ln->kind == SPL_AST_LABEL) lbl = ln->label.name; } if (vec_size(sema->label_stack) == 0) { spl_diag_error(sema->diag, SPL_DIAG_KIND_CONTINUE_OUTSIDE_LOOP, &n->dbg, "`continue` outside of loop"); return 0; } if (lbl) { bool found = false; for (usize i = vec_size(sema->label_stack); i > 0; --i) { const char *cur = vec_at(sema->label_stack, i - 1); if (cur && strcmp(cur, lbl) == 0) { found = true; break; } } if (!found) { spl_diag_error(sema->diag, SPL_DIAG_KIND_UNDEFINED_LABEL, &n->dbg, "undefined label `$%s`", lbl); } } return 0; } case SPL_AST_STATEMENT_DEFER: if (n->statement_defer.body) sema_infer(sema, n->statement_defer.body); return 0; case SPL_AST_STATEMENT_COMPTIME: if (n->statement_comptime.body) sema_infer(sema, n->statement_comptime.body); return 0; case SPL_AST_STATEMENT_DECL: if (n->statement_decl.decl) sema_infer(sema, n->statement_decl.decl); return 0; case SPL_AST_STATEMENT_EXPR: if (n->statement_expr.expr) sema_infer(sema, n->statement_expr.expr); return 0; case SPL_AST_TOP_LEVEL_BLOCK: if (n->top_level_block.block) sema_infer(sema, n->top_level_block.block); return 0; default: return 0; } return 0; } void spl_sema_init(spl_sema_t *sema, spl_ast_t *ast, spl_lang_type_t *type, spl_layout_t *layout, spl_scope_t *scope, spl_attr_t *attr) { Assert(sema != NULL && ast != NULL && type != NULL && scope != NULL && attr != NULL); sema->ast = ast; sema->type = type; sema->layout = layout; sema->scope = scope; sema->current_fn_ret_tid = 0; sema->current_aggregate_lang = 0; sema->current_loop_break_tid = 0; sema->attr = attr; sema->root = (spl_symbol_t){0}; vec_init(sema->label_stack); sema->loader = NULL; sema->diag = NULL; } void spl_sema_drop(spl_sema_t *sema) { vec_free(sema->label_stack); } void spl_sema_run_module(spl_sema_t *sema, spl_ast_node_ref_t module_ref) { if (!sema->ast || !module_ref) return; spl_ast_node_t *mod = spl_ast_node(sema->ast, module_ref); if (!mod || mod->kind != SPL_AST_CONTAINER_MEMBERS) return; spl_scope_id_t saved_scope = sema->scope->current_scope; spl_lang_type_id_t saved_agg = sema->current_aggregate_lang; sema_collect_names(sema, module_ref); sema_collect_type_unpacks(sema, module_ref); spl_lang_type_id_t container_lang = 0; if (mod->resolved_def_id) container_lang = sema_eval_decl(sema, mod->resolved_def_id); sema->current_aggregate_lang = container_lang; sema_collect_types(sema, module_ref); sema_collect_signatures(sema); sema_infer(sema, module_ref); sema->current_aggregate_lang = saved_agg; sema->scope->current_scope = saved_scope; } void spl_sema_run(spl_sema_t *sema) { if (!sema->ast || !sema->ast->root) return; spl_ast_node_t *root = spl_ast_node(sema->ast, sema->ast->root); if (!root || root->kind != SPL_AST_ROOT) return; spl_sema_run_module(sema, root->root.members); } typedef struct { const char *key; spl_symbol_t sym; } spl_sema_sym_t; static int spl_sema_sym_cmp(const void *a, const void *b) { return strcmp(((const spl_sema_sym_t *)a)->key, ((const spl_sema_sym_t *)b)->key); } void spl_sema_dump(spl_sema_t *sema) { printf("Scopes:\n"); for (usize i = 1; i < vec_size(sema->scope->scopes); ++i) { spl_scope_node_t *scope = &vec_at(sema->scope->scopes, i); Assert(scope != NULL); printf("scope[%zu] parent=%zu\n", i, scope->parent); /* 按符号名排序输出:与哈希表内部布局解耦,保证确定顺序。 */ usize nsyms = scope->symbols.size; spl_sema_sym_t *arr = nsyms ? malloc(nsyms * sizeof(*arr)) : NULL; usize k = 0; map_for(scope->symbols, j) { Assert(arr != NULL); arr[k].key = unsafe_map_at(scope->symbols, j).key; arr[k].sym = unsafe_map_at(scope->symbols, j).val; k++; } if (arr && nsyms > 1) qsort(arr, nsyms, sizeof(*arr), spl_sema_sym_cmp); for (k = 0; k < nsyms; ++k) { Assert(arr != NULL); printf(" %s -> def`%s`#%zu\n", arr[k].key, arr[k].sym.name, arr[k].sym.node); } free(arr); } printf("LangTypeTable:\n"); for (usize i = 1; i < vec_size(sema->type->lang_table); ++i) { printf(" lang_id#%zu ", i); spl_lang_type_dump(sema->type, i); printf("\n"); } printf("TypeTable:\n"); for (usize i = 1; i < vec_size(sema->type->ir.type_table); ++i) { printf(" type_id#%zu ", i); spl_type_pure_dump(&sema->type->ir, i); printf("\n"); } printf("DefTable:\n"); for (usize i = 1; i < vec_size(sema->type->def_table); ++i) { printf(" def#%zu ", i); spl_type_def_dump(sema->type, i); printf("\n"); } spl_attr_dump(sema->attr); }