diff --git a/examples/198-pack-tuple-materialize.sx b/examples/198-pack-tuple-materialize.sx new file mode 100644 index 0000000..3bcf603 --- /dev/null +++ b/examples/198-pack-tuple-materialize.sx @@ -0,0 +1,25 @@ +// Feature 1 — materialize a tuple from a pack via `(..xs.method)` (Decision 2: +// a pack is stored by materializing a tuple). `(..xs.get)` projects `get` over +// the pack and collects the results into a real tuple value, which can then be +// stored, indexed, and (for `Box(T)`) is heterogeneous per position. + +#import "modules/std.sx"; + +Box :: protocol(T: Type) { + get :: () -> T; +} +IntCell :: struct { v: s64; } +StrCell :: struct { s: string; } +impl Box(s64) for IntCell { get :: (self: *IntCell) -> s64 => self.v; } +impl Box(string) for StrCell { get :: (self: *StrCell) -> string => self.s; } + +snapshot :: (..xs: Box) -> void { + t := (..xs.get); // tuple (s64, string) materialized from the pack + print("0={} 1={}\n", t.0, t.1); +} + +main :: () -> s32 { + snapshot(IntCell.{ v = 42 }, StrCell.{ s = "hi" }); + snapshot(StrCell.{ s = "x" }, IntCell.{ v = 7 }); // order swapped → (string, s64) + 0; +} diff --git a/src/ir/lower.zig b/src/ir/lower.zig index c42fca6..57951ca 100644 --- a/src/ir/lower.zig +++ b/src/ir/lower.zig @@ -4703,18 +4703,43 @@ pub const Lowering = struct { // ambient scalar `target_type` (e.g. the enclosing fn's int return // type) can't narrow an element below its field width. Otherwise each // element's type is inferred independently. + // A pack-spread element `(..xs)` / `(..xs.method)` expands to N fields, + // so element-count ≠ field-count and a contextual target tuple can't be + // aligned by index — infer field types from the expanded refs instead. + var has_spread = false; + for (tl.elements) |elem| { + if (elem.value.data == .spread_expr) has_spread = true; + } + var target_fields: ?[]const TypeId = null; - if (self.target_type) |tt| { - if (!tt.isBuiltin()) { - const tinfo = self.module.types.get(tt); - if (tinfo == .tuple and tinfo.tuple.fields.len == tl.elements.len) { - target_fields = tinfo.tuple.fields; + if (!has_spread) { + if (self.target_type) |tt| { + if (!tt.isBuiltin()) { + const tinfo = self.module.types.get(tt); + if (tinfo == .tuple and tinfo.tuple.fields.len == tl.elements.len) { + target_fields = tinfo.tuple.fields; + } } } } const saved_target = self.target_type; for (tl.elements, 0..) |elem, i| { + // Pack-spread element → splice its per-element values as fields. + if (elem.value.data == .spread_expr) { + if (self.packSpreadRefs(elem.value.data.spread_expr.operand, elem.value.span)) |refs| { + defer self.alloc.free(refs); + for (refs) |r| { + elems.append(self.alloc, r) catch unreachable; + field_type_ids.append(self.alloc, self.builder.getRefType(r)) catch unreachable; + name_ids.append(self.alloc, self.module.types.internString("")) catch unreachable; + } + continue; + } + // Not a pack spread (e.g. tuple-value spread) — not yet handled. + _ = self.lowerExpr(elem.value); // surfaces the spread_expr diagnostic + continue; + } const field_ty = if (target_fields) |tf| tf[i] else self.inferExprType(elem.value); self.target_type = field_ty; var val = self.lowerExpr(elem.value); diff --git a/tests/expected/198-pack-tuple-materialize.exit b/tests/expected/198-pack-tuple-materialize.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/expected/198-pack-tuple-materialize.exit @@ -0,0 +1 @@ +0 diff --git a/tests/expected/198-pack-tuple-materialize.txt b/tests/expected/198-pack-tuple-materialize.txt new file mode 100644 index 0000000..5409d7c --- /dev/null +++ b/tests/expected/198-pack-tuple-materialize.txt @@ -0,0 +1,2 @@ +0=42 1=hi +0=x 1=7