Writing a Vector lane (`v.x = …`, `.y/.z/.w` + colour aliases) panicked with "unresolved type reached LLVM emission". The store path had no vector branch: a `.field_access` target on a Vector fell through to struct-field lookup, matched nothing, left `field_ty = .unresolved`, and built a `ptrTo(.unresolved)` that tripped the LLVM emission guard. The read path resolved the lane fine — the two had diverged (issue-0083 two-resolver class). Extract a shared `Lowering.vectorLaneIndex` resolver and route BOTH paths through it. The read path (`lowerFieldAccessOnType`) delegates to it, dropping its silent `else 0` fallback. A new vector branch in `lowerAssignment` GEPs a typed pointer to the lane (`structGepTyped`) and stores via `storeOrCompound` (plain + compound). `emitStructGep` now addresses a vector base type with a `[0, lane]` GEP. A non-lane field now reports field-not-found on both paths instead of silent-lane-0 / panic. Regression: examples/1506-vectors-lane-store.sx (panicked pre-fix, now reads back written values) + a vectorLaneIndex unit test. Resolves issue 0086; spec documents element assignment.
4.5 KiB
0086 — writing to a Vector lane (v.x = …) panics with "unresolved type reached LLVM emission"
RESOLVED (F0.5).
Root cause. The vector-lane STORE path had no vector branch. In
Lowering.lowerAssignment(src/ir/lower.zig) a.field_accesstarget on aVectorfell through to the struct-field lookup, where no field matched a lane name, sofield_tystayed.unresolved. The store then built aptrTo(.unresolved)whose pointee reached LLVM inemitStore(src/backend/llvm/ops.zig) →toLLVMTypeInfo.unresolvedtripwire panic. The READ path resolved the lane fine; the two paths had diverged (issue-0083 two-resolver class).Fix (per file).
src/ir/lower.zig— extracted a sharedLowering.vectorLaneIndex(field)resolver (.x/.y/.z/.w+ colour aliases.r/.g/.b/.a→ lane 0..3,nullotherwise). The READ path (lowerFieldAccessOnType) now delegates to it (dropping its silentelse 0fallback), and a new vector branch inlowerAssignmentuses the SAME resolver tostructGepTypeda typed pointer to the lane andstoreOrCompoundwith the vector element type (plain and compound assignment). A non-lane field now reports a field-not-found error on both paths instead of silently reading lane 0 / panicking.src/backend/llvm/ops.zig—emitStructGepnow addresses a vectorbase_typewith a[0, lane]GEP2, yielding a pointer to the lane element for the scalar store.Regression test.
examples/1506-vectors-lane-store.sx—.[…]-init and= ----init writes, every lane of a 4-lane vector, colour aliases, and a compound lane assignment, reading each value back. Unit testsrc/ir/lower.test.zigpins thevectorLaneIndexcontract.
Symptom
Assigning to a component of a Vector local — v.x = 1.0 (also .y / .z /
.w) — aborts the compiler with the internal panic:
thread … panic: unresolved type reached LLVM emission — a type resolution
failure was not diagnosed/aborted
src/backend/llvm/types.zig:175 toLLVMTypeInfo (.unresolved arm @panic)
src/backend/llvm/ops.zig:358 emitStore (.pointer => toLLVMType(p.pointee))
READING a lane (x := v.x) is fine; only the STORE side hits it. The init form
(= --- undefined vs = .[…] literal) does not matter — both panic once a lane
is written. A literal lane count (Vector(3, f32)) triggers it, so this is NOT
the lane-count resolution class (issue 0083); it is a distinct bug in the
vector-lane store path, where the store's pointee type resolves to the
.unresolved sentinel instead of the lane element type.
Discovered while fixing issue 0083 (attempt 5). It is pre-existing and orthogonal
— confirmed by reproducing on the pristine pre-0083-attempt-5 compiler — so it was
NOT introduced by the lane-count fix. The standard vector idiom (construct via a
.[…] literal / a constructor function returning .[…], then read components or
use vector arithmetic, as in examples/1500-vectors-vector-math.sx) is
unaffected; only component ASSIGNMENT is broken.
Reproduction
#import "modules/std.sx";
main :: () {
v : Vector(3, f32) = .[0.0, 0.0, 0.0];
v.x = 1.0; // panic here
print("x={}\n", v.x);
}
./zig-out/bin/sx run panics. Removing the v.x = 1.0 line (read-only) prints
x=0.000000 and exits 0.
Investigation prompt
A store to a Vector lane (v.x = …) lowers a pointer-to-lane whose pointee
type reaches LLVM as .unresolved, so emitStore
(src/backend/llvm/ops.zig:358, the .pointer => toLLVMType(p.pointee) arm)
hits the .unresolved tripwire panic in
src/backend/llvm/types.zig:175. The lane READ path computes the lane element
type correctly, so compare the lvalue/store lowering for a vector-component
assignment against the rvalue/load path — the component-write path is likely
building the lane pointer's pointee from a vector .x/.y/.z/.w field
resolution that returns .unresolved (or a vector field-access that resolves the
element type on load but not on store). Find where a Vector swizzle/component
assignment lowers its destination pointer (grep for vector component handling in
lower.zig assignment lowering and in the LLVM emitStore GEP path) and resolve
the lane element type there the same way the load path does. Verify with the
repro (expect x=1.000000) plus a .[…]-init write and a write to each of
.x/.y/.z/.w on a 4-lane vector, then zig build && zig build test && bash tests/run_examples.sh green.