Implement storage arrays#469
Draft
axic wants to merge 13 commits into
Draft
Conversation
Adds an array(member, index) data type to std alongside mapping(member, index), with parallel Typedef, StorageSize, CanStore, LValueIdxAccess and RValueIdxAccess instances. Slot for arr[i] follows Solidity's dynamic array layout: keccak256(p) + i. Also exports larr/rarr helpers analogous to lidx/ridx.
Adds 129arraystorage.solc which constructs a storage(array(uint256, uint256)) at a fixed slot, writes two elements via Assign + larr, and reads them back via rarr. Registered alongside the storage mapping tests in test/Cases.hs.
Drops the index type parameter from array since the index is always a Typedef(word) value, leaving the element type as the sole parameter (matching Solidity's T[] surface form). Updates the existing storage test and adds a new test that defines a contract field `arr : array(uint256)` and accesses it via larr/rarr.
Adds test/examples/dispatch/storage.{solc,json} which exercises a
MemberRegistry contract with a storage array of addresses:
- addMember(address) appends to the array
- removeMember(address) performs swap-pop, reverting if not found
- numberOfMembers() returns the count
- getMembers() returns the full address[]
The contract maintains a separate memberCount alongside the storage
array. It also defines an ABIEncode/ABIAttribs instance for
memory(DynArray(t)) (t:Typedef(word)) so that getMembers can return a
true dynamic array via the dispatch ABI encoder.
Adds an Array typeclass to std with length/setLength methods and an instance for storage(array(t)) that reads/writes the slot itself (Solidity storage layout: length at slot p, elements at keccak256(p) + i). The dispatch test no longer carries a separate memberCount field — it calls Array.length(members) and Array.setLength(members, n) instead.
- Array typeclass gains push/pop methods (with element type as a class parameter); push grows the length, pop shrinks it (and reverts on empty). - Array bounds-check: LValueIdxAccess/RValueIdxAccess on storage(array(a)) now consult Array.length and revert via out_of_bounds when i >= length. - lidx/ridx become generic typeclass wrappers (LValueIdxAccess/ RValueIdxAccess), so arr[i] works for both mappings and arrays via the shared FieldAccess desugaring; larr/rarr are dropped. - NameResolution learns a small UFCS rule: when the receiver of a method-style call resolves to a contract field (`field.method(args)`) and no class is otherwise selected, search the class env for a uniquely-named method and rewrite to `Class.method(field, args)`. This enables `members.push(addr)`, `members.length()`, `members.pop()`. - Tests updated accordingly: dispatch/storage.solc no longer carries a separate memberCount; spec/129arraystorage and spec/130arrayfield use push and the bounds-checked accessors.
memory(ty):ABIAttribs (for any ty:ABIAttribs) + DynArray(t):ABIAttribs already derive it. Keep only the ABIEncode instance, which has no generic alternative (the commented-out memory(ty):ABIEncode based on MemoryType still fails patterson/coverage).
Switches removeMember from swap-pop to the user-specified pattern: foundIdx starts at length() as a not-found sentinel, the scan keeps going past the match (solcore has no break), then subsequent elements are shifted one slot down to close the gap and pop() drops the duplicated last item. Preserves order of remaining members.
The previous Array.push/pop bodies dispatched recursively through Array.length/Array.setLength on the same instance, which the specialiser appears to have trouble pinning down for the storage(array(t)):Array(t) MPTC instance (it produced 'no resolution found for StorageType.store : word -> array(address) -> ()'). Inlining the slot/length arithmetic with sload_/sstore_/StorageType.store directly avoids the recursive class dispatch entirely. Same inlining for the LValueIdxAccess bounds check (also drops its previously redundant a:StorageType constraint).
Restructure to avoid MPTC inference issues on length/setLength/pop, which never mention elem in their signatures. push, which actually binds the element type via val:elem, lives in its own MPTC class ArrayPush(elem). UFCS still finds each method via the class-method search, so members.push(addr), members.length(), members.pop() all keep working unchanged in the tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.