fix: optional-chain getter/field correctness from 0160 adversarial review
Five adversarial reviews of the issue-0160 fix surfaced three more bugs in the touched optional-chain / optional-coercion code; all fixed here: 1. A COLD generic-instance getter through `?.` (`?*Vec(i64)` `.getter`, never called directly first) panicked with "unresolved type reached LLVM emission": a cold instance method is absent from resolveFuncByName, so the getter's return type resolved to .unresolved → a ?unresolved merge type. lowerOptionalChain and getterReturnTypeOnDeref now warm the monomorph (ensureGenericInstanceMethodLowered) before querying its return type. (The 0907 test passed only by luck — List(i64) is warmed by stdlib use; 0907 now also exercises a cold user generic.) 2. A real-field read through a `?*T` chain (`op?.field`, op: ?*T) reinterpreted the pointer bits as the field (silent garbage) — the some-branch real-field path didn't load through the pointer. It now derefs `?*T` before the field access. (Pre-existing — the else-branch predates 0160 — but it's the same function and a silent miscompile, so fixed here.) 3. `?[]T = array` skipped the array→slice promotion (corrupt .len/.ptr): the lowerVarDecl optional arm wrapped the raw array. It now coerces the value to the optional's child type (array→slice) before wrapping. Regression examples 0906/0907 extended to cover all three. Distinct PRE-EXISTING bugs the reviews surfaced in untouched subsystems are filed as issues 0161 (struct-literal vs scalar), 0162 (#run returning an optional aggregate), 0163 (untagged-union payload-binding match).
This commit is contained in:
@@ -37,5 +37,11 @@ main :: () -> i64 {
|
||||
// array element
|
||||
arr : [2]?T = .[ .{ a = 20 }, .{ a = 21 } ];
|
||||
if arr[0] != null { if arr[1] != null { print("arr: {} {}\n", arr[0]!.a, arr[1]!.a); } } // 20 21
|
||||
|
||||
// array value into an optional-of-slice `?[]T`: the array→slice promotion
|
||||
// must run on the child BEFORE wrapping, or `.len`/`.ptr` are corrupted.
|
||||
nums : [3]i64 = .[ 1, 2, 3 ];
|
||||
os : ?[]i64 = nums;
|
||||
if os != null { s := os!; print("optslice: {} {} {}\n", s.len, s[0], s[2]); } // 3 1 3
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// A `#get` property accessor is reachable through an optional chain
|
||||
// (`obj?.getter`): the some-branch dispatches the getter and the result is
|
||||
// re-wrapped as `?R`; a null receiver short-circuits to null. Works for a value
|
||||
// optional (`?T`), a pointer optional (`?*T`), and a generic-instance getter
|
||||
// (`List.len`), and types correctly without an explicit annotation. Real fields
|
||||
// through `?.` keep working unchanged.
|
||||
// optional (`?T`), a pointer optional (`?*T`), and a generic-instance getter —
|
||||
// including a COLD user generic reached ONLY through the chain (its monomorph
|
||||
// is warmed so its return type resolves, not `?unresolved`). Real fields read
|
||||
// correctly through `?.` for both `?T` and `?*T` (the pointer is loaded, not
|
||||
// reinterpreted). All type correctly without an explicit annotation.
|
||||
// Regression (issue 0160).
|
||||
#import "modules/std.sx";
|
||||
|
||||
@@ -12,6 +14,15 @@ Temp :: struct {
|
||||
doubled :: (self: *Temp) -> i64 #get => self.raw * 2;
|
||||
}
|
||||
|
||||
// A user generic with a type-parameter field, reached ONLY through `?.` (never
|
||||
// a direct call first) — exercises the cold-monomorph warming.
|
||||
Cell :: struct ($T: Type) {
|
||||
slot: T; // type-parameter field — the cold-panic trigger
|
||||
n: i64;
|
||||
count :: (self: *Cell($T)) -> i64 #get => self.n;
|
||||
head :: (self: *Cell($T)) -> T #get => self.slot; // returns the type param
|
||||
}
|
||||
|
||||
main :: () -> i64 {
|
||||
t : Temp = .{ raw = 4 };
|
||||
|
||||
@@ -35,5 +46,18 @@ main :: () -> i64 {
|
||||
xs.append(10); xs.append(20); xs.append(30);
|
||||
pxs : ?*List(i64) = @xs;
|
||||
print("?*List len: {}\n", pxs?.len ?? -1); // 3
|
||||
|
||||
// COLD user generic getter through chain (no direct call first): concrete
|
||||
// return and type-parameter return, via ?*Cell and ?Cell.
|
||||
c := Cell(i64).{ slot = 99, n = 5 };
|
||||
pc : ?*Cell(i64) = @c;
|
||||
print("cold ?*Cell count: {}\n", pc?.count ?? -1); // 5
|
||||
print("cold ?*Cell head: {}\n", pc?.head ?? -1); // 99
|
||||
oc : ?Cell(i64) = c;
|
||||
print("cold ?Cell count: {}\n", oc?.count ?? -1); // 5
|
||||
|
||||
// real field read through ?*T (the pointer is loaded, not reinterpreted)
|
||||
print("?*Cell field n: {}\n", pc?.n ?? -1); // 5
|
||||
print("?*Cell field slot: {}\n", pc?.slot ?? -1); // 99
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4,3 +4,4 @@ ret: 5 6
|
||||
nested: 1 2 8
|
||||
reassign: 11 12
|
||||
arr: 20 21
|
||||
optslice: 3 1 3
|
||||
|
||||
@@ -3,3 +3,8 @@
|
||||
null getter: -1
|
||||
?T field: 4
|
||||
?*List len: 3
|
||||
cold ?*Cell count: 5
|
||||
cold ?*Cell head: 99
|
||||
cold ?Cell count: 5
|
||||
?*Cell field n: 5
|
||||
?*Cell field slot: 99
|
||||
|
||||
Reference in New Issue
Block a user