ffi M5.A.next.2a.B: pack typed indexing — args[$i] substitutes call arg
Pack-fn bodies that index the pack via `args[<int_literal>]` now resolve to the i-th call-site argument's lowered value directly, propagating the call arg's concrete type instead of the boxed `Any` that the `[]Any` slice path returns. New plumbing in `src/ir/lower.zig`: - `pack_arg_nodes: ?std.StringHashMap([]const *const Node)` on Lowering. Maps a pack param name (e.g. "args") to the slice of call-site arg AST nodes. - `lowerComptimeCall` populates the map when the variadic param is heterogeneous (`is_variadic AND is_comptime`, i.e. the `..$args` form). Plain `args: ..Any` keeps the existing `[]Any` slice path so stdlib's `format`/`print` continue unchanged. The map is saved/restored across nested calls mirroring `comptime_param_nodes`. - `packArgNodeAt(ie)` returns the call-arg node when an index_expr matches `<pack_name>[<comptime_int_literal>]` with the index in range; null otherwise (fall through to standard slice indexing for runtime indices or non-pack bases). - `lowerIndexExpr` checks `packArgNodeAt` first; on a hit it lowers the call arg node directly. `inferExprType`'s `index_expr` arm does the parallel check so AST-level type inference (e.g., for field-access type checking) sees the concrete call-arg type. `examples/156-pack-typed-index.sx` flips from "field 'x' not found on type 'Any'" to `7` — `args[0].x` now resolves through the concrete `Point` type instead of Any. Out of scope (deferred): non-literal comptime indices (`args[$i]` where `$i` is an arbitrary comptime expression); `$args[$i]` in type positions (step 3); per-mono mangling (monomorphisation stays inline-only). 195/195 example tests + `zig build test` green.
This commit is contained in:
@@ -155,6 +155,15 @@ pub const Lowering = struct {
|
||||
/// and trips LLVM's "Terminator found in the middle of a basic
|
||||
/// block" verifier.
|
||||
inline_return_target: ?InlineReturnInfo = null,
|
||||
/// Active pack-arg-node bindings during a comptime call's body lowering.
|
||||
/// Maps the pack-param name (e.g. `args`) to the slice of call-site
|
||||
/// argument AST nodes. `lowerIndexExpr` (and `inferExprType`) check
|
||||
/// this map when the index expression's base is an identifier matching
|
||||
/// a pack name AND the index is a comptime int literal — substitutes
|
||||
/// with the i-th call arg's lowered value so the static type tracks
|
||||
/// the call arg's real type instead of `Any`. The `[]Any` slice path
|
||||
/// remains the runtime-indexed fallback for non-literal indices.
|
||||
pack_arg_nodes: ?std.StringHashMap([]const *const Node) = null,
|
||||
struct_const_map: std.StringHashMap(StructConstInfo) = std.StringHashMap(StructConstInfo).init(std.heap.page_allocator), // "Struct.CONST" → value info
|
||||
module_const_map: std.StringHashMap(ModuleConstInfo) = std.StringHashMap(ModuleConstInfo).init(std.heap.page_allocator), // module-level value constants (e.g. AF_INET :s32: 2)
|
||||
foreign_name_map: std.StringHashMap([]const u8) = std.StringHashMap([]const u8).init(std.heap.page_allocator), // sx name → C name for #foreign renames
|
||||
@@ -4193,6 +4202,15 @@ pub const Lowering = struct {
|
||||
}
|
||||
|
||||
fn lowerIndexExpr(self: *Lowering, ie: *const ast.IndexExpr) Ref {
|
||||
// Pack-arg substitution: `args[<int_literal>]` inside a body
|
||||
// whose enclosing comptime call bound `args` as a pack name.
|
||||
// Lowering the i-th call-site arg directly gives the concrete
|
||||
// call-arg type — bypasses the `[]Any` slice boxing that would
|
||||
// otherwise lose the type. Non-literal indices fall through to
|
||||
// the standard slice indexing path.
|
||||
if (self.packArgNodeAt(ie)) |arg_node| {
|
||||
return self.lowerExpr(arg_node);
|
||||
}
|
||||
const obj = self.lowerExpr(ie.object);
|
||||
const idx = self.lowerExpr(ie.index);
|
||||
// Infer element type from the object's slice/array type
|
||||
@@ -4201,6 +4219,22 @@ pub const Lowering = struct {
|
||||
return self.builder.emit(.{ .index_get = .{ .lhs = obj, .rhs = idx } }, elem_ty);
|
||||
}
|
||||
|
||||
/// Returns the call-site arg AST node when `ie` matches
|
||||
/// `<pack_name>[<comptime_int_literal>]` with the pack name bound
|
||||
/// in the active `pack_arg_nodes` map and the index in range.
|
||||
/// Otherwise null — caller falls back to standard slice indexing.
|
||||
fn packArgNodeAt(self: *Lowering, ie: *const ast.IndexExpr) ?*const Node {
|
||||
const pan = self.pack_arg_nodes orelse return null;
|
||||
if (ie.object.data != .identifier) return null;
|
||||
const arg_nodes = pan.get(ie.object.data.identifier.name) orelse return null;
|
||||
if (ie.index.data != .int_literal) return null;
|
||||
const raw: i64 = ie.index.data.int_literal.value;
|
||||
if (raw < 0) return null;
|
||||
const i: usize = @intCast(raw);
|
||||
if (i >= arg_nodes.len) return null;
|
||||
return arg_nodes[i];
|
||||
}
|
||||
|
||||
fn lowerSliceExpr(self: *Lowering, se: *const ast.SliceExpr) Ref {
|
||||
const obj = self.lowerExpr(se.object);
|
||||
const lo = if (se.start) |s| self.lowerExpr(s) else self.builder.constInt(0, .s64);
|
||||
@@ -7183,11 +7217,26 @@ pub const Lowering = struct {
|
||||
// Build comptime param substitution map: param_name → call_site AST node
|
||||
var cpn = std.StringHashMap(*const Node).init(self.alloc);
|
||||
var call_arg_idx: usize = 0;
|
||||
// Pack-arg-node registration (step 2 of the variadic heterogeneous
|
||||
// type packs feature): when the fn declares a pack param, record
|
||||
// the slice of call-site arg nodes under the pack name so the
|
||||
// body's `args[$i]` lowering can substitute the i-th arg with
|
||||
// its concrete-typed value instead of the `[]Any` slice load.
|
||||
var pack_arg_name: ?[]const u8 = null;
|
||||
var pack_arg_slice: []const *const Node = &.{};
|
||||
|
||||
for (fd.params) |param| {
|
||||
if (param.is_variadic) {
|
||||
// Variadic param: pack remaining call args into []Any slice
|
||||
self.lowerVariadicArgs(param.name, call_node.args, call_arg_idx);
|
||||
// Only heterogeneous pack form `..$args` (is_comptime AND
|
||||
// is_variadic) registers for typed indexing. Plain
|
||||
// `args: ..Any` keeps the existing []Any path so stdlib's
|
||||
// `format`/`print` continue boxing through Any.
|
||||
if (param.is_comptime and call_arg_idx <= call_node.args.len) {
|
||||
pack_arg_name = param.name;
|
||||
pack_arg_slice = call_node.args[call_arg_idx..];
|
||||
}
|
||||
break; // variadic is always the last param
|
||||
}
|
||||
if (call_arg_idx >= call_node.args.len) break;
|
||||
@@ -7227,6 +7276,23 @@ pub const Lowering = struct {
|
||||
self.comptime_param_nodes = cpn;
|
||||
defer self.comptime_param_nodes = saved_cpn;
|
||||
|
||||
// Install pack-arg-node binding. Mirrors `comptime_param_nodes`:
|
||||
// each call owns its own map, nested calls shadow. `lowerIndexExpr`
|
||||
// reads the map for `args[<int_literal>]` substitution.
|
||||
const saved_pan = self.pack_arg_nodes;
|
||||
var pan_map: std.StringHashMap([]const *const Node) = undefined;
|
||||
var pan_installed = false;
|
||||
if (pack_arg_name) |pn| {
|
||||
pan_map = std.StringHashMap([]const *const Node).init(self.alloc);
|
||||
pan_map.put(pn, pack_arg_slice) catch {};
|
||||
self.pack_arg_nodes = pan_map;
|
||||
pan_installed = true;
|
||||
}
|
||||
defer {
|
||||
if (pan_installed) pan_map.deinit();
|
||||
self.pack_arg_nodes = saved_pan;
|
||||
}
|
||||
|
||||
// Lower the body — capture return value for functions with return type
|
||||
const ret_ty = self.resolveReturnType(fd);
|
||||
if (ret_ty != .void) {
|
||||
@@ -11068,6 +11134,9 @@ pub const Lowering = struct {
|
||||
} });
|
||||
},
|
||||
.index_expr => |ie| {
|
||||
if (self.packArgNodeAt(&ie)) |arg_node| {
|
||||
return self.inferExprType(arg_node);
|
||||
}
|
||||
const obj_ty = self.inferExprType(ie.object);
|
||||
return self.getElementType(obj_ty);
|
||||
},
|
||||
|
||||
@@ -1 +1 @@
|
||||
1
|
||||
0
|
||||
|
||||
@@ -1 +1 @@
|
||||
/Users/agra/projects/sx/examples/156-pack-typed-index.sx:25:30: error: field 'x' not found on type 'Any'
|
||||
7
|
||||
|
||||
Reference in New Issue
Block a user