fix: xx pack[i] to a protocol target heap-copies the element
Erasing a single comptime-pack element to a protocol value (`xx sources[0]` with a protocol target) tripped the pack-as-value error: buildProtocolErasure treated the index_expr as an lvalue and took its address via lowerExprAsPtr, whose .index_expr arm lowers the bare pack as a value (a pack is comptime-only with no runtime storage). isLvalueExpr now reports a comptime pack index as an rvalue, decided via the same packArgNodeAt predicate the value path uses — so the value and lvalue paths can't diverge on what counts as a pack element — and erasure heap-copies the already-materialized element instead. Resolves issue 0135. Regression tests: examples/0547, 0548.
This commit is contained in:
@@ -333,9 +333,26 @@ pub fn tryUserConversion(self: *Lowering, operand: Ref, operand_node: *const Nod
|
||||
/// `xx <struct-typed expr>` to decide between borrow (lvalue → take the
|
||||
/// address) and heap-copy (rvalue → allocate a fresh copy).
|
||||
pub fn isLvalueExpr(self: *Lowering, node: *const Node) bool {
|
||||
_ = self;
|
||||
return switch (node.data) {
|
||||
.identifier, .field_access, .index_expr, .deref_expr => true,
|
||||
.identifier, .field_access, .deref_expr => true,
|
||||
// A comptime pack index (`pack[i]`) is NOT an lvalue: a pack is
|
||||
// comptime-only with no runtime storage — `pack[i]` resolves to the
|
||||
// call-site arg node, which only acquires storage when lowered as a
|
||||
// value. Taking its address via `lowerExprAsPtr` would lower the bare
|
||||
// pack as a value and trip the pack-as-value error (issue 0135).
|
||||
// Reporting it as an rvalue routes `buildProtocolErasure` into its
|
||||
// heap-copy branch, which copies the already-materialized element.
|
||||
// A non-pack index (array/slice element) is a genuine lvalue.
|
||||
//
|
||||
// Decide pack-ness with the SAME predicate the value path uses —
|
||||
// `packArgNodeAt` (the `pack_arg_nodes` substitution map) — NOT
|
||||
// `isPackName` (the `pack_param_count` map). The two maps are set
|
||||
// together in the pack-fn path but the comptime-call path
|
||||
// (comptime.zig) installs only `pack_arg_nodes`; using `isPackName`
|
||||
// there would disagree with the value substitution and mis-route the
|
||||
// erasure. Sharing one predicate keeps the value/lvalue paths from
|
||||
// diverging on what counts as a pack element.
|
||||
.index_expr => self.packArgNodeAt(&node.data.index_expr) == null,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user