ffi: more inferExprType silent-default holes — null_coalesce, struct const, reflection builtins
Three additional arms that previously silently fell through to
`.s64`:
- `.null_coalesce`: `lhs ?? rhs` now returns the inner type of
lhs's optional (when applicable), else the rhs's inferred type.
Without this, `print("{}\n", iw ?? 0.0)` for `iw: ?f32`
inferred as s64 and the float value got truncated through the
pack-mono's Any boxing.
- `.field_access` struct constant: `Phys.GRAVITY` (a `Struct.CONST`
declaration) now consults `struct_const_map` for the resolved
field type. Previously the path hit only `lowerFieldAccess`'s
constant-resolution shortcut, not the AST-level `inferExprType`,
so pack-fn callers misinferred the const's type as `.s64`.
- Reflection builtins (`type_name`, `type_eq`, `has_impl`,
`field_count`, `field_index`, `field_name`, `is_flags`,
`type_of`, `field_value`): their return types live outside
`resolveBuiltin`'s table (they dispatch via
`tryLowerReflectionCall` instead). Recognise them directly in
the `inferExprType` call arm so pack-fn callers mangle the
results with the right tag (.bool for `type_eq` / `has_impl` /
`is_flags`, .string for `type_name` / `field_name`, etc).
All three holes surfaced while attempting the print/format
`..$args` migration; the fixes themselves are general
improvements and stand independently. 218/218.
This commit is contained in:
@@ -11868,6 +11868,20 @@ pub const Lowering = struct {
|
||||
else => .s64,
|
||||
};
|
||||
}
|
||||
// Reflection builtins live outside `resolveBuiltin`'s
|
||||
// table (their lowering goes through
|
||||
// `tryLowerReflectionCall`, not the `BuiltinId`
|
||||
// dispatch). Recognize them here so pack-fn callers
|
||||
// mangle their results with the right tag.
|
||||
if (std.mem.eql(u8, bare_name, "type_name")) return .string;
|
||||
if (std.mem.eql(u8, bare_name, "type_eq")) return .bool;
|
||||
if (std.mem.eql(u8, bare_name, "has_impl")) return .bool;
|
||||
if (std.mem.eql(u8, bare_name, "field_count")) return .s64;
|
||||
if (std.mem.eql(u8, bare_name, "field_index")) return .s64;
|
||||
if (std.mem.eql(u8, bare_name, "field_name")) return .string;
|
||||
if (std.mem.eql(u8, bare_name, "is_flags")) return .bool;
|
||||
if (std.mem.eql(u8, bare_name, "type_of")) return .any;
|
||||
if (std.mem.eql(u8, bare_name, "field_value")) return .any;
|
||||
// Check if it's a generic function — infer return type via type bindings
|
||||
if (self.fn_ast_map.get(name)) |fd| {
|
||||
if (fd.type_params.len > 0) {
|
||||
@@ -12008,6 +12022,17 @@ pub const Lowering = struct {
|
||||
if (ppc.contains(fa.object.data.identifier.name)) return .s64;
|
||||
}
|
||||
}
|
||||
// Struct constant access: `Struct.CONST` — mirrors the
|
||||
// lowerFieldAccess intercept (line 3851). Without this,
|
||||
// `Phys.GRAVITY` (f64) inferred as s64 and pack-fn
|
||||
// callers boxed the float into the int slot.
|
||||
if (fa.object.data == .identifier) {
|
||||
const obj_name = fa.object.data.identifier.name;
|
||||
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ obj_name, fa.field }) catch fa.field;
|
||||
if (self.struct_const_map.get(qualified)) |info| {
|
||||
if (info.ty) |t| return t;
|
||||
}
|
||||
}
|
||||
// M1.3 — `obj.class` on an Obj-C-class pointer returns Class (*void).
|
||||
if (std.mem.eql(u8, fa.field, "class")) {
|
||||
if (self.isObjcClassPointer(self.inferExprType(fa.object))) {
|
||||
@@ -12172,6 +12197,20 @@ pub const Lowering = struct {
|
||||
return .s64;
|
||||
},
|
||||
.chained_comparison => .bool,
|
||||
.null_coalesce => |nc| blk: {
|
||||
// `opt ?? default` — result is the inner type when lhs is
|
||||
// optional (the unwrap path's value), else falls back to
|
||||
// the rhs's type. Without this arm pack-fn callers
|
||||
// misinferred float-optional coalesces as s64 and the
|
||||
// pack mono mangled the arg as int — the actual f64 value
|
||||
// got truncated through Any boxing.
|
||||
const lhs_ty = self.inferExprType(nc.lhs);
|
||||
if (!lhs_ty.isBuiltin()) {
|
||||
const info = self.module.types.get(lhs_ty);
|
||||
if (info == .optional) break :blk info.optional.child;
|
||||
}
|
||||
break :blk self.inferExprType(nc.rhs);
|
||||
},
|
||||
// Statements don't produce values
|
||||
.assignment, .var_decl, .const_decl, .fn_decl, .return_stmt,
|
||||
.defer_stmt, .push_stmt, .multi_assign, .destructure_decl,
|
||||
|
||||
Reference in New Issue
Block a user