Files
spl/stage1/spl_sema.c
T

2662 lines
101 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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, "<none>");
return;
}
spl_lang_type_node_t *n = spl_lang_type_node(sema->type, tid);
if (!n) {
snprintf(buf, cap, "<invalid lang#%zu>", 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;
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(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);
} else 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);
} 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);
}
}
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);
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;
}
/* 文件 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)
t = sema_eval_decl(sema, cm_after->resolved_def_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);
} else {
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_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);
if (d->kind == SPL_DEF_FN && d->state == SPL_DECL_UNRESOLVED)
sema_eval_decl(sema, i);
}
}
static void sema_collect(spl_sema_t *sema, spl_ast_node_ref_t ref) {
sema_collect_names(sema, ref);
sema_collect_types(sema, ref);
sema_collect_signatures(sema);
}
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_SIZEOF:
case SPL_INTRIN_ALIGNOF: {
sema_infer(sema, vec_at(*args, 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_INT_FROM_FLOAT:
case SPL_INTRIN_FLOAT_FROM_INT:
case SPL_INTRIN_PTR_FROM_INT:
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_INT_FROM_BOOL:
case SPL_INTRIN_INT_FROM_PTR: {
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;
}
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_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 且 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,
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;
}
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))
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_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;
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_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->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 = sema->scope->current_scope;
sema_collect(sema, module_ref);
sema_infer(sema, module_ref);
sema->scope->current_scope = saved;
}
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);
spl_ast_node_t *cm = spl_ast_node(sema->ast, root->root.members);
if (cm && cm->resolved_def_id)
sema_eval_decl(sema, cm->resolved_def_id);
}
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);
printf("scope[%zu] parent=%zu\n", i, scope->parent);
map_for(scope->symbols, j) {
spl_symbol_t *symbol = &unsafe_map_at(scope->symbols, j).val;
printf(" %s -> def`%s`#%zu\n", unsafe_map_at(scope->symbols, j).key, symbol->name,
symbol->node);
}
}
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);
}