fix: diagnose indexing a non-indexable type instead of panicking (issue 0183)
lowerIndexExpr fell through to an index_get with an .unresolved element
type for any non-indexable object (*T, *[]T, struct, scalar), reaching
codegen -> 'unresolved type reached LLVM emission' panic. Add a guard
after all indexable arms: if getElementType(obj_ty) is .unresolved and
obj_ty is itself resolved (genuinely non-indexable, not a prior-error
placeholder), emit a located 'cannot index a value of type <T>'
diagnostic + placeholder (hasErrors aborts before codegen). A single
pointer hints by pointee: ptr-to-scalar -> many-pointer/dereference;
ptr-to-array/slice -> dereference first. No false-positives (generics,
aliases, late-resolved, every indexable shape verified).
Regression: examples/diagnostics/1203-diagnostics-index-non-indexable.sx.
Verified by 3 adversarial reviews, suite 799/0. Filed adjacent pre-existing
panic 0184 (untyped positional .{ } literal with no target type).
This commit is contained in:
@@ -1,5 +1,21 @@
|
||||
# 0183 — indexing a non-indexable type (`*T`, `*[]T`, struct, …) panics instead of a clean diagnostic
|
||||
|
||||
> **RESOLVED.** `lowerIndexExpr` (`src/ir/lower/expr.zig`) fell through to an
|
||||
> `index_get` with an `.unresolved` element type for any non-indexable object,
|
||||
> reaching codegen → panic. Added a guard after all indexable arms: if
|
||||
> `getElementType(obj_ty)` is `.unresolved` and `obj_ty` is itself resolved (so a
|
||||
> genuinely non-indexable type, not a prior-error placeholder), emit a located
|
||||
> `cannot index a value of type '<T>'` diagnostic and return a placeholder
|
||||
> (`hasErrors()` aborts before codegen). A single pointer hints by pointee:
|
||||
> pointer-to-scalar → "use a many-pointer `[*]T`, or dereference first";
|
||||
> pointer-to-array/slice → "dereference first (`(*p)[i]`)". No false-positives —
|
||||
> generics, type aliases, late-resolved objects, and every indexable shape
|
||||
> (`[N]T`/`[]T`/`[*]T`/`string`/`Vector`/`*[N]T`/optional-chain) still work
|
||||
> (verified by 3 adversarial reviews; suite 799/0). Regression:
|
||||
> `examples/diagnostics/1203-diagnostics-index-non-indexable.sx`. (Adjacent
|
||||
> pre-existing panic found + filed: **0184** — an untyped positional `.{ }`
|
||||
> literal with no target type panics; the guard correctly defers on it.)
|
||||
|
||||
## Symptom
|
||||
|
||||
`expr[i]` where `expr`'s type is not array / slice / many-pointer / string —
|
||||
|
||||
46
issues/0184-untyped-positional-literal-no-target-panics.md
Normal file
46
issues/0184-untyped-positional-literal-no-target-panics.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# 0184 — an untyped positional literal `.{ ... }` with no inferable target type panics instead of diagnosing
|
||||
|
||||
## Symptom
|
||||
|
||||
A positional struct/tuple literal `.{ a, b, ... }` used where NO target type is
|
||||
available (e.g. `t := .{ 1, 2, 3 };` with no annotation) cannot resolve its type
|
||||
and is left `.unresolved`. Any later use (`t.0`, `t[0]`, passing it, etc.) then
|
||||
panics `unresolved type reached LLVM emission` (exit 134) — with no diagnostic.
|
||||
The literal's type is never resolved upstream nor reported.
|
||||
|
||||
Found during adversarial review of issue 0183 (the index guard correctly DEFERS
|
||||
on an already-`.unresolved` object to avoid double-reporting, so this surfaces as
|
||||
the upstream panic rather than the index diagnostic).
|
||||
|
||||
## Reproduction
|
||||
|
||||
```sx
|
||||
#import "modules/std.sx";
|
||||
main :: () {
|
||||
t := .{ 1, 2, 3 }; // untyped positional literal, no target type
|
||||
print("{}\n", t.0); // panic: unresolved type reached LLVM emission, exit 134
|
||||
}
|
||||
```
|
||||
|
||||
Expected: a located diagnostic, e.g. `error: cannot infer the type of this `.{ }`
|
||||
literal — annotate the binding (`t : (i64, i64, i64) = …` or a struct type) or
|
||||
provide a target type`, exit 1. (A TYPED positional literal `t : (i64,i64,i64) =
|
||||
.{1,2,3}` or `S.{...}` works.)
|
||||
|
||||
## Investigation prompt
|
||||
|
||||
`src/ir/lower/expr.zig` `lowerStructLiteral` (and `expr_typer.zig`'s inference for
|
||||
an untyped `.{ }`). When a positional `.{ }` literal has no `self.target_type`
|
||||
(and isn't a named struct literal that names its own type), its `struct_init.ty`
|
||||
stays `.unresolved` and flows to codegen → panic. Add a diagnostic at the literal
|
||||
site: if a `.{ }` literal cannot determine a target/struct type, emit
|
||||
`self.diagnostics.addFmt(.err, span, "cannot infer the type of this '.{{ }}'
|
||||
literal — annotate the binding or provide a target type", .{})` and return a
|
||||
placeholder (so `hasErrors()` aborts before codegen), instead of emitting an
|
||||
`.unresolved`-typed `struct_init`. Follow the no-silent-fallback rule (here it is
|
||||
a loud PANIC that must become a clean diagnostic). Verify: the repro exits 1 with
|
||||
the diagnostic; a TYPED positional literal (annotated binding, `S.{...}`,
|
||||
array/tuple target) still works. Add an `examples/diagnostics/12xx-...` negative
|
||||
regression. (Related: 0173 closed the same silent-fallback for typed
|
||||
`.[...]`-array-literal heads with undefined element names; this is the
|
||||
no-target-type variant for `.{ }`.)
|
||||
Reference in New Issue
Block a user