fix(0138): diagnose @scalar-const address-of (no storage)
A scalar `::` constant folds to its value and has no storage. The unary `.address_of` lowering (src/ir/lower/expr.zig) skipped the alloca path (is_alloca == false) and resolveGlobalRef (scalar consts get no storage global), falling through to the generic addr_of arm, which reinterpreted the folded value as a pointer: `inttoptr (i64 <value> to ptr)`. That wild pointer segfaulted on deref and emitted invalid stores for inline-asm `-> @const`. Diagnose instead, in the address_of(identifier) path: a non-alloca, non-ref-capture, non-pack-elem scope binding (local scalar const) and a module_const_map name not backed by storage (module scalar const) both report "cannot take the address of constant '<name>' — a scalar '::' constant has no storage …" and return a placeholder Ref. Chose diagnose over materializing read-only storage (consistent with the fold-only scalar model). Array/struct consts keep real storage and stay addressable (@K/@LIT unchanged). Also gives the ASM stream's planned output-to-const rejection for free — asm `-> @const` lowers through the same path. Regression: examples/1177-diagnostics-addr-of-const-rejected.sx. Resolves 0138.
This commit is contained in:
@@ -2000,6 +2000,21 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
const ptr_ty = self.module.types.ptrTo(binding.ty);
|
||||
break :blk self.builder.emit(.{ .addr_of = .{ .operand = binding.ref } }, ptr_ty);
|
||||
}
|
||||
// A non-storage value binding — a scalar `::` constant
|
||||
// folded to its value (`is_alloca == false`, not a
|
||||
// ref-capture pointer or pack-element alias). It has NO
|
||||
// address: array/struct consts get real storage (reached
|
||||
// via `resolveGlobalRef` below), but a scalar const does
|
||||
// not. Taking `@const` would otherwise fall through to the
|
||||
// generic `addr_of` arm and reinterpret the folded value
|
||||
// as a pointer — `inttoptr (i64 <value> to ptr)`, a wild
|
||||
// pointer that segfaults on deref and emits invalid stores
|
||||
// for asm `-> @const` (issue 0138). Diagnose loudly.
|
||||
if (!binding.is_ref_capture and binding.pack_elem == null) {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, node.span, "cannot take the address of constant '{s}' — a scalar '::' constant has no storage (use a '=' variable or a local copy for mutable data)", .{id_name});
|
||||
break :blk self.emitPlaceholder("addr_of_const");
|
||||
}
|
||||
}
|
||||
}
|
||||
// address_of(global) → emit global_addr (pointer to global, not load)
|
||||
@@ -2007,6 +2022,15 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
const ptr_ty = self.module.types.ptrTo(gi.ty);
|
||||
break :blk self.builder.emit(.{ .global_addr = gi.id }, ptr_ty);
|
||||
}
|
||||
// A module-scope scalar `::` constant (not in lexical `scope`, and
|
||||
// not a storage-backed array/struct const — those resolve above).
|
||||
// Same defect as the local case: without storage, `@FORTY` would
|
||||
// become `inttoptr (i64 <value> to ptr)`. Diagnose (issue 0138).
|
||||
if (self.program_index.module_const_map.get(id_name) != null) {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, node.span, "cannot take the address of constant '{s}' — a scalar '::' constant has no storage (use a '=' variable or a local copy for mutable data)", .{id_name});
|
||||
break :blk self.emitPlaceholder("addr_of_const");
|
||||
}
|
||||
}
|
||||
// Fold a negated integer literal into one constant: `-128` must
|
||||
// range-check as -128, not as an out-of-range +128 intermediate.
|
||||
|
||||
Reference in New Issue
Block a user