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).
90 lines
3.5 KiB
Plaintext
90 lines
3.5 KiB
Plaintext
// 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
|
|
}
|