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:
@@ -1,5 +1,20 @@
|
||||
# 0181 — `?.`-chain (and `?`-postfix) on an optional whose child struct contains an ARRAY field panics `unresolved type reached LLVM emission`
|
||||
|
||||
> **RESOLVED.** `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
|
||||
> issue-0101 `!`-unwrap fix: added `lowerOptionalChainIndex`
|
||||
> (`src/ir/lower/expr.zig`) — `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, so `[N]?T` →
|
||||
> `?T`). The typer (`src/ir/expr_typer.zig`) and the dispatch guard both 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, verified in IR).
|
||||
> Regression: `examples/optionals/0915-optional-chain-array-field-index.sx`.
|
||||
> Verified by 3 adversarial reviews; suite 794/0. (Broader pre-existing gap
|
||||
> found + filed: **0183** — indexing a non-indexable type `*T`/`*[]T`/struct
|
||||
> panics instead of a diagnostic, reproduces without optionals.)
|
||||
## Symptom
|
||||
|
||||
A `?.` optional-chain access (or the `?` optional-test postfix used in a
|
||||
|
||||
47
issues/0183-index-non-indexable-type-panics.md
Normal file
47
issues/0183-index-non-indexable-type-panics.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# 0183 — indexing a non-indexable type (`*T`, `*[]T`, struct, …) panics instead of a clean diagnostic
|
||||
|
||||
## Symptom
|
||||
|
||||
`expr[i]` where `expr`'s type is not array / slice / many-pointer / string —
|
||||
e.g. a single-element pointer `*T`, a pointer-to-slice `*[]T`, or a struct — does
|
||||
NOT emit a type error. It falls through `lowerIndexExpr` to an `index_get` with an
|
||||
`.unresolved` element type and reaches codegen, panicking `unresolved type
|
||||
reached LLVM emission` (exit 134). Pure runtime, no optionals/comptime. (`[*]T`
|
||||
many-pointers and `[N]T`/`[]T`/`string` ARE indexable and unaffected.)
|
||||
|
||||
Found during adversarial review of issue 0181 (the optional-chain index fix); the
|
||||
same fall-through underlies the `?*[]T`/`?*T`/`?struct` chain-index panics, but it
|
||||
reproduces identically WITHOUT optional chaining, so it is a separate, broader
|
||||
gap in the index lowering.
|
||||
|
||||
## Reproduction
|
||||
|
||||
```sx
|
||||
#import "modules/std.sx";
|
||||
main :: () {
|
||||
x := 5;
|
||||
p : *i64 = @x;
|
||||
print("{}\n", p[0]); // panic: unresolved type reached LLVM emission, exit 134
|
||||
}
|
||||
```
|
||||
|
||||
Also panics: indexing a `*[]i64` (pointer-to-slice), indexing a plain struct
|
||||
value. Expected: a located diagnostic, e.g. `error: cannot index a value of type
|
||||
'*i64' (use a many-pointer '[*]T', or dereference first)`, exit 1.
|
||||
|
||||
## Investigation prompt
|
||||
|
||||
`src/ir/lower/expr.zig` `lowerIndexExpr`: after the array / slice / many-pointer /
|
||||
string / optional-chain dispatch arms, the fall-through emits `index_get` with
|
||||
`getElementType(obj_ty)` even when that is `.unresolved`. Add a final guard: if
|
||||
the object type is not indexable (element type resolves to `.unresolved` and the
|
||||
type isn't a recognized indexable shape), emit
|
||||
`self.diagnostics.addFmt(.err, span, "cannot index a value of type '{s}'", .{...})`
|
||||
and return a placeholder — never emit an `index_get` with an unresolved element
|
||||
type. Mirror the located-diagnostic + placeholder pattern used elsewhere in the
|
||||
lowering. The static typer (`src/ir/expr_typer.zig` `index_expr`) should likewise
|
||||
yield `.unresolved` (already does) so this is the single choke point. Follow the
|
||||
no-silent-fallback rule (here it's a loud PANIC, which must become a clean
|
||||
diagnostic). Verify: the repro exits 1 with the diagnostic; `[*]T`/`[]T`/`[N]T`/
|
||||
`string`/optional-chain indexing all still work. Add an
|
||||
`examples/diagnostics/12xx-index-non-indexable.sx` negative regression.
|
||||
Reference in New Issue
Block a user