Files
spl/stage1/spl_sema.c
T

2377 lines
90 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 bool sema_is_infer(spl_sema_t *sema, spl_lang_type_id_t tid) {
spl_lang_type_node_t *n = spl_lang_type_node(sema->type, tid);
return n && n->kind == SPL_LANG_TYPE_INFER;
}
static 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 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 =
spl_def_alloc(sema->type, SPL_DEF_FN, mn->fn_decl.name, mn->dbg, sema->ast, mref);
mn->resolved_def_id = fn_def;
spl_def_node_t *fn_d = spl_def_node(sema->type, fn_def);
if (fn_d)
fn_d->owner_def = owner_def_id;
spl_lang_method_t meth = {.name = mn->fn_decl.name, .def_id = fn_def};
vec_push(methods, meth);
} else if (mn->kind == SPL_AST_TYPE_DECL) {
spl_def_id_t tdef = mn->resolved_def_id;
if (!tdef)
continue;
spl_lang_method_t meth = {.name = mn->type_decl.name, .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_lang_method_t meth = {.name = d->type_decl.name, .def_id = tdef};
vec_push(methods, meth);
} else if (d->kind == SPL_AST_VAR_DECL || d->kind == SPL_AST_CONST_DECL) {
spl_def_id_t vdef = d->resolved_def_id;
if (!vdef)
continue;
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_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: {
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_symbol_t sym = {.name = n->type_decl.name, .kind = SPL_SYMBOL_KIND_TYPE};
spl_ast_node_t *te = spl_ast_node(sema->ast, n->type_decl.type_expr);
if (!te) {
spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg,
"type declaration `%s` missing type expression", n->type_decl.name);
return;
}
if (te->kind == SPL_AST_TYPE_AGGREGATE)
sym.node = spl_def_alloc(sema->type, SPL_DEF_AGG, sym.name, te->dbg, sema->ast, ref);
else
sym.node = spl_def_alloc(sema->type, SPL_DEF_SCALAR, sym.name, te->dbg, sema->ast, ref);
n->resolved_def_id = sym.node;
if (!spl_scope_insert(sema->scope, sema->scope->current_scope, sym)) {
spl_diag_error(sema->diag, SPL_DIAG_KIND_DUPLICATE_SYMBOL, &n->dbg,
"duplicate symbol `%s`", sym.name);
}
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) {
const char *name = pat->pattern_ident.name;
spl_symbol_t sym = {.name = name, .kind = SPL_SYMBOL_KIND_VAR};
sym.node = spl_def_alloc(sema->type, SPL_DEF_VAR, name, n->dbg, sema->ast, ref);
n->resolved_def_id = sym.node;
if (!spl_scope_insert(sema->scope, sema->scope->current_scope, sym)) {
spl_diag_error(sema->diag, SPL_DIAG_KIND_DUPLICATE_SYMBOL, &n->dbg,
"duplicate symbol `%s`", name);
}
} else {
spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg,
"unsupported variable pattern (only simple identifier)");
}
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 && 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);
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 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;
switch (pat->kind) {
case SPL_AST_PATTERN_IDENT: {
const char *name = pat->pattern_ident.name;
spl_symbol_t sym = {.name = name, .kind = SPL_SYMBOL_KIND_VAR};
sym.node = spl_def_alloc(sema->type, SPL_DEF_VAR, name, 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_TUPLE: {
spl_lang_type_node_t *tn = spl_lang_type_node(sema->type, matched_tid);
if (!tn || tn->kind != SPL_LANG_TYPE_TUPLE) {
spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg,
"tuple pattern requires a tuple value");
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;
}
/* 递归 bind 会 push lang_table,tn 会悬垂;先把元素类型拷到本地 */
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:
case SPL_AST_PATTERN_AGGREGATE:
default:
spl_diag_error(sema->diag, SPL_DIAG_KIND_UNHANDLED_NODE, &pat->dbg,
"pattern kind %d not yet supported", (int)pat->kind);
return;
}
}
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: {
sema_infer(sema, n->ifvar_expr.var_decl);
if (n->ifvar_expr.then_block)
sema_infer(sema, n->ifvar_expr.then_block);
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);
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);
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);
for (usize i = 0; i < nsrc; ++i) {
spl_lang_type_id_t container_tid =
sema_infer(sema, vec_at(n->for_expr.iter_exprs, i));
spl_lang_type_id_t elem_tid = sema_for_elem_type(sema, container_tid);
if (i < natoms)
sema_bind_pattern(sema, vec_at(pat->pattern_list.patterns, i), elem_tid);
}
if (natoms > nsrc) {
spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg,
"for: pattern has %zu atoms but only %zu sources", natoms, nsrc);
}
} else {
for (usize i = 0; i < nsrc; ++i) {
spl_lang_type_id_t container_tid =
sema_infer(sema, vec_at(n->for_expr.iter_exprs, i));
if (i == 0 && pat_ref) {
spl_lang_type_id_t elem_tid = sema_for_elem_type(sema, container_tid);
sema_bind_pattern(sema, pat_ref, elem_tid);
}
}
if (nsrc > 1 && pat_ref) {
spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg,
"for: multiple sources require pattern list");
}
}
if (n->for_expr.body)
sema_infer(sema, n->for_expr.body);
if (n->for_expr.else_branch)
sema_infer(sema, n->for_expr.else_branch);
sema_pop_scope(sema);
vec_pop(sema->label_stack);
return 0;
}
case SPL_AST_EXPR_MATCH: {
sema_infer(sema, n->match_expr.matched_expr);
for (usize i = 0; i < vec_size(n->match_expr.arms); ++i) {
spl_ast_node_t *arm = spl_ast_node(sema->ast, vec_at(n->match_expr.arms, i));
if (arm && arm->kind == SPL_AST_MATCH_ARM) {
sema_infer(sema, arm->match_arm.pattern);
sema_infer(sema, arm->match_arm.body);
}
}
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: {
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: {
spl_ast_node_t *pat = spl_ast_node(sema->ast, n->var_const_decl.pattern);
if (!pat || pat->kind != SPL_AST_PATTERN_IDENT) {
spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg,
"unsupported variable pattern");
return 0;
}
const char *name = pat->pattern_ident.name;
if (n->resolved_def_id == 0) {
spl_symbol_t sym = {.name = name, .kind = SPL_SYMBOL_KIND_VAR};
sym.node = spl_def_alloc(sema->type, SPL_DEF_VAR, name, n->dbg, sema->ast, ref);
n->resolved_def_id = sym.node;
if (!spl_scope_insert(sema->scope, sema->scope->current_scope, sym)) {
spl_diag_error(sema->diag, SPL_DIAG_KIND_DUPLICATE_SYMBOL, &n->dbg,
"duplicate symbol `%s`", name);
}
}
spl_lang_type_id_t decl_tid = 0;
if (n->var_const_decl.type)
decl_tid = sema_infer(sema, n->var_const_decl.type);
if (!n->var_const_decl.init_expr) {
spl_diag_error(sema->diag, SPL_DIAG_KIND_VAR_UNINITIALIZED, &n->dbg,
"variable `%s` must be initialized", name);
return 0;
}
if (decl_tid) {
sema_coerce(sema, n->var_const_decl.init_expr, decl_tid);
} else {
decl_tid = sema_infer(sema, n->var_const_decl.init_expr);
if (!decl_tid || sema_is_infer(sema, decl_tid)) {
spl_diag_error(sema->diag, SPL_DIAG_KIND_CANNOT_INFER, &n->dbg,
"cannot infer type of `%s`", name);
return 0;
}
}
spl_def_resolve(sema->type, n->resolved_def_id, decl_tid);
n->resolved_lang_tid = decl_tid;
return decl_tid;
}
case SPL_AST_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);
}