Skip to content

fix(compiler): lower slice-to-array conversions with Go's length check - #23

Merged
Mister-Meeseeks merged 1 commit into
CrocSwap:mainfrom
paulbalaji:fix-slice-array-pointer
Oct 19, 2026
Merged

Mister-Meeseeks merged 1 commit into
CrocSwap:mainfrom
paulbalaji:fix-slice-array-pointer

Conversation

@paulbalaji

Copy link
Copy Markdown
Contributor

Problem

(*[8]byte)(b) passes gosvm check and native tests, then gosvm build fails on both targets: the (*T)(p) rule never looked at the operand, so the backend cast the C slice struct to a pointer. [N]T(s) was rejected by Check outright.

Solution

Both forms now follow Go:

  • len(s) < N aborts with the existing bounds trap, no new code
  • (*[N]T)(s) is the slice's data pointer, so writes through either view (account data included) see each other; a nil slice gives nil, which only N == 0 reaches
  • [N]T(s) copies the first N elements; [0]T(s) reads nothing

Both are plain operations in gc's order: in (*[8]byte)(b)[0] + f(), f runs first, its panic wins, and a b it replaced is the one converted. Converting package initializers run at run time; the compile-time evaluator used to return the slice as the pointer and panic. gosvm features reports slice_array_conversions. ERROR_CONTRACT changelog entry included (announce to Qushta).

Tests

  • TestSliceArray*: two programs, 256 inputs each, native Go (host 1.23.2) vs host C, comparing output, writes before a failure and panic lines: len == N, len > N, len < N, aliasing, nil and empty slices, struct/array/generic elements, nested generics, escapes, initializers, evaluation order.
  • Real SBF on fogo-v0 and solana-v3: 96 inputs per program, all match, failures roll back.
  • Dropping the evaluation-order event or the initializer stop fails the tests.
  • The five examples/* gosvm projects, a fresh starter and the 4 reference ELFs build byte-identical to 79cf25c. The frozen corpus's one slice conversion ([24]byte(s)) now compiles, recorded in corpusChanges. make test passes apart from the known TestStdlib* and TestFullSwapFacts host failures.

Linked work

Closes #7. hyperlane-xyz/hyperlane-monorepo#9852 worked around it by copying keys into a [32]byte. Evidence: results/compiler/2026-10-19-slice-array-conversions. Conflicts with the #8 fix on ERROR_CONTRACT and HANDOFF are mechanical.

(*[N]T)(s) passed Check and then failed the built-in backend's C compile
(CrocSwap#7): the pointer-conversion rule never looked at the operand, so the
emitter cast the C slice struct to a pointer. [N]T(s) was rejected.

Both now follow Go. len(s) < N aborts with the bounds trap; the pointer is
the slice's data pointer, so writes through either view see each other,
and a nil slice gives nil (only N == 0 reaches it); the array is a copy of
the first N elements, and [0]T(s) reads nothing. Both are plain operations
in gc's evaluation order: calls in the same statement run first, so their
panics win and a slice they replace is the one converted. Initializers
that convert run at run time instead of at compile time, where the
interpreter returned the slice as the pointer. gosvm features reports
slice_array_conversions.

Host differentials against native Go (output bytes, writes before a
failure, panic lines) and SBF runs on fogo-v0 and solana-v3. The corpus's
one recorded slice conversion now compiles; every example ELF and the
reference ELFs are unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Slice-to-array-pointer conversion passes check but emits invalid C

2 participants