fix: optional-chain index opt?.xs[i] over array/ptr-array field (issue 0181)
opt?.xs[i] typed and lowered the index over the optional CONTAINER (?[N]T); getElementType returned .unresolved, so index_get reached LLVM with an unresolved element type and panicked. Mirroring the 0101 !-unwrap fix: add lowerOptionalChainIndex (optional_has_value -> some: unwrap + index (index_gep+load for ?*[N]T, else index_get) + optional_wrap; none: const_null; merge -> ?ElemType, element-optional flattened). The typer + dispatch guard compute the element via ptrToArrayElem(child) orelse getElementType(child), so value-arrays, slices, many-pointers, AND pointer-to-array (?*[N]T) children resolve. Null receivers short-circuit (no null deref). Regression: examples/optionals/0915-optional-chain-array-field-index.sx. Verified by 3 adversarial reviews, suite 794/0. Filed broader pre-existing gap 0183 (indexing a non-indexable type panics instead of diagnosing).
This commit is contained in:
89
examples/optionals/0915-optional-chain-array-field-index.sx
Normal file
89
examples/optionals/0915-optional-chain-array-field-index.sx
Normal file
@@ -0,0 +1,89 @@
|
||||
// Optional-chain `?.` reaching an indexable field, then indexing it:
|
||||
// `opt?.xs[i]`. The `?.` makes the whole expression optional — it short-
|
||||
// circuits to null when the receiver is null, else unwraps and indexes —
|
||||
// so `opt?.xs[i]` is `?ElemType`, coalesced/unwrapped at the use site.
|
||||
//
|
||||
// Regression (issue 0181): `opt?.xs[i]` typed its element through
|
||||
// `getElementType(?[N]T)` as `.unresolved`, and an `index_get` on the
|
||||
// optional value reached LLVM emission (panic: "unresolved type reached
|
||||
// LLVM emission", exit 134). This is the `?.`-chain analogue of issue 0101
|
||||
// (the `!`-unwrap form, fixed earlier).
|
||||
//
|
||||
// Also covers the `?*[N]T` shape — a `?.` field that is a POINTER to an
|
||||
// array. Indexing auto-derefs (GEP the pointee array element through the
|
||||
// unwrapped pointer), so `opt?.p[i]` is `?ElemType` too. `getElementType`
|
||||
// has no pointer arm, so the dispatch guard / typer consult
|
||||
// `ptrToArrayElem` first (else this branch was unreachable dead code and
|
||||
// the `?*[N]T` case fell through to the same exit-134 panic).
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
Arr3 :: struct { xs: [3]i64; }
|
||||
Pt :: struct { x: i64; y: i64; }
|
||||
Holder :: struct { ps: [2]Pt; sl: []i64; }
|
||||
|
||||
PArr :: struct { p: *[3]i64; } // pointer-to-array field
|
||||
PU8 :: struct { p: *[4]u8; } // sub-word element
|
||||
|
||||
g_i64 : [3]i64 = ---;
|
||||
g_u8 : [4]u8 = ---;
|
||||
|
||||
mk_parr :: (present: bool) -> ?PArr {
|
||||
if present { return PArr.{ p = @g_i64 }; }
|
||||
return null;
|
||||
}
|
||||
mk_pu8 :: () -> ?PU8 { return PU8.{ p = @g_u8 }; }
|
||||
|
||||
mk_arr :: (present: bool) -> ?Arr3 {
|
||||
if present {
|
||||
r : Arr3 = ---;
|
||||
r.xs[0] = 1; r.xs[1] = 2; r.xs[2] = 3;
|
||||
return r;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
mk_holder :: (data: []i64) -> ?Holder {
|
||||
r : Holder = ---;
|
||||
r.ps[0].x = 10; r.ps[0].y = 11;
|
||||
r.ps[1].x = 20; r.ps[1].y = 21;
|
||||
r.sl = data;
|
||||
return r;
|
||||
}
|
||||
|
||||
main :: () {
|
||||
// Present receiver: unwraps + indexes.
|
||||
print("present[0]: {}\n", mk_arr(true)?.xs[0] ?? 99); // 1
|
||||
print("present[2]: {}\n", mk_arr(true)?.xs[2] ?? 99); // 3
|
||||
|
||||
// Null receiver: short-circuits to null, coalesced to the default.
|
||||
print("null: {}\n", mk_arr(false)?.xs[0] ?? 99); // 99
|
||||
|
||||
// Result is `?ElemType` — bind it, then unwrap.
|
||||
x := mk_arr(true)?.xs[1]; // ?i64
|
||||
print("bound: {}\n", x ?? 0); // 2
|
||||
print("unwrap: {}\n", mk_arr(true)?.xs[2]!); // 3
|
||||
|
||||
// Struct-element array `[2]Pt`: indexing yields `?Pt`.
|
||||
p := mk_holder(.[])?.ps[1]; // ?Pt
|
||||
print("struct.x: {}\n", p?.x ?? -1); // 20
|
||||
print("struct.y: {}\n", p?.y ?? -1); // 21
|
||||
|
||||
// Slice field through the chain.
|
||||
arr : [3]i64 = .[5, 6, 7];
|
||||
print("slice[1]: {}\n", mk_holder(arr[..])?.sl[1] ?? -1); // 6
|
||||
|
||||
// Pointer-to-array field `?*[N]T`: indexing auto-derefs through the
|
||||
// unwrapped pointer (GEP the pointee array element), multi-element.
|
||||
g_i64[0] = 10; g_i64[1] = 20; g_i64[2] = 30;
|
||||
print("ptr[0]: {}\n", mk_parr(true)?.p[0] ?? 99); // 10
|
||||
print("ptr[1]: {}\n", mk_parr(true)?.p[1] ?? 99); // 20
|
||||
print("ptr[2]: {}\n", mk_parr(true)?.p[2] ?? 99); // 30
|
||||
// Null receiver short-circuits — no deref of the null pointer.
|
||||
print("ptr null: {}\n", mk_parr(false)?.p[0] ?? 99); // 99
|
||||
|
||||
// Sub-word element `?*[4]u8` — adjacent lanes not clobbered.
|
||||
g_u8[0] = 7; g_u8[1] = 8; g_u8[2] = 9; g_u8[3] = 200;
|
||||
print("u8[0]: {}\n", mk_pu8()?.p[0] ?? 0); // 7
|
||||
print("u8[3]: {}\n", mk_pu8()?.p[3] ?? 0); // 200
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
present[0]: 1
|
||||
present[2]: 3
|
||||
null: 99
|
||||
bound: 2
|
||||
unwrap: 3
|
||||
struct.x: 20
|
||||
struct.y: 21
|
||||
slice[1]: 6
|
||||
ptr[0]: 10
|
||||
ptr[1]: 20
|
||||
ptr[2]: 30
|
||||
ptr null: 99
|
||||
u8[0]: 7
|
||||
u8[3]: 200
|
||||
Reference in New Issue
Block a user