diff --git a/src/ir/lower.zig b/src/ir/lower.zig index cb3f207..f6cbe27 100644 --- a/src/ir/lower.zig +++ b/src/ir/lower.zig @@ -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[]` 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 + /// `[]` 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[]` 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); }, diff --git a/tests/expected/156-pack-typed-index.exit b/tests/expected/156-pack-typed-index.exit index d00491f..573541a 100644 --- a/tests/expected/156-pack-typed-index.exit +++ b/tests/expected/156-pack-typed-index.exit @@ -1 +1 @@ -1 +0 diff --git a/tests/expected/156-pack-typed-index.txt b/tests/expected/156-pack-typed-index.txt index ab39be2..7f8f011 100644 --- a/tests/expected/156-pack-typed-index.txt +++ b/tests/expected/156-pack-typed-index.txt @@ -1 +1 @@ -/Users/agra/projects/sx/examples/156-pack-typed-index.sx:25:30: error: field 'x' not found on type 'Any' +7