ffi: fill inferExprType + inferGenericReturnType silent-default holes
Three general fixes to AST-level type inference that previously fell through to `.s64`: - `inferGenericReturnType` resolved the function's return type only when `tmp_bindings` was non-empty; otherwise it bailed to `.s64`, which silently mis-typed pack-fns with non-generic literal return types (e.g. `walk(..$args) -> string`). Always resolve via `resolveTypeWithBindings`, even with empty bindings. - `inferExprType` `binary_op` arm: `.in_op` now returns `.bool` alongside the other comparison/logical ops. Previously the `else` branch returned the LHS type (e.g. `2 in (1,2,3)` → `s64`). - `inferExprType` field-access call arm: when a namespace-qualified call (`pkg.hello()`) hasn't been lowered yet, consult `fn_ast_map` for the qualified name AND the bare field name (matches `lowerCall`'s effective-name resolution order). Without this, cross-module calls returned `.s64`. Surfaces during the still-deferred print/format → `..$args` migration where the pack mono's per-position type tag depends on correct call-arg type inference. The fixes themselves are general improvements that stand independently. 217/217.
This commit is contained in:
@@ -11813,7 +11813,7 @@ pub const Lowering = struct {
|
||||
.bool_literal => .bool,
|
||||
.null_literal => .void,
|
||||
.binary_op => |bop| switch (bop.op) {
|
||||
.eq, .neq, .lt, .lte, .gt, .gte, .and_op, .or_op => .bool,
|
||||
.eq, .neq, .lt, .lte, .gt, .gte, .and_op, .or_op, .in_op => .bool,
|
||||
else => self.inferExprType(bop.lhs),
|
||||
},
|
||||
.unary_op => |uop| switch (uop.op) {
|
||||
@@ -11965,11 +11965,30 @@ pub const Lowering = struct {
|
||||
const ti = self.module.types.get(ty);
|
||||
if (ti == .tagged_union or ti == .@"enum") return ty;
|
||||
}
|
||||
// Check for qualified function call
|
||||
// Check for qualified function call. `resolveFuncByName`
|
||||
// only finds ALREADY-LOWERED functions; namespace
|
||||
// imports are typically lowered lazily on demand, so
|
||||
// a fresh `pkg.hello()` call site may resolve through
|
||||
// `fn_ast_map` first. Without this, the call's return
|
||||
// type silently falls through to `.s64` and any
|
||||
// pack-fn caller (e.g. `print("{}\n", pkg.hello())`)
|
||||
// mangles the arg as s64, mis-tagging the actual
|
||||
// string in the Any box.
|
||||
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ tn, cfa.field }) catch cfa.field;
|
||||
if (self.resolveFuncByName(qualified)) |fid| {
|
||||
return self.module.functions.items[@intFromEnum(fid)].ret;
|
||||
}
|
||||
if (self.fn_ast_map.get(qualified)) |qfd| {
|
||||
if (qfd.return_type) |rt| return self.resolveType(rt);
|
||||
return .void;
|
||||
}
|
||||
// Namespace aliases sometimes register the function
|
||||
// under its bare name (matches `lowerCall`'s effective-
|
||||
// name resolution order).
|
||||
if (self.fn_ast_map.get(cfa.field)) |bfd| {
|
||||
if (bfd.return_type) |rt| return self.resolveType(rt);
|
||||
return .void;
|
||||
}
|
||||
}
|
||||
} else if (c.callee.data == .enum_literal) {
|
||||
// .Variant(args) — dot-shorthand enum construction
|
||||
@@ -12209,15 +12228,20 @@ pub const Lowering = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve return type with all bindings
|
||||
if (tmp_bindings.count() > 0) {
|
||||
const saved = self.type_bindings;
|
||||
self.type_bindings = tmp_bindings;
|
||||
const ret = self.resolveTypeWithBindings(fd.return_type.?);
|
||||
self.type_bindings = saved;
|
||||
return ret;
|
||||
}
|
||||
return .s64;
|
||||
// Resolve return type with whatever bindings we built. Even an
|
||||
// empty `tmp_bindings` is a valid input — non-generic literal
|
||||
// return types (e.g. `walk(..$args) -> string`) still need to
|
||||
// resolve through `resolveTypeWithBindings`, not fall through
|
||||
// to the historical `.s64` default. The default silently
|
||||
// misclassified pack-fn calls whose return type was a fixed
|
||||
// literal — every consumer (e.g. print's pack-shape mangling)
|
||||
// inferred `s64` and routed the value through the wrong Any
|
||||
// tag.
|
||||
const saved = self.type_bindings;
|
||||
self.type_bindings = tmp_bindings;
|
||||
const ret = self.resolveTypeWithBindings(fd.return_type.?);
|
||||
self.type_bindings = saved;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Lower the `xx` operator (type coercion).
|
||||
|
||||
Reference in New Issue
Block a user