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