fix: type-safe stores + Any unbox/eq; finish multi-return deferrals
Type-checking gaps (segfault/corruption → compile errors): - 0197: reject a store into an annotated slot whose value has no modeled coercion AND a different byte width (a 16-byte string into a 4-byte i32 overran the slot and segfaulted). New checkAssignable / noneReinterpretIsUnsafe (coerce.zig, width via the LLVM-accurate typeSizeBytes) wired into every store site: var/const-decl, single + multi assignment (identifier/field/index/ element/deref), named-return defaults. Same-width reinterpretations (*T→[*]T, i64→isize, fn-ref) and explicit xx/cast stay allowed; cascades suppressed via externalErrorsExist. Examples 1205, 1206. - 0198: an implicit `Any → T` unbox is now a compile error (it blindly reinterpreted the boxed payload — silent garbage for a wrong scalar, a segfault for an aggregate). xx and compiler-generated match/pack unboxes are unaffected. Example 1207. - 0199: `Any == <concrete>` (one operand Any) aborted the LLVM verifier — the comparison arm now fires when either operand is Any, boxing the concrete side first. Example 0654. Multi-return deferrals (PLAN-MULTIRET #6 + named-order + D3 + generic): - Reorder named return elements by name instead of requiring slot order; error on unknown/duplicate/missing (value-only AND full-failable-tuple forms). Examples 0210, 0214. - Reject a bare-paren (A, B) multi-return signature in generic-arg position (return-position-only). Example 0215. - Multi-return closure types / lambda literals work via the reused tuple machinery (destructure, single-bind+field, lambda arg). Example 0216. - Generic multi-return: positional works (0217); 0200: the named-slot implicit-return form now works for generic free fns + struct methods — monomorphizeFunction now calls bindNamedReturnSlots. Example 0218. readme.md documents the annotated-store coercion rule; CHECKPOINT-MULTIRET.md updated. Full corpus green (850/0).
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
// Initializing (or reassigning) an explicitly-annotated slot with a value
|
||||
// whose type has NO coercion to the annotation is a type error, diagnosed at
|
||||
// lowering with a located message.
|
||||
//
|
||||
// Regression (issue 0197): `x : i32 = "hi"` was accepted with no diagnostic —
|
||||
// the incompatible value passed through a `.none` coercion plan UNCHANGED, so a
|
||||
// 16-byte `string` was stored into a 4-byte `i32` slot, bit-mangling the slot
|
||||
// and SIGSEGV'ing at run time (`sx ir` lowered fine; only the run crashed). The
|
||||
// guard (`checkAssignable`) now rejects an un-coercible initializer at every
|
||||
// store-into-annotated-slot site — var-decl, body-local const-decl, and
|
||||
// reassignment — emitting a diagnostic and aborting the build cleanly (exit 1).
|
||||
//
|
||||
// The explicit `xx` / `cast(T)` escape hatch is unaffected: a deliberate
|
||||
// reinterpretation (pointer↔int, etc.) still passes through.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
main :: () -> i64 {
|
||||
x : i32 = "hi"; // error: cannot initialize 'x' (string ↛ i32)
|
||||
|
||||
y : i32 = 0;
|
||||
y = "nope"; // error: cannot reassign 'y' (string ↛ i32)
|
||||
|
||||
C : i32 : "also"; // error: cannot initialize 'C' (string ↛ i32)
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// A store into ANY annotated slot whose value type has no coercion to the slot
|
||||
// AND a different byte width is a type error — the raw `.none` passthrough would
|
||||
// overrun the slot and corrupt adjacent memory (issue 0197). The guard covers
|
||||
// every store site, not just plain var-decls: a struct field, an array element,
|
||||
// a pointer deref, and a multi-assignment target.
|
||||
//
|
||||
// Regression (issue 0197): `struct{a:i32; b:i32}` is 8 bytes, but a 16-byte
|
||||
// `string` stored raw into one of its fields (or into an i32 array element, or
|
||||
// through an `*i32`) overran the slot and SIGSEGV'd / clobbered neighbors. The
|
||||
// discriminator is BYTE WIDTH (via `typeSizeBytes`), so a same-width
|
||||
// reinterpretation (`*T → [*]T`, a bare fn-ref into a function slot) still
|
||||
// passes — only a genuine width mismatch is rejected.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
S :: struct { a: i32; b: i32; }
|
||||
|
||||
main :: () -> i64 {
|
||||
s : S = ---;
|
||||
s.a = 1; s.b = 2;
|
||||
s.a = "field"; // error: struct field, string ↛ i32
|
||||
|
||||
arr := i32.[1, 2, 3];
|
||||
arr[0] = "elem"; // error: array element, string ↛ i32
|
||||
|
||||
n : i32 = 0;
|
||||
p : *i32 = @n;
|
||||
p.* = "deref"; // error: pointer deref, string ↛ i32
|
||||
|
||||
u : i32 = 0; v : i32 = 0;
|
||||
u, v = "multi", 9; // error: multi-assign target, string ↛ i32
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// An `Any` does not IMPLICITLY unbox to a concrete type. A blind unbox
|
||||
// reinterprets the boxed payload word as the target with NO runtime tag check,
|
||||
// so a wrong target silently yields garbage (a scalar) or dereferences the
|
||||
// payload as a pointer and segfaults (an aggregate). sx rejects the implicit
|
||||
// unbox at compile time — like the no-implicit-optional-unwrap rule — and
|
||||
// directs the user to `match` on the value's type or an explicit `xx`.
|
||||
//
|
||||
// Regression (issue 0198): `s : S = some_any` segfaulted and `f : f64 = some_any`
|
||||
// silently produced 0.0; both are now compile errors. The fix is in `coerceMode`
|
||||
// (`.unbox_any` arm, mode == .implicit). The `xx` escape hatch and the
|
||||
// compiler-generated type-dispatch / pack-extraction unboxes are unaffected.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
S :: struct { a: i64; }
|
||||
|
||||
main :: () -> i64 {
|
||||
x : Any = 5;
|
||||
n : i64 = x; // error: 'Any' does not implicitly unbox to 'i64'
|
||||
s : S = x; // error: 'Any' does not implicitly unbox to 'S'
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,17 @@
|
||||
error: cannot initialize 'x' of type 'i32' with a value of type 'string'
|
||||
--> examples/diagnostics/1205-diagnostics-annotated-init-type-mismatch.sx:19:15
|
||||
|
|
||||
19 | x : i32 = "hi"; // error: cannot initialize 'x' (string ↛ i32)
|
||||
| ^^^^
|
||||
|
||||
error: cannot reassign 'y' of type 'i32' with a value of type 'string'
|
||||
--> examples/diagnostics/1205-diagnostics-annotated-init-type-mismatch.sx:22:9
|
||||
|
|
||||
22 | y = "nope"; // error: cannot reassign 'y' (string ↛ i32)
|
||||
| ^^^^^^
|
||||
|
||||
error: cannot initialize 'C' of type 'i32' with a value of type 'string'
|
||||
--> examples/diagnostics/1205-diagnostics-annotated-init-type-mismatch.sx:24:15
|
||||
|
|
||||
24 | C : i32 : "also"; // error: cannot initialize 'C' (string ↛ i32)
|
||||
| ^^^^^^
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,23 @@
|
||||
error: cannot assign 'a' of type 'i32' with a value of type 'string'
|
||||
--> examples/diagnostics/1206-diagnostics-store-width-mismatch.sx:21:11
|
||||
|
|
||||
21 | s.a = "field"; // error: struct field, string ↛ i32
|
||||
| ^^^^^^^
|
||||
|
||||
error: cannot assign 'element' of type 'i64' with a value of type 'string'
|
||||
--> examples/diagnostics/1206-diagnostics-store-width-mismatch.sx:24:14
|
||||
|
|
||||
24 | arr[0] = "elem"; // error: array element, string ↛ i32
|
||||
| ^^^^^^
|
||||
|
||||
error: cannot assign 'target' of type 'i32' with a value of type 'string'
|
||||
--> examples/diagnostics/1206-diagnostics-store-width-mismatch.sx:28:11
|
||||
|
|
||||
28 | p.* = "deref"; // error: pointer deref, string ↛ i32
|
||||
| ^^^^^^^
|
||||
|
||||
error: cannot assign 'u' of type 'i32' with a value of type 'string'
|
||||
--> examples/diagnostics/1206-diagnostics-store-width-mismatch.sx:31:12
|
||||
|
|
||||
31 | u, v = "multi", 9; // error: multi-assign target, string ↛ i32
|
||||
| ^^^^^^^
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,11 @@
|
||||
error: an 'Any' does not implicitly unbox to 'i64': the boxed type is not checked, so a wrong target reinterprets the payload (a wrong scalar silently yields garbage; an aggregate dereferences it and crashes). Dispatch on the value's type with `match`, or force it with `xx` if you know the boxed type.
|
||||
--> examples/diagnostics/1207-diagnostics-any-implicit-unbox-rejected.sx:19:5
|
||||
|
|
||||
19 | n : i64 = x; // error: 'Any' does not implicitly unbox to 'i64'
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: an 'Any' does not implicitly unbox to 'S': the boxed type is not checked, so a wrong target reinterprets the payload (a wrong scalar silently yields garbage; an aggregate dereferences it and crashes). Dispatch on the value's type with `match`, or force it with `xx` if you know the boxed type.
|
||||
--> examples/diagnostics/1207-diagnostics-any-implicit-unbox-rejected.sx:20:5
|
||||
|
|
||||
20 | s : S = x; // error: 'Any' does not implicitly unbox to 'S'
|
||||
| ^^^^^^^^^^
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
Reference in New Issue
Block a user