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:
@@ -1,8 +1,19 @@
|
||||
// Negative: named return elements must be given in SLOT ORDER. A mismatched
|
||||
// name would otherwise be matched positionally and silently produce the wrong
|
||||
// result, so it is rejected. (Here `b` is given where slot `a` is expected.)
|
||||
// Named return elements may be given in ANY order — they are matched to the
|
||||
// return slots BY NAME and permuted to slot order before lowering. Here `b` is
|
||||
// given before `a`; the result still destructures as (a, b).
|
||||
#import "modules/std.sx";
|
||||
pair :: (n: i32) -> (a: i32, b: i32) {
|
||||
return b = n, a = n + 1; // out of slot order
|
||||
return b = n, a = n + 1; // out of slot order — reordered by name
|
||||
}
|
||||
// Works through the value-carrying-failable channel too (error slot is implicit).
|
||||
ErrX :: error { Bad }
|
||||
fpair :: (n: i32) -> (a: i32, b: i32, !) {
|
||||
return b = n, a = n + 1;
|
||||
}
|
||||
main :: () -> i64 {
|
||||
x, y := pair(5); // a = 6, b = 5
|
||||
print("{} {}\n", x, y);
|
||||
r := fpair(10) catch { return 9; };
|
||||
print("{} {}\n", r.a, r.b); // a = 11, b = 10
|
||||
return 0;
|
||||
}
|
||||
main :: () -> i64 { x, y := pair(5); print("{} {}\n", x, y); return 0; }
|
||||
|
||||
17
examples/types/0214-types-multi-return-name-invalid.sx
Normal file
17
examples/types/0214-types-multi-return-name-invalid.sx
Normal file
@@ -0,0 +1,17 @@
|
||||
// Negative: named return elements are matched to slots BY NAME (any order), so
|
||||
// a name that matches NO slot, or a slot named MORE THAN ONCE, is a hard error
|
||||
// (rather than a silent positional mismatch). Missing/extra arity is caught
|
||||
// separately. Here `c` names no slot and `a` would be duplicated.
|
||||
#import "modules/std.sx";
|
||||
bad_unknown :: (n: i32) -> (a: i32, b: i32) {
|
||||
return a = n, c = n + 1; // error: 'c' names no return slot
|
||||
}
|
||||
bad_dup :: (n: i32) -> (a: i32, b: i32) {
|
||||
return a = n, a = n + 1; // error: 'a' given more than once
|
||||
}
|
||||
main :: () -> i64 {
|
||||
x, y := bad_unknown(5);
|
||||
p, q := bad_dup(5);
|
||||
print("{} {} {} {}\n", x, y, p, q);
|
||||
return 0;
|
||||
}
|
||||
11
examples/types/0215-types-multi-return-as-generic-arg.sx
Normal file
11
examples/types/0215-types-multi-return-as-generic-arg.sx
Normal file
@@ -0,0 +1,11 @@
|
||||
// Negative: a bare-paren `(A, B)` is a MULTI-RETURN signature — valid ONLY as a
|
||||
// function/closure return type, never as a value type. As a generic type
|
||||
// argument it is rejected (a tuple-valued argument uses `Tuple(A, B)`). This
|
||||
// completes the return-position-only gating (param / field / variable positions
|
||||
// were already rejected; 0213 covers those).
|
||||
#import "modules/std.sx";
|
||||
|
||||
main :: () -> i64 {
|
||||
xs : List((i32, bool)) = ---; // error: multi-return signature, not a type
|
||||
return 0;
|
||||
}
|
||||
26
examples/types/0216-types-multi-return-closure.sx
Normal file
26
examples/types/0216-types-multi-return-closure.sx
Normal file
@@ -0,0 +1,26 @@
|
||||
// Multi-return CLOSURE types and lambda literals work via the same tuple
|
||||
// machinery as function multi-returns (D3): a `Closure() -> (A, B)` value's call
|
||||
// result destructures (`a, b := cb()`), single-binds with field access
|
||||
// (`c := cb(); c.0`), and a `() => { return v1, v2; }` lambda literal satisfies a
|
||||
// multi-return closure parameter. No dedicated ClosureInfo marker is needed —
|
||||
// the return slots ride as the reused `.tuple` TypeId, consistent with the
|
||||
// function-decl multi-return surface.
|
||||
#import "modules/std.sx";
|
||||
|
||||
apply :: (cb: Closure() -> (i32, bool)) -> i32 {
|
||||
a, b := cb(); // destructure a multi-return closure result
|
||||
return if b { a } else { 0 };
|
||||
}
|
||||
|
||||
main :: () -> i64 {
|
||||
cb : Closure() -> (i32, bool) = () => { return 7, true; };
|
||||
x, y := cb();
|
||||
print("{} {}\n", x, y); // 7 true
|
||||
|
||||
c := cb(); // single-bind + positional field access
|
||||
print("{} {}\n", c.0, c.1); // 7 true
|
||||
|
||||
r := apply(() => { return 9, true; }); // lambda literal as the closure arg
|
||||
print("{}\n", r); // 9
|
||||
return 0;
|
||||
}
|
||||
24
examples/types/0217-types-multi-return-generic.sx
Normal file
24
examples/types/0217-types-multi-return-generic.sx
Normal file
@@ -0,0 +1,24 @@
|
||||
// Generic multi-return: a positional multi-return whose slots are generic type
|
||||
// params resolves with the inferred (or explicit) bindings — `-> (T, U)` with
|
||||
// `a: $T, b: $U` infers from the args; an explicit `$T: Type` form also works.
|
||||
// (The NAMED-slot implicit-return form with generics is a separate gap — issue
|
||||
// 0200; the positional explicit-`return` form here is the supported surface.)
|
||||
#import "modules/std.sx";
|
||||
|
||||
// inferred type params from the value args
|
||||
pair :: (a: $T, b: $U) -> (T, U) { return a, b; }
|
||||
|
||||
// explicit comptime type params
|
||||
mk :: ($T: Type, $U: Type, a: T, b: U) -> (T, U) { return a, b; }
|
||||
|
||||
main :: () -> i64 {
|
||||
x, y := pair(7, true);
|
||||
print("{} {}\n", x, y); // 7 true
|
||||
|
||||
s, n := pair("hi", 42);
|
||||
print("{} {}\n", s, n); // hi 42
|
||||
|
||||
p, q := mk(i32, bool, 3, false);
|
||||
print("{} {}\n", p, q); // 3 false
|
||||
return 0;
|
||||
}
|
||||
40
examples/types/0218-types-multi-return-generic-named.sx
Normal file
40
examples/types/0218-types-multi-return-generic-named.sx
Normal file
@@ -0,0 +1,40 @@
|
||||
// A NAMED multi-return whose slots are generic type params, using the implicit
|
||||
// return (assign the named slot locals, no explicit `return`), works for both a
|
||||
// generic free function and a generic struct method.
|
||||
//
|
||||
// Regression (issue 0200): the generic monomorph path (`monomorphizeFunction`)
|
||||
// bound params but never called `bindNamedReturnSlots`, so `named_return_names`
|
||||
// stayed null and the implicit-return synthesis didn't fire — the body wrongly
|
||||
// reported "produces no value". Now the binder runs on the generic path too
|
||||
// (mirroring `lowerFunctionBodyInto`), incl. with defaults and the failable
|
||||
// error channel.
|
||||
#import "modules/std.sx";
|
||||
|
||||
ErrX :: error { Bad }
|
||||
|
||||
// generic free function, named slots, implicit return, mixed inference + default
|
||||
split :: (a: $T, b: $U) -> (first: T, second: U) { first = a; second = b; }
|
||||
withd :: (a: $T) -> (x: T, y: i32 = 99) { x = a; }
|
||||
fallible :: (a: $T, b: $U) -> (x: T, y: U, !) { x = a; y = b; }
|
||||
|
||||
// generic struct method, named slots, implicit return
|
||||
Box :: struct ($T: Type) {
|
||||
v: T;
|
||||
pair :: (self: *Box(T)) -> (a: T, b: T) { a = self.v; b = self.v + 1; }
|
||||
}
|
||||
|
||||
main :: () -> i64 {
|
||||
x, y := split(7, true);
|
||||
print("{} {}\n", x, y); // 7 true
|
||||
|
||||
p, q := withd(5);
|
||||
print("{} {}\n", p, q); // 5 99
|
||||
|
||||
r := fallible(3, false) catch { return 9; };
|
||||
print("{} {}\n", r.x, r.y); // 3 false
|
||||
|
||||
bx := Box(i32).{ v = 10 };
|
||||
m, n := bx.pair();
|
||||
print("{} {}\n", m, n); // 10 11
|
||||
return 0;
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
1
|
||||
0
|
||||
|
||||
@@ -1,11 +1 @@
|
||||
error: named return element 'b' does not match the slot 'a' at position 0 — name the elements in slot order
|
||||
--> examples/types/0210-types-multi-return-name-order.sx:6:5
|
||||
|
|
||||
6 | return b = n, a = n + 1; // out of slot order
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: named return element 'a' does not match the slot 'b' at position 1 — name the elements in slot order
|
||||
--> examples/types/0210-types-multi-return-name-order.sx:6:5
|
||||
|
|
||||
6 | return b = n, a = n + 1; // out of slot order
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
|
||||
6 5
|
||||
11 10
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,11 @@
|
||||
error: named return element 'c' does not name any return slot
|
||||
--> examples/types/0214-types-multi-return-name-invalid.sx:7:5
|
||||
|
|
||||
7 | return a = n, c = n + 1; // error: 'c' names no return slot
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: named return element 'a' is given more than once
|
||||
--> examples/types/0214-types-multi-return-name-invalid.sx:10:5
|
||||
|
|
||||
10 | return a = n, a = n + 1; // error: 'a' given more than once
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,5 @@
|
||||
error: a bare-paren `(A, B)` is a multi-return signature, valid only as a return type; a tuple-valued generic type argument uses `Tuple(…)`
|
||||
--> examples/types/0215-types-multi-return-as-generic-arg.sx:9:15
|
||||
|
|
||||
9 | xs : List((i32, bool)) = ---; // error: multi-return signature, not a type
|
||||
| ^^^^^^^^^^^
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
7 true
|
||||
7 true
|
||||
9
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
7 true
|
||||
hi 42
|
||||
3 false
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
7 true
|
||||
5 99
|
||||
3 false
|
||||
10 11
|
||||
Reference in New Issue
Block a user