Files
spl/stage1/spl_sema.c
T

2028 lines
75 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_type.h"
void spl_scope_init(spl_scope_t *scope) {
vec_init(scope->scopes);
vec_push(scope->scopes, (spl_scope_node_t){0});
scope->current_scope = 0;
scope->root_scope = 0;
}
void spl_scope_drop(spl_scope_t *scope) {
for (usize i = 0; i < vec_size(scope->scopes); ++i) {
map_free(vec_at(scope->scopes, i).symbols);
}
vec_free(scope->scopes);
scope->current_scope = 0;
scope->root_scope = 0;
}
spl_scope_id_t spl_scope_alloc(spl_scope_t *scope) {
spl_scope_node_t n = {0};
n.parent = scope->current_scope;
map_init(n.symbols, MAP_HASH_STR, MAP_CMP_STR);
vec_push(scope->scopes, n);
return vec_size(scope->scopes) - 1;
}
bool spl_scope_insert(spl_scope_t *scope, spl_scope_id_t id, spl_symbol_t symbol) {
if (!id || id >= vec_size(scope->scopes)) {
LOG_FATAL("sema scope insert error");
return false;
}
spl_scope_node_t *node = &vec_at(scope->scopes, id);
spl_symbol_t old = {0};
if (map_get(node->symbols, symbol.name, &old)) {
SPL_ERROR(&(spl_dbg_node_t){0}, "duplicate symbol `%s`", symbol.name);
return false;
}
map_put(node->symbols, symbol.name, symbol);
return true;
}
bool spl_scope_find(spl_scope_t *scope, const char *name, spl_symbol_t *out) {
if (!scope || !name || !out)
return false;
for (spl_scope_id_t id = scope->current_scope; id != 0; id = vec_at(scope->scopes, id).parent) {
spl_scope_node_t *s = &vec_at(scope->scopes, id);
if (map_get(s->symbols, name, out)) {
return true;
}
}
return false;
}
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_ERROR(&a->dbg, "unknown attribute `%s`", a->tag_item.name);
sema->error_count++;
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_ERROR(&a->dbg, "attribute `%s` expects %d..%d args, got %zu", info->name,
info->min_args, info->max_args, nargs);
sema->error_count++;
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_ERROR(&a->dbg, "attribute arg #%zu has unsupported form", j);
sema->error_count++;
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 bool sema_is_literal(spl_ast_node_t *n) {
switch (n->kind) {
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:
case SPL_AST_EXPR_UNDEFINED_LIT:
return true;
default:
return false;
}
}
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, 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->store_scope = sema->scope->current_scope;
sema->scope->current_scope = new_scope;
}
static inline void sema_pop_scope(spl_sema_t *sema) {
sema->scope->current_scope = sema->store_scope;
}
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;
}
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_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 = 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, mref);
mn->resolved_def_id = fn_def;
spl_def_node_t *fn_d = spl_def_node(sema->type, fn_def);
if (fn_d)
fn_d->owner_def = owner_def_id;
spl_lang_method_t meth = {.name = mn->fn_decl.name, .def_id = fn_def};
vec_push(methods, meth);
}
}
spl_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_type_layout_t layout = {.mode = SPL_TYPE_LAYOUT_AUTO};
spl_type_id_t agg_ir = spl_type_builder_agg(sema->type, tk, &members,
spl_type_builder_int(sema->type, 32, 1), layout);
vec_free(members);
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_lang_type_node_t ln = {0};
ln.kind = lk;
ln.ir_type_id = agg_ir;
ln.methods = methods;
return spl_lang_node_push(sema->type, &ln);
}
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;
if (def->lang_id != 0) {
def->state = SPL_DECL_RESOLVED;
return def->lang_id;
}
if (def->state == SPL_DECL_RESOLVING) {
SPL_ERROR(&def->dbg_node, "circular dependency on `%s`", def->name ? def->name : "?");
sema->error_count++;
def->state = SPL_DECL_ERROR;
return 0;
}
def->state = SPL_DECL_RESOLVING;
spl_ast_node_t *n = spl_ast_node(sema->ast, def->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 (def->owner_def) {
spl_def_node_t *owner = spl_def_node(sema->type, def->owner_def);
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;
}
default:
break;
}
}
spl_def_node_t *final_def = spl_def_node(sema->type, def_id);
if (final_def) {
final_def->lang_id = result;
final_def->state = result ? SPL_DECL_RESOLVED : SPL_DECL_ERROR;
}
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: {
/* root_scope 只在第一次创建,后续 module 复用 */
if (sema->scope->root_scope == 0) {
spl_scope_id_t root = spl_scope_alloc(sema->scope);
sema->scope->root_scope = root;
}
/* 进入 root_scope,再为当前 module 开一个子 scope */
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, 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));
}
/* 故意不恢复 current_scope
保持 mod_scope 交给随后的 sema_parse 使用。
恢复由 spl_sema_run_module 统一负责 */
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_ERROR(&n->dbg, "type declaration `%s` missing type expression", n->type_decl.name);
sema->error_count++;
return;
}
if (te->kind == SPL_AST_TYPE_AGGREGATE)
sym.node = spl_def_alloc(sema->type, SPL_DEF_AGG, sym.name, te->dbg, ref);
else
sym.node = spl_def_alloc(sema->type, SPL_DEF_SCALAR, sym.name, te->dbg, ref);
n->resolved_def_id = sym.node;
spl_scope_insert(sema->scope, sema->scope->current_scope, sym);
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, ref);
n->resolved_def_id = sym.node;
spl_scope_insert(sema->scope, sema->scope->current_scope, sym);
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, ref);
n->resolved_def_id = sym.node;
spl_scope_insert(sema->scope, sema->scope->current_scope, sym);
} else {
SPL_ERROR(&n->dbg, "unsupported variable pattern (only simple identifier)");
sema->error_count++;
}
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_ERROR(&n->dbg, "`%s` expects %d..%d args, got %zu", info->name, info->min_args,
info->max_args, nargs);
sema->error_count++;
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);
}
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_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, an->ir_type_id) : NULL;
/* comptime 与 infer 都属于"可重定型":清空 resolved 后按 expected 重新推导 */
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_ERROR(&n->dbg, "type mismatch: node kind=%d already typed as %s, expected %s",
(int)n->kind, gbuf, ebuf);
sema->error_count++;
return 0;
}
/* 清空 resolved,走未定型路径,让递归按 expected 下推子节点 */
n->resolved_lang_tid = 0;
}
/* 未定型的构造性节点 */
switch (n->kind) {
case SPL_AST_EXPR_UNARY: {
/* & 与 .* 的结果类型由操作数决定,不能靠 expected 反向定型 */
if (n->unary.op == SPL_UNARY_OP_ADDRESS || n->unary.op == SPL_UNARY_OP_DEREF)
return sema_infer(sema, ref);
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_ERROR(&n->dbg, "comparison operands type mismatch: lhs=%s rhs=%s", lbuf, rbuf);
sema->error_count++;
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;
}
/* ptr +/- int:expected 是 ptr 且左侧推断为 ptr。
* 右侧保留原整型;仅 comptime_int(bits==0)收紧到 isize。 */
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_ERROR(&n->dbg, "pointer offset must be integer");
sema->error_count++;
return 0;
}
spl_type_node_t *rn_ir = spl_type_node(sema->type, 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_ERROR(&n->dbg, "tuple arity mismatch (expected %zu, got %zu)", ntypes, nelems);
sema->error_count++;
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;
/* 有名字时,名字必须与 expected 指向同一类型 */
if (n->aggregate_literal.name) {
spl_symbol_t sym;
if (!spl_scope_find(sema->scope, n->aggregate_literal.name, &sym)) {
SPL_ERROR(&n->dbg, "unknown type `%s`", n->aggregate_literal.name);
sema->error_count++;
return 0;
}
spl_def_node_t *def = spl_def_node(sema->type, sym.node);
if (!def || def->lang_id != expected) {
SPL_ERROR(&n->dbg, "aggregate `%s` does not match expected type",
n->aggregate_literal.name);
sema->error_count++;
return 0;
}
}
spl_type_id_t agg_ir = spl_lang_type_get_ir_type(sema->type, expected);
spl_type_node_t *irn = spl_type_node(sema->type, agg_ir);
if (!irn) {
SPL_ERROR(&n->dbg, "aggregate type unresolved");
sema->error_count++;
return 0;
}
if (en->kind == SPL_LANG_TYPE_ENUM) {
if (vec_size(n->aggregate_literal.fields) != 1) {
SPL_ERROR(&n->dbg, "enum literal must have exactly one variant");
sema->error_count++;
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_ERROR(&n->dbg, "invalid enum literal");
sema->error_count++;
return 0;
}
usize idx = 0;
if (!spl_type_agg_member_idx(sema->type, agg_ir, item->struct_init_item.name, &idx)) {
SPL_ERROR(&item->dbg, "no variant `%s`", item->struct_init_item.name);
sema->error_count++;
return 0;
}
usize field_tid_ir = vec_at(irn->agg_type.variants, idx).type_id;
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))
return 0;
}
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, agg_ir, item->struct_init_item.name, &idx)) {
SPL_ERROR(&item->dbg, "no field `%s`", item->struct_init_item.name);
sema->error_count++;
continue;
}
usize field_tid_ir = vec_at(irn->agg_type.variants, idx).type_id;
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))
return 0;
}
}
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_BLOCK: {
for (usize i = 0; i < vec_size(n->block.stmts); ++i)
sema_infer(sema, vec_at(n->block.stmts, i));
if (n->block.tail_expr) {
spl_lang_type_id_t t = sema_coerce(sema, n->block.tail_expr, expected);
if (!t)
return 0;
n->resolved_lang_tid = t;
return t;
}
spl_lang_type_id_t u = spl_lang_make_unit(sema->type);
n->resolved_lang_tid = u;
return u;
}
default:
break;
}
/* 兜底:infer 后按子类型 / comptime 判定 */
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, from_n->ir_type_id) : NULL;
spl_type_node_t *to_ir = to_n ? spl_type_node(sema->type, 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_ERROR(&n->dbg, "comptime value does not fit target type");
sema->error_count++;
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_ERROR(&n->dbg, "type mismatch: expect %s, got %s (node kind=%d)", ebuf, gbuf, (int)n->kind);
sema->error_count++;
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;
}
/* ---------- IDENT:同时写 def_id 和 lang_tid 副本 ---------- */
case SPL_AST_EXPR_IDENT: {
const char *name = n->ident.name;
spl_symbol_t sym;
if (!spl_scope_find(sema->scope, name, &sym)) {
SPL_ERROR(&n->dbg, "undefined identifier `%s`", name);
sema->error_count++;
return 0;
}
if (sym.node == 0) {
SPL_ERROR(&n->dbg, "identifier `%s` has no definition", name);
sema->error_count++;
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;
}
/* ---------- UNARY ---------- */
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_ERROR(&n->dbg, "unary minus requires numeric operand");
sema->error_count++;
return 0;
}
n->resolved_lang_tid = op;
return op;
case SPL_UNARY_OP_BIT_NOT:
if (tn->kind != SPL_LANG_TYPE_INT) {
SPL_ERROR(&n->dbg, "bitwise not requires integer operand");
sema->error_count++;
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_ERROR(&n->dbg, "deref requires pointer operand");
sema->error_count++;
return 0;
}
spl_lang_type_id_t elem = tn->extra.elem;
n->resolved_lang_tid = elem;
return elem;
}
default:
UNREACHABLE();
}
}
/* ---------- BINARY ---------- */
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_ERROR(&n->dbg, "comparison operands type mismatch: lhs=%s rhs=%s", lbuf, rbuf);
sema->error_count++;
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;
}
/* Range */
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_ERROR(&n->dbg, "shift requires integer operands");
sema->error_count++;
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);
/* ptr +/- int 特判:右侧保留自己的整型;
* 仅当右操作数是 comptime_int(bits==0)时收紧到 isize,
* 避免 comptime 类型泄漏到 IR。 */
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, 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_ERROR(&n->dbg, "operand types cannot be joined: lhs=%s rhs=%s", lbuf, rbuf);
sema->error_count++;
return 0;
}
sema_coerce(sema, n->binary.left, common);
sema_coerce(sema, n->binary.right, common);
n->resolved_lang_tid = common;
return common;
}
/* ---------- CALL ---------- */
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_ERROR(&n->dbg, "callee is not a function");
sema->error_count++;
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_ERROR(&n->dbg, "too few arguments (expected at least %zu)", fixed);
sema->error_count++;
}
} else if (nargs != fixed) {
SPL_ERROR(&n->dbg, "argument count mismatch (expected %zu, got %zu)", fixed, nargs);
sema->error_count++;
}
/* 固定参数:check 到形参类型 */
for (usize i = 0; i < nargs && i < fixed; ++i) {
spl_lang_type_id_t pt =
vec_at(spl_lang_type_node(sema->type, callee_tid)->extra.fn.params, i);
sema_coerce(sema, vec_at(n->call.args, i), pt);
}
/* varargs:默认提升到 isize */
if (has_varargs) {
spl_lang_type_id_t vt = spl_lang_make_int(sema->type, sizeof(void *) * 8, 1);
for (usize i = fixed; i < nargs; ++i)
sema_coerce(sema, vec_at(n->call.args, i), vt);
}
spl_lang_type_id_t ret = fn->extra.fn.ret;
n->resolved_lang_tid = ret;
return ret;
}
/* ---------- FIELD ---------- */
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_ERROR(&n->dbg, "field access on invalid type");
sema->error_count++;
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, agg_ir, n->field.field);
if (m) {
spl_lang_type_id_t ft = sema_lang_from_ir(sema, m->type_id);
if (!ft) {
SPL_ERROR(&n->dbg, "cannot resolve field type");
sema->error_count++;
return 0;
}
n->resolved_lang_tid = ft;
return ft;
}
}
for (usize i = 0; i < vec_size(agg->methods); ++i) {
spl_lang_method_t *meth = &vec_at(agg->methods, i);
if (strcmp(meth->name, n->field.field) != 0)
continue;
n->resolved_def_id = meth->def_id;
spl_lang_type_id_t t = sema_eval_decl(sema, meth->def_id);
n->resolved_lang_tid = t;
return t;
}
SPL_ERROR(&n->dbg, "no field `%s`", n->field.field);
sema->error_count++;
return 0;
}
/* ---------- INDEX ---------- */
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_ERROR(&n->dbg, "tuple index must be an integer literal");
sema->error_count++;
return 0;
}
isize i = (isize)idx_node->int_lit.bits;
if (i < 0 || (usize)i >= vec_size(obj->extra.elems)) {
SPL_ERROR(&n->dbg, "tuple index out of range");
sema->error_count++;
return 0;
}
elem = vec_at(obj->extra.elems, (usize)i);
break;
}
default:
SPL_ERROR(&n->dbg, "index base must be array/slice/ptr/range/tuple");
sema->error_count++;
return 0;
}
n->resolved_lang_tid = elem;
return elem;
}
/* ---------- SLICE ---------- */
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_ERROR(&n->dbg, "slice base must be array/ptr/slice");
sema->error_count++;
return 0;
}
spl_lang_type_id_t s = spl_lang_make_slice(sema->type, elem);
n->resolved_lang_tid = s;
return s;
}
/* ---------- CAST ---------- */
case SPL_AST_EXPR_CAST: {
spl_lang_type_id_t dst = sema_infer(sema, n->cast.type);
if (!dst)
return 0;
spl_lang_type_id_t src = sema_infer(sema, n->cast.expr);
if (!src)
return 0;
/* 仅当 src 是 comptime 时收紧到 dst;具体类型间的 as 直接放行(截断/扩展由 ast2ir 生成) */
spl_lang_type_node_t *sn = spl_lang_type_node(sema->type, src);
spl_type_node_t *sir = sn ? spl_type_node(sema->type, sn->ir_type_id) : NULL;
bool src_comptime =
(sn && sn->kind == SPL_LANG_TYPE_INT && sir && sir->int_type.bits == 0) ||
(sn && sn->kind == SPL_LANG_TYPE_FLOAT && sir && sir->float_type.bits == 0);
if (src_comptime) {
if (!sema_coerce(sema, n->cast.expr, dst))
return 0;
}
if (sn && !(sn->kind == SPL_LANG_TYPE_INT || sn->kind == SPL_LANG_TYPE_FLOAT ||
sn->kind == SPL_LANG_TYPE_BOOL || sn->kind == SPL_LANG_TYPE_PTR)) {
SPL_ERROR(&n->dbg, "invalid cast from non-primitive type");
sema->error_count++;
return 0;
}
n->resolved_lang_tid = dst;
return dst;
}
/* ---------- TUPLE ---------- */
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;
}
/* ---------- ARRAY_LIT ---------- */
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;
}
/* ---------- AGGREGATE_LIT ---------- */
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_ERROR(&n->dbg, "unknown type `%s`", type_name);
sema->error_count++;
return 0;
}
spl_def_node_t *def = spl_def_node(sema->type, sym.node);
if (!def || !def->lang_id) {
SPL_ERROR(&n->dbg, "type `%s` not resolved", type_name);
sema->error_count++;
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_ERROR(&n->dbg, "`%s` is not a struct/union/enum", type_name);
sema->error_count++;
return 0;
}
if (an->kind == SPL_LANG_TYPE_ENUM && vec_size(n->aggregate_literal.fields) != 1) {
SPL_ERROR(&n->dbg, "enum literal must have exactly one variant");
sema->error_count++;
return 0;
}
spl_type_id_t agg_ir = spl_lang_type_get_ir_type(sema->type, agg);
spl_type_node_t *irn = spl_type_node(sema->type, agg_ir);
if (irn) {
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, agg_ir, item->struct_init_item.name,
&idx)) {
SPL_ERROR(&item->dbg, "no field `%s`", item->struct_init_item.name);
sema->error_count++;
continue;
}
usize field_tid_ir = vec_at(irn->agg_type.variants, idx).type_id;
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);
}
}
n->resolved_lang_tid = agg;
return agg;
}
/* ---------- COMPTIME_CALL ---------- */
case SPL_AST_EXPR_COMPTIME_CALL: {
spl_intrin_t in = spl_intrin_lookup_call(n->comptime_call.name);
if (in == SPL_INTRIN_NONE) {
SPL_ERROR(&n->dbg, "unknown comptime call `@%s`", n->comptime_call.name);
sema->error_count++;
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;
}
/* ---------- BLOCK ---------- */
case SPL_AST_EXPR_BLOCK: {
spl_lang_type_id_t last = 0;
for (usize i = 0; i < vec_size(n->block.stmts); ++i)
last = sema_infer(sema, vec_at(n->block.stmts, i));
if (n->block.tail_expr)
last = sema_infer(sema, n->block.tail_expr);
n->resolved_lang_tid = last;
return last;
}
/* ---------- IF ---------- */
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;
}
/* ---------- IFVAR / WHILE / LOOP / FOR / MATCH ---------- */
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);
for (usize i = 0; i < vec_size(n->for_expr.iter_exprs); ++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 < vec_size(n->for_expr.idents)) {
spl_ast_node_t *id_node = spl_ast_node(sema->ast, vec_at(n->for_expr.idents, i));
if (id_node && id_node->kind == SPL_AST_EXPR_IDENT) {
const char *var_name = id_node->ident.name;
if (strcmp(var_name, "_") != 0) {
spl_symbol_t sym = {.name = var_name, .kind = SPL_SYMBOL_KIND_VAR};
sym.node = spl_def_alloc(sema->type, SPL_DEF_VAR, var_name, id_node->dbg,
vec_at(n->for_expr.idents, i));
spl_def_resolve(sema->type, sym.node, elem_tid);
spl_scope_insert(sema->scope, sema->scope->current_scope, sym);
id_node->resolved_def_id = sym.node;
id_node->resolved_lang_tid = elem_tid;
}
}
}
}
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;
}
/* ---------- FN_TYPE ---------- */
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_ERROR(&n->dbg, "type `%s` not found", name);
sema->error_count++;
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_ERROR(&n->dbg, "`@self` outside of aggregate");
sema->error_count++;
return 0;
}
n->resolved_lang_tid = sema->current_aggregate_lang;
return sema->current_aggregate_lang;
}
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,
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;
spl_scope_insert(sema->scope, sema->scope->current_scope, psym);
}
}
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;
}
/* ---------- VAR / CONST ---------- */
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_ERROR(&n->dbg, "unsupported variable pattern");
sema->error_count++;
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, ref);
n->resolved_def_id = sym.node;
spl_scope_insert(sema->scope, sema->scope->current_scope, sym);
}
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_ERROR(&n->dbg, "variable `%s` must be initialized", name);
sema->error_count++;
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_ERROR(&n->dbg, "cannot infer type of `%s`", name);
sema->error_count++;
return 0;
}
}
spl_def_resolve(sema->type, n->resolved_def_id, decl_tid);
n->resolved_lang_tid = decl_tid;
return decl_tid;
}
/* ---------- MEMBER / PARAM ---------- */
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_ERROR(&n->dbg, "function must return a value");
sema->error_count++;
}
}
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_ERROR(&n->dbg, "`break` outside of loop");
sema->error_count++;
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_ERROR(&n->dbg, "undefined label `$%s`", lbl);
sema->error_count++;
}
}
if (n->statement_break.expr)
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_ERROR(&n->dbg, "`continue` outside of loop");
sema->error_count++;
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_ERROR(&n->dbg, "undefined label `$%s`", lbl);
sema->error_count++;
}
}
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_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->error_count = 0;
sema->current_fn_ret_tid = 0;
sema->current_aggregate_lang = 0;
sema->attr = attr;
sema->root = (spl_symbol_t){0};
sema->store_scope = 0;
vec_init(sema->label_stack);
}
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);
}
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->type_table); ++i) {
printf(" type_id#%zu ", i);
spl_type_pure_dump(sema->type, 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);
}