3088 lines
103 KiB
Plaintext
3088 lines
103 KiB
Plaintext
// Expect retcode = 0
|
|
|
|
// 语义分析:名字/类型/def 解析与推断。持有类型表、作用域树、属性表;AST 与诊断
|
|
// 为借用。comptime 求值经 eval seam 注入。
|
|
|
|
type .{ .Error, .Status } = @import("utils/error.spl");
|
|
type .{ .Vec } = @import("utils/vec.spl");
|
|
type .{ .Map, .hash_kind_usize } = @import("utils/map.spl");
|
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
|
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
|
|
type .{ .append_usize } = @import("utils/fmt.spl");
|
|
type BigInt = @import("utils/mp.spl");
|
|
type Ast = @import("./ast.spl");
|
|
type .{ .Node } = @import("./ast.spl");
|
|
type .{ .Span, .span_none, .Code, .no_file } = @import("./diag.spl");
|
|
type Diag = @import("./diag.spl");
|
|
type .{ .Sources, .set_at } = @import("utils/source.spl");
|
|
type LangType = @import("./lang_type.spl");
|
|
type .{ .LangKind, .LangEntry, .LangMethod, .LangExtra } = @import("./lang_type.spl");
|
|
type TypeTable = @import("./type_table.spl");
|
|
type .{ .TypeNode, .TypeEntry, .Member, .AggKind, .LayoutMode } = @import("./type_table.spl");
|
|
type .{ .DefTable, .DefKind, .DefNode, .DeclState } = @import("./def.spl");
|
|
type .{ .Scope, .Symbol, .SymbolKind } = @import("./scope.spl");
|
|
type .{ .Attr, .AttrArg, .AttrArgKind } = @import("./attr.spl");
|
|
type .{ .Intrin, .IntrinInfo, .IntrinForm, .IntrinResult, .IntrinOps } = @import("./intrin.spl");
|
|
type .{ .Eval, .EvalOutcome, .EvalValue, .DeclSink } = @import("./eval.spl");
|
|
type .{ .hash_usize, .eq_usize } = @import("utils/hash.spl");
|
|
type .{ .Punct, .Token } = @import("./token.spl");
|
|
type .{ .Lexer } = @import("./lexer.spl");
|
|
type .{ .Parser } = @import("./parser.spl");
|
|
type .{ .set_new, .set_add, .set_drop } = @import("utils/source.spl");
|
|
|
|
// 模块加载接口(seam):由驱动提供;可为 null。
|
|
type LoadResult = enum {
|
|
Ok: *Ast,
|
|
Missing,
|
|
}
|
|
|
|
type Loader = struct {
|
|
load: fn(*any, ByteView, ByteView, *Diag): LoadResult,
|
|
user: *any,
|
|
}
|
|
|
|
type Sema = @self;
|
|
|
|
ast: *Ast, // 借用
|
|
diag: *Diag, // 借用
|
|
sources: *Sources, // 借用,可为 null(解析 import 的 from 文件名)
|
|
types: LangType,
|
|
scope: Scope,
|
|
attr: Attr,
|
|
eval: Eval,
|
|
loader: *Loader, // 借用,可为 null
|
|
current_aggregate_lang: usize,
|
|
current_fn_ret_tid: usize,
|
|
current_loop_break_tid: usize,
|
|
label_stack: Vec, // ByteView
|
|
ir_to_lang: Map, // ir_type_id -> lang_id 缓存(剪枝 lang_from_ir 的线性扫描)
|
|
|
|
type SemaResult = enum {
|
|
Ok: Sema,
|
|
Err: Error,
|
|
}
|
|
|
|
|
|
fn list_at(v: Vec, i: usize): usize {
|
|
if i >= v.len { ret 0; }
|
|
ret ((v.data as *usize) + i).*;
|
|
}
|
|
|
|
fn push_u(v: *Vec, x: usize) {
|
|
Vec.push(v, &x as *u8);
|
|
}
|
|
|
|
fn push_view(v: *Vec, x: ByteView) {
|
|
Vec.push(v, &x as *u8);
|
|
}
|
|
|
|
fn ref_vec(): Vec {
|
|
ret Vec.blank(@sizeof(usize), @alignof(usize));
|
|
}
|
|
|
|
fn ulist_vec(): Vec {
|
|
ret Vec.blank(@sizeof(usize), @alignof(usize));
|
|
}
|
|
|
|
fn node(self: *Sema, ref: usize): *Node {
|
|
ret Ast.node(self.ast, ref);
|
|
}
|
|
|
|
fn def_node(self: *Sema, id: usize): *DefNode {
|
|
ret LangType.def_node(&self.types, id);
|
|
}
|
|
|
|
fn ir_entry(self: *Sema, ir_id: usize): *TypeEntry {
|
|
ret TypeTable.node(&self.types.ir, ir_id);
|
|
}
|
|
|
|
fn ir_of(self: *Sema, lang_id: usize): usize {
|
|
ret LangType.get_ir_type(&self.types, lang_id);
|
|
}
|
|
|
|
fn lang_of(self: *Sema, id: usize): *LangEntry {
|
|
ret LangType.lang_node(&self.types, id);
|
|
}
|
|
|
|
fn kind_is(self: *Sema, lang_id: usize, want: LangKind): bool {
|
|
var e = LangType.lang_node(&self.types, lang_id);
|
|
if e == null { ret false; }
|
|
ret @enum2int(e.kind) == @enum2int(want);
|
|
}
|
|
|
|
fn is_infer(self: *Sema, tid: usize): bool {
|
|
ret kind_is(self, tid, .Infer);
|
|
}
|
|
|
|
fn lang_from_ir(self: *Sema, ir: usize): usize {
|
|
if ir == 0 { ret 0; }
|
|
|
|
var cached: usize = 0;
|
|
match Map.get(&self.ir_to_lang, &ir as *u8, &cached as *u8) {
|
|
.Found => { ret cached; },
|
|
.NotFound => {},
|
|
}
|
|
|
|
var i: usize = 1;
|
|
while i < self.types.lang_table.len {
|
|
var e = LangType.lang_node(&self.types, i);
|
|
if e != null && e.ir_type_id == ir {
|
|
Map.put(&self.ir_to_lang, &ir as *u8, &i as *u8);
|
|
ret i;
|
|
}
|
|
i = i + 1;
|
|
}
|
|
ret 0;
|
|
}
|
|
|
|
fn make_bool(self: *Sema): usize { ret LangType.make_bool(&self.types); }
|
|
fn make_unit(self: *Sema): usize { ret LangType.make_unit(&self.types); }
|
|
fn make_never(self: *Sema): usize { ret LangType.make_never(&self.types); }
|
|
fn make_any(self: *Sema): usize { ret LangType.make_any(&self.types); }
|
|
fn make_infer(self: *Sema): usize { ret LangType.make_infer(&self.types); }
|
|
fn ptr_bits(self: *Sema): usize { ret @sizeof(usize) * 8; }
|
|
fn make_usize(self: *Sema): usize { ret LangType.make_int(&self.types, ptr_bits(self), false); }
|
|
fn make_isize(self: *Sema): usize { ret LangType.make_int(&self.types, ptr_bits(self), true); }
|
|
|
|
fn def_is_type(self: *Sema, id: usize): bool {
|
|
ret LangType.def_is_type(&self.types, id);
|
|
}
|
|
|
|
fn def_state_is(d: *DefNode, want: DeclState): bool {
|
|
ret @enum2int(d.state) == @enum2int(want);
|
|
}
|
|
|
|
|
|
fn diag(self: *Sema, span: Span, code: Code, msg: []u8) {
|
|
Diag.err(self.diag, span, code, msg);
|
|
}
|
|
|
|
fn diag_name(self: *Sema, span: Span, code: Code, prefix: []u8, name: ByteView) {
|
|
var b = ByteBuffer.new();
|
|
defer { ByteBuffer.drop(&b); }
|
|
ByteBuffer.append(&b, prefix);
|
|
ByteBuffer.append(&b, ByteView.as_slice(name));
|
|
Diag.err(self.diag, span, code, ByteBuffer.as_slice(b));
|
|
}
|
|
|
|
fn diag_ty(self: *Sema, span: Span, code: Code, prefix: []u8, tid: usize) {
|
|
var b = ByteBuffer.new();
|
|
defer { ByteBuffer.drop(&b); }
|
|
ByteBuffer.append(&b, prefix);
|
|
LangType.dump(&self.types, tid, &b);
|
|
Diag.err(self.diag, span, code, ByteBuffer.as_slice(b));
|
|
}
|
|
|
|
fn diag_ty2(self: *Sema, span: Span, code: Code, prefix: []u8, a: usize, b: usize) {
|
|
var buf = ByteBuffer.new();
|
|
defer { ByteBuffer.drop(&buf); }
|
|
ByteBuffer.append(&buf, prefix);
|
|
LangType.dump(&self.types, a, &buf);
|
|
ByteBuffer.append(&buf, " vs ");
|
|
LangType.dump(&self.types, b, &buf);
|
|
Diag.err(self.diag, span, code, ByteBuffer.as_slice(buf));
|
|
}
|
|
|
|
|
|
fn new(ast: *Ast, diag: *Diag): SemaResult {
|
|
var types: LangType = undefined;
|
|
match LangType.new() {
|
|
.Ok(v) => { types = v; },
|
|
.Err(error) => { ret .Err(error); },
|
|
}
|
|
var scope: Scope = undefined;
|
|
match Scope.new() {
|
|
.Ok(v) => { scope = v; },
|
|
.Err(error) => { LangType.drop(&types); ret .Err(error); },
|
|
}
|
|
var attr: Attr = undefined;
|
|
match Attr.new() {
|
|
.Ok(v) => { attr = v; },
|
|
.Err(error) => {
|
|
Scope.drop(&scope);
|
|
LangType.drop(&types);
|
|
ret .Err(error);
|
|
},
|
|
}
|
|
var s = Sema {
|
|
.ast = ast,
|
|
.diag = diag,
|
|
.sources = null,
|
|
.types = types,
|
|
.scope = scope,
|
|
.attr = attr,
|
|
.eval = Eval.new(null, diag, false),
|
|
.loader = null,
|
|
.current_aggregate_lang = 0,
|
|
.current_fn_ret_tid = 0,
|
|
.current_loop_break_tid = 0,
|
|
.label_stack = Vec.blank(@sizeof(ByteView), @alignof(ByteView)),
|
|
.ir_to_lang = Map.new(@sizeof(usize), @alignof(usize), @sizeof(usize), @alignof(usize),
|
|
hash_kind_usize()),
|
|
};
|
|
ret .Ok(s);
|
|
}
|
|
|
|
fn drop(self: *Sema) {
|
|
Map.drop(&self.ir_to_lang);
|
|
Vec.drop(&self.label_stack);
|
|
Attr.drop(&self.attr);
|
|
Scope.drop(&self.scope);
|
|
LangType.drop(&self.types);
|
|
}
|
|
|
|
|
|
fn push_scope(self: *Sema) {
|
|
var id = Scope.alloc(&self.scope);
|
|
self.scope.current_scope = id;
|
|
}
|
|
|
|
fn pop_scope(self: *Sema) {
|
|
var n = Scope.node(&self.scope, self.scope.current_scope);
|
|
if n != null {
|
|
self.scope.current_scope = n.parent;
|
|
}
|
|
}
|
|
|
|
|
|
fn node_attr_raw(self: *Sema, n: *Node, raw: Vec) {
|
|
var i: usize = 0;
|
|
while i < raw.len {
|
|
var a = node(self, list_at(raw, i));
|
|
if a != null {
|
|
var id = resolve_attr_item(self, a);
|
|
if id != 0 { push_u(&n.attrs, id); }
|
|
}
|
|
i = i + 1;
|
|
}
|
|
}
|
|
|
|
fn resolve_attr_item(self: *Sema, a: *Node): usize {
|
|
var name = ByteView.empty();
|
|
var cargs = ref_vec();
|
|
var found = false;
|
|
match a.body {
|
|
.TagItem(t) => {
|
|
name = t.name;
|
|
cargs = t.args;
|
|
found = true;
|
|
},
|
|
.ExternalTagItem(t) => {
|
|
name = t.name;
|
|
cargs = t.args;
|
|
found = true;
|
|
},
|
|
_ => {},
|
|
}
|
|
if !found { ret 0; }
|
|
|
|
var idx = Attr.lookup(&self.attr, ByteView.empty(), name);
|
|
if idx == 0 {
|
|
diag_name(self, a.span, .UnknownAttr, "unknown attribute `", name);
|
|
ret 0;
|
|
}
|
|
var info = Attr.info_at(&self.attr, idx);
|
|
if info == null { ret 0; }
|
|
var nargs = cargs.len;
|
|
if (nargs as i32) < info.min_args || (info.max_args >= 0 && (nargs as i32) > info.max_args) {
|
|
diag(self, a.span, .UnknownAttr, "attribute arg count mismatch");
|
|
ret 0;
|
|
}
|
|
|
|
var args = Vec.blank(@sizeof(AttrArg), @alignof(AttrArg));
|
|
var i: usize = 0;
|
|
while i < cargs.len {
|
|
var arg = node(self, list_at(cargs, i));
|
|
if arg == null { i = i + 1; continue; }
|
|
match arg.body {
|
|
.ExprIdent(e) => {
|
|
var av: AttrArg = .Str(e.name);
|
|
Vec.push(&args, &av as *u8);
|
|
},
|
|
.ExprStringLit(sl) => {
|
|
var av: AttrArg = .Str(sl.data);
|
|
Vec.push(&args, &av as *u8);
|
|
},
|
|
.ExprIntLit(iv) => {
|
|
var num: i64 = 0;
|
|
BigInt.to_i64(&num, iv.value);
|
|
var av: AttrArg = .Int(num);
|
|
Vec.push(&args, &av as *u8);
|
|
},
|
|
_ => {
|
|
diag(self, arg.span, .UnknownAttr, "unsupported attribute arg");
|
|
},
|
|
}
|
|
i = i + 1;
|
|
}
|
|
var id = Attr.alloc(&self.attr, idx, args);
|
|
ret id;
|
|
}
|
|
|
|
fn resolve_attrs(self: *Sema, n: *Node) {
|
|
if n.attrs.len != 0 { ret; }
|
|
match n.body {
|
|
.FnDecl(&x) => { node_attr_raw(self, n, x.tag_list); },
|
|
.VarDecl(&x) => { node_attr_raw(self, n, x.attr_list); },
|
|
.ConstDecl(&x) => { node_attr_raw(self, n, x.attr_list); },
|
|
.TypeDecl(&x) => { node_attr_raw(self, n, x.attr_list); },
|
|
.MemberDecl(&x) => { node_attr_raw(self, n, x.attr_list); },
|
|
.ParamDecl(&x) => { node_attr_raw(self, n, x.attr_list); },
|
|
.TopLevelBlock(&x) => { node_attr_raw(self, n, x.tag_list); },
|
|
_ => {},
|
|
}
|
|
}
|
|
|
|
|
|
fn build_aggregate(self: *Sema, te: *Node, owner_def_id: usize): usize {
|
|
var kind_name = ByteView.empty();
|
|
match te.body {
|
|
.TypeAggregate(&x) => { kind_name = x.kind_name; },
|
|
_ => {},
|
|
}
|
|
var lk: LangKind = .Struct;
|
|
var tk: TypeNode = .Struct(.{ .mode = .Auto, .fixed_align_bits = 0, .tag_type = 0,
|
|
.variants = ref_vec() });
|
|
if ByteView.eq(kind_name, ByteView.from_slice("union")) {
|
|
lk = .Union;
|
|
} else if ByteView.eq(kind_name, ByteView.from_slice("enum")) {
|
|
lk = .Enum;
|
|
} else if ByteView.eq(kind_name, ByteView.from_slice("shape")) {
|
|
lk = .Shape;
|
|
}
|
|
|
|
var agg_ir = LangType.reserve_ir(&self.types);
|
|
var ln = LangEntry {
|
|
.hash_next = 0,
|
|
.kind = lk,
|
|
.ir_type_id = agg_ir,
|
|
.methods = LangType.empty_methods(),
|
|
.extra = .None,
|
|
};
|
|
var agg_lang = LangType.push(&self.types, &ln);
|
|
if owner_def_id != 0 {
|
|
var d = def_node(self, owner_def_id);
|
|
if d != null { d.lang_id = agg_lang; }
|
|
}
|
|
|
|
var saved = self.current_aggregate_lang;
|
|
self.current_aggregate_lang = agg_lang;
|
|
// 聚合体自成命名空间:成员类型可引用同体声明的嵌套类型(裸名)。
|
|
var saved_scope = self.scope.current_scope;
|
|
var agg_scope = Scope.alloc(&self.scope);
|
|
self.scope.current_scope = agg_scope;
|
|
|
|
var members = Vec.blank(@sizeof(Member), @alignof(Member));
|
|
var methods = Vec.blank(@sizeof(LangMethod), @alignof(LangMethod));
|
|
|
|
var mlist = ref_vec();
|
|
match te.body {
|
|
.TypeAggregate(&x) => { mlist = x.members; },
|
|
_ => {},
|
|
}
|
|
|
|
// 第一遍:登记嵌套类型/函数的 def,挂进 methods 与聚合作用域。成员类型可能引用它们,
|
|
// 必须早于成员类型推断,否则限定名(经 methods)与裸名(经作用域)都解析不到。
|
|
var i: usize = 0;
|
|
while i < mlist.len {
|
|
var mref = list_at(mlist, i);
|
|
var mn = node(self, mref);
|
|
if mn != null {
|
|
match mn.body {
|
|
.FnDecl(fd) => {
|
|
var fn_def = mn.resolved_def_id;
|
|
if fn_def == 0 {
|
|
fn_def = LangType.alloc_def(&self.types, .Fn, fd.name, mn.span,
|
|
self.ast as *any, mref);
|
|
mn.resolved_def_id = fn_def;
|
|
}
|
|
var fn_d = def_node(self, fn_def);
|
|
if fn_d != null { fn_d.owner_def = owner_def_id; }
|
|
var meth = LangMethod { .name = fd.name, .def_id = fn_def };
|
|
Vec.push(&methods, &meth as *u8);
|
|
Scope.insert(&self.scope, agg_scope,
|
|
Symbol { .name = fd.name, .kind = .Fn, .node = fn_def });
|
|
},
|
|
.TypeDecl(td) => {
|
|
var tpat = node(self, td.pattern);
|
|
if tpat != null && is_pattern_ident(tpat) {
|
|
var tname = pattern_name(tpat);
|
|
var tdef = mn.resolved_def_id;
|
|
if tdef == 0 {
|
|
var dk: DefKind = .Scalar;
|
|
var tek = node(self, td.type_expr);
|
|
if tek != null && is_type_aggregate(tek) { dk = .Agg; }
|
|
tdef = LangType.alloc_def(&self.types, dk, tname, mn.span,
|
|
self.ast as *any, mref);
|
|
mn.resolved_def_id = tdef;
|
|
}
|
|
var meth = LangMethod { .name = tname, .def_id = tdef };
|
|
Vec.push(&methods, &meth as *u8);
|
|
Scope.insert(&self.scope, agg_scope,
|
|
Symbol { .name = tname, .kind = .Type, .node = tdef });
|
|
}
|
|
},
|
|
_ => {},
|
|
}
|
|
}
|
|
i = i + 1;
|
|
}
|
|
|
|
var lslot = LangType.lang_node(&self.types, agg_lang);
|
|
if lslot != null {
|
|
Vec.drop(&lslot.methods);
|
|
lslot.methods = methods;
|
|
}
|
|
|
|
// 第二遍:成员(字段/变体)类型推断;此时嵌套名字已可解析。
|
|
i = 0;
|
|
while i < mlist.len {
|
|
var mref = list_at(mlist, i);
|
|
var mn = node(self, mref);
|
|
if mn != null {
|
|
match mn.body {
|
|
.MemberDecl(md) => {
|
|
var mtid: usize = 0;
|
|
if md.type_expr != 0 { mtid = infer(self, md.type_expr); }
|
|
var mm = Member { .name = md.name, .type_id = ir_of(self, mtid),
|
|
.ast_node_ref = mref };
|
|
Vec.push(&members, &mm as *u8);
|
|
},
|
|
_ => {},
|
|
}
|
|
}
|
|
i = i + 1;
|
|
}
|
|
|
|
self.scope.current_scope = saved_scope;
|
|
self.current_aggregate_lang = saved;
|
|
|
|
// IR 节点
|
|
match lk {
|
|
.Enum => {
|
|
var tag_ir = TypeTable.make_int(&self.types.ir, 32, true);
|
|
tk = .Enum(.{ .mode = .Auto, .fixed_align_bits = 0, .tag_type = tag_ir,
|
|
.variants = members });
|
|
},
|
|
.Union => {
|
|
tk = .Union(.{ .mode = .Auto, .fixed_align_bits = 0, .tag_type = 0, .variants = members });
|
|
},
|
|
.Shape => {
|
|
tk = .Shape(.{ .mode = .Auto, .fixed_align_bits = 0, .tag_type = 0, .variants = members });
|
|
},
|
|
_ => {
|
|
tk = .Struct(.{ .mode = .Auto, .fixed_align_bits = 0, .tag_type = 0, .variants = members });
|
|
},
|
|
}
|
|
TypeTable.define(&self.types.ir, agg_ir, &tk);
|
|
|
|
ret agg_lang;
|
|
}
|
|
|
|
fn build_container_aggregate(self: *Sema, n: *Node, owner_def_id: usize): usize {
|
|
var agg_ir = LangType.reserve_ir(&self.types);
|
|
var ln = LangEntry {
|
|
.hash_next = 0,
|
|
.kind = .Struct,
|
|
.ir_type_id = agg_ir,
|
|
.methods = LangType.empty_methods(),
|
|
.extra = .None,
|
|
};
|
|
var agg_lang = LangType.push(&self.types, &ln);
|
|
if owner_def_id != 0 {
|
|
var d = def_node(self, owner_def_id);
|
|
if d != null { d.lang_id = agg_lang; }
|
|
}
|
|
|
|
var saved = self.current_aggregate_lang;
|
|
self.current_aggregate_lang = agg_lang;
|
|
|
|
var members = Vec.blank(@sizeof(Member), @alignof(Member));
|
|
var methods = Vec.blank(@sizeof(LangMethod), @alignof(LangMethod));
|
|
|
|
var decls = ref_vec();
|
|
match n.body {
|
|
.ContainerMembers(x) => { decls = x.declarations; },
|
|
_ => {},
|
|
}
|
|
var i: usize = 0;
|
|
while i < decls.len {
|
|
var dref = list_at(decls, i);
|
|
var d = node(self, dref);
|
|
if d != null {
|
|
match d.body {
|
|
.MemberDecl(md) => {
|
|
var mtid: usize = 0;
|
|
if md.type_expr != 0 { mtid = infer(self, md.type_expr); }
|
|
var mm = Member { .name = md.name, .type_id = ir_of(self, mtid),
|
|
.ast_node_ref = dref };
|
|
Vec.push(&members, &mm as *u8);
|
|
},
|
|
.FnDecl(fd) => {
|
|
var fn_def = d.resolved_def_id;
|
|
if fn_def != 0 {
|
|
var fn_d = def_node(self, fn_def);
|
|
if fn_d != null { fn_d.owner_def = owner_def_id; }
|
|
var meth = LangMethod { .name = fd.name, .def_id = fn_def };
|
|
Vec.push(&methods, &meth as *u8);
|
|
}
|
|
},
|
|
.TypeDecl(td) => {
|
|
var tdef = d.resolved_def_id;
|
|
if tdef != 0 {
|
|
var tpat = node(self, td.pattern);
|
|
if tpat != null && is_pattern_ident(tpat) {
|
|
var meth = LangMethod { .name = pattern_name(tpat), .def_id = tdef };
|
|
Vec.push(&methods, &meth as *u8);
|
|
}
|
|
}
|
|
},
|
|
.VarDecl(vd) => { container_var_method(self, vd.pattern, &methods); },
|
|
.ConstDecl(cd) => { container_var_method(self, cd.pattern, &methods); },
|
|
_ => {},
|
|
}
|
|
}
|
|
i = i + 1;
|
|
}
|
|
|
|
self.current_aggregate_lang = saved;
|
|
|
|
var tk: TypeNode = .Struct(.{ .mode = .Auto, .fixed_align_bits = 0, .tag_type = 0,
|
|
.variants = members });
|
|
TypeTable.define(&self.types.ir, agg_ir, &tk);
|
|
|
|
var lslot = LangType.lang_node(&self.types, agg_lang);
|
|
if lslot != null {
|
|
Vec.drop(&lslot.methods);
|
|
lslot.methods = methods;
|
|
}
|
|
ret agg_lang;
|
|
}
|
|
|
|
fn container_var_method(self: *Sema, pat_ref: usize, methods: *Vec) {
|
|
var pat = node(self, pat_ref);
|
|
if pat == null || !is_pattern_ident(pat) { ret; }
|
|
var vdef = pat.resolved_def_id;
|
|
if vdef == 0 { ret; }
|
|
var meth = LangMethod { .name = pattern_name(pat), .def_id = vdef };
|
|
Vec.push(methods, &meth as *u8);
|
|
}
|
|
|
|
fn is_pattern_ident(n: *Node): bool {
|
|
match n.body { .PatternIdent(&x) => { ret true; }, _ => { ret false; } }
|
|
ret false;
|
|
}
|
|
fn is_type_aggregate(n: *Node): bool {
|
|
match n.body { .TypeAggregate(&x) => { ret true; }, _ => { ret false; } }
|
|
ret false;
|
|
}
|
|
fn is_param_decl(n: *Node): bool {
|
|
match n.body { .ParamDecl(&x) => { ret true; }, _ => { ret false; } }
|
|
ret false;
|
|
}
|
|
fn is_type_decl(n: *Node): bool {
|
|
match n.body { .TypeDecl(&x) => { ret true; }, _ => { ret false; } }
|
|
ret false;
|
|
}
|
|
fn is_pattern_field(n: *Node): bool {
|
|
match n.body { .PatternField(&x) => { ret true; }, _ => { ret false; } }
|
|
ret false;
|
|
}
|
|
fn is_expr_string_lit(n: *Node): bool {
|
|
match n.body { .ExprStringLit(&x) => { ret true; }, _ => { ret false; } }
|
|
ret false;
|
|
}
|
|
fn is_expr_ident(n: *Node): bool {
|
|
match n.body { .ExprIdent(&x) => { ret true; }, _ => { ret false; } }
|
|
ret false;
|
|
}
|
|
fn is_expr_int_lit(n: *Node): bool {
|
|
match n.body { .ExprIntLit(&x) => { ret true; }, _ => { ret false; } }
|
|
ret false;
|
|
}
|
|
|
|
fn pattern_name(pat: *Node): ByteView {
|
|
match pat.body {
|
|
.PatternIdent(&x) => { ret x.name; },
|
|
_ => { ret ByteView.empty(); },
|
|
}
|
|
}
|
|
|
|
|
|
fn sema_import(self: *Sema, n: *Node, args: Vec): usize {
|
|
if self.loader == null {
|
|
diag(self, n.span, .Internal, "@import: no module loader configured");
|
|
ret 0;
|
|
}
|
|
if args.len != 1 {
|
|
diag(self, n.span, .IntrinArgCount, "@import expects 1 argument");
|
|
ret 0;
|
|
}
|
|
var arg = node(self, list_at(args, 0));
|
|
if arg == null || !is_expr_string_lit(arg) {
|
|
diag(self, n.span, .TypeMismatch, "@import expects a string literal");
|
|
ret 0;
|
|
}
|
|
var path = ByteView.empty();
|
|
match arg.body {
|
|
.ExprStringLit(sl) => { path = sl.data; },
|
|
_ => {},
|
|
}
|
|
var fname = ByteView.empty();
|
|
if self.sources != null && n.span.file != no_file() {
|
|
var src = set_at(self.sources, n.span.file);
|
|
if src != null { fname = src.fname; }
|
|
}
|
|
var loaded = self.loader.load(self.loader.user, fname, path, self.diag);
|
|
var mod_ast: *Ast = null;
|
|
match loaded {
|
|
.Ok(a) => { mod_ast = a; },
|
|
.Missing => { ret 0; },
|
|
}
|
|
if mod_ast == null { ret 0; }
|
|
var mod_root = Ast.node(mod_ast, mod_ast.root);
|
|
if mod_root == null { ret 0; }
|
|
var cm_ref: usize = 0;
|
|
match mod_root.body {
|
|
.Root(r) => { cm_ref = r.members; },
|
|
_ => {},
|
|
}
|
|
if cm_ref == 0 { ret 0; }
|
|
var cm = Ast.node(mod_ast, cm_ref);
|
|
if cm != null && cm.resolved_def_id != 0 {
|
|
var d = LangType.def_node(&self.types, cm.resolved_def_id);
|
|
if d != null && d.lang_id != 0 { ret d.lang_id; }
|
|
}
|
|
|
|
var saved_ast = self.ast;
|
|
var saved_root = self.scope.root_scope;
|
|
var saved_cur = self.scope.current_scope;
|
|
var saved_agg = self.current_aggregate_lang;
|
|
|
|
self.ast = mod_ast;
|
|
self.scope.root_scope = 0;
|
|
self.scope.current_scope = 0;
|
|
self.current_aggregate_lang = 0;
|
|
|
|
run_module(self, cm_ref);
|
|
|
|
var t: usize = 0;
|
|
var cm_after = Ast.node(mod_ast, cm_ref);
|
|
if cm_after != null && cm_after.resolved_def_id != 0 {
|
|
var d = LangType.def_node(&self.types, cm_after.resolved_def_id);
|
|
if d != null { t = d.lang_id; }
|
|
}
|
|
|
|
self.ast = saved_ast;
|
|
self.scope.root_scope = saved_root;
|
|
self.scope.current_scope = saved_cur;
|
|
self.current_aggregate_lang = saved_agg;
|
|
ret t;
|
|
}
|
|
|
|
|
|
fn eval_decl(self: *Sema, def_id: usize): usize {
|
|
var d = def_node(self, def_id);
|
|
if d == null { ret 0; }
|
|
if def_state_is(d, .Resolved) || def_state_is(d, .Error) { ret d.lang_id; }
|
|
if d.lang_id != 0 {
|
|
if def_state_is(d, .Resolving) { ret d.lang_id; }
|
|
d.state = .Resolved;
|
|
ret d.lang_id;
|
|
}
|
|
if def_state_is(d, .Resolving) {
|
|
diag_name(self, d.span, .CircularDep, "circular dependency on `", d.name);
|
|
d.state = .Error;
|
|
ret 0;
|
|
}
|
|
|
|
var owner = d.owner;
|
|
var owner_def_id = d.owner_def;
|
|
var node_ref = d.ast_node_ref;
|
|
var dbg = d.span;
|
|
if owner == null {
|
|
diag(self, dbg, .Internal, "def has no owner_ast");
|
|
d.state = .Error;
|
|
ret 0;
|
|
}
|
|
|
|
var saved_ast = self.ast;
|
|
self.ast = owner as *Ast;
|
|
|
|
d = def_node(self, def_id);
|
|
if d != null { d.state = .Resolving; }
|
|
|
|
var n = node(self, node_ref);
|
|
var result: usize = 0;
|
|
if n != null {
|
|
match n.body {
|
|
.TypeDecl(td) => {
|
|
var pat = node(self, td.pattern);
|
|
if pat != null && is_pattern_ident(pat) {
|
|
if td.type_expr != 0 {
|
|
var te = node(self, td.type_expr);
|
|
if te != null && is_type_aggregate(te) {
|
|
result = build_aggregate(self, te, def_id);
|
|
} else {
|
|
result = infer(self, td.type_expr);
|
|
}
|
|
}
|
|
n.resolved_lang_tid = result;
|
|
}
|
|
},
|
|
.FnDecl(fd) => {
|
|
var saved_agg = self.current_aggregate_lang;
|
|
if owner_def_id != 0 {
|
|
var owner_def = def_node(self, owner_def_id);
|
|
if owner_def != null && owner_def.lang_id != 0 {
|
|
self.current_aggregate_lang = owner_def.lang_id;
|
|
}
|
|
}
|
|
result = infer_fn_signature(self, n);
|
|
self.current_aggregate_lang = saved_agg;
|
|
},
|
|
.VarDecl(vd) => {
|
|
if vd.type_expr != 0 { result = infer(self, vd.type_expr); }
|
|
else if vd.init_expr != 0 { result = infer(self, vd.init_expr); }
|
|
},
|
|
.ConstDecl(cd) => {
|
|
if cd.type_expr != 0 { result = infer(self, cd.type_expr); }
|
|
else if cd.init_expr != 0 { result = infer(self, cd.init_expr); }
|
|
},
|
|
.ContainerMembers(x) => {
|
|
result = build_container_aggregate(self, n, def_id);
|
|
n.resolved_lang_tid = result;
|
|
},
|
|
_ => {},
|
|
}
|
|
}
|
|
|
|
var fd = def_node(self, def_id);
|
|
if fd != null {
|
|
if result != 0 { fd.lang_id = result; }
|
|
fd.state = if result != 0 { .Resolved } else { .Error };
|
|
}
|
|
|
|
self.ast = saved_ast;
|
|
ret result;
|
|
}
|
|
|
|
fn infer_fn_signature(self: *Sema, n: *Node): usize {
|
|
if n.resolved_def_id == 0 { ret 0; }
|
|
var d = def_node(self, n.resolved_def_id);
|
|
if d != null && d.lang_id != 0 { ret d.lang_id; }
|
|
|
|
var params = ref_vec();
|
|
var params_list = ref_vec();
|
|
var ret_ref: usize = 0;
|
|
match n.body {
|
|
.FnDecl(fd) => {
|
|
params_list = fd.params;
|
|
ret_ref = fd.ret_type;
|
|
},
|
|
_ => {},
|
|
}
|
|
var i: usize = 0;
|
|
while i < params_list.len {
|
|
var pd = node(self, list_at(params_list, i));
|
|
if pd != null && is_param_decl(pd) {
|
|
var tref: usize = 0;
|
|
match pd.body { .ParamDecl(&x) => { tref = x.type_expr; }, _ => {} }
|
|
if tref != 0 {
|
|
push_u(¶ms, infer(self, tref));
|
|
} else {
|
|
push_u(¶ms, LangType.make_varargs(&self.types));
|
|
}
|
|
} else {
|
|
push_u(¶ms, LangType.make_varargs(&self.types));
|
|
}
|
|
i = i + 1;
|
|
}
|
|
var ret_tid = infer(self, ret_ref);
|
|
if ret_tid == 0 { ret_tid = make_unit(self); }
|
|
var fn_tid = LangType.make_fn(&self.types, ret_tid, params);
|
|
LangType.resolve_def(&self.types, n.resolved_def_id, fn_tid);
|
|
ret fn_tid;
|
|
}
|
|
|
|
fn collect_names(self: *Sema, ref: usize) {
|
|
var n = node(self, ref);
|
|
if n == null { ret; }
|
|
match n.body {
|
|
.ContainerMembers(cm) => {
|
|
if self.scope.root_scope == 0 {
|
|
self.scope.root_scope = Scope.alloc(&self.scope);
|
|
}
|
|
self.scope.current_scope = self.scope.root_scope;
|
|
var mod_scope = Scope.alloc(&self.scope);
|
|
self.scope.current_scope = mod_scope;
|
|
|
|
var file_def = LangType.alloc_def(&self.types, .Agg, ByteView.from_slice("$file"),
|
|
n.span, self.ast as *any, ref);
|
|
n.resolved_def_id = file_def;
|
|
var file_sym = Symbol { .name = ByteView.from_slice("$file"), .kind = .Type,
|
|
.node = file_def };
|
|
Scope.insert(&self.scope, mod_scope, file_sym);
|
|
|
|
var i: usize = 0;
|
|
while i < cm.declarations.len {
|
|
collect_names(self, list_at(cm.declarations, i));
|
|
i = i + 1;
|
|
}
|
|
},
|
|
.TypeDecl(td) => {
|
|
var pat = node(self, td.pattern);
|
|
if pat == null || !is_pattern_ident(pat) { ret; }
|
|
var name = pattern_name(pat);
|
|
var te = node(self, td.type_expr);
|
|
if te == null {
|
|
diag_name(self, n.span, .TypeMismatch, "type declaration missing type expr: `",
|
|
name);
|
|
ret;
|
|
}
|
|
var sym = Symbol { .name = name, .kind = .Type, .node = 0 };
|
|
if is_type_aggregate(te) {
|
|
sym.node = LangType.alloc_def(&self.types, .Agg, name, te.span,
|
|
self.ast as *any, ref);
|
|
} else {
|
|
sym.node = LangType.alloc_def(&self.types, .Scalar, name, te.span,
|
|
self.ast as *any, ref);
|
|
}
|
|
n.resolved_def_id = sym.node;
|
|
if !Scope.insert(&self.scope, self.scope.current_scope, sym) {
|
|
diag_name(self, n.span, .DuplicateSymbol, "duplicate symbol `", name);
|
|
}
|
|
},
|
|
.FnDecl(fd) => {
|
|
var sym = Symbol { .name = fd.name, .kind = .Fn, .node = 0 };
|
|
sym.node = LangType.alloc_def(&self.types, .Fn, fd.name, n.span,
|
|
self.ast as *any, ref);
|
|
n.resolved_def_id = sym.node;
|
|
if !Scope.insert(&self.scope, self.scope.current_scope, sym) {
|
|
diag_name(self, n.span, .DuplicateSymbol, "duplicate symbol `", fd.name);
|
|
}
|
|
},
|
|
.VarDecl(vd) => { collect_var(self, n, vd.pattern); },
|
|
.ConstDecl(cd) => { collect_var(self, n, cd.pattern); },
|
|
.TopLevelBlock(tb) => {
|
|
if tb.block != 0 { collect_names(self, tb.block); }
|
|
},
|
|
_ => {},
|
|
}
|
|
}
|
|
|
|
fn collect_var(self: *Sema, n: *Node, pat_ref: usize) {
|
|
var pat = node(self, pat_ref);
|
|
if pat == null || !is_pattern_ident(pat) {
|
|
diag(self, n.span, .TypeMismatch, "container-level var only supports simple identifier");
|
|
ret;
|
|
}
|
|
var name = pattern_name(pat);
|
|
var sym = Symbol { .name = name, .kind = .Var, .node = 0 };
|
|
sym.node = LangType.alloc_def(&self.types, .Var, name, n.span, self.ast as *any, pat_ref);
|
|
pat.resolved_def_id = sym.node;
|
|
if !Scope.insert(&self.scope, self.scope.current_scope, sym) {
|
|
diag_name(self, n.span, .DuplicateSymbol, "duplicate symbol `", name);
|
|
}
|
|
}
|
|
|
|
fn collect_types(self: *Sema, ref: usize) {
|
|
var n = node(self, ref);
|
|
if n == null { ret; }
|
|
match n.body {
|
|
.ContainerMembers(cm) => {
|
|
var i: usize = 0;
|
|
while i < cm.declarations.len {
|
|
var d = node(self, list_at(cm.declarations, i));
|
|
if d != null && is_type_decl(d) {
|
|
match d.body {
|
|
.TypeDecl(td) => {
|
|
var pat = node(self, td.pattern);
|
|
if pat != null && is_pattern_ident(pat) &&
|
|
d.resolved_def_id != 0 {
|
|
eval_decl(self, d.resolved_def_id);
|
|
}
|
|
},
|
|
_ => {},
|
|
}
|
|
}
|
|
i = i + 1;
|
|
}
|
|
},
|
|
.TopLevelBlock(tb) => {
|
|
if tb.block != 0 { collect_types(self, tb.block); }
|
|
},
|
|
_ => {},
|
|
}
|
|
}
|
|
|
|
fn collect_signatures(self: *Sema) {
|
|
var i: usize = 1;
|
|
while i < self.types.defs.table.len {
|
|
var d = def_node(self, i);
|
|
if d != null && d.owner == (self.ast as *any) &&
|
|
@enum2int(d.kind) == @enum2int(DefKind.Fn) &&
|
|
def_state_is(d, .Unresolved) {
|
|
eval_decl(self, i);
|
|
}
|
|
i = i + 1;
|
|
}
|
|
}
|
|
|
|
fn collect_type_unpacks(self: *Sema, ref: usize) {
|
|
var n = node(self, ref);
|
|
if n == null { ret; }
|
|
match n.body {
|
|
.ContainerMembers(cm) => {
|
|
var i: usize = 0;
|
|
while i < cm.declarations.len {
|
|
var d = node(self, list_at(cm.declarations, i));
|
|
if d != null && is_type_decl(d) {
|
|
match d.body {
|
|
.TypeDecl(td) => {
|
|
var pat = node(self, td.pattern);
|
|
if pat != null && !is_pattern_ident(pat) {
|
|
do_type_unpack(self, d, pat, td.type_expr);
|
|
}
|
|
},
|
|
_ => {},
|
|
}
|
|
}
|
|
i = i + 1;
|
|
}
|
|
},
|
|
.TopLevelBlock(tb) => {
|
|
if tb.block != 0 { collect_type_unpacks(self, tb.block); }
|
|
},
|
|
_ => {},
|
|
}
|
|
}
|
|
|
|
|
|
fn infer_type_context(self: *Sema, ref: usize): usize {
|
|
var n = node(self, ref);
|
|
if n == null { ret 0; }
|
|
match n.body {
|
|
.ExprIdent(&x) => {
|
|
var builtin = LangType.lookup_builtin(&self.types, x.name);
|
|
if builtin != 0 {
|
|
n.resolved_lang_tid = builtin;
|
|
ret builtin;
|
|
}
|
|
var sym = Symbol { .name = ByteView.empty(), .kind = .Error, .node = 0 };
|
|
if !Scope.find(&self.scope, x.name, &sym) || @enum2int(sym.kind) != @enum2int(SymbolKind.Type) {
|
|
diag_name(self, n.span, .TypeMismatch, "`", x.name);
|
|
ret 0;
|
|
}
|
|
if !def_is_type(self, sym.node) {
|
|
diag_name(self, n.span, .TypeMismatch, "not a type: `", x.name);
|
|
ret 0;
|
|
}
|
|
n.resolved_def_id = sym.node;
|
|
var t = eval_decl(self, sym.node);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
},
|
|
.ExprField(&x) => {
|
|
var owner_id = infer_type_context(self, x.obj);
|
|
var owner = lang_of(self, owner_id);
|
|
if owner == null { ret 0; }
|
|
var i: usize = 0;
|
|
while i < owner.methods.len {
|
|
var m = method_at(&owner.methods, i);
|
|
if m != null && ByteView.eq(m.name, x.name) {
|
|
if !def_is_type(self, m.def_id) { ret 0; }
|
|
n.resolved_def_id = m.def_id;
|
|
var t = eval_decl(self, m.def_id);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
}
|
|
i = i + 1;
|
|
}
|
|
diag_name(self, n.span, .TypeMismatch, "not a type member `", x.name);
|
|
ret 0;
|
|
},
|
|
.ExprUnary(&x) => {
|
|
if punct_is(x.op, .Mul) {
|
|
var pointee = infer_type_context(self, x.operand);
|
|
if pointee == 0 { ret 0; }
|
|
var r = LangType.make_ptr(&self.types, pointee);
|
|
n.resolved_lang_tid = r;
|
|
ret r;
|
|
}
|
|
ret 0;
|
|
},
|
|
.ExprTuple(&x) => {
|
|
var elems = ref_vec();
|
|
var i: usize = 0;
|
|
while i < x.items.len {
|
|
var e = infer_type_context(self, list_at(x.items, i));
|
|
if e == 0 { ret 0; }
|
|
push_u(&elems, e);
|
|
i = i + 1;
|
|
}
|
|
var r = LangType.make_tuple(&self.types, elems);
|
|
n.resolved_lang_tid = r;
|
|
ret r;
|
|
},
|
|
.ExprComptimeCall(&x) => {
|
|
var r = infer(self, ref);
|
|
n.resolved_lang_tid = r;
|
|
ret r;
|
|
},
|
|
_ => {},
|
|
}
|
|
diag(self, n.span, .TypeMismatch, "intrinsic argument is not a type expression");
|
|
ret 0;
|
|
}
|
|
|
|
|
|
fn punct_is(op: Punct, want: Punct): bool {
|
|
ret @enum2int(op) == @enum2int(want);
|
|
}
|
|
|
|
fn is_assign_arith(op: Punct): bool {
|
|
match op {
|
|
.AddAssign => { ret true; },
|
|
.SubAssign => { ret true; },
|
|
.MulAssign => { ret true; },
|
|
.DivAssign => { ret true; },
|
|
.ModAssign => { ret true; },
|
|
.AndAssign => { ret true; },
|
|
.OrAssign => { ret true; },
|
|
.XorAssign => { ret true; },
|
|
.LShAssign => { ret true; },
|
|
.RShAssign => { ret true; },
|
|
_ => { ret false; },
|
|
}
|
|
ret false;
|
|
}
|
|
|
|
fn is_cmp_op(op: Punct): bool {
|
|
match op {
|
|
.Eq => { ret true; },
|
|
.Neq => { ret true; },
|
|
.Lt => { ret true; },
|
|
.Le => { ret true; },
|
|
.Gt => { ret true; },
|
|
.Ge => { ret true; },
|
|
_ => { ret false; },
|
|
}
|
|
ret false;
|
|
}
|
|
|
|
fn is_shift_op(op: Punct): bool {
|
|
match op {
|
|
.LSh => { ret true; },
|
|
.RSh => { ret true; },
|
|
_ => { ret false; },
|
|
}
|
|
ret false;
|
|
}
|
|
|
|
fn is_addsub(op: Punct): bool {
|
|
ret punct_is(op, .Add) || punct_is(op, .Sub);
|
|
}
|
|
|
|
fn intrin_is(a: Intrin, b: Intrin): bool {
|
|
ret @enum2int(a) == @enum2int(b);
|
|
}
|
|
|
|
fn intrin_call(self: *Sema, n: *Node, in: Intrin, args: Vec): usize {
|
|
var info = IntrinOps.info(in);
|
|
var nargs = args.len;
|
|
if (nargs as i32) < info.min_args || (info.max_args >= 0 && (nargs as i32) > info.max_args) {
|
|
diag(self, n.span, .IntrinArgCount, "intrinsic argument count mismatch");
|
|
ret 0;
|
|
}
|
|
|
|
if intrin_is(in, .BitSizeof) || intrin_is(in, .Sizeof) || intrin_is(in, .Alignof) {
|
|
if infer_type_context(self, list_at(args, 0)) == 0 { ret 0; }
|
|
ret make_usize(self);
|
|
}
|
|
if intrin_is(in, .Assert) {
|
|
var bt = make_bool(self);
|
|
coerce(self, list_at(args, 0), bt);
|
|
if nargs == 2 { infer(self, list_at(args, 1)); }
|
|
ret make_unit(self);
|
|
}
|
|
if intrin_is(in, .IntCast) || intrin_is(in, .Truncate) || intrin_is(in, .BitCast) ||
|
|
intrin_is(in, .FloatCast) || intrin_is(in, .Float2Int) || intrin_is(in, .Int2Float) ||
|
|
intrin_is(in, .Int2Ptr) || intrin_is(in, .PtrCast) {
|
|
var src = infer(self, list_at(args, 0));
|
|
if src == 0 { ret 0; }
|
|
var dst = infer(self, list_at(args, 1));
|
|
if dst == 0 { ret 0; }
|
|
var ft = ir_of(self, src);
|
|
var tt = ir_of(self, dst);
|
|
var expected = IntrinOps.lookup_cast(&self.types.ir, ft, tt);
|
|
if intrin_is(expected, .None) {
|
|
diag_name(self, n.span, .CastInvalid, "incompatible cast `", info.name);
|
|
ret 0;
|
|
}
|
|
n.resolved_lang_tid = dst;
|
|
ret dst;
|
|
}
|
|
if intrin_is(in, .Bool2Int) || intrin_is(in, .Ptr2Int) {
|
|
infer(self, list_at(args, 0));
|
|
if nargs >= 2 {
|
|
var dst = infer(self, list_at(args, 1));
|
|
if dst == 0 { ret 0; }
|
|
n.resolved_lang_tid = dst;
|
|
ret dst;
|
|
}
|
|
var r = make_usize(self);
|
|
n.resolved_lang_tid = r;
|
|
ret r;
|
|
}
|
|
if intrin_is(in, .Enum2Int) {
|
|
var src = infer(self, list_at(args, 0));
|
|
if src == 0 { ret 0; }
|
|
var sn = lang_of(self, src);
|
|
if sn == null || !kind_is(self, src, .Enum) {
|
|
diag(self, n.span, .TypeMismatch, "@enum2int requires an enum operand");
|
|
ret 0;
|
|
}
|
|
var sir = ir_entry(self, sn.ir_type_id);
|
|
var tag_ir: usize = 0;
|
|
if sir != null {
|
|
match sir.node {
|
|
.Enum(a) => { tag_ir = a.tag_type; },
|
|
_ => {},
|
|
}
|
|
}
|
|
if tag_ir == 0 {
|
|
diag(self, n.span, .Internal, "@enum2int: enum has no tag type");
|
|
ret 0;
|
|
}
|
|
var tag = lang_from_ir(self, tag_ir);
|
|
n.resolved_lang_tid = tag;
|
|
ret tag;
|
|
}
|
|
if intrin_is(in, .FieldCount) {
|
|
if infer_type_context(self, list_at(args, 0)) == 0 { ret 0; }
|
|
ret make_usize(self);
|
|
}
|
|
if intrin_is(in, .OffsetOf) {
|
|
var agg_lang = infer_type_context(self, list_at(args, 0));
|
|
if agg_lang == 0 { ret 0; }
|
|
var agg_ir = ir_of(self, agg_lang);
|
|
var fnode = node(self, list_at(args, 1));
|
|
if fnode == null { ret 0; }
|
|
if is_expr_ident(fnode) {
|
|
var fname = ByteView.empty();
|
|
match fnode.body { .ExprIdent(&x) => { fname = x.name; }, _ => {} }
|
|
var idx: usize = 0;
|
|
if !TypeTable.agg_member_idx(&self.types.ir, agg_ir, fname, &idx) {
|
|
diag_name(self, n.span, .NoField, "no field `", fname);
|
|
ret 0;
|
|
}
|
|
} else if !is_expr_int_lit(fnode) {
|
|
diag(self, n.span, .TypeMismatch, "@offsetof expects field name or index");
|
|
ret 0;
|
|
}
|
|
ret make_usize(self);
|
|
}
|
|
ret 0;
|
|
}
|
|
|
|
fn intrin_field(self: *Sema, n: *Node, in: Intrin): usize {
|
|
var info = IntrinOps.info(in);
|
|
match info.result {
|
|
.Usize => { ret make_usize(self); },
|
|
.Bool => { ret make_bool(self); },
|
|
.Unit => { ret make_unit(self); },
|
|
}
|
|
ret 0;
|
|
}
|
|
|
|
|
|
fn block_body(self: *Sema, n: *Node, expected: usize): usize {
|
|
var stmts = ref_vec();
|
|
var tail: usize = 0;
|
|
match n.body {
|
|
.ExprBlock(x) => {
|
|
stmts = x.stmts;
|
|
tail = x.tail_expr;
|
|
},
|
|
_ => {},
|
|
}
|
|
push_scope(self);
|
|
var i: usize = 0;
|
|
while i < stmts.len {
|
|
infer(self, list_at(stmts, i));
|
|
i = i + 1;
|
|
}
|
|
var r: usize = 0;
|
|
if tail != 0 {
|
|
if expected != 0 {
|
|
r = coerce(self, tail, expected);
|
|
if r == 0 {
|
|
pop_scope(self);
|
|
ret 0;
|
|
}
|
|
} else {
|
|
r = infer(self, tail);
|
|
}
|
|
} else {
|
|
r = make_unit(self);
|
|
}
|
|
pop_scope(self);
|
|
n.resolved_lang_tid = r;
|
|
ret r;
|
|
}
|
|
|
|
fn pattern_is_literal(n: *Node): bool {
|
|
match n.body {
|
|
.ExprIntLit(x) => { ret true; },
|
|
.ExprFloatLit(x) => { ret true; },
|
|
.ExprCharLit(x) => { ret true; },
|
|
.ExprStringLit(x) => { ret true; },
|
|
.ExprBoolLit(x) => { ret true; },
|
|
.ExprNullLit => { ret true; },
|
|
_ => { ret false; },
|
|
}
|
|
ret false;
|
|
}
|
|
|
|
fn bind_pattern(self: *Sema, pat_ref: usize, matched_tid: usize) {
|
|
if pat_ref == 0 { ret; }
|
|
var pat = node(self, pat_ref);
|
|
if pat == null { ret; }
|
|
|
|
if pattern_is_literal(pat) {
|
|
var lt = infer(self, pat_ref);
|
|
if lt != 0 && matched_tid != 0 {
|
|
coerce(self, pat_ref, matched_tid);
|
|
}
|
|
ret;
|
|
}
|
|
|
|
match pat.body {
|
|
.PatternIdent(x) => {
|
|
if x.name.len == 0 { ret; }
|
|
if ByteView.eq(x.name, ByteView.from_slice("_")) { ret; }
|
|
if pat.resolved_def_id != 0 {
|
|
LangType.resolve_def(&self.types, pat.resolved_def_id, matched_tid);
|
|
pat.resolved_lang_tid = matched_tid;
|
|
ret;
|
|
}
|
|
var sym = Symbol { .name = x.name, .kind = .Var, .node = 0 };
|
|
sym.node = LangType.alloc_def(&self.types, .Var, x.name, pat.span,
|
|
self.ast as *any, pat_ref);
|
|
LangType.resolve_def(&self.types, sym.node, matched_tid);
|
|
if !Scope.insert(&self.scope, self.scope.current_scope, sym) {
|
|
diag_name(self, pat.span, .DuplicateSymbol, "duplicate symbol `", x.name);
|
|
}
|
|
pat.resolved_def_id = sym.node;
|
|
pat.resolved_lang_tid = matched_tid;
|
|
},
|
|
.PatternUnderscore => {},
|
|
.PatternRef(x) => {
|
|
if matched_tid == 0 {
|
|
diag(self, pat.span, .TypeMismatch, "ref pattern requires a concrete type");
|
|
ret;
|
|
}
|
|
var ptr = LangType.make_ptr(&self.types, matched_tid);
|
|
bind_pattern(self, x.pat, ptr);
|
|
pat.resolved_lang_tid = ptr;
|
|
},
|
|
.PatternList(x) => {
|
|
var i: usize = 0;
|
|
while i < x.items.len {
|
|
bind_pattern(self, list_at(x.items, i), matched_tid);
|
|
i = i + 1;
|
|
}
|
|
},
|
|
.PatternTuple(x) => {
|
|
var tn = lang_of(self, matched_tid);
|
|
if tn == null || !kind_is(self, matched_tid, .Tuple) {
|
|
diag(self, pat.span, .TypeMismatch, "tuple pattern requires tuple value");
|
|
ret;
|
|
}
|
|
var elem_tids = ref_vec();
|
|
match tn.extra {
|
|
.Tuple(v) => { elem_tids = v; },
|
|
_ => {},
|
|
}
|
|
var natoms = x.items.len;
|
|
var nelems = elem_tids.len;
|
|
if natoms != nelems {
|
|
diag(self, pat.span, .TypeMismatch, "tuple pattern arity mismatch");
|
|
ret;
|
|
}
|
|
var i: usize = 0;
|
|
while i < nelems {
|
|
bind_pattern(self, list_at(x.items, i), list_at(elem_tids, i));
|
|
i = i + 1;
|
|
}
|
|
pat.resolved_lang_tid = matched_tid;
|
|
},
|
|
.PatternDot(x) => {
|
|
if !kind_is(self, matched_tid, .Enum) {
|
|
diag(self, pat.span, .TypeMismatch, "variant pattern requires enum value");
|
|
ret;
|
|
}
|
|
var agg_ir = ir_of(self, matched_tid);
|
|
var idx: usize = 0;
|
|
if !TypeTable.agg_member_idx(&self.types.ir, agg_ir, x.name, &idx) {
|
|
diag_name(self, pat.span, .NoField, "no variant `", x.name);
|
|
ret;
|
|
}
|
|
var payload_ir = agg_variant_ir(self, agg_ir, idx);
|
|
var payload_lang = lang_from_ir(self, payload_ir);
|
|
pat.resolved_def_id = idx + 1;
|
|
pat.resolved_lang_tid = matched_tid;
|
|
if x.sub_pattern != 0 && payload_lang != 0 {
|
|
bind_pattern(self, x.sub_pattern, payload_lang);
|
|
}
|
|
},
|
|
.PatternAggregate(x) => {
|
|
if !(kind_is(self, matched_tid, .Struct) || kind_is(self, matched_tid, .Union) ||
|
|
kind_is(self, matched_tid, .Enum)) {
|
|
diag(self, pat.span, .TypeMismatch, "aggregate pattern requires aggregate value");
|
|
ret;
|
|
}
|
|
var agg_ir = ir_of(self, matched_tid);
|
|
if kind_is(self, matched_tid, .Enum) && x.items.len != 1 {
|
|
diag(self, pat.span, .TypeMismatch, "enum pattern must have one variant");
|
|
ret;
|
|
}
|
|
var i: usize = 0;
|
|
while i < x.items.len {
|
|
var f = node(self, list_at(x.items, i));
|
|
if f != null && is_pattern_field(f) {
|
|
var fname = ByteView.empty();
|
|
var subp: usize = 0;
|
|
match f.body {
|
|
.PatternField(pf) => {
|
|
fname = pf.name;
|
|
subp = pf.pattern;
|
|
},
|
|
_ => {},
|
|
}
|
|
var idx: usize = 0;
|
|
if !TypeTable.agg_member_idx(&self.types.ir, agg_ir, fname, &idx) {
|
|
diag_name(self, f.span, .NoField, "no field `", fname);
|
|
i = i + 1;
|
|
continue;
|
|
}
|
|
var fir = agg_variant_ir(self, agg_ir, idx);
|
|
var ft = lang_from_ir(self, fir);
|
|
f.resolved_def_id = idx;
|
|
if subp != 0 && ft != 0 {
|
|
bind_pattern(self, subp, ft);
|
|
}
|
|
}
|
|
i = i + 1;
|
|
}
|
|
pat.resolved_lang_tid = matched_tid;
|
|
},
|
|
_ => {
|
|
diag(self, pat.span, .UnhandledNode, "unsupported pattern kind");
|
|
},
|
|
}
|
|
}
|
|
|
|
fn agg_variant_ir(self: *Sema, agg_ir: usize, idx: usize): usize {
|
|
var irn = ir_entry(self, agg_ir);
|
|
if irn == null { ret 0; }
|
|
match irn.node {
|
|
.Struct(a) => { ret variant_at(a.variants, idx); },
|
|
.Union(a) => { ret variant_at(a.variants, idx); },
|
|
.Enum(a) => { ret variant_at(a.variants, idx); },
|
|
.Shape(a) => { ret variant_at(a.variants, idx); },
|
|
_ => { ret 0; },
|
|
}
|
|
ret 0;
|
|
}
|
|
|
|
fn variant_at(v: Vec, idx: usize): usize {
|
|
var m = TypeTable.member_at(&v, idx);
|
|
if m == null { ret 0; }
|
|
ret m.type_id;
|
|
}
|
|
|
|
fn type_unpack_one(self: *Sema, pat: *Node, name: ByteView, src_lang: usize) {
|
|
if name.len == 0 { ret; }
|
|
var src = lang_of(self, src_lang);
|
|
if src == null { ret; }
|
|
var i: usize = 0;
|
|
while i < src.methods.len {
|
|
var m = method_at(&src.methods, i);
|
|
if m != null && ByteView.eq(m.name, name) {
|
|
var d = def_node(self, m.def_id);
|
|
var k: SymbolKind = .Var;
|
|
if d != null {
|
|
match d.kind {
|
|
.Fn => { k = .Fn; },
|
|
.Agg, .Scalar, .Distinct, .Alias, .Builtin => { k = .Type; },
|
|
_ => {},
|
|
}
|
|
}
|
|
var sym = Symbol { .name = name, .kind = k, .node = m.def_id };
|
|
if !Scope.insert(&self.scope, self.scope.current_scope, sym) {
|
|
diag_name(self, pat.span, .DuplicateSymbol, "type unpack duplicate `", name);
|
|
}
|
|
pat.resolved_def_id = m.def_id;
|
|
ret;
|
|
}
|
|
i = i + 1;
|
|
}
|
|
diag_name(self, pat.span, .NoField, "type unpack: no symbol `", name);
|
|
}
|
|
|
|
fn type_unpack_walk(self: *Sema, pat: *Node, src_lang: usize) {
|
|
if pat == null { ret; }
|
|
match pat.body {
|
|
.PatternUnderscore => {},
|
|
.PatternDot(x) => {
|
|
if x.sub_pattern != 0 {
|
|
diag(self, pat.span, .TypeMismatch, "type unpack: payload not allowed");
|
|
ret;
|
|
}
|
|
type_unpack_one(self, pat, x.name, src_lang);
|
|
},
|
|
.PatternAggregate(x) => {
|
|
var i: usize = 0;
|
|
while i < x.items.len {
|
|
var f = node(self, list_at(x.items, i));
|
|
if f != null && is_pattern_field(f) {
|
|
var fname = ByteView.empty();
|
|
var subp: usize = 0;
|
|
match f.body {
|
|
.PatternField(pf) => {
|
|
fname = pf.name;
|
|
subp = pf.pattern;
|
|
},
|
|
_ => {},
|
|
}
|
|
if subp != 0 {
|
|
diag(self, f.span, .TypeMismatch, "type unpack: field pattern not allowed");
|
|
} else {
|
|
type_unpack_one(self, f, fname, src_lang);
|
|
}
|
|
}
|
|
i = i + 1;
|
|
}
|
|
},
|
|
.PatternList(x) => {
|
|
var i: usize = 0;
|
|
while i < x.items.len {
|
|
type_unpack_walk(self, node(self, list_at(x.items, i)), src_lang);
|
|
i = i + 1;
|
|
}
|
|
},
|
|
_ => {},
|
|
}
|
|
}
|
|
|
|
fn do_type_unpack(self: *Sema, decl: *Node, pat: *Node, type_expr_ref: usize) {
|
|
if type_expr_ref == 0 {
|
|
diag(self, decl.span, .TypeMismatch, "type unpack missing source type");
|
|
ret;
|
|
}
|
|
var src_lang = infer(self, type_expr_ref);
|
|
if src_lang == 0 { ret; }
|
|
if !kind_is(self, src_lang, .Struct) {
|
|
diag(self, decl.span, .TypeMismatch, "type unpack source must be struct");
|
|
ret;
|
|
}
|
|
type_unpack_walk(self, pat, src_lang);
|
|
}
|
|
|
|
|
|
fn var_body(self: *Sema, n: *Node): usize {
|
|
var decl_tid: usize = 0;
|
|
var tref: usize = 0;
|
|
var iref: usize = 0;
|
|
var pat_ref: usize = 0;
|
|
match n.body {
|
|
.VarDecl(x) => {
|
|
tref = x.type_expr;
|
|
iref = x.init_expr;
|
|
pat_ref = x.pattern;
|
|
},
|
|
.ConstDecl(x) => {
|
|
tref = x.type_expr;
|
|
iref = x.init_expr;
|
|
pat_ref = x.pattern;
|
|
},
|
|
_ => {},
|
|
}
|
|
if tref != 0 { decl_tid = infer(self, tref); }
|
|
|
|
if iref == 0 {
|
|
diag(self, n.span, .VarUninitialized, "variable must be initialized");
|
|
ret 0;
|
|
}
|
|
if decl_tid != 0 {
|
|
if coerce(self, iref, decl_tid) == 0 { ret 0; }
|
|
} else {
|
|
decl_tid = infer(self, iref);
|
|
if decl_tid == 0 || is_infer(self, decl_tid) {
|
|
diag(self, n.span, .CannotInfer, "cannot infer variable type");
|
|
ret 0;
|
|
}
|
|
}
|
|
bind_pattern(self, pat_ref, decl_tid);
|
|
n.resolved_lang_tid = decl_tid;
|
|
ret decl_tid;
|
|
}
|
|
|
|
fn for_elem_type(self: *Sema, lang_tid: usize): usize {
|
|
var e = lang_of(self, lang_tid);
|
|
if e == null { ret 0; }
|
|
ret elem_of_lang(e.*);
|
|
}
|
|
|
|
|
|
fn run_module(self: *Sema, module_ref: usize) {
|
|
if self.ast == null || module_ref == 0 { ret; }
|
|
var m = Ast.node(self.ast, module_ref);
|
|
if m == null { ret; }
|
|
match m.body {
|
|
.ContainerMembers(x) => {},
|
|
_ => { ret; },
|
|
}
|
|
|
|
var saved_scope = self.scope.current_scope;
|
|
var saved_agg = self.current_aggregate_lang;
|
|
|
|
collect_names(self, module_ref);
|
|
collect_type_unpacks(self, module_ref);
|
|
|
|
var container_lang: usize = 0;
|
|
if m.resolved_def_id != 0 {
|
|
container_lang = eval_decl(self, m.resolved_def_id);
|
|
}
|
|
self.current_aggregate_lang = container_lang;
|
|
|
|
collect_types(self, module_ref);
|
|
collect_signatures(self);
|
|
infer(self, module_ref);
|
|
|
|
self.current_aggregate_lang = saved_agg;
|
|
self.scope.current_scope = saved_scope;
|
|
}
|
|
|
|
fn run(self: *Sema) {
|
|
if self.ast == null || self.ast.root == 0 { ret; }
|
|
var root = Ast.node(self.ast, self.ast.root);
|
|
if root == null { ret; }
|
|
var members: usize = 0;
|
|
match root.body {
|
|
.Root(r) => { members = r.members; },
|
|
_ => { ret; },
|
|
}
|
|
run_module(self, members);
|
|
}
|
|
|
|
fn method_at(v: *Vec, i: usize): *LangMethod {
|
|
match Vec.get(v, i) {
|
|
.Ok(ptr) => { ret ptr as *LangMethod; },
|
|
.Err(error) => { ret null; },
|
|
}
|
|
ret null;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// infer / coerce:下面两块是主体(step 2 填入)。
|
|
// ============================================================
|
|
|
|
fn int_bits(self: *Sema, lang_id: usize): usize {
|
|
var e = ir_entry(self, ir_of(self, lang_id));
|
|
if e == null { ret 0; }
|
|
match e.node {
|
|
.Int(x) => { ret x.bits; },
|
|
_ => { ret 0; },
|
|
}
|
|
ret 0;
|
|
}
|
|
|
|
fn float_bits(self: *Sema, lang_id: usize): usize {
|
|
var e = ir_entry(self, ir_of(self, lang_id));
|
|
if e == null { ret 0; }
|
|
match e.node {
|
|
.Float(x) => { ret x.bits; },
|
|
_ => { ret 0; },
|
|
}
|
|
ret 0;
|
|
}
|
|
|
|
fn lang_find_method(self: *Sema, lang_id: usize, name: ByteView): usize {
|
|
var e = lang_of(self, lang_id);
|
|
if e == null { ret 0; }
|
|
var i: usize = 0;
|
|
while i < e.methods.len {
|
|
var m = method_at(&e.methods, i);
|
|
if m != null && ByteView.eq(m.name, name) { ret m.def_id; }
|
|
i = i + 1;
|
|
}
|
|
ret 0;
|
|
}
|
|
|
|
fn label_name(self: *Sema, ref: usize): ByteView {
|
|
if ref == 0 { ret ByteView.empty(); }
|
|
var ln = node(self, ref);
|
|
if ln == null { ret ByteView.empty(); }
|
|
match ln.body {
|
|
.Label(l) => { ret l.name; },
|
|
_ => { ret ByteView.empty(); },
|
|
}
|
|
ret ByteView.empty();
|
|
}
|
|
|
|
fn agg_variants_copy(self: *Sema, agg_ir: usize): Vec {
|
|
var out = ref_vec();
|
|
var irn = ir_entry(self, agg_ir);
|
|
if irn == null { ret out; }
|
|
var v = ref_vec();
|
|
match irn.node {
|
|
.Struct(a) => { v = a.variants; },
|
|
.Union(a) => { v = a.variants; },
|
|
.Enum(a) => { v = a.variants; },
|
|
.Shape(a) => { v = a.variants; },
|
|
_ => {},
|
|
}
|
|
var i: usize = 0;
|
|
while i < v.len {
|
|
push_u(&out, variant_at(v, i));
|
|
i = i + 1;
|
|
}
|
|
ret out;
|
|
}
|
|
|
|
|
|
// 二元运算两侧都 coerce 到 expected;成功返回 expected,失败返回 0。
|
|
fn coerce_binop_same(self: *Sema, left: usize, right: usize, expected: usize): usize {
|
|
if coerce(self, left, expected) == 0 { ret 0; }
|
|
if coerce(self, right, expected) == 0 { ret 0; }
|
|
ret expected;
|
|
}
|
|
|
|
fn coerce(self: *Sema, ref: usize, expected: usize): usize {
|
|
if ref == 0 || expected == 0 { ret 0; }
|
|
var n = node(self, ref);
|
|
if n == null { ret 0; }
|
|
|
|
var already = n.resolved_lang_tid;
|
|
if already == expected { ret expected; }
|
|
|
|
if already != 0 {
|
|
var a_rebindable = is_infer(self, already) ||
|
|
(kind_is(self, already, .Int) && int_bits(self, already) == 0) ||
|
|
(kind_is(self, already, .Float) && float_bits(self, already) == 0);
|
|
if !a_rebindable {
|
|
if LangType.is_subtype(&self.types, already, expected) {
|
|
n.expected_lang_tid = expected;
|
|
ret expected;
|
|
}
|
|
diag_ty2(self, n.span, .TypeMismatch, "type mismatch: ", already, expected);
|
|
ret 0;
|
|
}
|
|
n.resolved_lang_tid = 0;
|
|
}
|
|
|
|
var handled = false;
|
|
var result: usize = 0;
|
|
|
|
match n.body {
|
|
.ExprUnary(&x) => {
|
|
if !(punct_is(x.op, .And) || punct_is(x.op, .Mul)) {
|
|
handled = true;
|
|
result = coerce(self, x.operand, expected);
|
|
}
|
|
},
|
|
.ExprBinary(&x) => {
|
|
handled = true;
|
|
var op = x.op;
|
|
if punct_is(op, .Assign) || is_assign_arith(op) {
|
|
var lt = infer(self, x.left);
|
|
if lt == 0 { ret 0; }
|
|
if coerce(self, x.right, lt) == 0 { ret 0; }
|
|
result = lt;
|
|
} else if punct_is(op, .OrOr) || punct_is(op, .AndAnd) {
|
|
var bt = make_bool(self);
|
|
if coerce(self, x.left, bt) == 0 { ret 0; }
|
|
if coerce(self, x.right, bt) == 0 { ret 0; }
|
|
result = bt;
|
|
} else if is_cmp_op(op) {
|
|
var lt2 = infer(self, x.left);
|
|
var rt2 = infer(self, x.right);
|
|
if lt2 == 0 || rt2 == 0 { ret 0; }
|
|
var common = if lt2 == rt2 { lt2 } else { LangType.join(&self.types, lt2, rt2) };
|
|
if common == 0 {
|
|
diag_ty2(self, n.span, .TypeMismatch, "comparison mismatch: ", lt2, rt2);
|
|
ret 0;
|
|
}
|
|
coerce(self, x.left, common);
|
|
coerce(self, x.right, common);
|
|
result = make_bool(self);
|
|
} else if is_addsub(op) {
|
|
var en = lang_of(self, expected);
|
|
if en != null && kind_is(self, expected, .Ptr) {
|
|
var left_tid = infer(self, x.left);
|
|
var ln = lang_of(self, left_tid);
|
|
if ln != null && kind_is(self, left_tid, .Ptr) {
|
|
var rt3 = infer(self, x.right);
|
|
if !kind_is(self, rt3, .Int) {
|
|
diag(self, n.span, .TypeMismatch, "pointer offset must be integer");
|
|
ret 0;
|
|
}
|
|
if int_bits(self, rt3) == 0 {
|
|
var it = make_isize(self);
|
|
if coerce(self, x.right, it) == 0 { ret 0; }
|
|
}
|
|
result = expected;
|
|
} else {
|
|
result = coerce_binop_same(self, x.left, x.right, expected);
|
|
}
|
|
} else {
|
|
result = coerce_binop_same(self, x.left, x.right, expected);
|
|
}
|
|
} else {
|
|
result = coerce_binop_same(self, x.left, x.right, expected);
|
|
}
|
|
},
|
|
.ExprArrayLit(&x) => {
|
|
var en = lang_of(self, expected);
|
|
if en != null && (kind_is(self, expected, .Array) || kind_is(self, expected, .Slice)) {
|
|
handled = true;
|
|
var et = elem_of_lang(en.*);
|
|
var i: usize = 0;
|
|
while i < x.elems.len {
|
|
if coerce(self, list_at(x.elems, i), et) == 0 { ret 0; }
|
|
i = i + 1;
|
|
}
|
|
result = expected;
|
|
}
|
|
},
|
|
.ExprTuple(&x) => {
|
|
var en = lang_of(self, expected);
|
|
if en != null && kind_is(self, expected, .Tuple) {
|
|
handled = true;
|
|
var elems = ref_vec();
|
|
match en.extra { .Tuple(v) => { elems = v; }, _ => {} }
|
|
if x.items.len != elems.len {
|
|
diag(self, n.span, .TypeMismatch, "tuple arity mismatch");
|
|
ret 0;
|
|
}
|
|
var i: usize = 0;
|
|
while i < x.items.len {
|
|
if coerce(self, list_at(x.items, i), list_at(elems, i)) == 0 { ret 0; }
|
|
i = i + 1;
|
|
}
|
|
result = expected;
|
|
}
|
|
},
|
|
.ExprAggregateLit(&x) => {
|
|
var en = lang_of(self, expected);
|
|
if en != null && (kind_is(self, expected, .Struct) || kind_is(self, expected, .Union) ||
|
|
kind_is(self, expected, .Enum) || kind_is(self, expected, .Slice)) {
|
|
handled = true;
|
|
if x.name.len != 0 {
|
|
var sym = Symbol { .name = ByteView.empty(), .kind = .Error, .node = 0 };
|
|
if !Scope.find(&self.scope, x.name, &sym) {
|
|
diag_name(self, n.span, .UndefinedSymbol, "unknown type `", x.name);
|
|
ret 0;
|
|
}
|
|
var d = def_node(self, sym.node);
|
|
if d == null || d.lang_id != expected {
|
|
diag(self, n.span, .TypeMismatch, "aggregate does not match expected type");
|
|
ret 0;
|
|
}
|
|
}
|
|
var agg_ir = ir_of(self, expected);
|
|
var field_irs = agg_variants_copy(self, agg_ir);
|
|
if kind_is(self, expected, .Enum) {
|
|
if x.fields.len != 1 {
|
|
diag(self, n.span, .TypeMismatch, "enum literal must have one variant");
|
|
ret 0;
|
|
}
|
|
var item = node(self, list_at(x.fields, 0));
|
|
if item == null || !kind_is_body_struct_init(item) {
|
|
diag(self, n.span, .TypeMismatch, "invalid enum literal");
|
|
ret 0;
|
|
}
|
|
var iname = ByteView.empty();
|
|
var iexpr: usize = 0;
|
|
match item.body { .StructInitItem(&si) => { iname = si.name; iexpr = si.expr; }, _ => {} }
|
|
var idx: usize = 0;
|
|
if !TypeTable.agg_member_idx(&self.types.ir, agg_ir, iname, &idx) {
|
|
diag_name(self, item.span, .NoField, "no variant `", iname);
|
|
ret 0;
|
|
}
|
|
if idx >= field_irs.len { ret 0; }
|
|
var ft = lang_from_ir(self, list_at(field_irs, idx));
|
|
if ft != 0 && iexpr != 0 {
|
|
if coerce(self, iexpr, ft) == 0 { ret 0; }
|
|
}
|
|
result = expected;
|
|
} else {
|
|
var i: usize = 0;
|
|
while i < x.fields.len {
|
|
var item = node(self, list_at(x.fields, i));
|
|
if item != null && kind_is_body_struct_init(item) {
|
|
var iname = ByteView.empty();
|
|
var iexpr: usize = 0;
|
|
match item.body { .StructInitItem(&si) => { iname = si.name; iexpr = si.expr; }, _ => {} }
|
|
var idx: usize = 0;
|
|
if !TypeTable.agg_member_idx(&self.types.ir, agg_ir, iname, &idx) {
|
|
diag_name(self, item.span, .NoField, "no field `", iname);
|
|
i = i + 1;
|
|
continue;
|
|
}
|
|
if idx < field_irs.len {
|
|
var ft = lang_from_ir(self, list_at(field_irs, idx));
|
|
if ft != 0 && iexpr != 0 {
|
|
if coerce(self, iexpr, ft) == 0 { ret 0; }
|
|
}
|
|
}
|
|
}
|
|
i = i + 1;
|
|
}
|
|
result = expected;
|
|
}
|
|
}
|
|
},
|
|
.ExprIf(&x) => {
|
|
handled = true;
|
|
var bt = make_bool(self);
|
|
coerce(self, x.cond, bt);
|
|
if x.then_block != 0 { coerce(self, x.then_block, expected); }
|
|
if x.else_branch != 0 { coerce(self, x.else_branch, expected); }
|
|
result = expected;
|
|
},
|
|
.ExprLoop(&x) => {
|
|
handled = true;
|
|
var saved = self.current_loop_break_tid;
|
|
self.current_loop_break_tid = expected;
|
|
infer(self, ref);
|
|
self.current_loop_break_tid = saved;
|
|
result = expected;
|
|
},
|
|
.ExprBlock(&x) => {
|
|
ret block_body(self, n, expected);
|
|
},
|
|
_ => {},
|
|
}
|
|
|
|
if handled {
|
|
if result == 0 { ret 0; }
|
|
n.resolved_lang_tid = result;
|
|
ret result;
|
|
}
|
|
|
|
var from = infer(self, ref);
|
|
if from == 0 { ret 0; }
|
|
if from == expected { ret expected; }
|
|
|
|
if kind_is(self, from, .Int) && int_bits(self, from) == 0 && kind_is(self, expected, .Int) {
|
|
if is_expr_int_lit(n) && int_bits(self, expected) != 0 {
|
|
var fits = true;
|
|
match n.body {
|
|
.ExprIntLit(&v) => {
|
|
if !LangType.int_fits(&self.types, expected, v.value) { fits = false; }
|
|
},
|
|
_ => {},
|
|
}
|
|
if !fits {
|
|
diag(self, n.span, .FitsOverflow, "comptime value does not fit target type");
|
|
ret 0;
|
|
}
|
|
}
|
|
n.resolved_lang_tid = expected;
|
|
ret expected;
|
|
}
|
|
if kind_is(self, from, .Float) && float_bits(self, from) == 0 && kind_is(self, expected, .Float) {
|
|
n.resolved_lang_tid = expected;
|
|
ret expected;
|
|
}
|
|
if LangType.is_subtype(&self.types, from, expected) {
|
|
n.expected_lang_tid = expected;
|
|
ret expected;
|
|
}
|
|
diag_ty2(self, n.span, .TypeMismatch, "type mismatch: ", from, expected);
|
|
ret 0;
|
|
}
|
|
|
|
fn elem_of_lang(e: LangEntry): usize {
|
|
match e.extra {
|
|
.Elem(x) => { ret x; },
|
|
.Array(a) => { ret a.elem; },
|
|
_ => { ret 0; },
|
|
}
|
|
ret 0;
|
|
}
|
|
|
|
fn kind_is_body_struct_init(n: *Node): bool {
|
|
match n.body { .StructInitItem(&x) => { ret true; }, _ => { ret false; } }
|
|
ret false;
|
|
}
|
|
|
|
|
|
fn infer(self: *Sema, ref: usize): usize {
|
|
if ref == 0 { ret 0; }
|
|
var n = node(self, ref);
|
|
if n == null { ret 0; }
|
|
resolve_attrs(self, n);
|
|
|
|
match n.body {
|
|
.ExprIntLit(&x) => {
|
|
var t = LangType.make_int(&self.types, 0, true);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
},
|
|
.ExprFloatLit(&x) => {
|
|
var t = LangType.make_float(&self.types, 0);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
},
|
|
.ExprCharLit(&x) => {
|
|
var t = LangType.make_int(&self.types, 8, false);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
},
|
|
.ExprStringLit(&x) => {
|
|
var u8t = LangType.make_int(&self.types, 8, false);
|
|
var s = LangType.make_slice(&self.types, u8t);
|
|
n.resolved_lang_tid = s;
|
|
ret s;
|
|
},
|
|
.ExprBoolLit(&x) => {
|
|
var t = make_bool(self);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
},
|
|
.ExprNullLit => {
|
|
var nv = make_never(self);
|
|
var p = LangType.make_ptr(&self.types, nv);
|
|
n.resolved_lang_tid = p;
|
|
ret p;
|
|
},
|
|
.ExprUndefinedLit => {
|
|
var t = make_never(self);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
},
|
|
.ExprIdent(&x) => {
|
|
var sym = Symbol { .name = ByteView.empty(), .kind = .Error, .node = 0 };
|
|
if !Scope.find(&self.scope, x.name, &sym) {
|
|
diag_name(self, n.span, .UndefinedSymbol, "undefined identifier `", x.name);
|
|
ret 0;
|
|
}
|
|
if sym.node == 0 {
|
|
diag_name(self, n.span, .UndefinedSymbol, "identifier has no def `", x.name);
|
|
ret 0;
|
|
}
|
|
n.resolved_def_id = sym.node;
|
|
var t = eval_decl(self, sym.node);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
},
|
|
.ExprUnary(&x) => {
|
|
var op = infer(self, x.operand);
|
|
if op == 0 { ret 0; }
|
|
var tn = lang_of(self, op);
|
|
if tn == null { ret 0; }
|
|
if punct_is(x.op, .Sub) {
|
|
if !(kind_is(self, op, .Int) || kind_is(self, op, .Float)) {
|
|
diag(self, n.span, .TypeMismatch, "unary minus requires numeric operand");
|
|
ret 0;
|
|
}
|
|
n.resolved_lang_tid = op;
|
|
ret op;
|
|
}
|
|
if punct_is(x.op, .BitNot) {
|
|
if !kind_is(self, op, .Int) {
|
|
diag(self, n.span, .TypeMismatch, "bitwise not requires integer operand");
|
|
ret 0;
|
|
}
|
|
n.resolved_lang_tid = op;
|
|
ret op;
|
|
}
|
|
if punct_is(x.op, .Not) {
|
|
var bt = make_bool(self);
|
|
coerce(self, x.operand, bt);
|
|
n.resolved_lang_tid = bt;
|
|
ret bt;
|
|
}
|
|
if punct_is(x.op, .And) {
|
|
var operand = node(self, x.operand);
|
|
if operand != null && kind_is_body_ident(operand) {
|
|
var d = def_node(self, operand.resolved_def_id);
|
|
if d != null && @enum2int(d.kind) == @enum2int(DefKind.Fn) {
|
|
diag(self, n.span, .TypeMismatch, "cannot take address of a function");
|
|
ret 0;
|
|
}
|
|
}
|
|
var p = LangType.make_ptr(&self.types, op);
|
|
n.resolved_lang_tid = p;
|
|
ret p;
|
|
}
|
|
if punct_is(x.op, .Mul) {
|
|
if !kind_is(self, op, .Ptr) {
|
|
diag(self, n.span, .TypeMismatch, "deref requires pointer operand");
|
|
ret 0;
|
|
}
|
|
var elem = elem_of_lang(tn.*);
|
|
n.resolved_lang_tid = elem;
|
|
ret elem;
|
|
}
|
|
ret 0;
|
|
},
|
|
.ExprBinary(&x) => {
|
|
ret infer_binary(self, n, x.op, x.left, x.right);
|
|
},
|
|
.ExprCall(&x) => {
|
|
ret infer_call(self, n, x.callee, x.args);
|
|
},
|
|
.ExprField(&x) => {
|
|
ret infer_field(self, n, x.obj, x.name);
|
|
},
|
|
.ExprIndex(&x) => {
|
|
ret infer_index(self, n, x.obj, x.index);
|
|
},
|
|
.ExprSlice(&x) => {
|
|
ret infer_slice(self, n, x.obj, x.begin, x.end);
|
|
},
|
|
.ExprCast(&x) => {
|
|
ret infer_cast(self, n, x.expr, x.type_expr);
|
|
},
|
|
.ExprTuple(&x) => {
|
|
var es = ref_vec();
|
|
var i: usize = 0;
|
|
while i < x.items.len {
|
|
push_u(&es, infer(self, list_at(x.items, i)));
|
|
i = i + 1;
|
|
}
|
|
var t = LangType.make_tuple(&self.types, es);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
},
|
|
.ExprArrayLit(&x) => {
|
|
ret infer_array(self, n, x.elem_type, x.has_len, x.len, x.is_underscore_len, x.elems);
|
|
},
|
|
.ExprAggregateLit(&x) => {
|
|
ret infer_aggregate(self, n, x.name, x.fields);
|
|
},
|
|
.ExprComptimeCall(&x) => {
|
|
if ByteView.eq(x.name, ByteView.from_slice("import")) {
|
|
var t = sema_import(self, n, x.args);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
}
|
|
if ByteView.eq(x.name, ByteView.from_slice("self")) {
|
|
if self.current_aggregate_lang == 0 {
|
|
diag(self, n.span, .UndefinedSymbol, "`@self` outside of aggregate");
|
|
ret 0;
|
|
}
|
|
n.resolved_lang_tid = self.current_aggregate_lang;
|
|
ret self.current_aggregate_lang;
|
|
}
|
|
var in = IntrinOps.lookup_call(x.name);
|
|
if intrin_is(in, .None) {
|
|
diag_name(self, n.span, .UnknownIntrin, "unknown comptime call `", x.name);
|
|
ret 0;
|
|
}
|
|
var t = intrin_call(self, n, in, x.args);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
},
|
|
.ExprBlock(&x) => {
|
|
ret block_body(self, n, 0);
|
|
},
|
|
.ExprIf(&x) => {
|
|
var bt = make_bool(self);
|
|
coerce(self, x.cond, bt);
|
|
var then_t = if x.then_block != 0 { infer(self, x.then_block) } else { 0 };
|
|
var else_t = if x.else_branch != 0 { infer(self, x.else_branch) } else { 0 };
|
|
var result: usize = 0;
|
|
if then_t != 0 && else_t != 0 {
|
|
result = if then_t == else_t { then_t } else { LangType.join(&self.types, then_t, else_t) };
|
|
} else if then_t != 0 {
|
|
result = then_t;
|
|
} else {
|
|
result = else_t;
|
|
}
|
|
n.resolved_lang_tid = result;
|
|
ret result;
|
|
},
|
|
.ExprIfvar(&x) => {
|
|
if x.var_decl == 0 { ret 0; }
|
|
var vd = node(self, x.var_decl);
|
|
if vd == null { ret 0; }
|
|
push_scope(self);
|
|
var_body(self, vd);
|
|
if x.then_block != 0 { infer(self, x.then_block); }
|
|
pop_scope(self);
|
|
if x.else_branch != 0 { infer(self, x.else_branch); }
|
|
ret 0;
|
|
},
|
|
.ExprWhile(&x) => {
|
|
push_view(&self.label_stack, label_name(self, x.label));
|
|
var bt = make_bool(self);
|
|
coerce(self, x.cond, bt);
|
|
if x.body != 0 { infer(self, x.body); }
|
|
if x.else_branch != 0 { infer(self, x.else_branch); }
|
|
pop_label(self);
|
|
ret 0;
|
|
},
|
|
.ExprLoop(&x) => {
|
|
push_view(&self.label_stack, label_name(self, x.label));
|
|
if x.body != 0 { infer(self, x.body); }
|
|
pop_label(self);
|
|
ret 0;
|
|
},
|
|
.ExprFor(&x) => {
|
|
ret infer_for(self, n, x.label, x.iter_exprs, x.pattern, x.body, x.else_branch);
|
|
},
|
|
.ExprMatch(&x) => {
|
|
var matched = infer(self, x.matched_expr);
|
|
var i: usize = 0;
|
|
while i < x.arms.len {
|
|
var arm = node(self, list_at(x.arms, i));
|
|
if arm != null {
|
|
var pat: usize = 0;
|
|
var body: usize = 0;
|
|
match arm.body {
|
|
.MatchArm(ma) => {
|
|
pat = ma.pattern;
|
|
body = ma.body;
|
|
},
|
|
_ => {},
|
|
}
|
|
push_scope(self);
|
|
bind_pattern(self, pat, matched);
|
|
if body != 0 { infer(self, body); }
|
|
pop_scope(self);
|
|
}
|
|
i = i + 1;
|
|
}
|
|
ret 0;
|
|
},
|
|
.ExprFnType(&x) => {
|
|
var ps = ref_vec();
|
|
var i: usize = 0;
|
|
while i < x.params.len {
|
|
push_u(&ps, infer(self, list_at(x.params, i)));
|
|
i = i + 1;
|
|
}
|
|
var rt = infer(self, x.ret_type);
|
|
var fn_lang = LangType.make_fn(&self.types, rt, ps);
|
|
n.resolved_lang_tid = fn_lang;
|
|
ret fn_lang;
|
|
},
|
|
.TypePath(&x) => {
|
|
var b = LangType.lookup_builtin(&self.types, x.name);
|
|
if b != 0 {
|
|
n.resolved_lang_tid = b;
|
|
ret b;
|
|
}
|
|
var sym = Symbol { .name = ByteView.empty(), .kind = .Error, .node = 0 };
|
|
if !Scope.find(&self.scope, x.name, &sym) {
|
|
diag_name(self, n.span, .UndefinedSymbol, "type not found `", x.name);
|
|
ret 0;
|
|
}
|
|
var t = eval_decl(self, sym.node);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
},
|
|
.TypePointer(&x) => {
|
|
var p = infer(self, x.pointee);
|
|
var r = LangType.make_ptr(&self.types, p);
|
|
n.resolved_lang_tid = r;
|
|
ret r;
|
|
},
|
|
.TypeArray(&x) => {
|
|
var e = infer(self, x.elem_type);
|
|
var len: usize = 0;
|
|
if x.has_len { len = x.len as usize; }
|
|
var r = LangType.make_array(&self.types, e, len);
|
|
n.resolved_lang_tid = r;
|
|
ret r;
|
|
},
|
|
.TypeSlice(&x) => {
|
|
var e = infer(self, x.elem_type);
|
|
var r = LangType.make_slice(&self.types, e);
|
|
n.resolved_lang_tid = r;
|
|
ret r;
|
|
},
|
|
.TypeFn(&x) => {
|
|
var ps = ref_vec();
|
|
var i: usize = 0;
|
|
while i < x.params.len {
|
|
push_u(&ps, infer(self, list_at(x.params, i)));
|
|
i = i + 1;
|
|
}
|
|
var rt = infer(self, x.ret_type);
|
|
var r = LangType.make_fn(&self.types, rt, ps);
|
|
n.resolved_lang_tid = r;
|
|
ret r;
|
|
},
|
|
.TypeParen(&x) => {
|
|
if x.types.len == 0 {
|
|
var u = make_unit(self);
|
|
n.resolved_lang_tid = u;
|
|
ret u;
|
|
}
|
|
if x.types.len == 1 {
|
|
var t = infer(self, list_at(x.types, 0));
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
}
|
|
var es = ref_vec();
|
|
var i: usize = 0;
|
|
while i < x.types.len {
|
|
push_u(&es, infer(self, list_at(x.types, i)));
|
|
i = i + 1;
|
|
}
|
|
var t = LangType.make_tuple(&self.types, es);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
},
|
|
.TypeAggregate(&x) => {
|
|
ret build_aggregate(self, n, 0);
|
|
},
|
|
.TypeComptimeCall(&x) => {
|
|
if ByteView.eq(x.name, ByteView.from_slice("self")) {
|
|
if self.current_aggregate_lang == 0 {
|
|
diag(self, n.span, .UndefinedSymbol, "`@self` outside of aggregate");
|
|
ret 0;
|
|
}
|
|
n.resolved_lang_tid = self.current_aggregate_lang;
|
|
ret self.current_aggregate_lang;
|
|
}
|
|
if ByteView.eq(x.name, ByteView.from_slice("import")) {
|
|
var t = sema_import(self, n, x.args);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
}
|
|
ret 0;
|
|
},
|
|
.ContainerMembers(&x) => {
|
|
var i: usize = 0;
|
|
while i < x.declarations.len {
|
|
infer(self, list_at(x.declarations, i));
|
|
i = i + 1;
|
|
}
|
|
ret 0;
|
|
},
|
|
.FnDecl(&x) => {
|
|
ret infer_fn_decl(self, n, x.params, x.ret_type, x.body);
|
|
},
|
|
.TypeDecl(&x) => {
|
|
ret infer_type_decl(self, n, x.pattern, x.type_expr);
|
|
},
|
|
.VarDecl(&x) => { ret var_body(self, n); },
|
|
.ConstDecl(&x) => { ret var_body(self, n); },
|
|
.MemberDecl(&x) => {
|
|
if x.type_expr != 0 { ret infer(self, x.type_expr); }
|
|
ret 0;
|
|
},
|
|
.ParamDecl(&x) => {
|
|
if x.type_expr != 0 { ret infer(self, x.type_expr); }
|
|
ret 0;
|
|
},
|
|
.StatementRet(&x) => {
|
|
ret infer_ret(self, n, x.expr);
|
|
},
|
|
.StatementBreak(&x) => {
|
|
ret infer_break(self, n, x.label, x.expr);
|
|
},
|
|
.StatementContinue(&x) => {
|
|
ret infer_continue(self, n, x.label);
|
|
},
|
|
.StatementDefer(&x) => {
|
|
if x.body != 0 { infer(self, x.body); }
|
|
ret 0;
|
|
},
|
|
.StatementComptime(&x) => {
|
|
if x.body != 0 { infer(self, x.body); }
|
|
ret 0;
|
|
},
|
|
.StatementDecl(&x) => {
|
|
if x.decl != 0 { infer(self, x.decl); }
|
|
ret 0;
|
|
},
|
|
.StatementExpr(&x) => {
|
|
if x.expr != 0 { infer(self, x.expr); }
|
|
ret 0;
|
|
},
|
|
.TopLevelBlock(&x) => {
|
|
if x.block != 0 { infer(self, x.block); }
|
|
ret 0;
|
|
},
|
|
_ => { ret 0; },
|
|
}
|
|
ret 0;
|
|
}
|
|
|
|
fn kind_is_body_ident(n: *Node): bool {
|
|
match n.body { .ExprIdent(&x) => { ret true; }, _ => { ret false; } }
|
|
ret false;
|
|
}
|
|
|
|
fn pop_label(self: *Sema) {
|
|
var out = ByteView.empty();
|
|
Vec.pop(&self.label_stack, &out as *u8);
|
|
}
|
|
|
|
fn infer_binary(self: *Sema, n: *Node, op: Punct, left: usize, right: usize): usize {
|
|
if punct_is(op, .Assign) || is_assign_arith(op) {
|
|
var lt = infer(self, left);
|
|
if lt == 0 { ret 0; }
|
|
coerce(self, right, lt);
|
|
n.resolved_lang_tid = lt;
|
|
ret lt;
|
|
}
|
|
if punct_is(op, .OrOr) || punct_is(op, .AndAnd) {
|
|
var bt = make_bool(self);
|
|
coerce(self, left, bt);
|
|
coerce(self, right, bt);
|
|
n.resolved_lang_tid = bt;
|
|
ret bt;
|
|
}
|
|
if is_cmp_op(op) {
|
|
var lt = infer(self, left);
|
|
var rt = infer(self, right);
|
|
if lt == 0 || rt == 0 { ret 0; }
|
|
var common = if lt == rt { lt } else { LangType.join(&self.types, lt, rt) };
|
|
if common == 0 {
|
|
diag_ty2(self, n.span, .TypeMismatch, "comparison mismatch: ", lt, rt);
|
|
ret 0;
|
|
}
|
|
coerce(self, left, common);
|
|
coerce(self, right, common);
|
|
var b = make_bool(self);
|
|
n.resolved_lang_tid = b;
|
|
ret b;
|
|
}
|
|
if punct_is(op, .Range) {
|
|
infer(self, left);
|
|
infer(self, right);
|
|
var i64t = LangType.make_int(&self.types, 64, true);
|
|
var r = LangType.make_range(&self.types, i64t);
|
|
n.resolved_lang_tid = r;
|
|
ret r;
|
|
}
|
|
if is_shift_op(op) {
|
|
var lt = infer(self, left);
|
|
if lt == 0 { ret 0; }
|
|
if !kind_is(self, lt, .Int) {
|
|
diag(self, n.span, .TypeMismatch, "shift requires integer operands");
|
|
ret 0;
|
|
}
|
|
infer(self, right);
|
|
n.resolved_lang_tid = lt;
|
|
ret lt;
|
|
}
|
|
var lt2 = infer(self, left);
|
|
var rt2 = infer(self, right);
|
|
if lt2 == 0 || rt2 == 0 { ret 0; }
|
|
if kind_is(self, lt2, .Ptr) && kind_is(self, rt2, .Int) && is_addsub(op) {
|
|
if int_bits(self, rt2) == 0 {
|
|
var it = make_isize(self);
|
|
coerce(self, right, it);
|
|
}
|
|
n.resolved_lang_tid = lt2;
|
|
ret lt2;
|
|
}
|
|
var common2 = if lt2 == rt2 { lt2 } else { LangType.join(&self.types, lt2, rt2) };
|
|
if common2 == 0 {
|
|
diag_ty2(self, n.span, .TypeMismatch, "operand types cannot be joined: ", lt2, rt2);
|
|
ret 0;
|
|
}
|
|
coerce(self, left, common2);
|
|
coerce(self, right, common2);
|
|
n.resolved_lang_tid = common2;
|
|
ret common2;
|
|
}
|
|
|
|
fn infer_call(self: *Sema, n: *Node, callee_ref: usize, args: Vec): usize {
|
|
var callee = node(self, callee_ref);
|
|
if callee != null && kind_is_body_ident(callee) {
|
|
var cname = ByteView.empty();
|
|
match callee.body { .ExprIdent(e) => { cname = e.name; }, _ => {} }
|
|
var in = IntrinOps.lookup_call(cname);
|
|
if !intrin_is(in, .None) {
|
|
var t = intrin_call(self, n, in, args);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
}
|
|
}
|
|
|
|
var callee_tid = infer(self, callee_ref);
|
|
var fn_lang = lang_of(self, callee_tid);
|
|
if fn_lang == null || !kind_is(self, callee_tid, .Fn) {
|
|
diag(self, n.span, .NotAFunction, "callee is not a function");
|
|
ret 0;
|
|
}
|
|
var params = ref_vec();
|
|
var call_ret: usize = 0;
|
|
match fn_lang.extra {
|
|
.Fn(f) => {
|
|
params = f.params;
|
|
call_ret = f.ret_type;
|
|
},
|
|
_ => {},
|
|
}
|
|
var has_varargs = false;
|
|
var fixed = params.len;
|
|
if fixed > 0 {
|
|
var last = list_at(params, fixed - 1);
|
|
if kind_is(self, last, .Varargs) {
|
|
has_varargs = true;
|
|
fixed = fixed - 1;
|
|
}
|
|
}
|
|
var nargs = args.len;
|
|
if has_varargs {
|
|
if nargs < fixed {
|
|
diag(self, n.span, .ArgCountMismatch, "too few arguments");
|
|
}
|
|
} else if nargs != fixed {
|
|
diag(self, n.span, .ArgCountMismatch, "argument count mismatch");
|
|
}
|
|
|
|
var i: usize = 0;
|
|
while i < nargs && i < fixed {
|
|
var pt = list_at(params, i);
|
|
if coerce(self, list_at(args, i), pt) == 0 { ret 0; }
|
|
i = i + 1;
|
|
}
|
|
if has_varargs {
|
|
var j = fixed;
|
|
while j < nargs {
|
|
if infer(self, list_at(args, j)) == 0 { ret 0; }
|
|
j = j + 1;
|
|
}
|
|
}
|
|
n.resolved_lang_tid = call_ret;
|
|
ret call_ret;
|
|
}
|
|
|
|
fn infer_field(self: *Sema, n: *Node, obj_ref: usize, field_name: ByteView): usize {
|
|
var obj_tid = infer(self, obj_ref);
|
|
var obj = lang_of(self, obj_tid);
|
|
if obj == null { ret 0; }
|
|
|
|
var objn = node(self, obj_ref);
|
|
if objn != null && kind_is_body_ident(objn) {
|
|
var od = def_node(self, objn.resolved_def_id);
|
|
if od != null && @enum2int(od.kind) == @enum2int(DefKind.Agg) &&
|
|
kind_is(self, obj_tid, .Enum) {
|
|
var eir = ir_of(self, obj_tid);
|
|
var vidx: usize = 0;
|
|
if eir != 0 && TypeTable.agg_member_idx(&self.types.ir, eir, field_name, &vidx) {
|
|
n.resolved_lang_tid = obj_tid;
|
|
ret obj_tid;
|
|
}
|
|
}
|
|
}
|
|
|
|
var in = IntrinOps.lookup_field(obj.kind, field_name);
|
|
if !intrin_is(in, .None) {
|
|
var t = intrin_field(self, n, in);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
}
|
|
|
|
var agg_lang = obj_tid;
|
|
if kind_is(self, obj_tid, .Ptr) {
|
|
agg_lang = elem_of_lang(obj.*);
|
|
}
|
|
var agg = lang_of(self, agg_lang);
|
|
if agg == null {
|
|
diag(self, n.span, .NoField, "field access on invalid type");
|
|
ret 0;
|
|
}
|
|
|
|
var agg_ir = ir_of(self, agg_lang);
|
|
if agg_ir != 0 {
|
|
var idx: usize = 0;
|
|
if TypeTable.agg_member_idx(&self.types.ir, agg_ir, field_name, &idx) {
|
|
var fir = agg_variant_ir(self, agg_ir, idx);
|
|
var ft = lang_from_ir(self, fir);
|
|
if ft == 0 {
|
|
diag(self, n.span, .NoField, "cannot resolve field type");
|
|
ret 0;
|
|
}
|
|
n.resolved_lang_tid = ft;
|
|
ret ft;
|
|
}
|
|
}
|
|
|
|
var did = lang_find_method(self, agg_lang, field_name);
|
|
if did != 0 {
|
|
n.resolved_def_id = did;
|
|
var t = eval_decl(self, did);
|
|
n.resolved_lang_tid = t;
|
|
ret t;
|
|
}
|
|
diag_name(self, n.span, .NoField, "no field `", field_name);
|
|
ret 0;
|
|
}
|
|
|
|
fn infer_index(self: *Sema, n: *Node, obj_ref: usize, index_ref: usize): usize {
|
|
var obj_tid = infer(self, obj_ref);
|
|
infer(self, index_ref);
|
|
var obj = lang_of(self, obj_tid);
|
|
if obj == null { ret 0; }
|
|
var elem: usize = 0;
|
|
if kind_is(self, obj_tid, .Array) || kind_is(self, obj_tid, .Slice) ||
|
|
kind_is(self, obj_tid, .Range) || kind_is(self, obj_tid, .Ptr) {
|
|
elem = elem_of_lang(obj.*);
|
|
} else if kind_is(self, obj_tid, .Tuple) {
|
|
var idx_node = node(self, index_ref);
|
|
if idx_node == null || !is_expr_int_lit(idx_node) {
|
|
diag(self, n.span, .TupleIndexOob, "tuple index must be integer literal");
|
|
ret 0;
|
|
}
|
|
var iv: i64 = -1;
|
|
match idx_node.body {
|
|
.ExprIntLit(lit) => {
|
|
BigInt.to_i64(&iv, lit.value);
|
|
},
|
|
_ => {},
|
|
}
|
|
var elems = ref_vec();
|
|
match obj.extra { .Tuple(v) => { elems = v; }, _ => {} }
|
|
if iv < 0 || (iv as usize) >= elems.len {
|
|
diag(self, n.span, .TupleIndexOob, "tuple index out of range");
|
|
ret 0;
|
|
}
|
|
elem = list_at(elems, iv as usize);
|
|
} else {
|
|
diag(self, n.span, .IndexBaseInvalid, "index base must be array/slice/ptr/range/tuple");
|
|
ret 0;
|
|
}
|
|
n.resolved_lang_tid = elem;
|
|
ret elem;
|
|
}
|
|
|
|
fn infer_slice(self: *Sema, n: *Node, obj_ref: usize, begin_ref: usize, end_ref: usize): usize {
|
|
var obj_tid = infer(self, obj_ref);
|
|
if begin_ref != 0 { infer(self, begin_ref); }
|
|
if end_ref != 0 { infer(self, end_ref); }
|
|
var obj = lang_of(self, obj_tid);
|
|
if obj == null { ret 0; }
|
|
if !(kind_is(self, obj_tid, .Array) || kind_is(self, obj_tid, .Ptr) ||
|
|
kind_is(self, obj_tid, .Slice)) {
|
|
diag(self, n.span, .IndexBaseInvalid, "slice base must be array/ptr/slice");
|
|
ret 0;
|
|
}
|
|
var elem = elem_of_lang(obj.*);
|
|
var s = LangType.make_slice(&self.types, elem);
|
|
n.resolved_lang_tid = s;
|
|
ret s;
|
|
}
|
|
|
|
fn infer_cast(self: *Sema, n: *Node, expr_ref: usize, type_ref: usize): usize {
|
|
var dst = infer(self, type_ref);
|
|
if dst == 0 { ret 0; }
|
|
var src = infer(self, expr_ref);
|
|
if src == 0 { ret 0; }
|
|
if (kind_is(self, src, .Int) && int_bits(self, src) == 0) ||
|
|
(kind_is(self, src, .Float) && float_bits(self, src) == 0) {
|
|
var src_node = node(self, expr_ref);
|
|
if src_node != null && (is_expr_int_lit(src_node) || is_expr_float_lit(src_node)) {
|
|
src_node.resolved_lang_tid = dst;
|
|
} else if coerce(self, expr_ref, dst) == 0 {
|
|
ret 0;
|
|
}
|
|
n.resolved_lang_tid = dst;
|
|
ret dst;
|
|
}
|
|
var ft = ir_of(self, src);
|
|
var tt = ir_of(self, dst);
|
|
if ft == 0 || tt == 0 {
|
|
diag(self, n.span, .CastInvalid, "as: unresolved source or target type");
|
|
ret 0;
|
|
}
|
|
var in = IntrinOps.lookup_cast(&self.types.ir, ft, tt);
|
|
if intrin_is(in, .None) {
|
|
diag(self, n.span, .CastInvalid, "as: invalid conversion");
|
|
ret 0;
|
|
}
|
|
n.resolved_lang_tid = dst;
|
|
ret dst;
|
|
}
|
|
|
|
fn is_expr_float_lit(n: *Node): bool {
|
|
match n.body { .ExprFloatLit(&x) => { ret true; }, _ => { ret false; } }
|
|
ret false;
|
|
}
|
|
|
|
fn infer_array(self: *Sema, n: *Node, elem_type_ref: usize, has_len: bool, len_decl: usize,
|
|
is_underscore_len: bool, elems: Vec): usize {
|
|
var elem_t: usize = 0;
|
|
if elem_type_ref != 0 { elem_t = infer(self, elem_type_ref); }
|
|
var nelems = elems.len;
|
|
var i: usize = 0;
|
|
while i < nelems {
|
|
var et = infer(self, list_at(elems, i));
|
|
if et == 0 { ret 0; }
|
|
if elem_t == 0 {
|
|
elem_t = et;
|
|
} else {
|
|
var c = if elem_t == et { elem_t } else { LangType.join(&self.types, elem_t, et) };
|
|
if c != 0 { elem_t = c; }
|
|
}
|
|
i = i + 1;
|
|
}
|
|
if elem_t == 0 { elem_t = make_infer(self); }
|
|
var len = nelems;
|
|
if !is_underscore_len && has_len { len = len_decl; }
|
|
var arr = LangType.make_array(&self.types, elem_t, len);
|
|
n.resolved_lang_tid = arr;
|
|
ret arr;
|
|
}
|
|
|
|
fn infer_aggregate(self: *Sema, n: *Node, type_name: ByteView, fields: Vec): usize {
|
|
if type_name.len == 0 {
|
|
var inf = make_infer(self);
|
|
n.resolved_lang_tid = inf;
|
|
ret inf;
|
|
}
|
|
var sym = Symbol { .name = ByteView.empty(), .kind = .Error, .node = 0 };
|
|
if !Scope.find(&self.scope, type_name, &sym) {
|
|
diag_name(self, n.span, .UndefinedSymbol, "unknown type `", type_name);
|
|
ret 0;
|
|
}
|
|
var d = def_node(self, sym.node);
|
|
if d == null || d.lang_id == 0 {
|
|
diag_name(self, n.span, .UndefinedSymbol, "type not resolved `", type_name);
|
|
ret 0;
|
|
}
|
|
var agg = d.lang_id;
|
|
if !(kind_is(self, agg, .Struct) || kind_is(self, agg, .Union) || kind_is(self, agg, .Enum)) {
|
|
diag_name(self, n.span, .TypeMismatch, "not a struct/union/enum `", type_name);
|
|
ret 0;
|
|
}
|
|
if kind_is(self, agg, .Enum) && fields.len != 1 {
|
|
diag(self, n.span, .TypeMismatch, "enum literal must have one variant");
|
|
ret 0;
|
|
}
|
|
var agg_ir = ir_of(self, agg);
|
|
var field_irs = agg_variants_copy(self, agg_ir);
|
|
var i: usize = 0;
|
|
while i < fields.len {
|
|
var item = node(self, list_at(fields, i));
|
|
if item != null && kind_is_body_struct_init(item) {
|
|
var iname = ByteView.empty();
|
|
var iexpr: usize = 0;
|
|
match item.body { .StructInitItem(si) => { iname = si.name; iexpr = si.expr; }, _ => {} }
|
|
var idx: usize = 0;
|
|
if !TypeTable.agg_member_idx(&self.types.ir, agg_ir, iname, &idx) {
|
|
diag_name(self, item.span, .NoField, "no field `", iname);
|
|
i = i + 1;
|
|
continue;
|
|
}
|
|
if idx < field_irs.len {
|
|
var ft = lang_from_ir(self, list_at(field_irs, idx));
|
|
if ft != 0 && iexpr != 0 { coerce(self, iexpr, ft); }
|
|
}
|
|
}
|
|
i = i + 1;
|
|
}
|
|
n.resolved_lang_tid = agg;
|
|
ret agg;
|
|
}
|
|
|
|
fn infer_for(self: *Sema, n: *Node, label_ref: usize, iter_exprs: Vec, pattern_ref: usize,
|
|
body_ref: usize, else_ref: usize): usize {
|
|
push_view(&self.label_stack, label_name(self, label_ref));
|
|
push_scope(self);
|
|
|
|
var nsrc = iter_exprs.len;
|
|
var pat = node(self, pattern_ref);
|
|
var is_list = pat != null && kind_is_body_pattern_list(pat);
|
|
if is_list {
|
|
var atoms = ref_vec();
|
|
match pat.body { .PatternList(pl) => { atoms = pl.items; }, _ => {} }
|
|
if atoms.len != nsrc {
|
|
diag(self, pat.span, .TypeMismatch, "for: pattern count != source count");
|
|
}
|
|
var i: usize = 0;
|
|
while i < nsrc && i < atoms.len {
|
|
var ct = infer(self, list_at(iter_exprs, i));
|
|
bind_pattern(self, list_at(atoms, i), for_elem_type(self, ct));
|
|
i = i + 1;
|
|
}
|
|
} else {
|
|
if nsrc > 1 {
|
|
diag(self, n.span, .TypeMismatch, "for: multiple sources require pattern list");
|
|
}
|
|
var i: usize = 0;
|
|
while i < nsrc {
|
|
var ct = infer(self, list_at(iter_exprs, i));
|
|
if i == 0 { bind_pattern(self, pattern_ref, for_elem_type(self, ct)); }
|
|
i = i + 1;
|
|
}
|
|
}
|
|
|
|
if body_ref != 0 { infer(self, body_ref); }
|
|
if else_ref != 0 { infer(self, else_ref); }
|
|
|
|
pop_scope(self);
|
|
pop_label(self);
|
|
ret 0;
|
|
}
|
|
|
|
fn kind_is_body_pattern_list(n: *Node): bool {
|
|
match n.body { .PatternList(&x) => { ret true; }, _ => { ret false; } }
|
|
ret false;
|
|
}
|
|
|
|
fn infer_fn_decl(self: *Sema, n: *Node, params_ref: Vec, ret_ref: usize, body_ref: usize): usize {
|
|
if n.resolved_def_id == 0 { ret 0; }
|
|
var d = def_node(self, n.resolved_def_id);
|
|
if d == null { ret 0; }
|
|
var owner_def_id = d.owner_def;
|
|
var fn_tid = d.lang_id;
|
|
if fn_tid == 0 { fn_tid = eval_decl(self, n.resolved_def_id); }
|
|
var fn_ln = lang_of(self, fn_tid);
|
|
if fn_ln == null || !kind_is(self, fn_tid, .Fn) { ret 0; }
|
|
var ret_tid: usize = 0;
|
|
match fn_ln.extra { .Fn(f) => { ret_tid = f.ret_type; }, _ => {} }
|
|
if body_ref == 0 { ret fn_tid; }
|
|
|
|
var saved_agg = self.current_aggregate_lang;
|
|
if owner_def_id != 0 {
|
|
var owner = def_node(self, owner_def_id);
|
|
if owner != null && owner.lang_id != 0 {
|
|
self.current_aggregate_lang = owner.lang_id;
|
|
}
|
|
}
|
|
|
|
push_scope(self);
|
|
var i: usize = 0;
|
|
while i < params_ref.len {
|
|
var pref = list_at(params_ref, i);
|
|
var pd = node(self, pref);
|
|
if pd != null && is_param_decl(pd) {
|
|
var pname = ByteView.empty();
|
|
var ptype: usize = 0;
|
|
match pd.body { .ParamDecl(p) => { pname = p.name; ptype = p.type_expr; }, _ => {} }
|
|
var psym = Symbol { .name = pname, .kind = .Var, .node = 0 };
|
|
psym.node = LangType.alloc_def(&self.types, .Var, pname, pd.span, self.ast as *any, pref);
|
|
var p_tid = infer(self, ptype);
|
|
var pdef = def_node(self, psym.node);
|
|
if pdef != null {
|
|
pdef.lang_id = p_tid;
|
|
pdef.state = if p_tid != 0 { .Resolved } else { .Error };
|
|
}
|
|
pd.resolved_def_id = psym.node;
|
|
if !Scope.insert(&self.scope, self.scope.current_scope, psym) {
|
|
diag_name(self, pd.span, .DuplicateSymbol, "duplicate symbol `", pname);
|
|
}
|
|
}
|
|
i = i + 1;
|
|
}
|
|
var old_ret = self.current_fn_ret_tid;
|
|
self.current_fn_ret_tid = ret_tid;
|
|
infer(self, body_ref);
|
|
self.current_fn_ret_tid = old_ret;
|
|
pop_scope(self);
|
|
self.current_aggregate_lang = saved_agg;
|
|
ret fn_tid;
|
|
}
|
|
|
|
fn infer_type_decl(self: *Sema, n: *Node, pattern_ref: usize, type_ref: usize): usize {
|
|
var pat = node(self, pattern_ref);
|
|
if pat == null || !is_pattern_ident(pat) { ret 0; }
|
|
if type_ref == 0 { ret 0; }
|
|
var def: *DefNode = null;
|
|
if n.resolved_def_id != 0 { def = def_node(self, n.resolved_def_id); }
|
|
if def == null { ret 0; }
|
|
var lang = def.lang_id;
|
|
if lang == 0 { lang = eval_decl(self, n.resolved_def_id); }
|
|
if lang == 0 { ret 0; }
|
|
var te = node(self, type_ref);
|
|
if te != null && is_type_aggregate(te) {
|
|
var members = ref_vec();
|
|
match te.body { .TypeAggregate(a) => { members = a.members; }, _ => {} }
|
|
var i: usize = 0;
|
|
while i < members.len {
|
|
var mref = list_at(members, i);
|
|
var mn = node(self, mref);
|
|
if mn != null && kind_is_body_fn_decl(mn) { infer(self, mref); }
|
|
i = i + 1;
|
|
}
|
|
}
|
|
ret lang;
|
|
}
|
|
|
|
fn kind_is_body_fn_decl(n: *Node): bool {
|
|
match n.body { .FnDecl(&x) => { ret true; }, _ => { ret false; } }
|
|
ret false;
|
|
}
|
|
|
|
fn infer_ret(self: *Sema, n: *Node, expr_ref: usize): usize {
|
|
if expr_ref != 0 {
|
|
if self.current_fn_ret_tid != 0 {
|
|
coerce(self, expr_ref, self.current_fn_ret_tid);
|
|
} else {
|
|
infer(self, expr_ref);
|
|
}
|
|
} else if self.current_fn_ret_tid != 0 {
|
|
var u = make_unit(self);
|
|
if self.current_fn_ret_tid != u {
|
|
diag(self, n.span, .MustReturnValue, "function must return a value");
|
|
}
|
|
}
|
|
ret 0;
|
|
}
|
|
|
|
fn infer_break(self: *Sema, n: *Node, label_ref: usize, expr_ref: usize): usize {
|
|
var lbl = label_name(self, label_ref);
|
|
if self.label_stack.len == 0 {
|
|
diag(self, n.span, .BreakOutsideLoop, "`break` outside of loop");
|
|
ret 0;
|
|
}
|
|
if lbl.len != 0 && !label_in_stack(self, lbl) {
|
|
diag_name(self, n.span, .UndefinedLabel, "undefined label `$", lbl);
|
|
}
|
|
if expr_ref != 0 {
|
|
if self.current_loop_break_tid != 0 {
|
|
coerce(self, expr_ref, self.current_loop_break_tid);
|
|
} else {
|
|
infer(self, expr_ref);
|
|
}
|
|
}
|
|
ret 0;
|
|
}
|
|
|
|
fn infer_continue(self: *Sema, n: *Node, label_ref: usize): usize {
|
|
var lbl = label_name(self, label_ref);
|
|
if self.label_stack.len == 0 {
|
|
diag(self, n.span, .ContinueOutsideLoop, "`continue` outside of loop");
|
|
ret 0;
|
|
}
|
|
if lbl.len != 0 && !label_in_stack(self, lbl) {
|
|
diag_name(self, n.span, .UndefinedLabel, "undefined label `$", lbl);
|
|
}
|
|
ret 0;
|
|
}
|
|
|
|
fn label_in_stack(self: *Sema, lbl: ByteView): bool {
|
|
var i = self.label_stack.len;
|
|
while i > 0 {
|
|
var cur: ByteView = ByteView.empty();
|
|
match Vec.get(&self.label_stack, i - 1) {
|
|
.Ok(ptr) => { cur = (ptr as *ByteView).*; },
|
|
.Err(e) => {},
|
|
}
|
|
if cur.len != 0 && ByteView.eq(cur, lbl) { ret true; }
|
|
i = i - 1;
|
|
}
|
|
ret false;
|
|
}
|
|
|
|
|
|
type SymPrint = struct {
|
|
key: ByteView,
|
|
name: ByteView,
|
|
node: usize,
|
|
}
|
|
|
|
fn sym_at(v: *Vec, i: usize): *SymPrint {
|
|
match Vec.get(v, i) {
|
|
.Ok(ptr) => { ret ptr as *SymPrint; },
|
|
.Err(error) => { ret null; },
|
|
}
|
|
ret null;
|
|
}
|
|
|
|
fn out_str(buf: *ByteBuffer, s: []u8): bool {
|
|
match ByteBuffer.append(buf, s) {
|
|
.Ok => { ret true; },
|
|
.Err(error) => { ret false; },
|
|
}
|
|
ret false;
|
|
}
|
|
|
|
fn out_u(buf: *ByteBuffer, v: usize): bool {
|
|
match append_usize(buf, v, 10, false) {
|
|
.Ok => { ret true; },
|
|
.Err(error) => { ret false; },
|
|
}
|
|
ret false;
|
|
}
|
|
|
|
fn dump(self: *Sema, out: *ByteBuffer): bool {
|
|
if !out_str(out, "Scopes:\n") { ret false; }
|
|
var i: usize = 1;
|
|
while i < self.scope.scopes.len {
|
|
var sn = Scope.node(&self.scope, i);
|
|
if sn == null { i = i + 1; continue; }
|
|
if !out_str(out, "scope[") { ret false; }
|
|
if !out_u(out, i) { ret false; }
|
|
if !out_str(out, "] parent=") { ret false; }
|
|
if !out_u(out, sn.parent) { ret false; }
|
|
if !out_str(out, "\n") { ret false; }
|
|
|
|
// 收集后按符号名排序输出(顺序与哈希布局解耦)。
|
|
var entries = Vec.blank(@sizeof(SymPrint), @alignof(SymPrint));
|
|
var sc = Map.slot_count(sn.symbols);
|
|
var j: usize = 0;
|
|
while j < sc {
|
|
if Map.slot_occupied(Map.slot_state(sn.symbols, j)) {
|
|
var kp = Map.key_at(&sn.symbols, j);
|
|
var vp = Map.val_at(&sn.symbols, j);
|
|
var sym = (vp as *Symbol).*;
|
|
var sp = SymPrint {
|
|
.key = (kp as *ByteView).*,
|
|
.name = sym.name,
|
|
.node = sym.node,
|
|
};
|
|
Vec.push(&entries, &sp as *u8);
|
|
}
|
|
j = j + 1;
|
|
}
|
|
|
|
// 插入排序(作用域内符号很少)
|
|
var a: usize = 1;
|
|
while a < entries.len {
|
|
var cur = sym_at(&entries, a).*;
|
|
var b = a;
|
|
while b > 0 {
|
|
var prev = sym_at(&entries, b - 1).*;
|
|
if ByteView.cmp(prev.key, cur.key) <= 0 { break; }
|
|
var pv = prev;
|
|
Vec.set(&entries, b, &pv as *u8);
|
|
b = b - 1;
|
|
}
|
|
var cv = cur;
|
|
Vec.set(&entries, b, &cv as *u8);
|
|
a = a + 1;
|
|
}
|
|
|
|
var e: usize = 0;
|
|
while e < entries.len {
|
|
var sp2 = sym_at(&entries, e).*;
|
|
if !out_str(out, " ") { ret false; }
|
|
if !out_str(out, ByteView.as_slice(sp2.key)) { ret false; }
|
|
if !out_str(out, " -> def`") { ret false; }
|
|
if !out_str(out, ByteView.as_slice(sp2.name)) { ret false; }
|
|
if !out_str(out, "`#") { ret false; }
|
|
if !out_u(out, sp2.node) { ret false; }
|
|
if !out_str(out, "\n") { ret false; }
|
|
e = e + 1;
|
|
}
|
|
Vec.drop(&entries);
|
|
i = i + 1;
|
|
}
|
|
|
|
if !out_str(out, "LangTypeTable:\n") { ret false; }
|
|
i = 1;
|
|
while i < self.types.lang_table.len {
|
|
if !out_str(out, " lang_id#") { ret false; }
|
|
if !out_u(out, i) { ret false; }
|
|
if !out_str(out, " ") { ret false; }
|
|
if !LangType.dump(&self.types, i, out) { ret false; }
|
|
if !out_str(out, "\n") { ret false; }
|
|
i = i + 1;
|
|
}
|
|
|
|
if !out_str(out, "TypeTable:\n") { ret false; }
|
|
i = 1;
|
|
while i < self.types.ir.table.len {
|
|
if !out_str(out, " type_id#") { ret false; }
|
|
if !out_u(out, i) { ret false; }
|
|
if !out_str(out, " ") { ret false; }
|
|
if !TypeTable.pure_dump(&self.types.ir, i, out) { ret false; }
|
|
if !out_str(out, "\n") { ret false; }
|
|
i = i + 1;
|
|
}
|
|
|
|
if !out_str(out, "DefTable:\n") { ret false; }
|
|
i = 1;
|
|
while i < self.types.defs.table.len {
|
|
if !out_str(out, " def#") { ret false; }
|
|
if !out_u(out, i) { ret false; }
|
|
if !out_str(out, " ") { ret false; }
|
|
if !DefTable.dump(&self.types.defs, i, out) { ret false; }
|
|
if !out_str(out, "\n") { ret false; }
|
|
i = i + 1;
|
|
}
|
|
|
|
ret Attr.dump(&self.attr, out);
|
|
}
|
|
|
|
|
|
// 走完整前端:lex -> parse -> sema;返回 0 表示无(新)诊断。
|
|
fn sema_source(text: []u8): i32 {
|
|
var sources = set_new();
|
|
match set_add(&sources, ByteView.from_slice("<sema-test>"), ByteView.from_slice(text)) {
|
|
.Ok(idx) => {},
|
|
.Err(error) => { ret 99; },
|
|
}
|
|
|
|
var diag = Diag.new(&sources);
|
|
|
|
var text_view = ByteView.from_slice(text);
|
|
var tokens = Vec.blank(@sizeof(Token), @alignof(Token));
|
|
match Lexer.lex(text_view, &diag, 0) {
|
|
.Ok(v) => { tokens = v; },
|
|
.Err(error) => {
|
|
Vec.drop(&tokens);
|
|
Diag.drop(&diag);
|
|
set_drop(&sources);
|
|
ret 99;
|
|
},
|
|
}
|
|
|
|
var ast: Ast = undefined;
|
|
match Ast.new(&sources) {
|
|
.Ok(v) => { ast = v; },
|
|
.Err(error) => {
|
|
Vec.drop(&tokens);
|
|
Diag.drop(&diag);
|
|
set_drop(&sources);
|
|
ret 99;
|
|
},
|
|
}
|
|
|
|
var p = Parser.new(&ast, tokens, &diag);
|
|
Parser.parse(&p);
|
|
if Diag.has_error(diag) || p.failed {
|
|
Ast.drop(&ast);
|
|
Vec.drop(&tokens);
|
|
Diag.drop(&diag);
|
|
set_drop(&sources);
|
|
ret 1;
|
|
}
|
|
|
|
var sema: Sema = undefined;
|
|
match new(&ast, &diag) {
|
|
.Ok(v) => { sema = v; },
|
|
.Err(error) => {
|
|
Ast.drop(&ast);
|
|
Vec.drop(&tokens);
|
|
Diag.drop(&diag);
|
|
set_drop(&sources);
|
|
ret 99;
|
|
},
|
|
}
|
|
run(&sema);
|
|
var bad = Diag.has_error(diag);
|
|
drop(&sema);
|
|
Ast.drop(&ast);
|
|
Vec.drop(&tokens);
|
|
Diag.drop(&diag);
|
|
set_drop(&sources);
|
|
if bad { ret 2; }
|
|
ret 0;
|
|
}
|
|
|
|
fn main() {
|
|
@assert(sema_source("fn main(): i32 { ret 0; }") == 0, "sema simple");
|
|
@assert(sema_source("fn main(): i32 { var x: i32 = 1; ret x; }") == 0, "sema var");
|
|
@assert(sema_source("fn main(): i32 { ret 0; }\nfn bad(): i32 { ret nope; }") != 0,
|
|
"sema undefined");
|
|
}
|