fix(lower): auto-ref compound lvalues passed to *T params
The implicit address-of that gives `*T` params reference semantics only fired for plain identifiers (`mut(v)`). For a field-access / index / deref lvalue (`make_move(self.board, m)`, `mut(w.s)`), the branch was skipped: the arg was loaded into a temporary and the callee mutated a throwaway copy — silent data loss, with the type check satisfied through the temp so no diagnostic fired. Now compound lvalues auto-ref too: take the real lvalue address via `lowerExprAsPtr`, normalizing the "place" ref to `*T` exactly as `@field_access` does. Mutations through the pointer are now visible to the caller, matching the identifier case. Regression: examples/255-autoref-compound-lvalue.sx.
This commit is contained in:
@@ -6784,6 +6784,37 @@ pub const Lowering = struct {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Implicit address-of for compound lvalues (field access / index /
|
||||
// deref): when the param expects `*T` and the arg is an addressable
|
||||
// lvalue of type `T`, pass the lvalue's real address (GEP) — same
|
||||
// reference semantics as the identifier case above. Without this the
|
||||
// arg would be loaded into a temporary and the callee would mutate a
|
||||
// throwaway copy (silent data loss — e.g. `make_move(self.board, m)`).
|
||||
if (ai < param_types.len and (arg.data == .field_access or arg.data == .index_expr or arg.data == .deref_expr)) {
|
||||
const pt = param_types[ai];
|
||||
if (!pt.isBuiltin()) {
|
||||
const pti = self.module.types.get(pt);
|
||||
if (pti == .pointer and self.inferExprType(arg) == pti.pointer.pointee) {
|
||||
// `lowerExprAsPtr` yields the lvalue's address, typed
|
||||
// either as `*T` already (index/deref) or as the pointee
|
||||
// `T` (a field "place" ref); normalize to `*T` — exactly
|
||||
// what `@field_access` does.
|
||||
const place = self.lowerExprAsPtr(arg);
|
||||
const place_ty = self.builder.getRefType(place);
|
||||
const ref: ?Ref = if (place_ty == pt)
|
||||
place
|
||||
else if (place_ty == pti.pointer.pointee)
|
||||
self.builder.emit(.{ .addr_of = .{ .operand = place } }, pt)
|
||||
else
|
||||
null;
|
||||
if (ref) |r| {
|
||||
args.append(self.alloc, r) catch unreachable;
|
||||
self.target_type = saved_target;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const val = self.lowerExpr(arg);
|
||||
self.target_type = saved_target;
|
||||
// Passing a `*T` where a `T` value is expected — a by-reference loop
|
||||
|
||||
Reference in New Issue
Block a user