Skip to content

bugc: lay storage arrays out as Solidity does - #364

Closed
gnidan wants to merge 1 commit into
mainfrom
bugc-fixed-array-layout
Closed

gnidan wants to merge 1 commit into
mainfrom
bugc-fixed-array-layout

Conversation

@gnidan

@gnidan gnidan commented Oct 7, 2026

Copy link
Copy Markdown
Member

Storage arrays now have Solidity's layout.

  • A fixed-size array is inline: its elements start at its own slot. Before, the code put every array's elements at keccak256(slot) + i, but the debug pointer of a fixed-size array described them inline at slot + i, so a debugger read the wrong slots.
  • A dynamic array still keeps its length in its slot and its elements from keccak256(slot).
  • In either kind of array, an element narrower than 16 bytes shares a slot with its neighbors, as many as fit, from the slot's low-order end, as Solidity packs them. For a dynamic array this changes the code too. The pointer already described narrow elements as packed (with the wrong offsets), but the code gave each element a slot, so a pointer and the code could not agree without one of them changing, and Solidity's layout packs them. Any other element starts a slot and takes as many slots as its type needs (a struct, or an inner fixed-size array).
  • In a struct, a fixed-size array member takes all its slots, and the next member starts the slot after them. Before, it took one slot.

How it is built:

  • types/storage.ts is the one statement of the layout: an elementary type's size, how many elements share a slot, and how many slots a type takes. The struct layout, the code, and the pointers all use it.
  • In IR generation, emitArrayElement replaces the two copies of the array branch in the storage chain load and store, and the array literal's storage writes. A fixed-size array literal no longer writes a length word into its first element's slot. For an element that shares its slot, the index's slot and byte offset are computed at run time (div/mod/mul, which constant folding removes for a constant index).
  • Code generation now accepts a storage read/write offset that is not a constant: it shifts by offset << 3 at run time.
  • The storage pointer generator takes a slot expression, so a struct or an array inside a list gets the element's own slot (before, it used slot 0). Each nested list has its own index variable.

arrays.bug declared its scalars at slots 1 to 5, inside its 10-slot array. They now start at slot 10. The test that expected one element of an int16 array at keccak256(0) + 1, and the IR test that expected a compute_slot for a fixed-size array in a mapping, now expect the inline layout. The storage verification test needed a local node and was skipped. It now runs on the in-process executor, and it checks the inline slots it always expected. The BUG case study describes the layout.

Known limit, unchanged here: a struct member that is itself a struct still takes one slot.

Conflicts to expect: #355 changes generateStorageWrite next to the offset handling here, and #361 changes irgen/generate/storage.ts.

@gnidan
gnidan force-pushed the bugc-fixed-array-layout branch from 9b8cfe6 to c0ab7fa Compare October 7, 2026 02:27
@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-10-07 03:11 UTC

@gnidan

gnidan commented Oct 7, 2026

Copy link
Copy Markdown
Member Author

Included in #368.

@gnidan gnidan closed this Oct 7, 2026
@gnidan
gnidan deleted the bugc-fixed-array-layout branch October 7, 2026 03:08
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.

1 participant