sema/ir: kill remaining s64 fallbacks (sema Type + getRefType)
- types.Type: add dedicated `unresolved` variant (mirrors ir.TypeId.unresolved) with eql/displayName arms; bridgeType maps it to TypeId.unresolved. - sema.inferExprType + signature/field resolution: every Type.fromTypeExpr / fromName / symbol lookup miss and call/field/index fallthrough now yields Type.unresolved instead of a fabricated s(64). A variadic `..xs: []T` slice element is taken from T, not a guessed "s32". Genuine literal defaults (int=>s64, float=>f32, .len=>s64) kept. - Builder.getRefType: an unlocatable ref (no active function / out-of-range) returns .unresolved, not .s64 -- this is the accurate type source the pack mono / binop / null-cmp fixes rely on, so it must not fabricate. 236 examples + unit tests (incl sema) green.
This commit is contained in:
@@ -321,9 +321,11 @@ pub const Builder = struct {
|
||||
blk.first_ref = self.inst_counter;
|
||||
}
|
||||
|
||||
/// Get the type of a previously emitted instruction Ref.
|
||||
/// Get the type of a previously emitted instruction Ref. A ref that can't
|
||||
/// be located (no active function, or an out-of-range ref) has no knowable
|
||||
/// type — return the `.unresolved` sentinel rather than a fabricated `.s64`.
|
||||
pub fn getRefType(self: *Builder, ref: Ref) TypeId {
|
||||
if (self.func == null) return .s64;
|
||||
if (self.func == null) return .unresolved;
|
||||
const func = self.currentFunc();
|
||||
const ref_idx = @intFromEnum(ref);
|
||||
// Check function parameters first (refs 0..N-1)
|
||||
@@ -336,7 +338,7 @@ pub const Builder = struct {
|
||||
return block.insts.items[ref_idx - first].ty;
|
||||
}
|
||||
}
|
||||
return .s64;
|
||||
return .unresolved;
|
||||
}
|
||||
|
||||
// ── Emit helpers ────────────────────────────────────────────────
|
||||
|
||||
@@ -163,6 +163,7 @@ pub fn bridgeType(ty: sx_types.Type, table: *TypeTable) TypeId {
|
||||
} });
|
||||
},
|
||||
.meta_type => .any, // meta types map to Any for now
|
||||
.unresolved => .unresolved,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
35
src/sema.zig
35
src/sema.zig
@@ -136,11 +136,16 @@ pub const Analyzer = struct {
|
||||
var param_types = std.ArrayList(Type).empty;
|
||||
var has_variadic = false;
|
||||
for (fd.params) |param| {
|
||||
const pt = Type.fromTypeExpr(param.type_expr) orelse Type.s(64);
|
||||
const pt = Type.fromTypeExpr(param.type_expr) orelse Type.unresolved;
|
||||
if (param.is_variadic) {
|
||||
has_variadic = true;
|
||||
// Variadic param becomes a slice type
|
||||
const elem_name = if (param.type_expr.data == .type_expr) param.type_expr.data.type_expr.name else "s32";
|
||||
const elem_name = switch (param.type_expr.data) {
|
||||
.type_expr => |te| te.name,
|
||||
// `..xs: []T` — the element is T, not a guessed s32.
|
||||
.slice_type_expr => |st| if (st.element_type.data == .type_expr) st.element_type.data.type_expr.name else "<unresolved>",
|
||||
else => "<unresolved>",
|
||||
};
|
||||
try param_types.append(self.allocator, .{ .slice_type = .{ .element_name = elem_name } });
|
||||
} else {
|
||||
try param_types.append(self.allocator, pt);
|
||||
@@ -169,7 +174,7 @@ pub const Analyzer = struct {
|
||||
const lam = cd.value.data.lambda;
|
||||
var param_types = std.ArrayList(Type).empty;
|
||||
for (lam.params) |param| {
|
||||
const pt = Type.fromTypeExpr(param.type_expr) orelse Type.s(64);
|
||||
const pt = Type.fromTypeExpr(param.type_expr) orelse Type.unresolved;
|
||||
try param_types.append(self.allocator, pt);
|
||||
}
|
||||
const ret = if (lam.return_type) |rt|
|
||||
@@ -222,7 +227,7 @@ pub const Analyzer = struct {
|
||||
}
|
||||
if (i < sd.field_names.len) {
|
||||
try all_names.append(self.allocator, sd.field_names[i]);
|
||||
const resolved = Type.fromTypeExpr(sd.field_types[i]) orelse Type.s(64);
|
||||
const resolved = Type.fromTypeExpr(sd.field_types[i]) orelse Type.unresolved;
|
||||
try all_types.append(self.allocator, resolved);
|
||||
}
|
||||
}
|
||||
@@ -233,7 +238,7 @@ pub const Analyzer = struct {
|
||||
} else {
|
||||
var field_types = std.ArrayList(Type).empty;
|
||||
for (sd.field_types) |ft| {
|
||||
const resolved = Type.fromTypeExpr(ft) orelse Type.s(64);
|
||||
const resolved = Type.fromTypeExpr(ft) orelse Type.unresolved;
|
||||
try field_types.append(self.allocator, resolved);
|
||||
}
|
||||
try self.struct_types.put(sd.name, .{
|
||||
@@ -369,11 +374,11 @@ pub const Analyzer = struct {
|
||||
j -= 1;
|
||||
const sym = self.symbols.items[indices.items[j]];
|
||||
if (sym.scope_depth <= self.scope_depth) {
|
||||
return sym.ty orelse Type.s(64);
|
||||
return sym.ty orelse Type.unresolved;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Type.s(64);
|
||||
return Type.unresolved;
|
||||
},
|
||||
.if_expr => |ie| {
|
||||
return self.inferExprType(ie.then_branch);
|
||||
@@ -391,7 +396,7 @@ pub const Analyzer = struct {
|
||||
return .void_type;
|
||||
},
|
||||
.call => |call_node| {
|
||||
const callee_name = self.resolveCalleeName(call_node) orelse return Type.s(64);
|
||||
const callee_name = self.resolveCalleeName(call_node) orelse return Type.unresolved;
|
||||
// Check fn_signatures registry
|
||||
if (self.fn_signatures.get(callee_name)) |sig| {
|
||||
return sig.return_type;
|
||||
@@ -405,7 +410,7 @@ pub const Analyzer = struct {
|
||||
if (call_node.args.len > 0) return self.inferExprType(call_node.args[0]);
|
||||
return .f32;
|
||||
}
|
||||
return Type.s(64);
|
||||
return Type.unresolved;
|
||||
},
|
||||
.unary_op => |unop| {
|
||||
return self.inferExprType(unop.operand);
|
||||
@@ -426,17 +431,17 @@ pub const Analyzer = struct {
|
||||
}
|
||||
}
|
||||
if (obj_ty.isArray()) {
|
||||
return Type.fromName(obj_ty.array_type.element_name) orelse Type.s(64);
|
||||
return Type.fromName(obj_ty.array_type.element_name) orelse Type.unresolved;
|
||||
}
|
||||
return Type.s(64);
|
||||
return Type.unresolved;
|
||||
},
|
||||
.index_expr => |ie| {
|
||||
const obj_ty = self.inferExprType(ie.object);
|
||||
if (obj_ty == .string_type) return Type.u(8);
|
||||
if (obj_ty.isArray()) {
|
||||
return Type.fromName(obj_ty.array_type.element_name) orelse Type.s(64);
|
||||
return Type.fromName(obj_ty.array_type.element_name) orelse Type.unresolved;
|
||||
}
|
||||
return Type.s(64);
|
||||
return Type.unresolved;
|
||||
},
|
||||
.slice_expr => |se| {
|
||||
const obj_ty = self.inferExprType(se.object);
|
||||
@@ -677,7 +682,7 @@ pub const Analyzer = struct {
|
||||
{
|
||||
var param_types = std.ArrayList(Type).empty;
|
||||
for (fd.params) |param| {
|
||||
const pt = Type.fromTypeExpr(param.type_expr) orelse Type.s(64);
|
||||
const pt = Type.fromTypeExpr(param.type_expr) orelse Type.unresolved;
|
||||
try param_types.append(self.allocator, pt);
|
||||
}
|
||||
try self.fn_signatures.put(fd.name, .{
|
||||
@@ -1026,7 +1031,7 @@ pub const Analyzer = struct {
|
||||
fn inferFnReturnType(self: *Analyzer, params: []const ast.Param, body: *const Node) ?Type {
|
||||
self.pushScope() catch return null;
|
||||
for (params) |param| {
|
||||
const pt = Type.fromTypeExpr(param.type_expr) orelse Type.s(64);
|
||||
const pt = Type.fromTypeExpr(param.type_expr) orelse Type.unresolved;
|
||||
self.addSymbol(param.name, .param, pt, param.name_span) catch {};
|
||||
}
|
||||
// Arrow fn_decl wraps body in block{[expr]} — unwrap to inner expression
|
||||
|
||||
@@ -29,6 +29,11 @@ pub const Type = union(enum) {
|
||||
optional_type: OptionalTypeInfo,
|
||||
meta_type: MetaTypeInfo,
|
||||
tuple_type: TupleTypeInfo,
|
||||
/// Type resolution failed (sema couldn't infer/resolve). A dedicated
|
||||
/// sentinel — never a legitimate type — so callers can't mistake it for a
|
||||
/// real result the way a fabricated `s(64)` would be. Mirrors
|
||||
/// `ir.TypeId.unresolved`.
|
||||
unresolved,
|
||||
|
||||
pub const SliceTypeInfo = struct {
|
||||
element_name: []const u8,
|
||||
@@ -84,7 +89,7 @@ pub const Type = union(enum) {
|
||||
return switch (self) {
|
||||
.signed => |w| w == other.signed,
|
||||
.unsigned => |w| w == other.unsigned,
|
||||
.f32, .f64, .void_type, .boolean, .string_type, .any_type, .usize_type, .isize_type => true,
|
||||
.f32, .f64, .void_type, .boolean, .string_type, .any_type, .usize_type, .isize_type, .unresolved => true,
|
||||
.enum_type => |n| std.mem.eql(u8, n, other.enum_type),
|
||||
.struct_type => |n| std.mem.eql(u8, n, other.struct_type),
|
||||
.union_type => |n| std.mem.eql(u8, n, other.union_type),
|
||||
@@ -555,6 +560,7 @@ pub const Type = union(enum) {
|
||||
.any_type => "Any",
|
||||
.usize_type => "usize",
|
||||
.isize_type => "isize",
|
||||
.unresolved => "<unresolved>",
|
||||
.enum_type => |name| name,
|
||||
.struct_type => |name| name,
|
||||
.union_type => |name| name,
|
||||
|
||||
Reference in New Issue
Block a user