diff --git a/README.md b/README.md index 4e78afdf..7b9ffe2a 100644 --- a/README.md +++ b/README.md @@ -7,21 +7,28 @@ Try it in the [online playground](https://solcore-rs-preview.solcore-rs-team.workers.dev/). -## Compatibility target +## Language and compatibility targets -The current compatibility target is the Haskell reference implementation at +The canonical source-language target is [`syntax.md`](syntax.md). It +intentionally replaces legacy Haskell Solcore spellings with the new Core +surface, so source-syntax compatibility with the Haskell implementation is not +a language goal. The compiler, standard library, examples, and fixtures use the +new `.sol` surface. + +For semantics already supported by Core, the comparison baseline remains the +Haskell reference implementation at [`argotorg/solcore@2f372bde`](https://github.com/argotorg/solcore/tree/2f372bde2801612814015a22319d0bc51486cbf0). -The standard library and the complete 499-source reference frontend corpus are -vendored from that exact revision. See +The standard library and the complete 499-source frontend corpus are semantic +ports of that exact revision to the canonical syntax. See [`SEMANTIC_DIFFERENCES.md`](SEMANTIC_DIFFERENCES.md) for intentional Rust extensions, shared upstream limitations, and phase-sensitive differences, and the [corpus README](crates/parser/tests/fixtures/corpus/README.md) for the reproducible reference verdict configuration. -Compatibility does not imply production readiness or byte-for-byte compiler -output. Rust deliberately keeps structured diagnostics and several safety -checks that are stricter than the reference target, while shared upstream -limitations remain explicitly unsupported. +Semantic compatibility does not imply source-syntax compatibility, production +readiness, or byte-for-byte compiler output. Rust deliberately keeps structured +diagnostics and several safety checks that are stricter than the reference +target, while shared upstream limitations remain explicitly unsupported. ## Build and test diff --git a/SEMANTIC_DIFFERENCES.md b/SEMANTIC_DIFFERENCES.md index 98258e29..38a4e3c9 100644 --- a/SEMANTIC_DIFFERENCES.md +++ b/SEMANTIC_DIFFERENCES.md @@ -5,12 +5,20 @@ the Haskell and Rust Solcore implementations. It records which behavior should win and where a fix belongs. The parity TSV files are executable test ledgers; they are not the language specification. +The canonical Core source surface is specified separately in +[`syntax.md`](syntax.md) and intentionally does not preserve legacy Haskell +spellings. The pinned Haskell compiler remains a comparison baseline for +already-supported semantics and the shared standard library, not a +source-syntax compatibility target. Consequently, legacy `.sol` corpus +acceptance records migration coverage but does not make a spelling canonical. + ## Comparison baseline - Haskell reference: [`argotorg/solcore@2f372bde`](https://github.com/argotorg/solcore/tree/2f372bde2801612814015a22319d0bc51486cbf0). - Rust baseline: `8536dc3dc673aee71cbc8f25d3e49208b9a614c2`; this change set synchronizes it with that reference. -- Standard library target: the byte-identical `2f372bde` snapshot in [`std/`](std/). +- Standard library target: the syntax-migrated semantic snapshot of `2f372bde` + in [`std/`](std/). - Validation date: 2026-08-11. The complete 499-source reference corpus in @@ -32,9 +40,10 @@ frontend, including generated dispatch. Keep these categories separate: 4. a shared-std defect; and 5. an implementation defect after both sides run in the same mode. -Written syntax and safety invariants take priority over accidentally accepted -legacy fixtures. For an external ABI, a type is supported only when ABI -metadata, selector spelling, argument decoding, and result encoding agree. +The canonical syntax specification and safety invariants take priority over +accidentally accepted legacy fixtures. For an external ABI, a type is supported +only when ABI metadata, selector spelling, argument decoding, and result +encoding agree. ## Decision summary @@ -43,22 +52,22 @@ the compiler behaviors already agree once the same options are used. | Area / witness | Observed behavior | Recommendation and owner | | --- | --- | --- | -| `for` post-clause `let` ([fixture](crates/parser/tests/fixtures/corpus/fail/test/examples/cases/for-let-post.solc)) | Rust accepts it; the Haskell parser accepts only assignments in the post clause. | A post clause has the same forms as an init clause, as the Haskell language documentation says. **Fix Haskell parser and its negative fixture.** | +| `for` post-clause `let` ([fixture](crates/parser/tests/fixtures/corpus/fail/test/examples/cases/for-let-post.sol)) | Rust accepts it; the Haskell parser accepts only assignments in the post clause. | A post clause has the same forms as an init clause, as the Haskell language documentation says. **Fix Haskell parser and its negative fixture.** | | Calling a `word` (`Uncurry`, `rec`) | Haskell accepts invocation of a value annotated as `word`; Rust reports a non-callable value. | Only function/invokable values are callable. **Fix Haskell type checking; keep Rust.** | -| Explicit closure desugaring ([fixture](crates/parser/tests/fixtures/corpus/fail/test/examples/cases/compose_desugared.solc)) | Haskell says the generated-style `invoke` implementation is not polymorphic enough; Rust accepts it. | Accept the explicit representation if it is valid closure-conversion output. **Fix Haskell rank-polymorphic checking**, while retaining a Rust specialization regression. | -| Narrowed instance member ([fixture](crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ixa.solc)) | Haskell accepts `size : Proxy(memory(a)) -> word` where the instantiated class requires `Proxy(memory(array(a)))`; Rust rejects it. | An instance member must implement the instantiated class signature. **Fix Haskell instance checking; keep Rust.** | +| Explicit closure desugaring ([fixture](crates/parser/tests/fixtures/corpus/fail/test/examples/cases/compose_desugared.sol)) | Haskell says the generated-style `invoke` implementation is not polymorphic enough; Rust accepts it. | Accept the explicit representation if it is valid closure-conversion output. **Fix Haskell rank-polymorphic checking**, while retaining a Rust specialization regression. | +| Narrowed instance member ([fixture](crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ixa.sol)) | Haskell accepts `size : Proxy(memory(a)) -> word` where the instantiated class requires `Proxy(memory(array(a)))`; Rust rejects it. | An instance member must implement the instantiated class signature. **Fix Haskell instance checking; keep Rust.** | | Recursive/table-reuse fixtures | Haskell legacy rejects `super-class-recursive-arg`, `tabled-answer-reuse`, and `tabled-mutual-chain`; Haskell tabled mode and Rust accept them. | These are not semantic differences under the tabled resolver. Make tabled canonical, or record the mode in each verdict. **Fix Haskell configuration and the harness.** | -| Polymorphic comptime argument ([fixture](crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_poly_runtime.solc)) | The Haskell legacy frontend first reports ambiguity. Both Haskell tabled full-pipeline mode and Rust specialization reject a runtime value passed to a comptime parameter; the Rust frontend-only parity probe intentionally defers it. | The latent comptime obligation is already preserved through Rust specialization. **Keep the specialization regression and record the phase in the harness.** | -| Parameterized contract `main` ([fixture](crates/parser/tests/fixtures/corpus/ok/test/examples/cases/multi-stmt-var-leaf.solc)) | Haskell suppresses generated dispatch whenever a local `main` exists and accepts parameters; Rust rejects them because the runtime entry receives no arguments. | A source runtime entry must be zero-argument. **Fix Haskell dispatch validation; keep Rust.** | +| Polymorphic comptime argument ([fixture](crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_poly_runtime.sol)) | The Haskell legacy frontend first reports ambiguity. Both Haskell tabled full-pipeline mode and Rust specialization reject a runtime value passed to a comptime parameter; the Rust frontend-only parity probe intentionally defers it. | The latent comptime obligation is already preserved through Rust specialization. **Keep the specialization regression and record the phase in the harness.** | +| Parameterized contract `main` ([fixture](crates/parser/tests/fixtures/corpus/ok/test/examples/cases/multi-stmt-var-leaf.sol)) | Haskell suppresses generated dispatch whenever a local `main` exists and accepts parameters; Rust rejects them because the runtime entry receives no arguments. | A source runtime entry must be zero-argument. **Fix Haskell dispatch validation; keep Rust.** | | Missing helper imports (`contract-local-derive`, `contract-local-type-same-name`, `field-helper-cxt-collision`, `pair-bug`, `ufcs-no-conflict`) | Haskell `-g` verdicts pass; both full frontends fail because the fixtures omit `std.dispatch`. | This is a mode mismatch. Compare both with dispatch or both without it. **Fix the harness/fixtures.** | | Primitive `word` in public ABI | Both metadata emitters call it `uint256`, but shared std cannot dispatch source `word`. Both compilers report missing evidence; current Rust tabled resolution terminates with a bounded `SC0207`. | Add complete `word` evidence in the **upstream Haskell std**, then re-vendor. Keep a Rust regression proving bounded failure while evidence is missing. | -| User ADTs in public ABI | At `2f372bde`, upstream supports non-recursive, compiler-derived `Generic` ADTs both directly and under `calldata(array(T))`; runtime selectors spell the Generic representation structurally. Its `ContractDispatch.abiTypeOf` emits source metadata only for a nullary `TyCon n []`, so a concrete parameterized ADT can derive runtime evidence but still fails upstream ABI JSON emission. Rust mirrors the nullary surface and intentionally extends metadata to source spellings such as `Point(uint256)`. Recursive, excluded, and manually represented ADTs lack the derived decode path. | Keep the safe Rust parameterized-metadata extension, but do not describe it as exact target-emitter parity. Keep non-derived forms rejected with structured diagnostics, and never infer ABI meaning from a same-named user `array`/`calldata` type. | -| Derived ADTs in storage | `2f372bde` and Rust derive per-type `StorageSize` and `storage(T):CanStore(T)` for eligible, non-recursive compiler-owned `Generic` ADTs when `std.StorageGeneric` is visible in the definition module. Direct fields, ADTs as mapping values, and `memory(bytes)`/`memory(string)` leaves are supported. Recursive ADTs and an ADT field whose leaf is a whole mapping remain unstorable. | This is parity. Keep derivation compiler-owned and definition-scoped, and reject unsupported leaves or recursion at the storage use site with structured diagnostics. The no-dispatch ledger's recursive-ADT pass is a phase difference, not a semantic acceptance. | -| ABI type validation | Haskell passes other nullary names through and uses `error` for unsupported shapes. Rust uses canonical checks and diagnostics, including rejection of `memory(DynArray(address))` outputs in `storage_array` and `ufcs_array`. | Validate against the dispatchable ABI surface. **Fix the Haskell ABI emitter; keep Rust's diagnostic model.** | +| User ADTs in public ABI | At `2f372bde`, upstream supports non-recursive, compiler-derived `Generic` ADTs both directly and under its legacy `calldata(array(T))` spelling; runtime selectors spell the Generic representation structurally. Its `ContractDispatch.abiTypeOf` emits source metadata only for a nullary `TyCon n []`, so a concrete parameterized ADT can derive runtime evidence but still fails upstream ABI JSON emission. Rust mirrors the nullary surface and intentionally extends metadata to source spellings such as `Point`. Recursive, excluded, and manually represented ADTs lack the derived decode path. | Keep the safe Rust parameterized-metadata extension, but do not describe it as exact target-emitter parity. Keep non-derived forms rejected with structured diagnostics, and never infer ABI meaning from a same-named user `array`/`calldata` type. | +| Derived ADTs in storage | `2f372bde` and Rust derive per-type `StorageSize` and `storage: CanStore` for eligible, non-recursive compiler-owned `Generic` ADTs when `std.StorageGeneric` is visible in the definition module. Direct fields, ADTs as mapping values, and `memory`/`memory` leaves are supported. Recursive ADTs and an ADT field whose leaf is a whole mapping remain unstorable. | This is parity. Keep derivation compiler-owned and definition-scoped, and reject unsupported leaves or recursion at the storage use site with structured diagnostics. The no-dispatch ledger's recursive-ADT pass is a phase difference, not a semantic acceptance. | +| ABI type validation | Haskell passes other nullary names through and uses `error` for unsupported shapes. Rust uses canonical checks and diagnostics, including rejection of `memory>` outputs in `storage_array` and `ufcs_array`. | Validate against the dispatchable ABI surface. **Fix the Haskell ABI emitter; keep Rust's diagnostic model.** | | Signature/selector collisions | Rust rejects duplicate signatures and distinct signatures with the same four-byte selector. Haskell has no equivalent preflight. | Reject both before code generation. **Fix Haskell dispatch generation.** | | Nested tuple boundary | Both flatten the language's right-nested pair representation at the top ABI boundary. | This is shared. **Fix both compilers and the language ABI design together** if nested boundaries must be preserved. | -| Top-level `abi_encode` result | `2f372bde` returns a valid length-prefixed `memory(bytes)` and dispatch returns only its payload. Rust vendors the same paired `std.solc`/`dispatch.solc` change. | This is parity. Keep the two std changes atomic and pin direct static/dynamic/ADT encodings in both backends. | -| Textual Yul identifiers and template meta expressions | Both implementations accept standard Yul names beginning with `_`/`$` and containing `$`. Haskell's shared parser also exposes its backtick/`${...}` Template Haskell antiquotes to `.solc` and `.hull` source, then prints their payload as raw Yul; Rust recognizes those forms only to issue a targeted source diagnostic. | **Keep identifier parity, but keep unresolved antiquotes out of source Yul.** Split Haskell's ordinary and quasiquote parsers; retain Rust's negative regressions so internal template syntax cannot bypass validation or reach a backend. | +| Top-level `abi_encode` result | `2f372bde` returns a valid length-prefixed `memory(bytes)` and dispatch returns only its payload. Rust vendors the same paired `std.sol`/`dispatch.sol` change. | This is parity. Keep the two std changes atomic and pin direct static/dynamic/ADT encodings in both backends. | +| Textual Yul identifiers and template meta expressions | Both implementations accept standard Yul names beginning with `_`/`$` and containing `$`. Haskell's shared parser also exposes its backtick/`${...}` Template Haskell antiquotes to legacy `.sol` and `.hull` source, then prints their payload as raw Yul; Rust recognizes those forms only to issue a targeted source diagnostic. | **Keep identifier parity, but keep unresolved antiquotes out of source Yul.** Split Haskell's ordinary and quasiquote parsers; retain Rust's negative regressions so internal template syntax cannot bypass validation or reach a backend. | ## Evidence and rationale @@ -84,7 +93,7 @@ Yul parser matches token variants; dedicated lexer and statement regressions pin the parity. Rust also accepts standard Yul-only identifiers beginning with `_`/`$` or containing `$` in every Yul name position without widening ordinary Solcore identifiers. The executable -[`yul-special-identifiers`](tests/e2e/yul-special-identifiers/main.solc) +[`yul-special-identifiers`](tests/e2e/yul-special-identifiers/main.sol) regression carries those names through both backends. The two superficially similar Haskell meta forms have a different status. @@ -93,7 +102,7 @@ Commit introduced `YMeta` as a "Yul antiquoter" for [`Language.Yul.QuasiQuote`](https://github.com/argotorg/solcore/blob/2f372bde2801612814015a22319d0bc51486cbf0/src/Language/Yul/QuasiQuote.hs#L55-L60). The ordinary source frontend and the quasiquoter both call the same `yulBlock` -parser, so backtick and `${...}` expressions currently leak into `.solc` and +parser, so backtick and `${...}` expressions currently leak into legacy `.sol` and `.hull`; the pretty-printer removes their delimiters and emits the contents as raw Yul. Neither the target's [`YulExpr` grammar](https://github.com/argotorg/solcore/blob/2f372bde2801612814015a22319d0bc51486cbf0/doc/railroad/sail.bnf#L308-L311) @@ -112,7 +121,7 @@ while its tabled tests select `TabledResolution` in Direct runs confirm that the three recursive/reuse fixtures pass in tabled mode. Their legacy failures must not be described as Rust solver extensions. -`ct_param_poly_runtime.solc` is a phase-sensitive case rather than a remaining +`ct_param_poly_runtime.sol` is a phase-sensitive case rather than a remaining semantic difference. Haskell tabled full-pipeline mode and Rust specialization both report a runtime value passed to `Wrap.unwrap`'s comptime parameter, while the Rust frontend-only corpus probe has not reached that phase. Haskell also @@ -152,11 +161,11 @@ semantic-difference counts. Upstream `2f372bde`'s [`DeriveGeneric`](https://github.com/argotorg/solcore/blob/2f372bde2801612814015a22319d0bc51486cbf0/src/Solcore/Desugarer/DeriveGeneric.hs#L31-L47) uses the `StorageDeriving` marker exported by -[`std.StorageGeneric`](std/StorageGeneric.solc) to emit concrete -`StorageSize` and `storage(T):CanStore(T)` instances beside each eligible +[`std.StorageGeneric`](std/StorageGeneric.sol) to emit concrete +`StorageSize` and `storage: CanStore` instances beside each eligible compiler-derived `Generic` instance. The structural std implementation stores sum, product, and unit representations leaf by leaf; it also bridges dynamic -`memory(bytes)` and `memory(string)` leaves. This supports direct ADT contract +`memory` and `memory` leaves. This supports direct ADT contract fields, nested ADTs, and ADTs used as mapping values without making whole mappings copyable values. @@ -172,7 +181,7 @@ fields, including an otherwise-unused mapping-valued field; the storage E2E fixtures cover direct, nested, mapping-value, enum, boolean, and dynamic-leaf round trips. -`storage-adt-recursive-fail.solc` passes only in the `-g` reference ledger +`storage-adt-recursive-fail.sol` passes only in the `-g` reference ledger because generated dispatch never makes its constructor/storage obligation reachable. The full target frontend and the Rust full-frontend gate both reject the required recursive storage assignment (`CanStore` upstream and the @@ -195,18 +204,18 @@ The current shared snapshot has this evidence matrix: | `address` | yes | yes | yes | complete | | `bytes32` | yes | yes | yes | complete | | `bytes4` | yes | yes | yes | complete at `2f372bde` | -| `memory(string)` | yes | yes | yes | complete | -| `memory(bytes)` | yes | yes | yes | complete | +| `memory` | yes | yes | yes | complete | +| `memory` | yes | yes | yes | complete | | `()` | yes | yes | yes | complete | | `bool` | yes | yes (strict) | yes | complete at `2f372bde` | | `word` (ABI `uint256`) | **no** | **no** | **no** | unsupported by dispatch | | pair/tuple | recursive | recursive | recursive | complete only when all components are complete | -| `calldata(array(t))` | recursive `SigString(t) <> "[]"` | lazy calldata handle | **no** | input-only; complete when `t` has the required input evidence | +| `calldata>` | recursive `SigString(t) <> "[]"` | lazy calldata handle | **no** | input-only; complete when `t` has the required input evidence | | non-recursive derived ADT (direct or array element) | structural Generic representation | compiler-derived `ABIDecode` | representation bridge | complete when every concrete type argument has the required evidence | | recursive/excluded/manual ADT | rejected | no compiler-owned derived decode path | not accepted by Rust ABI preflight | unsupported | The target's top-level `abi_encode` reserves a length word, writes the ABI -payload after it, and returns a valid `memory(bytes)`. Generated dispatch then +payload after it, and returns a valid `memory`. Generated dispatch then uses `MemoryPointer.ptr` and `MemorySize.len` to return only that payload, so existing external ABI results remain unchanged while direct callers can safely pass the encoded bytes to generic memory operations. The paired std update is @@ -223,16 +232,16 @@ bounds make the generated dispatch probe terminate with `SC0207`; the `missing_word_abi_evidence` UI regression exercises the real shared std path. At `2f372bde`, `std.dispatch` supplies `SigString` for sums and -`calldata(array(t))`, plus a default bridge through `Generic(rep)`; +`calldata>`, plus a default bridge through `Generic`; `std.ABIGeneric` supplies concrete compiler-derived `ABIAttribs` and `ABIDecode` evidence for each eligible ADT and the matching default representation-driven `ABIEncode` bridge. Rust accepts finite, compiler-owned -plans directly and beneath canonical std `calldata(array(...))` wrappers. It +plans directly and beneath canonical std `calldata>` wrappers. It computes selectors from the instantiated Generic `SigString` (comma-joined products and explicit `sum(l,r)` nodes), while ABI JSON keeps the source spelling (`T` or `T[]`). For a nullary ADT this mirrors the target convention. For a concrete parameterized ADT, however, upstream `abiTypeOf` has no matching -case and fails metadata emission; Rust's spelling such as `Point(uint256)` is +case and fails metadata emission; Rust's spelling such as `Point` is an intentional safe extension beyond exact target-emitter behavior. Neither metadata convention claims that an arbitrary Solidity ABI consumer understands Solcore sums. @@ -241,10 +250,10 @@ Under that Rust metadata extension, concrete parameterized ADTs are supported when every type argument discharges the generated ABI constraints, including phantom parameters that do not occur in the instantiated Generic representation. Recursion tracking distinguishes concrete instantiations, so -finite shapes such as `Box(Box(uint256))` remain valid, while a definition whose +finite shapes such as `Box>` remain valid, while a definition whose unspecialized representation mentions itself is rejected before expansion. `no-generic-instance-for` and visible manual `Generic` evidence remain errors. -The `calldata(array(t))` location itself remains input-only: the target std has +The `calldata>` location itself remains input-only: the target std has no `ABIEncode` instance for that lazy handle, so Rust follows derived Generic representations and rejects the handle from every nested result position even though the same type is valid in a parameter. @@ -269,20 +278,21 @@ allowlist or evidence-based query. The `storage_array` and `ufcs_array` rows in the Rust rejection allowance are also phase-sensitive: the target verdict was recorded with generated dispatch disabled, while Rust's full gate reaches external ABI validation. Rust rejects -their `memory(DynArray(address))` result with `SC0231` because only canonical -`memory(string)` and `memory(bytes)` currently have matching metadata and +their `memory>` result with `SC0231` because only canonical +`memory` and `memory` currently have matching metadata and runtime evidence. Keeping the structured rejection is intentional. ## Standard-library recommendation -The `.solc` files in [`std/`](std/) are a shared compatibility artifact, not a -Rust fork. Do not apply Rust-only semantic edits. A shared-std fix must: +The `.sol` files in [`std/`](std/) are a canonical-syntax port of the shared +semantic artifact, not a Rust semantic fork. Do not apply Rust-only semantic +edits. A shared-std fix must: 1. reproduce with the pinned Haskell compiler and upstream std; 2. be fixed and tested in upstream Haskell std first; 3. pin the new upstream revision; -4. be re-vendored byte-for-byte into `std/` and - `crates/parser/tests/fixtures/corpus/ok/std/`; and +4. be re-vendored, migrated to canonical syntax, and copied byte-for-byte into + `std/` and `crates/parser/tests/fixtures/corpus/ok/std/`; and 5. pass full dispatch and backend tests on both implementations. The required invariant is: @@ -294,7 +304,7 @@ The required invariant is: For the derived-ADT extension, “ABI JSON can represent it” means the explicit source-name metadata convention (`SourceName` directly and `SourceName[]` for lazy arrays), plus Rust's deliberate parameterized spelling extension such as -`Point(uint256)`. Selector hashing uses the structural Generic spelling. Both +`Point`. Selector hashing uses the structural Generic spelling. Both spellings must be derived from the same compiler-owned ADT plan; accepting a manual or recursive representation would break that link and is therefore prohibited. The parameterized spelling satisfies this Rust invariant even @@ -302,7 +312,7 @@ though the target's `abiTypeOf` fails before producing equivalent JSON. The next upstream std change should complete `word`, then extend the argument/result matrix for `word`, `uint256`, `address`, `bytes4`, `bytes32`, -`bool`, `memory(string)`, `memory(bytes)`, supported tuples, and canonical +`bool`, `memory`, `memory`, supported tuples, and canonical calldata-array inputs. Unsupported location wrappers, calldata-array results, std leaf types, and ADTs outside the finite compiler-derived surface remain explicitly rejected. @@ -310,7 +320,7 @@ Each test must use generated selector dispatch and must not define source `main`, because source `main` suppresses the path under test. Haskell ABI diagnostics and collision checks do not belong in std. Keep `import -std.dispatch.{*};` explicit until both compilers have a specified +* from std.dispatch;` explicit until both compilers have a specified compiler-private dependency mechanism. ## Keeping the parity ledger honest @@ -326,8 +336,8 @@ compiler-private dependency mechanism. After a std update, verify the copies and then the full pipelines: ```sh -for file in ABIGeneric.solc Generic.solc StorageGeneric.solc dispatch.solc \ - eip712.solc eip7951.solc opcodes.solc std.solc; do +for file in ABIGeneric.sol Generic.sol StorageGeneric.sol dispatch.sol \ + eip712.sol eip7951.sol opcodes.sol std.sol; do cmp "std/$file" "crates/parser/tests/fixtures/corpus/ok/std/$file" || exit 1 done cargo test -p solcore-parser -p solcore-hir-ty -p solcore-specialize --locked diff --git a/benchmarks/README.md b/benchmarks/README.md index 16e468a6..79972180 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -10,7 +10,7 @@ The fixed cases cover distinct compiler workloads: | Case | Fixture | Purpose | | --- | --- | --- | -| `std-free` | `SingleFun.solc` | Small frontend run without reachable std/runtime | +| `std-free` | `SingleFun.sol` | Small frontend run without reachable std/runtime | | `dispatch-small` | `tests/e2e/022add` | Small contract with compiler-owned dispatch | | `erc20-large` | `tests/e2e/128minierc20` | Larger std- and storage-heavy contract | | `multi-file` | `tests/e2e/ltimp` | Main module plus a local import | diff --git a/benchmarks/tofu/materialize.py b/benchmarks/tofu/materialize.py index 0e16b18d..3aaf730b 100644 --- a/benchmarks/tofu/materialize.py +++ b/benchmarks/tofu/materialize.py @@ -11,18 +11,18 @@ REPOSITORY = HERE.parents[1] CASES = { "std-free": { - "main.solc": REPOSITORY - / "crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SingleFun.solc", + "main.sol": REPOSITORY + / "crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SingleFun.sol", }, "dispatch-small": { - "main.solc": REPOSITORY / "tests/e2e/022add/main.solc", + "main.sol": REPOSITORY / "tests/e2e/022add/main.sol", }, "erc20-large": { - "main.solc": REPOSITORY / "tests/e2e/128minierc20/main.solc", + "main.sol": REPOSITORY / "tests/e2e/128minierc20/main.sol", }, "multi-file": { - "main.solc": REPOSITORY / "tests/e2e/ltimp/main.solc", - "ltproxy.solc": REPOSITORY / "tests/e2e/ltimp/ltproxy.solc", + "main.sol": REPOSITORY / "tests/e2e/ltimp/main.sol", + "ltproxy.sol": REPOSITORY / "tests/e2e/ltimp/ltproxy.sol", }, } @@ -36,7 +36,7 @@ def standard_json(sources): }, "settings": { "solcore": { - "entrypoint": "main.solc", + "entrypoint": "main.sol", "stage": "hull", }, "outputSelection": {"*": {"*": []}}, diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index f18a2765..02e9768c 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -415,7 +415,7 @@ mod tests { #[test] fn frontend_diagnostics_are_lowered() { let mut db = TestDb::default(); - let key = load_main_source(&mut db, "function main() -> word { return true; }\n"); + let key = load_main_source(&mut db, "function main() returns (word) { return true; }\n"); let entry = module_id_from_key(&db, &key); let diagnostics = collect_frontend_diagnostics(&db, entry); @@ -432,7 +432,7 @@ mod tests { let mut db = TestDb::default(); let key = load_main_source( &mut db, - "contract Main { public function answer() -> word { return 42; } }\n", + "contract Main { function answer() public returns (word) { return 42; } }\n", ); let entry = module_id_from_key(&db, &key); @@ -449,7 +449,7 @@ mod tests { #[test] fn clean_source_builds_checked_hull() { let mut db = TestDb::default(); - let key = load_main_source(&mut db, "function main() -> word { return 42; }\n"); + let key = load_main_source(&mut db, "function main() returns (word) { return 42; }\n"); let entry = module_id_from_key(&db, &key); let file = db.module_file(entry).expect("entry source"); @@ -465,17 +465,17 @@ mod tests { let mut db = TestDb::default(); let entry_key = load_main_source( &mut db, - "import a; import b;\nfunction main() -> word { return 0; }\n", + "import a; import b;\nfunction main() returns (word) { return 0; }\n", ); insert_main_module( &mut db, "a", - "contract Token { public function main() -> word { return 1; } }\n", + "contract Token { function main() public returns (word) { return 1; } }\n", ); insert_main_module( &mut db, "b", - "contract Token { public function main() -> word { return 2; } }\n", + "contract Token { function main() public returns (word) { return 2; } }\n", ); set_main_module_paths(&mut db, &["main", "a", "b"]); let entry = module_id_from_key(&db, &entry_key); @@ -512,7 +512,7 @@ mod tests { library: LibraryId::Main, logical_path: vec![name.to_owned()], }; - let url = Url::parse(&format!("memory:///main/{name}.solc")).expect("module URL"); + let url = Url::parse(&format!("memory:///main/{name}.sol")).expect("module URL"); let file = SourceFile::new(db, url, Some(source.to_owned())); db.insert_module_file(key, file); } @@ -521,7 +521,7 @@ mod tests { let root = PathBuf::from("/main"); let existing_files = stems .iter() - .map(|stem| root.join(format!("{stem}.solc"))) + .map(|stem| root.join(format!("{stem}.sol"))) .collect::>(); let sibling_stems = BTreeMap::from([(root, stems.iter().map(|stem| (*stem).to_owned()).collect())]); diff --git a/crates/driver/src/args.rs b/crates/driver/src/args.rs index 5174eb52..2a3d76f3 100644 --- a/crates/driver/src/args.rs +++ b/crates/driver/src/args.rs @@ -391,6 +391,12 @@ pub(crate) fn parse_args(args: Vec) -> Result { let Some(input) = input else { return Err("missing input file".to_owned()); }; + if input.extension() != Some(OsStr::new("sol")) { + return Err(format!( + "input source file `{}` must use the `.sol` extension", + input.display() + )); + } if emit_yul_object.is_some() && emit_yul.is_none() { return Err("--emit-yul-object requires --emit-yul".to_owned()); } @@ -600,7 +606,7 @@ pub(crate) fn default_diagnostic_width() -> usize { } pub(crate) fn usage_text(program: &str) -> String { - format!("usage: {program} [OPTIONS] \ntry `{program} --help` for more information") + format!("usage: {program} [OPTIONS] \ntry `{program} --help` for more information") } pub(crate) fn help_text(program: &str) -> String { @@ -608,7 +614,7 @@ pub(crate) fn help_text(program: &str) -> String { "\ Solcore Rust driver -Usage: {program} [OPTIONS] [] +Usage: {program} [OPTIONS] [] Options: -f, --file FILE Input source file (alternative to positional input) diff --git a/crates/driver/src/paths.rs b/crates/driver/src/paths.rs index cfb97053..ef2f1554 100644 --- a/crates/driver/src/paths.rs +++ b/crates/driver/src/paths.rs @@ -117,7 +117,7 @@ fn collect_module_fs_snapshot( }; for entry in entries.flatten() { let path = entry.path(); - if path.extension().and_then(|extension| extension.to_str()) == Some("solc") { + if path.extension().and_then(|extension| extension.to_str()) == Some("sol") { if path.is_file() { existing_files.insert(path.clone()); } @@ -180,8 +180,8 @@ mod tests { #[test] fn lexical_normalization_removes_dot_and_parent_components() { - let normalized = normalize_lexically(Path::new("alpha/./beta/../gamma/main.solc")); - assert_eq!(normalized, PathBuf::from("alpha/gamma/main.solc")); + let normalized = normalize_lexically(Path::new("alpha/./beta/../gamma/main.sol")); + assert_eq!(normalized, PathBuf::from("alpha/gamma/main.sol")); } #[test] diff --git a/crates/driver/src/standard_json.rs b/crates/driver/src/standard_json.rs index 25069b1f..d57b7d99 100644 --- a/crates/driver/src/standard_json.rs +++ b/crates/driver/src/standard_json.rs @@ -14,7 +14,7 @@ use std::{ use serde_json::{Map, Value, json}; use vfs::{Diagnostic, DiagnosticSeverity, Workspace, WorkspaceFileChange}; -const DEFAULT_ENTRYPOINT: &str = "main.solc"; +const DEFAULT_ENTRYPOINT: &str = "main.sol"; #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum Stage { @@ -154,10 +154,10 @@ fn validate_source_name(name: &str) -> Result<(), String> { || name.contains('\\') || name.contains(':') || !has_only_normal_components - || path.extension().and_then(|extension| extension.to_str()) != Some("solc") + || path.extension().and_then(|extension| extension.to_str()) != Some("sol") { return Err(format!( - "source name `{name}` must be a relative, traversal-free `.solc` path" + "source name `{name}` must be a relative, traversal-free `.sol` path" )); } Ok(()) @@ -295,7 +295,7 @@ mod tests { #[test] fn rejects_source_paths_that_escape_the_virtual_workspace() { - for source_name in ["../main.solc", "/main.solc", "dir\\main.solc", "main.sol"] { + for source_name in ["../main.sol", "/main.sol", "dir\\main.sol", "main.solc"] { assert!(validate_source_name(source_name).is_err(), "{source_name}"); } } @@ -304,11 +304,11 @@ mod tests { fn defaults_to_main_entrypoint_and_hull_stage() { let request = parse_request(json!({ "language": "Solcore", - "sources": {"main.solc": {"content": "function main() -> word { return 0; }"}}, + "sources": {"main.sol": {"content": "function main() returns (word) { return 0; }"}}, })) .expect("valid request"); - assert_eq!(request.entrypoint, "main.solc"); + assert_eq!(request.entrypoint, "main.sol"); assert_eq!(request.stage, Stage::Hull); } } diff --git a/crates/driver/tests/standard_json_cli.rs b/crates/driver/tests/standard_json_cli.rs index 255ff4e6..fb2f9495 100644 --- a/crates/driver/tests/standard_json_cli.rs +++ b/crates/driver/tests/standard_json_cli.rs @@ -53,9 +53,9 @@ fn standard_json_compiles_checked_hull_without_polluting_stdout() { let output = run_standard_json(json!({ "language": "Solcore", "sources": { - "main.solc": {"content": "function id(x: word) -> word { return x; }\n"} + "main.sol": {"content": "function id(x: word) returns (word) { return x; }\n"} }, - "settings": {"solcore": {"entrypoint": "main.solc", "stage": "hull"}}, + "settings": {"solcore": {"entrypoint": "main.sol", "stage": "hull"}}, })); let response = response(&output); @@ -68,10 +68,10 @@ fn standard_json_loads_multiple_virtual_source_files() { let output = run_standard_json(json!({ "language": "Solcore", "sources": { - "main.solc": {"content": "import helper.{id};\nfunction main() -> word { return id(0); }\n"}, - "helper.solc": {"content": "export { id };\nfunction id(x: word) -> word { return x; }\n"}, + "main.sol": {"content": "import {id} from helper;\nfunction main() returns (word) { return id(0); }\n"}, + "helper.sol": {"content": "export { id };\nfunction id(x: word) returns (word) { return x; }\n"}, }, - "settings": {"solcore": {"entrypoint": "main.solc", "stage": "frontend"}}, + "settings": {"solcore": {"entrypoint": "main.sol", "stage": "frontend"}}, })); let response = response(&output); @@ -82,7 +82,7 @@ fn standard_json_loads_multiple_virtual_source_files() { fn standard_json_reports_request_errors_in_json() { let output = run_standard_json(json!({ "language": "Solcore", - "sources": {"../escape.solc": {"content": "function main() -> word { return 0; }"}}, + "sources": {"../escape.sol": {"content": "function main() returns (word) { return 0; }"}}, })); let response = response(&output); diff --git a/crates/driver/tests/typeck_cli.rs b/crates/driver/tests/typeck_cli.rs index 09acefab..a3f26d5e 100644 --- a/crates/driver/tests/typeck_cli.rs +++ b/crates/driver/tests/typeck_cli.rs @@ -64,15 +64,62 @@ fn cli_reports_usage_errors_with_exit_code_2() { assert!(stderr.contains("--help"), "{stderr}"); } +#[test] +fn cli_accepts_sol_input_and_rejects_other_source_extensions() { + let dir = temp_dir("source-extension"); + fs::create_dir_all(&dir).expect("create temp dir"); + let source = "function main() returns (word) { return 0; }\n"; + let sol = dir.join("main.sol"); + let solc = dir.join("main.solc"); + let txt = dir.join("main.txt"); + for input in [&sol, &solc, &txt] { + fs::write(input, source).expect("write source"); + } + + let accepted = Command::new(env!("CARGO_BIN_EXE_solcore-driver")) + .arg(&sol) + .output() + .expect("run driver with .sol input"); + assert!( + accepted.status.success(), + ".sol input failed\nstdout:\n{}\nstderr:\n{}", + String::from_utf8_lossy(&accepted.stdout), + String::from_utf8_lossy(&accepted.stderr) + ); + + for (option, input) in [(None, &solc), (Some("--file"), &txt)] { + let mut command = Command::new(env!("CARGO_BIN_EXE_solcore-driver")); + if let Some(option) = option { + command.arg(option); + } + let rejected = command + .arg(input) + .output() + .expect("run driver with invalid source extension"); + assert_eq!(rejected.status.code(), Some(2)); + let stderr = String::from_utf8_lossy(&rejected.stderr); + assert!( + stderr.contains("must use the `.sol` extension"), + "stderr:\n{stderr}" + ); + assert!( + stderr.contains(&input.display().to_string()), + "stderr:\n{stderr}" + ); + } + + let _ = fs::remove_dir_all(&dir); +} + #[test] fn cli_trace_reports_pipeline_summaries_without_verbose_intern_events() { let dir = temp_dir("trace-pipeline"); let output_dir = dir.join("artifacts"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); + let input = dir.join("main.sol"); fs::write( &input, - "contract C { public function main() -> word { return 42; } }\n", + "contract C { function main() public returns (word) { return 42; } }\n", ) .expect("write source"); @@ -117,7 +164,10 @@ fn cli_trace_reports_pipeline_summaries_without_verbose_intern_events() { #[test] fn cli_prints_typeck_mismatch_diagnostic() { - let stderr = driver_stderr("mismatch", "function main() -> word { return true; }\n"); + let stderr = driver_stderr( + "mismatch", + "function main() returns (word) { return true; }\n", + ); assert!(stderr.contains("error[SC0201]"), "stderr:\n{stderr}"); assert_eq!( @@ -126,7 +176,7 @@ fn cli_prints_typeck_mismatch_diagnostic() { "expected one SC0201 diagnostic:\n{stderr}" ); assert!( - stderr.contains("1 | function main() -> word { return true; }"), + stderr.contains("1 | function main() returns (word) { return true; }"), "expected source line in stderr:\n{stderr}" ); assert!( @@ -139,8 +189,8 @@ fn cli_prints_typeck_mismatch_diagnostic() { fn cli_prints_short_diagnostics() { let dir = temp_dir("short-diagnostic"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); - fs::write(&input, "function main() -> word { return true; }\n").expect("write source"); + let input = dir.join("main.sol"); + fs::write(&input, "function main() returns (word) { return true; }\n").expect("write source"); let output = Command::new(env!("CARGO_BIN_EXE_solcore-driver")) .arg("--color=never") @@ -154,7 +204,7 @@ fn cli_prints_short_diagnostics() { assert_eq!(output.status.code(), Some(1)); let stderr = String::from_utf8_lossy(&output.stderr); assert!( - stderr.contains("main.solc:1:34: error[SC0201]: type mismatch: expected word, found bool"), + stderr.contains("main.sol:1:41: error[SC0201]: type mismatch: expected word, found bool"), "stderr:\n{stderr}" ); assert!( @@ -172,7 +222,7 @@ fn cli_reports_non_utf8_input_path_without_panic() { fs::create_dir_all(&dir).expect("create temp dir"); let root = dir.clone(); let mut raw = dir.into_os_string().into_vec(); - raw.extend_from_slice(b"/bad-\xff.solc"); + raw.extend_from_slice(b"/bad-\xff.sol"); let input = OsString::from_vec(raw); let output = Command::new(env!("CARGO_BIN_EXE_solcore-driver")) @@ -196,11 +246,11 @@ fn cli_reports_non_utf8_input_path_without_panic() { fn cli_reports_reachable_missing_external_lib_root() { let dir = temp_dir("missing-external-root"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); + let input = dir.join("main.sol"); let missing = dir.join("missing-ext"); fs::write( &input, - "import @pkg.util;\nfunction main() -> word { return 0; }\n", + "import @pkg.util;\nfunction main() returns (word) { return 0; }\n", ) .expect("write source"); @@ -234,11 +284,11 @@ fn cli_reports_reachable_missing_external_lib_root() { fn cli_reports_unreadable_reachable_module_as_io_error() { let dir = temp_dir("invalid-utf8-module"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); - let dependency = dir.join("util.solc"); + let input = dir.join("main.sol"); + let dependency = dir.join("util.sol"); fs::write( &input, - "import util;\nfunction main() -> word { return 0; }\n", + "import util;\nfunction main() returns (word) { return 0; }\n", ) .expect("write source"); fs::write(&dependency, [0xff, 0xfe]).expect("write invalid UTF-8 dependency"); @@ -271,8 +321,8 @@ fn cli_reports_unreadable_reachable_module_as_io_error() { fn cli_accepts_warning_policy_and_diagnostic_rendering_flags() { let dir = temp_dir("warning-policy"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); - fs::write(&input, "function main() -> word { return 0; }\n").expect("write source"); + let input = dir.join("main.sol"); + fs::write(&input, "function main() returns (word) { return 0; }\n").expect("write source"); for policy in ["default", "always", "never", "deny"] { let output = Command::new(env!("CARGO_BIN_EXE_solcore-driver")) @@ -322,15 +372,16 @@ fn cli_accepts_warning_policy_and_diagnostic_rendering_flags() { fn cli_warning_policy_default_prints_warnings() { let dir = temp_dir("warning-policy-output"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); + let input = dir.join("main.sol"); fs::write( &input, - r#"data Flag = Off | On; + r#"enum Flag {Off , On} -function pick(x : Flag) -> word { - match x { - | _ => return 0; - | Flag.Off => return 1; +function pick(x : Flag) returns (word) { + match (x) { + case Flag.Off { return 0; } + case Flag.Off { return 1; } + default { return 0; } } } "#, @@ -389,15 +440,15 @@ function pick(x : Flag) -> word { fn cli_prints_solver_diagnostic_with_obligation_span() { let stderr = driver_stderr( "solver", - r#"forall a . class a:C {} -forall a . a:C => function use(x : a) -> word { return 0; } -function main(x : word) -> word { return use(x); } + r#"trait C {} +function use(x : a) returns (word) where a: C { return 0; } +function main(x : word) returns (word) { return use(x); } "#, ); assert!(stderr.contains("error[SC0207]"), "stderr:\n{stderr}"); assert!( - stderr.contains("3 | function main(x : word) -> word { return use(x); }"), + stderr.contains("3 | function main(x : word) returns (word) { return use(x); }"), "expected source line in stderr:\n{stderr}" ); assert!( @@ -407,23 +458,23 @@ function main(x : word) -> word { return use(x); } } #[test] -fn cli_prints_instance_soundness_diagnostic_with_head_span() { +fn cli_prints_impl_soundness_diagnostic_with_head_span() { let stderr = driver_stderr( - "instance-soundness", - r#"data Box(a) = Box(word); -forall a b . class a:MyClass(b) {} -forall a b . instance Box(a):MyClass(b) {} + "impl-soundness", + r#"enum Box {Box(word)} +trait MyClass {} +impl MyClass,b> {} "#, ); assert!(stderr.contains("error[SC0212]"), "stderr:\n{stderr}"); assert!( - stderr.contains("3 | forall a b . instance Box(a):MyClass(b) {}"), - "expected instance source line in stderr:\n{stderr}" + stderr.contains("3 | impl MyClass,b> {}"), + "expected impl source line in stderr:\n{stderr}" ); assert!( - stderr.contains("^^^^^^^^^^^^^^^^^ instance head does not determine these variables"), - "expected instance head caret label in stderr:\n{stderr}" + stderr.contains("^^^^^^^^^^^^^^^^^ impl head does not determine these variables"), + "expected impl head caret label in stderr:\n{stderr}" ); } @@ -433,14 +484,14 @@ fn cli_uses_root_override_for_main_library() { let nested = dir.join("nested"); fs::create_dir_all(&nested).expect("create temp dirs"); fs::write( - dir.join("lib.solc"), - "export { value };\nfunction value() -> word { return 5; }\n", + dir.join("lib.sol"), + "export { value };\nfunction value() returns (word) { return 5; }\n", ) .expect("write lib"); - let input = nested.join("main.solc"); + let input = nested.join("main.sol"); fs::write( &input, - "import lib.lib;\nfunction main() -> word { return lib.value(); }\n", + "import lib.lib;\nfunction main() returns (word) { return lib.value(); }\n", ) .expect("write source"); @@ -469,7 +520,7 @@ fn cli_uses_explicit_std_root() { fs::create_dir_all(&std_root).expect("create std dir"); fs::create_dir_all(&input_dir).expect("create input dir"); write_fake_std(&std_root); - let input = input_dir.join("main.solc"); + let input = input_dir.join("main.sol"); write_fake_std_importer(&input); let output = Command::new(env!("CARGO_BIN_EXE_solcore-driver")) @@ -494,9 +545,9 @@ fn cli_uses_explicit_std_root() { fn cli_rejects_missing_std_root_with_actionable_configuration_help() { let dir = temp_dir("missing-std-root"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); + let input = dir.join("main.sol"); let missing = dir.join("missing-std"); - fs::write(&input, "function main() -> word { return 0; }\n").expect("write source"); + fs::write(&input, "function main() returns (word) { return 0; }\n").expect("write source"); let output = Command::new(env!("CARGO_BIN_EXE_solcore-driver")) .arg("--std-root") @@ -529,17 +580,17 @@ fn cli_normalizes_parent_components_before_deriving_the_entry_module() { let src = dir.join("src"); fs::create_dir_all(&src).expect("create source directory"); fs::write( - src.join("util.solc"), - "export { value }; function value() -> word { return 9; }\n", + src.join("util.sol"), + "export { value }; function value() returns (word) { return 9; }\n", ) .expect("write utility module"); - let input = src.join("main.solc"); + let input = src.join("main.sol"); fs::write( &input, - "import util; function main() -> word { return util.value(); }\n", + "import util; function main() returns (word) { return util.value(); }\n", ) .expect("write source"); - let spelled_with_parent = src.join("..").join("src").join("main.solc"); + let spelled_with_parent = src.join("..").join("src").join("main.sol"); let output = Command::new(env!("CARGO_BIN_EXE_solcore-driver")) .arg("--root") @@ -566,7 +617,7 @@ fn copied_binary_resolves_std_next_to_current_exe() { let copied_driver = dir.join("solcore-driver"); fs::copy(env!("CARGO_BIN_EXE_solcore-driver"), &copied_driver).expect("copy driver"); write_fake_std(&dir.join("std")); - let input = input_dir.join("main.solc"); + let input = input_dir.join("main.sol"); write_fake_std_importer(&input); let output = Command::new(&copied_driver) @@ -589,14 +640,14 @@ fn copied_binary_resolves_std_next_to_current_exe() { fn cli_emits_yul_to_stdout_and_hull_to_file() { let dir = temp_dir("emit-backends"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); + let input = dir.join("main.sol"); let output_dir = dir.join("artifacts"); let hull_output = output_dir.join("main.hull"); fs::write( &input, r#" contract C { - public function main() -> word { + function main() public returns (word) { return 42; } } @@ -648,10 +699,10 @@ contract C { fn cli_emits_sonatina_to_stdout_and_output_dir() { let dir = temp_dir("emit-sonatina"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); + let input = dir.join("main.sol"); let output_dir = dir.join("artifacts"); let sonatina_output = output_dir.join("main.sonatina"); - fs::write(&input, "function main() -> word { return 42; }\n").expect("write source"); + fs::write(&input, "function main() returns (word) { return 42; }\n").expect("write source"); let stdout_output = Command::new(env!("CARGO_BIN_EXE_solcore-driver")) .arg("--emit-sonatina") @@ -692,8 +743,8 @@ fn cli_emits_sonatina_to_stdout_and_output_dir() { fn cli_rejects_multiple_backend_stdout_targets() { let dir = temp_dir("multiple-backend-stdout"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); - fs::write(&input, "function main() -> word { return 42; }\n").expect("write source"); + let input = dir.join("main.sol"); + fs::write(&input, "function main() returns (word) { return 42; }\n").expect("write source"); for (first, second) in [ ("--emit-hull", "--emit-yul"), @@ -727,14 +778,14 @@ fn cli_rejects_multiple_backend_stdout_targets() { fn cli_emits_abi_to_output_dir() { let dir = temp_dir("emit-abi"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); + let input = dir.join("main.sol"); let output_dir = dir.join("abi"); let abi_output = output_dir.join("C.abi"); fs::write( &input, r#" contract C { - public function main() -> word { + function main() public returns (word) { return 42; } } @@ -770,14 +821,14 @@ fn cli_abi_ignores_reachable_external_library_contracts() { let output_dir = dir.join("abi"); fs::create_dir_all(&external).expect("create external root"); fs::write( - external.join("token.solc"), - "contract ExternalToken { public function main() -> word { return 7; } }\n", + external.join("token.sol"), + "contract ExternalToken { function main() public returns (word) { return 7; } }\n", ) .expect("write external module"); - let input = dir.join("main.solc"); + let input = dir.join("main.sol"); fs::write( &input, - "import @pkg.token; contract Local { public function main() -> word { return 1; } }\n", + "import @pkg.token; contract Local { function main() public returns (word) { return 1; } }\n", ) .expect("write main module"); @@ -809,19 +860,19 @@ fn cli_abi_rejects_colliding_local_contract_filenames_before_writing() { let output_dir = dir.join("abi"); fs::create_dir_all(&dir).expect("create temp dir"); fs::write( - dir.join("a.solc"), - "contract Token { public function main() -> word { return 1; } }\n", + dir.join("a.sol"), + "contract Token { function main() public returns (word) { return 1; } }\n", ) .expect("write first module"); fs::write( - dir.join("b.solc"), - "contract Token { public function main() -> word { return 2; } }\n", + dir.join("b.sol"), + "contract Token { function main() public returns (word) { return 2; } }\n", ) .expect("write second module"); - let input = dir.join("main.solc"); + let input = dir.join("main.sol"); fs::write( &input, - "import a; import b; function main() -> word { return 0; }\n", + "import a; import b; function main() returns (word) { return 0; }\n", ) .expect("write main module"); @@ -851,13 +902,13 @@ fn cli_abi_rejects_colliding_local_contract_filenames_before_writing() { fn cli_renders_backend_diagnostics_with_stable_codes() { let dir = temp_dir("backend-diagnostic"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); + let input = dir.join("main.sol"); fs::write( &input, r#" -import std.{string}; +import {string} from std; contract C { - public function main() -> string { + function main() public returns (string) { return "nope"; } } @@ -892,15 +943,15 @@ contract C { fn cli_partial_evaluation_fuel_is_configurable() { let dir = temp_dir("configurable-pe-fuel"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); + let input = dir.join("main.sol"); fs::write( &input, r#" -import std.{*}; -function g2() -> word { return 1; } -function g1() -> word { return g2() + g2(); } -function g0() -> word { return g1() + g1(); } -contract C { function main() -> word { return g0(); } } +import * from std; +function g2() returns (word) { return 1; } +function g1() returns (word) { return g2() + g2(); } +function g0() returns (word) { return g1() + g1(); } +contract C { function main() returns (word) { return g0(); } } "#, ) .expect("write source"); @@ -949,16 +1000,16 @@ contract C { function main() -> word { return g0(); } } fn cli_emit_yul_requires_one_top_level_object_or_selection() { let dir = temp_dir("emit-yul-multi-object"); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); + let input = dir.join("main.sol"); fs::write( &input, r#" contract A { - public function main() -> word { return 1; } + function main() public returns (word) { return 1; } } contract B { - public function main() -> word { return 2; } + function main() public returns (word) { return 2; } } "#, ) @@ -1005,7 +1056,7 @@ contract B { fn driver_stderr(label: &str, source: &str) -> String { let dir = temp_dir(label); fs::create_dir_all(&dir).expect("create temp dir"); - let input = dir.join("main.solc"); + let input = dir.join("main.sol"); fs::write(&input, source).expect("write source"); let output = Command::new(env!("CARGO_BIN_EXE_solcore-driver")) @@ -1022,8 +1073,8 @@ fn driver_stderr(label: &str, source: &str) -> String { fn write_fake_std(std_root: &Path) { fs::create_dir_all(std_root).expect("create fake std root"); fs::write( - std_root.join("std.solc"), - "export { solcoreTempStdValue };\nfunction solcoreTempStdValue() -> word { return 7; }\n", + std_root.join("std.sol"), + "export { solcoreTempStdValue };\nfunction solcoreTempStdValue() returns (word) { return 7; }\n", ) .expect("write fake std"); } @@ -1031,7 +1082,7 @@ fn write_fake_std(std_root: &Path) { fn write_fake_std_importer(path: &Path) { fs::write( path, - "import std;\nfunction main() -> word { return std.solcoreTempStdValue(); }\n", + "import std;\nfunction main() returns (word) { return std.solcoreTempStdValue(); }\n", ) .expect("write fake std importer"); } diff --git a/crates/hir-ty/src/contract/abi.rs b/crates/hir-ty/src/contract/abi.rs index 19ede1e4..37b01334 100644 --- a/crates/hir-ty/src/contract/abi.rs +++ b/crates/hir-ty/src/contract/abi.rs @@ -255,7 +255,7 @@ pub(super) fn abi_outputs<'db>( span, "ABI output", &format!( - "{} (calldata(array(t)) is input-only; the target std has no ABIEncode evidence for it)", + "{} (calldata> is input-only; the target std has no ABIEncode evidence for it)", ty.display(db) ), )); @@ -372,7 +372,7 @@ fn abi_type_of<'db>( } /// Returns the element of the one externally supported calldata location: -/// `calldata(array(t))`. Both wrappers must be the canonical definitions from +/// `calldata>`. Both wrappers must be the canonical definitions from /// `std`; same-named user ADTs must not acquire ABI meaning by spelling alone. fn canonical_calldata_array_element<'db>( db: &'db dyn Db, @@ -394,7 +394,7 @@ fn canonical_calldata_array_element<'db>( } = inner.kind(db) else { return Err(format!( - "{} (only calldata(array(t)) has canonical external ABI evidence)", + "{} (only calldata> has canonical external ABI evidence)", inner.display(db) )); }; @@ -566,7 +566,7 @@ fn compiler_owned_generic_sig_string<'db>( } if !abi_evidence.has_derived_abi(user.def) { return Err(format!( - "{name} (compiler-owned ABIAttribs and ABIDecode evidence is not visible from the contract module; add an instance import of its defining module along the re-export path)" + "{name} (compiler-owned ABIAttribs and ABIDecode evidence is not visible from the contract module; import the module containing its defining impl along the re-export path)" )); } let rep = substitute_bound_tys(db, plan.rep, args); @@ -756,13 +756,13 @@ fn canonical_location_abi_name<'db>( } = inner.kind(db) else { return Err(format!( - "{} (only memory(string) and memory(bytes) have canonical ABI evidence)", + "{} (only memory and memory have canonical ABI evidence)", inner.display(db) )); }; if !inner_args.is_empty() { return Err(format!( - "{} (only memory(string) and memory(bytes) have canonical ABI evidence)", + "{} (only memory and memory have canonical ABI evidence)", inner.display(db) )); } @@ -775,7 +775,7 @@ fn canonical_location_abi_name<'db>( return Ok(Some(inner_name)); } Err(format!( - "{} (only memory(string) and memory(bytes) have canonical ABI evidence)", + "{} (only memory and memory have canonical ABI evidence)", inner.display(db) )) } diff --git a/crates/hir-ty/src/contract/dispatch.rs b/crates/hir-ty/src/contract/dispatch.rs index 746fb7d6..9ca0ac58 100644 --- a/crates/hir-ty/src/contract/dispatch.rs +++ b/crates/hir-ty/src/contract/dispatch.rs @@ -303,13 +303,13 @@ pub(crate) fn module_manual_generic_abi_diagnostics<'db>( Some("external ABI evidence must be compiler-owned and canonical"), ) .with_note(format!( - "instance `{}` can override canonical `{class_name}` behavior", + "impl `{}` can override canonical `{class_name}` behavior", instance .name(db) .unwrap_or_else(|| class_name.to_string()) )) .with_help( - "remove the visible manual ABI instance or keep this declaration out of the external ABI", + "remove the visible manual ABI impl or keep this declaration out of the external ABI", ), ); } diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 0989f991..09176b7f 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -22,9 +22,15 @@ pub(crate) fn display_ty_source<'db>(db: &'db dyn Db, ty: Ty<'db>, names: &[Stri let name = display_ty_ctor_source(db, *ctor); if args.is_empty() { name + } else if name == "mapping" && args.len() == 2 { + format!( + "mapping({} => {})", + display_ty_source(db, args[0], names), + display_ty_source(db, args[1], names) + ) } else { format!( - "{name}({})", + "{name}<{}>", args.iter() .map(|arg| display_ty_source(db, *arg, names)) .collect::>() @@ -38,7 +44,10 @@ pub(crate) fn display_ty_source<'db>(db: &'db dyn Db, ty: Ty<'db>, names: &[Stri .map(|param| display_ty_source(db, *param, names)) .collect::>() .join(", "); - format!("({params}) -> {}", display_ty_source(db, *ret, names)) + format!( + "function({params}) returns ({})", + display_ty_source(db, *ret, names) + ) } TyKind::Tuple(elems) => { if elems.is_empty() { @@ -54,7 +63,9 @@ pub(crate) fn display_ty_source<'db>(db: &'db dyn Db, ty: Ty<'db>, names: &[Stri ) } } - TyKind::Comptime(inner) => format!("comptime {}", display_ty_source(db, *inner, names)), + TyKind::Comptime(inner) => { + format!("comptime<{}>", display_ty_source(db, *inner, names)) + } } } @@ -87,14 +98,14 @@ pub(crate) fn display_pred_source<'db>( let main = display_ty_source(db, *main, names); let class = display_class_source(db, *class); if args.is_empty() { - format!("{main} : {class}") + format!("{main}: {class}") } else { let args = args .iter() .map(|arg| display_ty_source(db, *arg, names)) .collect::>() .join(", "); - format!("{main} : {class}({args})") + format!("{main}: {class}<{args}>") } } PredKind::Eq { lhs, rhs } => format!( @@ -118,23 +129,32 @@ pub(crate) fn display_type_ref_source<'db>(db: &'db dyn HirDb, ty: TypeRef<'db>) out.push_str(&ident_text(db, qualifier)); out.push('.'); } - out.push_str(&ident_text(db, name)); + let name_text = ident_text(db, name); + out.push_str(&name_text); if !args.atom().is_empty() { - out.push('('); - out.push_str( - &args - .atom() - .iter() - .map(|arg| display_type_ref_source(db, *arg)) - .collect::>() - .join(", "), - ); - out.push(')'); + if name_text == "mapping" && args.atom().len() == 2 { + out.push('('); + out.push_str(&display_type_ref_source(db, args.atom()[0])); + out.push_str(" => "); + out.push_str(&display_type_ref_source(db, args.atom()[1])); + out.push(')'); + } else { + out.push('<'); + out.push_str( + &args + .atom() + .iter() + .map(|arg| display_type_ref_source(db, *arg)) + .collect::>() + .join(", "), + ); + out.push('>'); + } } out } TypeRefKind::Fn { params, ret } => format!( - "({}) -> {}", + "function({}) returns ({})", params .atom() .iter() @@ -144,7 +164,7 @@ pub(crate) fn display_type_ref_source<'db>(db: &'db dyn HirDb, ty: TypeRef<'db>) display_type_ref_source(db, *ret) ), TypeRefKind::Comptime { inner, .. } => { - format!("comptime {}", display_type_ref_source(db, *inner)) + format!("comptime<{}>", display_type_ref_source(db, *inner)) } TypeRefKind::Tuple { elems } => { format!( diff --git a/crates/hir-ty/src/infer/comptime.rs b/crates/hir-ty/src/infer/comptime.rs index 01fa5046..7c83b4c5 100644 --- a/crates/hir-ty/src/infer/comptime.rs +++ b/crates/hir-ty/src/infer/comptime.rs @@ -695,7 +695,7 @@ impl<'db> ComptimeChecker<'db> { let scheme = class_method_scheme_for_entry(self.db, self.entry_module, class, name.to_owned())?; let mut sig = callable_sig_from_semantic_scheme(self.db, method, scheme)?; - let class_name = class.name(self.db).unwrap_or_else(|| "class".to_owned()); + let class_name = class.name(self.db).unwrap_or_else(|| "trait".to_owned()); sig.name = format!("{class_name}.{name}"); Some(sig) } @@ -1019,10 +1019,6 @@ impl<'db> TypeckDiagnosticCollector<'db> { class: ClassDef<'db>, inherited_type_vars: &[hir_nameres::TypeVarBinding<'db>], ) { - if let Some(diagnostic) = implicit_class_head_binder_diagnostic(self.db, class) { - self.diagnostics - .push(AnyDiagnostic::Typeck(diagnostic.lower())); - } let mut type_vars = inherited_type_vars.to_vec(); type_vars.extend(type_var_bindings( class.def_id_value(self.db), diff --git a/crates/hir-ty/src/infer/diagnostics.rs b/crates/hir-ty/src/infer/diagnostics.rs index 62295364..90247717 100644 --- a/crates/hir-ty/src/infer/diagnostics.rs +++ b/crates/hir-ty/src/infer/diagnostics.rs @@ -162,14 +162,14 @@ pub enum TypeckDiagnostic { /// Span of the prior/generated definition source, when available. previous: Option, }, - /// `SC0207`: a class constraint could not be solved. + /// `SC0207`: a trait constraint could not be solved. UnsatisfiedConstraint { /// Source span for the obligation that could not be solved. span: LabelSpan, /// Predicate snapshot. pred: String, }, - /// `SC0208`: more than one non-default instance solved a class constraint. + /// `SC0208`: more than one non-default impl solved a trait constraint. AmbiguousConstraint { /// Source span for the ambiguous obligation. span: LabelSpan, @@ -496,7 +496,7 @@ impl TypeckDiagnostic { .with_code(DiagnosticCode::TYPECK_AMBIGUOUS_INFERENCE_OR_TYPE_CONSTRUCTOR_ARITY) .with_primary_label_span(span.clone(), Some("ambiguous inferred type")) .with_note(scheme.clone()) - .with_help("add a type annotation or a matching instance to fix the ambiguous type variable") + .with_help("add a type annotation or a matching impl to fix the ambiguous type variable") } TypeckDiagnostic::TypeConstructorArity { span, @@ -586,7 +586,7 @@ impl TypeckDiagnostic { } => { let subject = match namespace { ValueNamespace::Type => "type name", - ValueNamespace::Class => "class name", + ValueNamespace::Class => "trait name", ValueNamespace::Module => "module", ValueNamespace::TypeVariable => "type variable", }; @@ -600,9 +600,9 @@ impl TypeckDiagnostic { .with_help("use a constructor or value binding here, not a namespace name") } TypeckDiagnostic::ClassAsType { span, class } => { - Diagnostic::error(format!("class name used as type: `{class}`")) + Diagnostic::error(format!("trait name used as type: `{class}`")) .with_code(DiagnosticCode::TYPECK_CLASS_AS_TYPE) - .with_primary_label_span(span.clone(), Some("class is not a type")) + .with_primary_label_span(span.clone(), Some("trait is not a type")) } TypeckDiagnostic::DuplicateType { span, @@ -618,16 +618,16 @@ impl TypeckDiagnostic { Some("existing definition"), ) } else { - diagnostic.with_note(format!("existing definition: data {name}")) + diagnostic.with_note(format!("existing definition: enum {name}")) }; diagnostic.with_note("rename or remove the duplicate type definition") } TypeckDiagnostic::UnsatisfiedConstraint { span, pred } => { - Diagnostic::error(format!("cannot satisfy class constraint: {pred}")) + Diagnostic::error(format!("cannot satisfy trait constraint: {pred}")) .with_code(DiagnosticCode::TYPECK_UNSATISFIED_CONSTRAINT) .with_primary_label_span(span.clone(), Some("constraint originates here")) - .with_note(format!("no visible instance matches `{pred}`")) - .with_help("add a matching instance or strengthen the surrounding type context") + .with_note(format!("no visible impl matches `{pred}`")) + .with_help("add a matching impl or strengthen the surrounding type context") } TypeckDiagnostic::AmbiguousConstraint { span, @@ -635,22 +635,22 @@ impl TypeckDiagnostic { candidates, } => { let mut diagnostic = Diagnostic::error(format!( - "ambiguous class constraint: {pred}" + "ambiguous trait constraint: {pred}" )) .with_code(DiagnosticCode::TYPECK_AMBIGUOUS_CONSTRAINT) .with_primary_label_span(span.clone(), Some("ambiguous constraint here")) - .with_help("make the type more specific or remove overlapping instances"); + .with_help("make the type more specific or remove overlapping impls"); for candidate in candidates { diagnostic = diagnostic.with_note(candidate.clone()); } diagnostic } TypeckDiagnostic::SolverFuelExhausted { span, pred } => Diagnostic::error(format!( - "cannot solve class constraint `{pred}`: solver exceeded its iteration bound" + "cannot solve trait constraint `{pred}`: solver exceeded its iteration bound" )) .with_code(DiagnosticCode::TYPECK_SOLVER_FUEL_EXHAUSTED) .with_primary_label_span(span.clone(), Some("constraint originates here")) - .with_help("simplify the instance chain or add a more direct instance"), + .with_help("simplify the impl chain or add a more direct impl"), TypeckDiagnostic::NonFinalReturn { span } => { Diagnostic::error("illegal return statement") .with_code(DiagnosticCode::TYPECK_NON_FINAL_RETURN_OR_INVALID_CONSTRUCTOR_PATTERN) @@ -668,22 +668,22 @@ impl TypeckDiagnostic { main, undetermined, } => Diagnostic::error(format!( - "Coverage condition fails for class:\n{class}\n- the type:\n{main}\ndoes not determine:\n{}", + "Coverage condition fails for trait:\n{class}\n- the type:\n{main}\ndoes not determine:\n{}", undetermined.join(", ") )) .with_code(DiagnosticCode::TYPECK_COVERAGE_CONDITION) - .with_primary_label_span(span.clone(), Some("instance head does not determine these variables")), + .with_primary_label_span(span.clone(), Some("impl head does not determine these variables")), TypeckDiagnostic::PattersonCondition { span, head } => Diagnostic::error(format!( - "instance `{head}` does not satisfy the Patterson conditions" + "impl `{head}` does not satisfy the Patterson conditions" )) .with_code(DiagnosticCode::TYPECK_PATTERSON_CONDITION) - .with_primary_label_span(span.clone(), Some("instance head violates Patterson condition")) - .with_note("each instance context must be structurally smaller than the instance head") - .with_help("remove the recursive context, add a more specific instance, or use the Patterson-condition pragma intentionally"), + .with_primary_label_span(span.clone(), Some("impl head violates Patterson condition")) + .with_note("each impl context must be structurally smaller than the impl head") + .with_help("remove the recursive context, add a more specific impl, or use the Patterson-condition pragma intentionally"), TypeckDiagnostic::BoundedVariableCondition { span } => { Diagnostic::error("Bounded variable condition fails!") .with_code(DiagnosticCode::TYPECK_BOUNDED_VARIABLE_CONDITION) - .with_primary_label_span(span.clone(), Some("instance head is missing context variables")) + .with_primary_label_span(span.clone(), Some("impl head is missing context variables")) } TypeckDiagnostic::TypeAliasCycle { span, alias } => { Diagnostic::error(format!("recursive type alias `{alias}`")) @@ -711,10 +711,10 @@ impl TypeckDiagnostic { expected, actual, } => Diagnostic::error(format!( - "class arity mismatch for `{class}`: expected {expected}, got {actual}" + "trait arity mismatch for `{class}`: expected {expected}, got {actual}" )) .with_code(DiagnosticCode::TYPECK_CLASS_ARITY) - .with_primary_label_span(span.clone(), Some("class predicate arity mismatch")), + .with_primary_label_span(span.clone(), Some("trait predicate arity mismatch")), TypeckDiagnostic::OverlappingInstance { instance_span, overlaps_span, @@ -722,34 +722,34 @@ impl TypeckDiagnostic { overlaps, } => { let diagnostic = Diagnostic::error(format!( - "Overlapping instances are not supported\ninstance:\n{instance}\noverlaps with:\n{overlaps}" + "Overlapping impls are not supported\nimpl:\n{instance}\noverlaps with:\n{overlaps}" )) .with_code(DiagnosticCode::TYPECK_OVERLAPPING_INSTANCE) - .with_primary_label_span(instance_span.clone(), Some("overlapping instance")); + .with_primary_label_span(instance_span.clone(), Some("overlapping impl")); if let Some(overlaps_span) = overlaps_span { diagnostic.with_secondary_label_span( overlaps_span.clone(), - Some("previous overlapping instance"), + Some("previous overlapping impl"), ) } else { diagnostic } } TypeckDiagnostic::InvalidDefaultInstance { span, head } => Diagnostic::error(format!( - "Cannot have a default instance whose main argument contains no type variable: {head}" + "Cannot have a default impl whose main argument contains no type variable: {head}" )) .with_code(DiagnosticCode::TYPECK_INVALID_DEFAULT_INSTANCE) - .with_primary_label_span(span.clone(), Some("invalid default instance head")), + .with_primary_label_span(span.clone(), Some("invalid default impl head")), TypeckDiagnostic::IncompleteInstance { span, class, missing, } => Diagnostic::error(format!( - "Incomplete definition for class:\n{class}\nmissing definitions for:\n{}", + "Incomplete definition for trait:\n{class}\nmissing definitions for:\n{}", missing.join(", ") )) .with_code(DiagnosticCode::TYPECK_INCOMPLETE_INSTANCE) - .with_primary_label_span(span.clone(), Some("incomplete instance")), + .with_primary_label_span(span.clone(), Some("incomplete impl")), TypeckDiagnostic::UnknownInstanceMethod { span, name, @@ -761,7 +761,7 @@ impl TypeckDiagnostic { if let Some(class_span) = class_span { diagnostic.with_secondary_label_span( class_span.clone(), - Some("class defined here"), + Some("trait defined here"), ) } else { diagnostic @@ -773,9 +773,9 @@ impl TypeckDiagnostic { .with_code(DiagnosticCode::TYPECK_INCOMPLETE_SIGNATURE) .with_primary_label_span(span.clone(), Some("incomplete signature")) .with_note(format!("signature: {signature}")) - .with_note("annotate every parameter (name : Type) and provide a return type (-> Type)"), + .with_note("annotate every parameter (`name: Type`); add `returns (Type)` for a non-unit result"), TypeckDiagnostic::IncompleteMethodSignature { span, signature } => Diagnostic::error( - "class and instance methods must have complete type signatures", + "trait and impl methods must have complete type signatures", ) .with_code(DiagnosticCode::TYPECK_INCOMPLETE_METHOD_SIGNATURE) .with_primary_label_span(span.clone(), Some("incomplete method signature")) @@ -787,11 +787,11 @@ impl TypeckDiagnostic { reason, } => { Diagnostic::error(format!( - "invalid instance member signature for `{method}`: {reason}" + "invalid impl member signature for `{method}`: {reason}" )) .with_code(DiagnosticCode::TYPECK_INVALID_INSTANCE_METHOD_SIGNATURE) - .with_primary_label_span(span.clone(), Some("invalid instance method signature")) - .with_note("the instance method must match the class method after substituting the instance head") + .with_primary_label_span(span.clone(), Some("invalid impl method signature")) + .with_note("the impl method must match the trait method after substituting the impl head") } TypeckDiagnostic::InvalidConstructorPattern { span, name } => Diagnostic::error(format!( "constructor pattern `{name}` does not resolve to a constructor" @@ -809,10 +809,10 @@ impl TypeckDiagnostic { .with_code(DiagnosticCode::TYPECK_SHORTHAND_CONSTRUCTOR) .with_primary_label_span(span.clone(), Some("shorthand constructor")), TypeckDiagnostic::GenericDeriveConflict { span, ty } => Diagnostic::error(format!( - "type '{ty}' has a manual Generic instance but no 'pragma no-generic-instance-for {ty}'; add the pragma to suppress auto-derivation" + "type '{ty}' has a manual Generic impl but no 'pragma no-generic-instance-for {ty}'; add the pragma to suppress auto-derivation" )) .with_code(DiagnosticCode::TYPECK_GENERIC_DERIVE_CONFLICT) - .with_primary_label_span(span.clone(), Some("manual Generic instance conflicts with auto-derivation")), + .with_primary_label_span(span.clone(), Some("manual Generic impl conflicts with auto-derivation")), TypeckDiagnostic::InvalidDerive { span, ty, @@ -840,7 +840,7 @@ impl TypeckDiagnostic { .with_code(DiagnosticCode::TYPECK_COMPTIME_LET_RUNTIME) .with_primary_label_span(span.clone(), Some("runtime initializer")), TypeckDiagnostic::ComptimeReturnRuntime { span, context } => Diagnostic::error(format!( - "{context}: function annotated '-> comptime' returns a runtime expression" + "{context}: function with a comptime result returns a runtime expression" )) .with_code(DiagnosticCode::TYPECK_COMPTIME_RETURN_RUNTIME) .with_primary_label_span(span.clone(), Some("runtime return expression")), @@ -1098,7 +1098,7 @@ fn signature_from_scheme<'db>( }) .collect::>(); format!( - "{name}({}) -> {}", + "{name}({}) returns ({})", parameters.join(", "), display_ty_source(db, ret, type_var_names) ) @@ -1124,11 +1124,39 @@ fn source_signature_from_func_sig<'db>( } } let ret = sig.ret?; - Some(format!( - "{name}({}) -> {}", - params.join(", "), - display_type_ref_source(db, ret) - )) + let type_vars = if sig.type_vars.is_empty() { + String::new() + } else { + format!( + "<{}>", + sig.type_vars + .iter() + .map(|var| ident_text(db, var)) + .collect::>() + .join(", ") + ) + }; + let mut out = format!("{name}{type_vars}({})", params.join(", ")); + if sig.public.is_some() { + out.push_str(" public"); + } + if sig.payable.is_some() { + out.push_str(" payable"); + } + out.push_str(" returns ("); + out.push_str(&display_type_ref_source(db, ret)); + out.push(')'); + if !sig.preds.is_empty() { + out.push_str(" where "); + out.push_str( + &sig.preds + .iter() + .map(|pred| format_pred_ref(db, *pred)) + .collect::>() + .join(", "), + ); + } + Some(out) } fn def_hir_module<'db>(db: &'db dyn Db, def: DefId<'db>) -> Module<'db> { @@ -1657,44 +1685,6 @@ fn type_ref_constructor_name<'db>(db: &'db dyn HirDb, ty: TypeRef<'db>) -> Strin } } -pub(super) fn implicit_class_head_binder_diagnostic<'db>( - db: &'db dyn HirDb, - class: ClassDef<'db>, -) -> Option { - let vars = class.type_var_elems(db); - let [var] = vars.as_slice() else { - return None; - }; - let head = class.head(db).kind(db); - let TypeRefKind::Named { - qualifier: None, - name, - args, - } = head.ty.kind(db) - else { - return None; - }; - if !args.atom().is_empty() || builtin_type_name(ident_text(db, name).as_str()) { - return None; - } - if ident_text(db, var) != ident_text(db, name) || var.span(db) != name.span(db) { - return None; - } - Some(TypeckDiagnostic::UndefinedTypeVariables { - vars: vec![( - LabelSpan::from_span(db, name.span(db)), - ident_text(db, name), - )], - }) -} - -fn builtin_type_name(name: &str) -> bool { - matches!( - name, - "word" | "Word" | "bool" | "()" | "pair" | "sum" | "integer" - ) -} - #[derive(Clone)] struct DataCycleNode<'db> { adt: AdtDef<'db>, @@ -2232,35 +2222,19 @@ pub(super) fn is_complete_signature(sig: &FuncSig<'_>) -> bool { pub(super) fn format_func_sig<'db>(db: &'db dyn HirDb, sig: &FuncSig<'db>) -> String { let mut out = String::new(); + out.push_str("function "); + out.push_str(&ident_text(db, &sig.name)); if !sig.type_vars.is_empty() { - out.push_str("forall "); + out.push('<'); out.push_str( &sig.type_vars .iter() .map(|var| ident_text(db, var)) .collect::>() - .join(" "), - ); - out.push_str(". "); - } - if !sig.preds.is_empty() { - out.push_str( - &sig.preds - .iter() - .map(|pred| format_pred_ref(db, *pred)) - .collect::>() .join(", "), ); - out.push_str(" => "); - } - if sig.public.is_some() { - out.push_str("public "); - } - if sig.payable.is_some() { - out.push_str("payable "); + out.push('>'); } - out.push_str("function "); - out.push_str(&ident_text(db, &sig.name)); out.push('('); out.push_str( &sig.params @@ -2271,9 +2245,26 @@ pub(super) fn format_func_sig<'db>(db: &'db dyn HirDb, sig: &FuncSig<'db>) -> St .join(", "), ); out.push(')'); + if sig.public.is_some() { + out.push_str(" public"); + } + if sig.payable.is_some() { + out.push_str(" payable"); + } if let Some(ret) = sig.ret { - out.push_str(" -> "); + out.push_str(" returns ("); out.push_str(&format_type_ref(db, ret)); + out.push(')'); + } + if !sig.preds.is_empty() { + out.push_str(" where "); + out.push_str( + &sig.preds + .iter() + .map(|pred| format_pred_ref(db, *pred)) + .collect::>() + .join(", "), + ); } out } @@ -2286,7 +2277,7 @@ fn format_func_param<'db>(db: &'db dyn HirDb, param: &FuncParam<'db>) -> String out.push_str("comptime "); } out.push_str(&ident_text(db, name)); - out.push_str(" : "); + out.push_str(": "); out.push_str(&format_type_ref(db, *ty)); out } @@ -2305,12 +2296,12 @@ fn format_func_param<'db>(db: &'db dyn HirDb, param: &FuncParam<'db>) -> String fn format_pred_ref<'db>(db: &'db dyn HirDb, pred: hir::ast::ty::PredRef<'db>) -> String { let pred = pred.kind(db); let mut out = format!( - "{} : {}", + "{}: {}", format_type_ref(db, pred.ty), ident_text(db, &pred.class) ); if !pred.args.atom().is_empty() { - out.push('('); + out.push('<'); out.push_str( &pred .args @@ -2320,7 +2311,7 @@ fn format_pred_ref<'db>(db: &'db dyn HirDb, pred: hir::ast::ty::PredRef<'db>) -> .collect::>() .join(", "), ); - out.push(')'); + out.push('>'); } out } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 2b2cc295..1e98abff 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -62,7 +62,7 @@ impl<'db> InferCtx<'db> { args, expected.clone(), ), - ExprKind::Proxy { .. } => self.engine.fresh_var(), + ExprKind::Proxy { ty, .. } => self.infer_proxy_expr(*ty, expected.clone()), ExprKind::Lambda { params, ret, @@ -226,6 +226,49 @@ impl<'db> InferCtx<'db> { self.memory_dyn_array_ty(elem_ty).unwrap_or(InferTy::Error) } + /// Gives `@T` the same `Proxy` constructor selected by its call-site + /// context. A source tree can contain both the bundled std module and a + /// main-library mirror of it, so choosing an arbitrary canonical `Proxy` + /// definition would make otherwise identical types nominally distinct. + fn infer_proxy_expr( + &mut self, + ty: TypeRef<'db>, + expected: Option>, + ) -> InferTy<'db> { + let inner = self.lower_type_ref(ty); + if let Some(expected) = expected { + let resolved = self.engine.resolve(expected.clone()); + if let InferTy::Named { ctor, args } = &resolved + && args.len() == 1 + && matches!( + ctor, + TyCtor::User(user) + if user.def.name(self.db).as_deref() == Some("Proxy") + ) + { + self.unify_span(ty.span(self.db), args[0].clone(), inner); + return resolved; + } + } + + crate::support::canonical_std_adt_defs(self.db, "Proxy") + .into_iter() + .find(|def| { + self.entry_module.is_some_and(|entry| { + crate::support::module_for_def_via_graph(self.db, entry, *def).is_some() + }) + }) + .or_else(|| crate::support::canonical_std_adt_def(self.db, "Proxy")) + .map(|def| InferTy::Named { + ctor: TyCtor::User(UserTyCtor { + def, + kind: UserTyCtorKind::Adt, + }), + args: vec![inner], + }) + .unwrap_or_else(|| self.engine.fresh_var()) + } + fn report_numeric_if_branch_mismatch( &mut self, body: FuncBody<'db>, diff --git a/crates/hir-ty/src/infer/mod.rs b/crates/hir-ty/src/infer/mod.rs index 13b8641c..9fa4c6e7 100644 --- a/crates/hir-ty/src/infer/mod.rs +++ b/crates/hir-ty/src/infer/mod.rs @@ -35,7 +35,7 @@ use tracing::field; use crate::{ BinderEnv, BodyDesugarView, BodyPreTypeckDesugarPlan, BoolUnitSumView, BuiltinClassId, BuiltinTyCtor, ClassId, Db, LoweredFunction, Pred, PredKind, ProductShape, QualTy, - SourceOrigin, Ty, TyCtor, TyKind, TyScheme, TypeLowering, TypeLoweringDiagnostic, + SourceOrigin, Ty, TyCtor, TyKind, TyScheme, TypeLowering, TypeLoweringDiagnostic, UserTyCtor, UserTyCtorKind, alias::{AliasError, AliasNormalizer, AliasType, AliasTypeKind}, builtin_scheme, canonical_goal_with_allowed, class_method_type_vars, diff --git a/crates/hir-ty/src/infer/obligations.rs b/crates/hir-ty/src/infer/obligations.rs index 8e859a98..7ab8a982 100644 --- a/crates/hir-ty/src/infer/obligations.rs +++ b/crates/hir-ty/src/infer/obligations.rs @@ -529,7 +529,7 @@ impl<'db> InferCtx<'db> { index, TypeckDiagnostic::AmbiguousInferredType { span: self.body_label_span(self.root_body), - scheme: format!("forall _ . {pred_text} => {root_ty}"), + scheme: format!("<_> {root_ty} where {pred_text}"), }, )); } @@ -868,10 +868,10 @@ impl<'db> InferCtx<'db> { let preds = ambiguous .into_iter() - .map(|main| format!("{main} : Int")) + .map(|main| format!("{main}: Int")) .collect::>() .join(", "); - let scheme = format!("forall _ . {preds} => {}", self.display_infer_ty(root_ty)); + let scheme = format!("<_> {} where {preds}", self.display_infer_ty(root_ty)); self.diagnostics .push(TypeckDiagnostic::AmbiguousInferredType { span: self.body_label_span(self.root_body), diff --git a/crates/hir-ty/src/infer/tests.rs b/crates/hir-ty/src/infer/tests.rs index d4adfaee..675c0791 100644 --- a/crates/hir-ty/src/infer/tests.rs +++ b/crates/hir-ty/src/infer/tests.rs @@ -98,7 +98,7 @@ impl nameres::Db for TestDb { impl crate::Db for TestDb {} fn source_file(db: &TestDb, name: &str, src: &str) -> SourceFile { - let url = format!("memory:///{name}.solc").parse().expect("valid url"); + let url = format!("memory:///{name}.sol").parse().expect("valid url"); SourceFile::new(db, url, Some(src.to_owned())) } @@ -120,7 +120,7 @@ fn module_key(path: &[&str]) -> ModuleKey { fn insert_module_source(db: &mut TestDb, path: &[&str], src: &str) -> ModuleKey { let key = module_key(path); - let url = format!("memory:///{}.solc", path.join("/")) + let url = format!("memory:///{}.sol", path.join("/")) .parse() .expect("valid url"); let file = SourceFile::new(&*db, url, Some(src.to_owned())); @@ -136,114 +136,112 @@ fn db_with_main_typeck(src: &str) -> (TestDb, ModuleKey) { fn db_with_array_std(main_src: &str) -> (TestDb, ModuleKey) { let mut db = TestDb::default(); - let std_path = PathBuf::from("/std/std.solc"); - let main_path = PathBuf::from("/main/main.solc"); + let std_path = PathBuf::from("/std/std.sol"); + let main_path = PathBuf::from("/main/main.sol"); let std_file = source_file_at_path( &db, &std_path, r#" export { memory(*), storage(*), calldata(*), DynArray, array(*), uint256(*), address(*), string, Encoded(*), Decoded(*), concatLit, Add, Array, ArrayPush, Length, Typedef, CanStore, RValueIdxAccess }; -data memory(t) = memory(word); -data storage(t) = storage(word); -data calldata(t) = calldata(word); -data DynArray(t); -data array(t) = array(word); -data uint256 = uint256(word); -data address = address(word); -data string; -data Encoded = Encoded(word); -data Decoded = Decoded(word); +enum memory {memory(word)} +enum storage {storage(word)} +enum calldata {calldata(word)} +enum DynArray {} +enum array {array(word)} +enum uint256 {uint256(word)} +enum address {address(word)} +enum string {} +enum Encoded {Encoded(word)} +enum Decoded {Decoded(word)} -function concatLit(comptime lhs:string, comptime rhs:string) -> string { return lhs; } +function concatLit(comptime lhs:string, comptime rhs:string) returns (string) { return lhs; } -forall self . class self:Add { - function add(lhs:self, rhs:self) -> self; +trait Add { + function add(lhs:self, rhs:self) returns (self) ; } -instance uint256:Add { - function add(lhs:uint256, rhs:uint256) -> uint256 { return lhs; } +impl Add { + function add(lhs:uint256, rhs:uint256) returns (uint256) { return lhs; } } -forall abs rep . class abs:Typedef(rep) { - function abs(x:rep) -> abs; - function rep(x:abs) -> rep; +trait Typedef { + function abs(x:rep) returns (abs) ; + function rep(x:abs) returns (rep) ; } -forall t . default instance t:Typedef(t) { - function abs(x:t) -> t { return x; } - function rep(x:t) -> t { return x; } +default impl Typedef { + function abs(x:t) returns (t) { return x; } + function rep(x:t) returns (t) { return x; } } -instance uint256:Typedef(word) { - function abs(x:word) -> uint256 { return uint256(x); } - function rep(x:uint256) -> word { return 0; } +impl Typedef { + function abs(x:word) returns (uint256) { return uint256(x); } + function rep(x:uint256) returns (word) { return 0; } } -instance memory(string):Typedef(word) { - function abs(x:word) -> memory(string) { return memory(x); } - function rep(x:memory(string)) -> word { return 0; } +impl Typedef,word> { + function abs(x:word) returns (memory) { return memory(x); } + function rep(x:memory) returns (word) { return 0; } } -forall col_idx val . class col_idx:RValueIdxAccess(val) { - function lookup(xi:col_idx) -> val; +trait RValueIdxAccess { + function lookup(xi:col_idx) returns (val) ; } -forall i . i:Typedef(word) => -instance (calldata(array(Encoded)), i):RValueIdxAccess(Decoded) { - function lookup(xi:(calldata(array(Encoded)), i)) -> Decoded { +impl RValueIdxAccess<(calldata>, i),Decoded> where i: Typedef { + function lookup(xi:(calldata>, i)) returns (Decoded) { return Decoded(0); } } -forall dst value . class dst:CanStore(value) { - function store(dst:dst, value:value) -> (); - function load(dst:dst) -> value; +trait CanStore { + function store(dst:dst, value:value) returns () ; + function load(dst:dst) returns (value) ; } -instance storage(word):CanStore(word) { - function store(dst:storage(word), value:word) -> () { return (); } - function load(dst:storage(word)) -> word { return 0; } +impl CanStore,word> { + function store(dst:storage, value:word) returns () { return (); } + function load(dst:storage) returns (word) { return 0; } } -instance storage(uint256):CanStore(uint256) { - function store(dst:storage(uint256), value:uint256) -> () { return (); } - function load(dst:storage(uint256)) -> uint256 { return uint256(0); } +impl CanStore,uint256> { + function store(dst:storage, value:uint256) returns () { return (); } + function load(dst:storage) returns (uint256) { return uint256(0); } } -instance storage(string):CanStore(memory(string)) { - function store(dst:storage(string), value:memory(string)) -> () { return (); } - function load(dst:storage(string)) -> memory(string) { return memory(0); } +impl CanStore,memory> { + function store(dst:storage, value:memory) returns () { return (); } + function load(dst:storage) returns (memory) { return memory(0); } } -instance storage(array(word)):CanStore(storage(array(word))) { - function store(dst:storage(array(word)), value:storage(array(word))) -> () { return (); } - function load(dst:storage(array(word))) -> storage(array(word)) { return dst; } +impl CanStore>,storage>> { + function store(dst:storage>, value:storage>) returns () { return (); } + function load(dst:storage>) returns (storage>) { return dst; } } -forall self . class self:Length { - function length(value:self) -> uint256; +trait Length { + function length(value:self) returns (uint256) ; } -forall self . class self:Array { - function pop(value:self) -> (); +trait Array { + function pop(value:self) returns () ; } -forall self elem . class self:ArrayPush(elem) { - function push(value:self, elem:elem) -> (); +trait ArrayPush { + function push(value:self, elem:elem) returns () ; } -forall t . instance storage(array(t)):Length { - function length(value:storage(array(t))) -> uint256 { return uint256(0); } +impl Length>> { + function length(value:storage>) returns (uint256) { return uint256(0); } } -forall t . instance storage(array(t)):Array { - function pop(value:storage(array(t))) -> () { return (); } +impl Array>> { + function pop(value:storage>) returns () { return (); } } -forall t elem . storage(t):CanStore(elem) => -instance storage(array(t)):ArrayPush(elem) { - function push(value:storage(array(t)), elem:elem) -> () { return (); } +impl ArrayPush>,elem> where storage: CanStore { + function push(value:storage>, elem:elem) returns () { return (); } } "#, ); @@ -719,9 +717,9 @@ fn has_user_obligation<'db>( } #[test] -fn unannotated_function_scheme_uses_inferred_polymorphic_body_type() { +fn explicit_polymorphic_function_scheme_uses_declared_body_type() { let db = TestDb::default(); - let module = parse_module(&db, "function id(x) { return x; }"); + let module = parse_module(&db, "function id(x: a) returns (a) { return x; }"); let info = function_info_named(&db, module, "id"); let scheme = function_scheme_in_hir_module(&db, module, info.function.def_id_value(&db)) .expect("scheme"); @@ -742,14 +740,14 @@ fn unannotated_function_scheme_uses_inferred_polymorphic_body_type() { } #[test] -fn contract_entry_dispatch_uses_inferred_return_type() { +fn contract_entry_dispatch_uses_declared_return_type() { let mut db = TestDb::default(); let key = insert_module_source( &mut db, &["main"], r#" contract Answer { - public function main() { + function main() public returns (word) { return 42; } } @@ -778,19 +776,23 @@ fn inference_result_records_comptime_obligation_sites() { let module = parse_module( &db, r#" -function need(comptime x: word) -> comptime word { +function need(comptime x: word) returns (comptime) { return x; } -function g() -> comptime word { - let y : comptime word = need(2); +function g() returns (comptime) { + let y : comptime = need(2); return y; } -function f(x: word) -> comptime word { - match x { - | comptime 1 => return need(2); - | _ => return 0; +function f(x: word) returns (comptime) { + match (x) { + case comptime 1 { + return need(2); + } + default { + return 0; + } } } "#, @@ -842,7 +844,7 @@ fn inferred_integer_let_records_comptime_obligation() { let module = parse_module( &db, r#" -function f() -> word { +function f() returns (word) { let x = wordToInteger(20); return wordFromInteger(x); } @@ -866,56 +868,56 @@ function f() -> word { #[test] fn comptime_only_types_cover_params_returns_typed_lets_and_call_args() { let mut db = TestDb::default(); - let std_path = PathBuf::from("/std/std.solc"); - let main_path = PathBuf::from("/main/main.solc"); + let std_path = PathBuf::from("/std/std.sol"); + let main_path = PathBuf::from("/main/main.sol"); let std_file = source_file_at_path( &db, &std_path, r#" export { string }; -data string; +enum string {} "#, ); let main_file = source_file_at_path( &db, &main_path, r#" -import std.{string}; +import {string} from std; type Text = string; type Big = integer; -function explicitlyNeedsText(comptime value: Text) -> () { +function explicitlyNeedsText(comptime value: Text) returns () { return (); } -function explicitlyNeedsBig(comptime value: Big) -> () { +function explicitlyNeedsBig(comptime value: Big) returns () { return (); } -function textParamIsComptime(value: Text) -> () { +function textParamIsComptime(value: Text) returns () { return explicitlyNeedsText(value); } -function bigParamIsComptime(value: Big) -> () { +function bigParamIsComptime(value: Big) returns () { return explicitlyNeedsBig(value); } -function takesText(value: Text) -> () { +function takesText(value: Text) returns () { return (); } -function takesBig(value: Big) -> () { +function takesBig(value: Big) returns () { return (); } -function exerciseText(value: Text) -> Text { +function exerciseText(value: Text) returns (Text) { let copy: Text = value; takesText(copy); return copy; } -function exerciseBig(value: Big) -> Big { +function exerciseBig(value: Big) returns (Big) { let copy: Big = value; takesBig(copy); return copy; @@ -979,17 +981,17 @@ function exerciseBig(value: Big) -> Big { #[test] fn string_literals_and_concat_lit_use_str_conversion_only_at_literal_sites() { let mut db = TestDb::default(); - let std_path = PathBuf::from("/std/std.solc"); - let main_path = PathBuf::from("/main/main.solc"); + let std_path = PathBuf::from("/std/std.sol"); + let main_path = PathBuf::from("/main/main.sol"); let std_file = source_file_at_path( &db, &std_path, r#" export { memory(*), string, concatLit, strlenLit }; -data memory(a) = memory(word); -data string; -function concatLit(comptime lhs: string, comptime rhs: string) -> string { return lhs; } -function strlenLit(comptime value: string) -> word { return 0; } +enum memory {memory(word)} +enum string {} +function concatLit(comptime lhs: string, comptime rhs: string) returns (string) { return lhs; } +function strlenLit(comptime value: string) returns (word) { return 0; } "#, ); let main_file = source_file_at_path( @@ -997,45 +999,45 @@ function strlenLit(comptime value: string) -> word { return 0; } &main_path, r#" import std; -import std.{memory, string, strlenLit}; +import {memory, string, strlenLit} from std; -data Tag = Tag(word); -instance Tag : Str { - function fromString(comptime value: string) -> Tag { +enum Tag {Tag(word)} +impl Str { + function fromString(comptime value: string) returns (Tag) { return Tag(strlenLit(value)); } } -function literal() -> memory(string) { return "hello"; } -function concatLit(lhs: word, rhs: word) -> word { return lhs; } -function concatenated() -> memory(string) { return std.concatLit("he", "llo"); } -function explicit(value: string) -> memory(string) { return Str.fromString(value); } -function tagged() -> Tag { return "abcd"; } -function taggedFromLet() -> Tag { +function literal() returns (memory) { return "hello"; } +function concatLit(lhs: word, rhs: word) returns (word) { return lhs; } +function concatenated() returns (memory) { return std.concatLit("he", "llo"); } +function explicit(value: string) returns (memory) { return Str.fromString(value); } +function tagged() returns (Tag) { return "abcd"; } +function taggedFromLet() returns (Tag) { let value = "abcd"; return Str.fromString(value); } -function inferredLiteral() -> () { let value = "x"; return (); } -function inferredConcat() -> () { let value = std.concatLit("a", "b"); return (); } -function consumePair(value: (string, word)) -> word { return 0; } -function inferredTuple() -> word { +function inferredLiteral() returns () { let value = "x"; return (); } +function inferredConcat() returns () { let value = std.concatLit("a", "b"); return (); } +function consumePair(value: (string, word)) returns (word) { return 0; } +function inferredTuple() returns (word) { let value = ("x", 0); return consumePair(value); } -function inferredComptimeParam() -> () { - let sink = lam (comptime value) -> () { return (); }; +function inferredComptimeParam() returns () { + let sink = lam (comptime value: string) -> () { return (); }; sink("x"); return (); } -function invalidConcat() -> memory(string) { return concatLit(1, 2); } -function makeWord() -> word { return 0; } -function invalidSource() -> memory(string) { +function invalidConcat() returns (memory) { return concatLit(1, 2); } +function makeWord() returns (word) { return 0; } +function invalidSource() returns (memory) { let value; - let result: memory(string) = Str.fromString(value); + let result: memory = Str.fromString(value); value = makeWord(); return result; } -function runtime(value: string) -> memory(string) { return value; } +function runtime(value: string) returns (memory) { return value; } "#, ); let std_key = module_key_for_path(LibraryId::Std, &PathBuf::from("/std"), &std_path).unwrap(); @@ -1111,10 +1113,10 @@ function runtime(value: string) -> memory(string) { return value; } fn array_literals_infer_canonical_memory_dyn_array_and_empty_uses_context() { let (db, key) = db_with_array_std( r#" -import std.{memory, DynArray}; +import {memory, DynArray} from std; -function filled(x:word, y:word) -> memory(DynArray(word)) { return [x, y]; } -function empty() -> memory(DynArray(word)) { return []; } +function filled(x:word, y:word) returns (memory>) { return [x, y]; } +function empty() returns (memory>) { return []; } "#, ); let module = module_id_from_key(&db, &key); @@ -1135,9 +1137,9 @@ function empty() -> memory(DynArray(word)) { return []; } fn array_literal_rejects_mixed_element_types() { let (db, key) = db_with_array_std( r#" -import std.{memory, DynArray}; +import {memory, DynArray} from std; -function mixed(x:word, flag:bool) -> memory(DynArray(word)) { +function mixed(x:word, flag:bool) returns (memory>) { return [x, flag]; } "#, @@ -1160,33 +1162,33 @@ fn array_string_literals_use_memory_string_from_memory_and_storage_contexts() { let (db, key) = db_with_array_std( r#" import std; -import std.{memory, storage, DynArray, array, string, Typedef, CanStore}; +import {memory, storage, DynArray, array, string, Typedef, CanStore} from std; -function inMemory() -> memory(DynArray(memory(string))) { +function inMemory() returns (memory>>) { return ["hello"]; } -function explicitConversion() -> memory(DynArray(memory(string))) { +function explicitConversion() returns (memory>>) { return [Str.fromString("hello")]; } -function concatenated() -> memory(DynArray(memory(string))) { +function concatenated() returns (memory>>) { return [std.concatLit("hel", "lo")]; } -function conditional(flag:bool) -> memory(DynArray(memory(string))) { - return [if (flag) then "yes" else "no"]; +function conditional(flag:bool) returns (memory>>) { + return [((flag) ? "yes" : "no")]; } contract C { - names:array(string); + names:array; - function setNames() -> () { + function setNames() returns () { names = ["alice", "bob"]; return (); } - function clearNames() -> () { + function clearNames() returns () { names = []; return (); } @@ -1275,21 +1277,21 @@ contract C { fn storage_array_field_ufcs_prepends_receiver_once() { let (db, key) = db_with_array_std( r#" -import std.{array, storage, uint256, Array, ArrayPush, Length}; +import {array, storage, uint256, Array, ArrayPush, Length} from std; contract C { - members:array(uint256); + members:array; - function memberCount() -> uint256 { + function memberCount() returns (uint256) { return members.length(); } - function append(value:uint256) -> () { + function append(value:uint256) returns () { members.push(value); return (); } - function removeLast() -> () { + function removeLast() returns () { members.pop(); return (); } @@ -1344,19 +1346,19 @@ contract C { fn local_and_parameter_ufcs_infer_receiver_and_evidence_once() { let (db, key) = db_with_main_typeck( r#" -forall self . class self:Echo { - function echo(value:self) -> self; +trait Echo { + function echo(value:self) returns (self) ; } -instance word:Echo { - function echo(value:word) -> word { return value; } +impl Echo { + function echo(value:word) returns (word) { return value; } } -function parameterReceiver(value:word) -> word { +function parameterReceiver(value:word) returns (word) { return value.echo(); } -function localReceiver(value:word) -> word { +function localReceiver(value:word) returns (word) { let local:word = value; return local.echo(); } @@ -1400,24 +1402,24 @@ function localReceiver(value:word) -> word { fn field_ufcs_comptime_parameter_uses_explicit_argument_position() { let (db, key) = db_with_array_std( r#" -import std.{array, storage}; +import {array, storage} from std; -forall self . class self:Stamp { - function stamp(value:self, comptime tag:word) -> word; +trait Stamp { + function stamp(value:self, comptime tag:word) returns (word) ; } -instance storage(array(word)):Stamp { - function stamp(value:storage(array(word)), comptime tag:word) -> word { return tag; } +impl Stamp>> { + function stamp(value:storage>, comptime tag:word) returns (word) { return tag; } } contract C { - stored:array(word); + stored:array; - function literalTag() -> word { + function literalTag() returns (word) { return stored.stamp(7); } - function runtimeTag(tag:word) -> word { + function runtimeTag(tag:word) returns (word) { return stored.stamp(tag); } } @@ -1477,28 +1479,28 @@ contract C { fn local_and_parameter_ufcs_comptime_parameter_uses_explicit_argument_position() { let (db, key) = db_with_main_typeck( r#" -forall self . class self:Stamp { - function stamp(value:self, comptime tag:word) -> word; +trait Stamp { + function stamp(value:self, comptime tag:word) returns (word) ; } -instance word:Stamp { - function stamp(value:word, comptime tag:word) -> word { return tag; } +impl Stamp { + function stamp(value:word, comptime tag:word) returns (word) { return tag; } } -function parameterLiteral(value:word) -> word { +function parameterLiteral(value:word) returns (word) { return value.stamp(7); } -function localLiteral(value:word) -> word { +function localLiteral(value:word) returns (word) { let local:word = value; return local.stamp(7); } -function parameterRuntime(value:word, tag:word) -> word { +function parameterRuntime(value:word, tag:word) returns (word) { return value.stamp(tag); } -function localRuntime(value:word, tag:word) -> word { +function localRuntime(value:word, tag:word) returns (word) { let local:word = value; return local.stamp(tag); } @@ -1559,29 +1561,29 @@ function localRuntime(value:word, tag:word) -> word { fn memory_dyn_array_index_returns_element_and_requires_word_typedefs() { let (db, key) = db_with_array_std( r#" -import std.{memory, DynArray, uint256, Typedef}; +import {memory, DynArray, uint256, Typedef} from std; -function read(xs:memory(DynArray(word)), i:uint256) -> word { +function read(xs:memory>, i:uint256) returns (word) { return xs[i]; } -function write(xs:memory(DynArray(word)), i:uint256, value:word) -> () { +function write(xs:memory>, i:uint256, value:word) returns () { xs[i] = value; return (); } -function compound(xs:memory(DynArray(word)), i:uint256, value:word) -> () { +function compound(xs:memory>, i:uint256, value:word) returns () { xs[i] += value; return (); } -function annotatedWrite(xs:memory(DynArray(word)), i:uint256, value:word) -> () { - (xs[i] : word) : word = value; +function annotatedWrite(xs:memory>, i:uint256, value:word) returns () { + (xs[i] ) = value; return (); } -function annotatedCompound(xs:memory(DynArray(word)), i:uint256, value:word) -> () { - xs[i] : word += value; +function annotatedCompound(xs:memory>, i:uint256, value:word) returns () { + xs[i] += value; return (); } "#, @@ -1627,14 +1629,14 @@ function annotatedCompound(xs:memory(DynArray(word)), i:uint256, value:word) -> fn calldata_array_index_uses_rvalue_evidence_and_improves_decoded_type() { let (db, key) = db_with_array_std( r#" -import std.{*}; +import * from std; -function inferred(xs:calldata(array(Encoded)), i:uint256) -> () { +function inferred(xs:calldata>, i:uint256) returns () { let value = xs[i]; return (); } -function expected(xs:calldata(array(Encoded)), i:uint256) -> Decoded { +function expected(xs:calldata>, i:uint256) returns (Decoded) { return xs[i]; } "#, @@ -1692,41 +1694,25 @@ function expected(xs:calldata(array(Encoded)), i:uint256) -> Decoded { fn calldata_array_index_rejects_plain_and_compound_writes() { let (db, key) = db_with_array_std( r#" -import std.{*}; +import * from std; -function write( - xs:calldata(array(Encoded)), - i:uint256, - value:Decoded -) -> () { +function write(xs: calldata>, i: uint256, value: Decoded) { xs[i] = value; return (); } -function compound( - xs:calldata(array(Encoded)), - i:uint256, - value:Decoded -) -> () { +function compound(xs: calldata>, i: uint256, value: Decoded) { xs[i] += value; return (); } -function annotatedWrite( - xs:calldata(array(Encoded)), - i:uint256, - value:Decoded -) -> () { - (xs[i] : Decoded) : Decoded = value; +function annotatedWrite(xs: calldata>, i: uint256, value: Decoded) { + (xs[i] ) = value; return (); } -function annotatedCompound( - xs:calldata(array(Encoded)), - i:uint256, - value:Decoded -) -> () { - xs[i] : Decoded += value; +function annotatedCompound(xs: calldata>, i: uint256, value: Decoded) { + xs[i] += value; return (); } "#, @@ -1751,12 +1737,12 @@ function annotatedCompound( fn same_named_non_std_calldata_array_keeps_generic_index_typing() { let (db, key) = db_with_array_std( r#" -import std.{RValueIdxAccess}; +import {RValueIdxAccess} from std; -data calldata(t) = calldata(word); -data array(t) = array(word); +enum calldata {calldata(word)} +enum array {array(word)} -function read(xs:calldata(array(word)), i:word) -> word { +function read(xs:calldata>, i:word) returns (word) { return xs[i]; } "#, @@ -1789,12 +1775,9 @@ function read(xs:calldata(array(word)), i:word) -> word { fn direct_storage_array_handle_assignment_is_a_raw_rebind() { let (db, key) = db_with_array_std( r#" -import std.{storage, array, string, CanStore}; +import {storage, array, string, CanStore} from std; -function rebind( - lhs:storage(array(string)), - rhs:storage(array(string)) -) -> () { +function rebind(lhs: storage>, rhs: storage>) { lhs = rhs; return (); } @@ -1821,7 +1804,7 @@ fn importless_contract_field_assignment_keeps_the_declared_value_type() { contract C { value:word; - function write(flag:bool) -> () { + function write(flag:bool) returns () { value = flag; return (); } @@ -1844,24 +1827,24 @@ contract C { fn storage_load_and_assign_use_can_store_result_improvement() { let (db, key) = db_with_array_std( r#" -import std.{memory, storage, CanStore}; +import {memory, storage, CanStore} from std; -data Blob; +enum Blob {} -instance storage(Blob):CanStore(memory(Blob)) { - function store(dst:storage(Blob), value:memory(Blob)) -> () { return (); } - function load(dst:storage(Blob)) -> memory(Blob) { return memory(0); } +impl CanStore,memory> { + function store(dst:storage, value:memory) returns () { return (); } + function load(dst:storage) returns (memory) { return memory(0); } } contract C { value:Blob; - function write(src:memory(Blob)) -> () { + function write(src:memory) returns () { value = src; return (); } - function read() -> memory(Blob) { + function read() returns (memory) { return value; } } @@ -1880,61 +1863,56 @@ fn contract_field_assignment_uses_specific_assign_evidence() { r#" pragma no-patterson-condition Assign; -data storage(t) = storage(word); -data mapping(k, v) = mapping(word); -data Foo = Foo(word); +enum storage {storage(word)} +enum mapping {mapping(word)} +enum Foo {Foo(word)} -forall dst value . class dst:CanStore(value) { - function store(dst:dst, value:value) -> (); - function load(dst:dst) -> value; +trait CanStore { + function store(dst:dst, value:value) returns () ; + function load(dst:dst) returns (value) ; } -forall lhs rhs . class lhs:Assign(rhs) { - function assign(lhs:lhs, rhs:rhs) -> (); +trait Assign { + function assign(lhs:lhs, rhs:rhs) returns () ; } -instance storage(word):CanStore(word) { - function store(dst:storage(word), value:word) -> () { return (); } - function load(dst:storage(word)) -> word { return 0; } +impl CanStore,word> { + function store(dst:storage, value:word) returns () { return (); } + function load(dst:storage) returns (word) { return 0; } } -forall a b . a:CanStore(b) => instance a:Assign(b) { - function assign(lhs:a, rhs:b) -> () { CanStore.store(lhs, rhs); } +impl Assign where a: CanStore { + function assign(lhs:a, rhs:b) returns () { CanStore.store(lhs, rhs); } } -instance storage(word):Assign(bool) { - function assign(lhs:storage(word), rhs:bool) -> () { return (); } +impl Assign,bool> { + function assign(lhs:storage, rhs:bool) returns () { return (); } } -instance storage(mapping(word, word)):CanStore(storage(mapping(word, word))) { - function store( - dst:storage(mapping(word, word)), - value:storage(mapping(word, word)) - ) -> () { return (); } - function load( - dst:storage(mapping(word, word)) - ) -> storage(mapping(word, word)) { return storage(0); } +impl CanStore word)>,storage word)>> { + function store(dst: storage word)>, value: storage word)>) { return (); } + function load(dst: storage word)>) returns (storage word)>) { return storage(0); } } -instance storage(mapping(word, word)):Assign(Foo) { - function assign(lhs:storage(mapping(word, word)), rhs:Foo) -> () { return (); } +impl Assign word)>,Foo> { + function assign(lhs:storage word)>, rhs:Foo) returns () { return (); } } contract C { value:word; - values:mapping(word, word); + values:mapping(word => word); - function write(flag:bool) -> () { + function write(flag:bool) returns () { value = flag; return (); } - function writeAnnotated(flag:bool) -> () { - value : storage(word) = flag; + function writeAnnotated(flag:bool) returns () { + value = flag; return (); } - function writeMapping(value:Foo) -> () { + function writeMapping(value:Foo) returns () { values = value; return (); } @@ -1973,13 +1951,9 @@ contract C { fn compound_storage_array_index_recognizes_parameter_handles() { let (db, key) = db_with_array_std( r#" -import std.{*}; +import * from std; -function bump( - values:storage(array(uint256)), - index:uint256, - delta:uint256 -) -> () { +function bump(values: storage>, index: uint256, delta: uint256) { values[index] += delta; return (); } @@ -2002,8 +1976,8 @@ function bump( fn storage_index_numeric_guard_rejects_shadowed_uint_names() { let (db, key) = db_with_array_std( r#" -data uint; -data uint256; +enum uint {} +enum uint256 {} "#, ); let module_id = module_id_from_key(&db, &key); @@ -2042,26 +2016,29 @@ data uint256; } #[test] -fn storage_ref_annotations_are_checked_without_widening_array_literal_routing() { +fn typed_storage_bindings_are_checked_without_widening_array_literal_routing() { let (db, key) = db_with_array_std( r#" -import std.{*}; +import * from std; contract C { - xs:array(uint256); + xs:array; - function good(i:uint256, value:uint256) -> () { - (xs : storage(array(uint256)))[i] = value; + function good(i:uint256, value:uint256) returns () { + let typed: storage> = xs; + typed[i] = value; return (); } - function bad(i:uint256) -> () { - (xs : storage(array(address)))[i] = uint256(1); + function bad(i:uint256) returns () { + let typed: storage> = xs; + typed[i] = uint256(1); return (); } - function annotatedLiteral() -> () { - xs = ([uint256(1)] : memory(DynArray(uint256))); + function typedLiteral() returns () { + let values: memory> = [uint256(1)]; + xs = values; return (); } } @@ -2071,15 +2048,16 @@ contract C { let (body, good) = infer_module_function_with_solver(&db, module_id, "good"); assert_no_typeck(&good); - let annotation = body - .exprs(&db) + let binding = body + .top_level_stmts(&db) .iter() - .find_map(|(id, expr)| matches!(expr.kind, ExprKind::TypeAnnot { .. }).then_some(id)) - .expect("storage array annotation"); + .copied() + .find(|stmt| matches!(body.stmts(&db).get(*stmt).kind, StmtKind::Let { .. })) + .expect("typed storage binding"); let uint256 = canonical_std_adt_ty(&db, "uint256", Vec::new()); let array = canonical_std_adt_ty(&db, "array", vec![uint256]); let storage_array = canonical_std_adt_ty(&db, "storage", vec![array]); - assert_eq!(good.expr_ty(body, annotation), Some(storage_array)); + assert_eq!(good.let_ty(body, binding), Some(storage_array)); let (body, bad) = infer_module_function_with_solver(&db, module_id, "bad"); assert!( @@ -2089,42 +2067,39 @@ contract C { "{:?}", bad.diagnostics ); - let annotation = body - .exprs(&db) + let binding = body + .top_level_stmts(&db) .iter() - .find_map(|(id, expr)| matches!(expr.kind, ExprKind::TypeAnnot { .. }).then_some(id)) - .expect("mismatched storage array annotation"); - assert_eq!(bad.expr_ty(body, annotation), Some(Ty::error(&db))); + .copied() + .find(|stmt| matches!(body.stmts(&db).get(*stmt).kind, StmtKind::Let { .. })) + .expect("mismatched storage binding"); + assert_eq!(bad.let_ty(body, binding), Some(Ty::error(&db))); - let (_, annotated_literal) = - infer_module_function_with_solver(&db, module_id, "annotatedLiteral"); + let (_, typed_literal) = infer_module_function_with_solver(&db, module_id, "typedLiteral"); assert!( - annotated_literal + typed_literal .diagnostics .iter() .any(|diagnostic| matches!(diagnostic, TypeckDiagnostic::Mismatch { .. })), "{:?}", - annotated_literal.diagnostics + typed_literal.diagnostics ); let module = module_hir(&db, module_id).expect("module hir"); let plan = crate::frontend_desugar_plan(&db, module); - let annotated_literal = plan + let typed_literal = plan .bodies .iter() - .find(|body| body.function_name == "annotatedLiteral") - .expect("annotatedLiteral desugar plan"); + .find(|body| body.function_name == "typedLiteral") + .expect("typedLiteral desugar plan"); assert!( - annotated_literal - .transforms - .iter() - .any(|transform| matches!( + typed_literal.transforms.iter().any(|transform| matches!( transform, crate::FrontendTransform::FieldWrite { hook, .. } if hook.starts_with("Assign.assign(") - )), + )), "{:?}", - annotated_literal.transforms + typed_literal.transforms ); } @@ -2132,31 +2107,31 @@ contract C { fn storage_array_index_alias_and_literal_assignment_preserve_reference_types() { let (db, key) = db_with_array_std( r#" -import std.{storage, array, uint256, Typedef, CanStore}; +import {storage, array, uint256, Typedef, CanStore} from std; contract C { - xs:array(word); + xs:array; n:word; - function read(i:uint256) -> word { return xs[i]; } + function read(i:uint256) returns (word) { return xs[i]; } - function aliasRead(i:uint256) -> word { + function aliasRead(i:uint256) returns (word) { let ys = xs; return ys[i]; } - function aliasWrite(i:uint256, value:word) -> () { + function aliasWrite(i:uint256, value:word) returns () { let ys = xs; ys[i] = value; return (); } - function set(x:word) -> () { + function set(x:word) returns () { xs = [x, x]; return (); } - function bad(x:word) -> () { + function bad(x:word) returns () { n = [x]; return (); } @@ -2226,12 +2201,12 @@ contract C { fn array_literal_contract_field_write_plan_uses_store_array_lit() { let (db, key) = db_with_array_std( r#" -import std.{storage, array}; +import {storage, array} from std; contract C { - xs:array(word); + xs:array; - function set(x:word) -> () { + function set(x:word) returns () { xs = [x]; return (); } @@ -2261,18 +2236,18 @@ contract C { fn module_local_string_and_integer_adts_remain_runtime_types() { let diagnostics = lowered_module_typeck_diagnostics( r#" -data string = RuntimeString(word); -data integer = RuntimeInteger(word); +enum string {RuntimeString(word)} +enum integer {RuntimeInteger(word)} -function takesString(value: string) -> () { +function takesString(value: string) returns () { return (); } -function takesInteger(value: integer) -> () { +function takesInteger(value: integer) returns () { return (); } -function exercise(value: word) -> () { +function exercise(value: word) returns () { takesString(string.RuntimeString(value)); takesInteger(integer.RuntimeInteger(value)); return (); @@ -2287,12 +2262,16 @@ function exercise(value: word) -> () { fn module_local_string_adt_rejects_primitive_string_patterns() { let diagnostics = lowered_module_typeck_diagnostics( r#" -data string = RuntimeString(word); +enum string {RuntimeString(word)} -function inspect(value: string) -> word { - match value { - | "a" => return 1; - | _ => return 0; +function inspect(value: string) returns (word) { + match (value) { + case "a" { + return 1; + } + default { + return 0; + } } } "#, @@ -2311,18 +2290,18 @@ fn contract_local_string_and_integer_adts_remain_runtime_types() { let diagnostics = lowered_module_typeck_diagnostics( r#" contract RuntimeNames { - data string = RuntimeString(word); - data integer = RuntimeInteger(word); + enum string {RuntimeString(word)} + enum integer {RuntimeInteger(word)} - function takesString(value: string) -> () { + function takesString(value: string) returns () { return (); } - function takesInteger(value: integer) -> () { + function takesInteger(value: integer) returns () { return (); } - function exercise(value: word) -> () { + function exercise(value: word) returns () { takesString(string.RuntimeString(value)); takesInteger(integer.RuntimeInteger(value)); return (); @@ -2347,7 +2326,7 @@ fn inferred_string_let_records_comptime_obligation() { let module = parse_module( &db, r#" -function f() -> word { +function f() returns (word) { let message = "hello"; return 0; } @@ -2427,14 +2406,14 @@ fn scheme_instantiation_reuses_one_fresh_var_per_binder() { #[test] fn ambiguous_integer_literal_defaults_to_word() { let db = TestDb::default(); - let module = parse_module(&db, "function f() -> word { return 1; }"); + let module = parse_module(&db, "function f() returns (word) { return 1; }"); let (body, result) = infer_function(&db, module, "f"); assert!(result.diagnostics.is_empty()); let expr = return_expr(&db, body); assert_eq!(result.expr_ty(body, expr), Some(Ty::word(&db))); assert_eq!(result.obligations.len(), 1); - assert_eq!(result.obligations[0].pred.display(&db), "word:Int"); + assert_eq!(result.obligations[0].pred.display(&db), "word: Int"); } #[test] @@ -2443,17 +2422,17 @@ fn end_to_end_body_infers_word_arithmetic() { let module = parse_module( &db, r#" -class t:Add { - function add(l:t, r:t) -> t; +trait Add { + function add(l:t, r:t) returns (t) ; } -instance word:Add { - function add(l:word, r:word) -> word { +impl Add { + function add(l:word, r:word) returns (word) { return primAddWord(l, r); } } -function f(x: word) -> word { return x + 1; } +function f(x: word) returns (word) { return x + 1; } "#, ); let (body, result) = infer_function(&db, module, "f"); @@ -2472,7 +2451,7 @@ function f(x: word) -> word { return x + 1; } result .obligations .iter() - .any(|obligation| obligation.pred.display(&db) == "word:Int"), + .any(|obligation| obligation.pred.display(&db) == "word: Int"), "{:?}", result.obligations ); @@ -2484,13 +2463,13 @@ fn class_method_call_emits_obligation() { let module = parse_module( &db, r#" -forall a . class a: Enum { - function fromEnum(x : a) -> word; +trait Enum { + function fromEnum(x : a) returns (word) ; } -data Food = Curry | Beans | Other; +enum Food {Curry , Beans , Other} -function main() -> word { +function main() returns (word) { return Enum.fromEnum(Food.Beans); } "#, @@ -2501,7 +2480,7 @@ function main() -> word { result .obligations .iter() - .any(|obligation| obligation.pred.display(&db).contains(":Enum")), + .any(|obligation| obligation.pred.display(&db).contains(": Enum")), "expected Enum obligation, got {:?}", result.obligations ); @@ -2513,15 +2492,15 @@ fn pair_domains_preserve_source_call_arity_and_explicit_tuple_arguments() { let module = parse_module( &db, r#" -function call_zero(f : () -> word) -> word { +function call_zero(f : function() returns (word)) returns (word) { return f(); } -function call_pair(f : (word, bool) -> word, x : word, y : bool) -> word { +function call_pair(f : function(word, bool) returns (word), x : word, y : bool) returns (word) { return f(x, y); } -function call_tuple(f : ((word, bool)) -> word, x : (word, bool)) -> word { +function call_tuple(f : function((word, bool)) returns (word), x : (word, bool)) returns (word) { return f(x); } "#, @@ -2542,10 +2521,8 @@ fn class_method_local_forall_is_lowered_as_a_method_binder() { let module = parse_module( &db, r#" -forall b. -class b:IsA { - forall a. - function ais(p : (a,b)) -> a; +trait IsA { + function ais(p : (a, b)) returns (a) ; } "#, ); @@ -2589,15 +2566,12 @@ class b:IsA { fn method_local_forall_survives_instance_signature_soundness() { let diagnostics = lowered_module_typeck_diagnostics( r#" -forall b. -class b:IsA { - forall a. - function ais(x : a, witness : b) -> a; +trait IsA { + function ais(x : a, witness : b) returns (a) ; } -instance word:IsA { - forall a. - function ais(x : a, witness : word) -> a { +impl IsA { + function ais(x : a, witness : word) returns (a) { return x; } } @@ -2611,8 +2585,8 @@ instance word:IsA { fn builtin_str_instance_requires_from_string() { let (db, key) = db_with_main_typeck( r#" -data Wrapped = Wrapped(word); -instance Wrapped:Str {} +enum Wrapped {Wrapped(word)} +impl Str {} "#, ); let module = module_id_from_key(&db, &key); @@ -2632,9 +2606,9 @@ instance Wrapped:Str {} fn builtin_str_instance_rejects_unknown_methods() { let (db, key) = db_with_main_typeck( r#" -data Wrapped = Wrapped(word); -instance Wrapped:Str { - function unexpected(x:word) -> word { return x; } +enum Wrapped {Wrapped(word)} +impl Str { + function unexpected(x:word) returns (word) { return x; } } "#, ); @@ -2655,9 +2629,9 @@ instance Wrapped:Str { fn builtin_str_instance_rejects_wrong_from_string_signature() { let (db, key) = db_with_main_typeck( r#" -data Wrapped = Wrapped(word); -instance Wrapped:Str { - function fromString(s:word) -> Wrapped { return Wrapped(s); } +enum Wrapped {Wrapped(word)} +impl Str { + function fromString(s:word) returns (Wrapped) { return Wrapped(s); } } "#, ); @@ -2677,29 +2651,29 @@ instance Wrapped:Str { #[test] fn builtin_str_ground_instance_rejects_overlapping_source_instance() { let mut db = TestDb::default(); - let std_path = PathBuf::from("/std/std.solc"); - let main_path = PathBuf::from("/main/main.solc"); + let std_path = PathBuf::from("/std/std.sol"); + let main_path = PathBuf::from("/main/main.sol"); let std_file = source_file_at_path( &db, &std_path, r#" export { memory(*), string }; -data memory(a) = memory(word); -data string; +enum memory {memory(word)} +enum string {} "#, ); let main_file = source_file_at_path( &db, &main_path, r#" -import std.{*}; +import * from std; -instance string:Str { - function fromString(comptime value:string) -> string { return value; } +impl Str { + function fromString(comptime value:string) returns (string) { return value; } } -instance memory(string):Str { - function fromString(comptime value:string) -> memory(string) { +impl Str> { + function fromString(comptime value:string) returns (memory) { return Str.fromString(value); } } @@ -2735,17 +2709,25 @@ fn comptime_numeric_scrutinees_accept_integer_literal_patterns() { let module = parse_module( &db, r#" -function classify_word(comptime x : word) -> word { - match x { - | 0 => return 10; - | _ => return 20; +function classify_word(comptime x : word) returns (word) { + match (x) { + case 0 { + return 10; + } + default { + return 20; + } } } -function classify_integer(comptime x : integer) -> word { - match x { - | 0 => return 10; - | _ => return 20; +function classify_integer(comptime x : integer) returns (word) { + match (x) { + case 0 { + return 10; + } + default { + return 20; + } } } "#, @@ -2766,13 +2748,13 @@ fn unconstrained_phantom_constructor_result_is_ambiguous() { let module = parse_module( &db, r#" -data Foo(a) = Foo(word); +enum Foo {Foo(word)} -forall a . function read(x : Foo(a)) -> word { +function read(x : Foo) returns (word) { return 0; } -function main() -> word { +function main() returns (word) { return read(Foo(42)); } "#, @@ -2795,15 +2777,14 @@ fn payload_constrained_constructor_result_is_not_phantom() { let module = parse_module( &db, r#" -data Box(a) = Box(a); +enum Box {Box(a)} -forall a . function unwrap(x : Box(a)) -> a { - match x { - | Box(value) => return value; - } +function unwrap(x : Box) returns (a) { + match (x) { + case Box(value) { return value; }} } -function main() -> word { +function main() returns (word) { return unwrap(Box(42)); } "#, @@ -2819,9 +2800,9 @@ fn expected_type_constrains_phantom_constructor_result() { let module = parse_module( &db, r#" -data Foo(a) = Foo(word); +enum Foo {Foo(word)} -function main() -> Foo(word) { +function main() returns (Foo) { return Foo(42); } "#, @@ -2837,20 +2818,19 @@ fn storage_word_field_read_loads_as_word_without_context() { let module = parse_module( &db, r#" -data storage(t) = storage(word); +enum storage {storage(word)} -forall a b. -class a:CanStore(b) { - function store(r:a, v:b) -> (); - function load(r:a) -> b; +trait CanStore { + function store(r:a, v:b) returns () ; + function load(r:a) returns (b) ; } -instance storage(word):CanStore(word) { - function store(dst: storage(word), src: word) -> () { +impl CanStore,word> { + function store(dst: storage, src: word) returns () { return (); } - function load(src: storage(word)) -> word { + function load(src: storage) returns (word) { return 0; } } @@ -2860,7 +2840,7 @@ contract C { function get() { let x = value; -return x; +return (); } } "#, @@ -2885,22 +2865,21 @@ fn storage_string_field_read_loads_as_memory_string_without_context() { let module = parse_module( &db, r#" -data string; -data memory(t) = memory(word); -data storage(t) = storage(word); +enum string {} +enum memory {memory(word)} +enum storage {storage(word)} -forall a b. -class a:CanStore(b) { - function store(r:a, v:b) -> (); - function load(r:a) -> b; +trait CanStore { + function store(r:a, v:b) returns () ; + function load(r:a) returns (b) ; } -instance storage(string):CanStore(memory(string)) { - function store(dst: storage(string), src: memory(string)) -> () { +impl CanStore,memory> { + function store(dst: storage, src: memory) returns () { return (); } - function load(src: storage(string)) -> memory(string) { + function load(src: storage) returns (memory) { return memory(0); } } @@ -2910,7 +2889,7 @@ contract C { function get() { let x = value; -return x; +return (); } } "#, @@ -2937,29 +2916,28 @@ fn storage_mapping_assignment_records_concrete_base_ref_type() { let module = parse_module( &db, r#" -data mapping(index, member) = mapping(word); -data storage(t) = storage(word); +enum mapping {mapping(word)} +enum storage {storage(word)} -forall a b. -class a:CanStore(b) { - function store(r:a, v:b) -> (); - function load(r:a) -> b; +trait CanStore { + function store(r:a, v:b) returns () ; + function load(r:a) returns (b) ; } -instance storage(word):CanStore(word) { - function store(dst: storage(word), src: word) -> () { +impl CanStore,word> { + function store(dst: storage, src: word) returns () { return (); } - function load(src: storage(word)) -> word { + function load(src: storage) returns (word) { return 0; } } contract C { - m: mapping(word, word); + m: mapping(word => word); - function next() -> word { + function next() returns (word) { return 1; } @@ -2992,14 +2970,14 @@ fn constrained_function_call_records_call_site_evidence() { let module = parse_module( &db, r#" -data T = T; +enum T {T} -forall a . class a:C {} -instance T:C {} +trait C {} +impl C {} -forall a . a:C => function use(x: a) -> word { return 0; } +function use(x: a) returns (word) where a: C { return 0; } -function main(t: T) -> word { +function main(t: T) returns (word) { return use(t); } "#, @@ -3042,8 +3020,8 @@ fn trait_solver_rejects_unproductive_instance_cycle() { let module = parse_module( &db, r#" -forall a . class a:C {} -forall a . a:C => instance a:C {} +trait C {} +impl C where a: C {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3064,8 +3042,8 @@ fn tabled_solver_cycle_saturates_without_fuel_diagnostic() { let module = parse_module( &db, r#" -forall a . class a:C {} -forall a . a:C => instance a:C {} +trait C {} +impl C where a: C {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3085,15 +3063,15 @@ forall a . a:C => instance a:C {} r#" pragma no-patterson-condition C; -forall a . class a:C {} +trait C {} -forall a . a:C => instance a:C {} +impl C where a: C {} -forall a . a:C => function needsC(x:a) -> () { +function needsC(x:a) returns () where a: C { return (); } -function main(x: word) -> () { +function main(x: word) returns () { return needsC(x); } "#, @@ -3112,11 +3090,11 @@ fn tabled_solver_mutual_recursion_saturates_without_answers() { let module = parse_module( &db, r#" -forall a . class a:C {} -forall a . class a:D {} +trait C {} +trait D {} -forall a . a:D => instance a:C {} -forall a . a:C => instance a:D {} +impl C where a: D {} +impl D where a: C {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3141,16 +3119,16 @@ fn tabled_solver_shares_diamond_subgoals() { let module = parse_module( &db, r#" -forall a . class a:Leaf {} -forall a . class a:Left {} -forall a . class a:Right {} -forall a . class a:Top {} +trait Leaf {} +trait Left {} +trait Right {} +trait Top {} -instance word:Leaf {} +impl Leaf {} -forall a . a:Leaf => instance a:Left {} -forall a . a:Leaf => instance a:Right {} -forall a . a:Left, a:Right => instance a:Top {} +impl Left where a: Leaf {} +impl Right where a: Leaf {} +impl Top where a: Left, a: Right {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3179,18 +3157,18 @@ fn tabled_solver_shares_alpha_equivalent_flexible_subgoals() { let module = parse_module( &db, r#" -data Pair(a, b) = Pair(a, b); +enum Pair {Pair(a, b)} -forall a . class a:Leaf {} -forall a . class a:Left {} -forall a . class a:Right {} -forall a . class a:Top {} +trait Leaf {} +trait Left {} +trait Right {} +trait Top {} -forall a . instance a:Leaf {} +impl Leaf {} -forall a b c . Pair(b, c):Leaf => instance a:Left {} -forall a c b . Pair(b, c):Leaf => instance a:Right {} -forall a . a:Left, a:Right => instance a:Top {} +impl Left where Pair: Leaf {} +impl Right where Pair: Leaf {} +impl Top where a: Left, a: Right {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3220,12 +3198,12 @@ fn tabled_solver_dedups_replayed_identical_answer() { let module = parse_module( &db, r#" -forall a . class a:Seed {} -forall a . class a:Derived {} +trait Seed {} +trait Derived {} -instance word:Seed {} +impl Seed {} -forall a . a:Seed, a:Seed => instance a:Derived {} +impl Derived where a: Seed, a: Seed {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3253,14 +3231,14 @@ fn tabled_solver_replays_answers_to_late_consumers() { let module = parse_module( &db, r#" -forall a . class a:Seed {} -forall a . class a:Derived {} -forall a . class a:Needs {} +trait Seed {} +trait Derived {} +trait Needs {} -instance word:Seed {} +impl Seed {} -forall a . a:Seed => instance a:Derived {} -forall a . a:Seed, a:Derived => instance a:Needs {} +impl Derived where a: Seed {} +impl Needs where a: Seed, a: Derived {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3288,13 +3266,13 @@ fn trait_solver_resolves_recursive_pair_instance() { let module = parse_module( &db, r#" -data Pair(a, b) = Pair(a, b); +enum Pair {Pair(a, b)} -forall a . class a:StorageSize {} +trait StorageSize {} -instance word:StorageSize {} +impl StorageSize {} -forall a b . a:StorageSize, b:StorageSize => instance Pair(a, b):StorageSize {} +impl StorageSize> where a: StorageSize, b: StorageSize {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3328,22 +3306,22 @@ fn trait_solver_prefilters_only_heads_that_cannot_unify() { let module = parse_module( &db, r#" -forall a . class a:Target {} -forall a . class a:Noise {} -forall a . class a:DefaultTarget {} -forall a . class a:GenericTarget {} -forall a . class a:GivenTarget {} -forall a . class a:Parent {} -forall a . a:Parent => class a:Child {} -forall a . class a:AmbiguousTarget {} - -instance word:Target {} -instance bool:Noise {} -forall a . default instance a:Noise {} -forall a . default instance a:DefaultTarget {} -forall a . instance a:GenericTarget {} -instance word:AmbiguousTarget {} -instance word:AmbiguousTarget {} +trait Target {} +trait Noise {} +trait DefaultTarget {} +trait GenericTarget {} +trait GivenTarget {} +trait Parent {} +trait Child where a: Parent {} +trait AmbiguousTarget {} + +impl Target {} +impl Noise {} +default impl Noise {} +default impl DefaultTarget {} +impl GenericTarget {} +impl AmbiguousTarget {} +impl AmbiguousTarget {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3433,7 +3411,7 @@ fn trait_solver_preserves_comptime_transparent_fixed_local_given() { let module = parse_module( &db, r#" -forall abs rep . class abs:Typedef(rep) {} +trait Typedef {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3467,12 +3445,12 @@ fn trait_solver_preserves_rigid_origin_across_nested_goal_canonicalization() { let module = parse_module( &db, r#" -data Wrap(a) = Wrap(a); +enum Wrap {Wrap(a)} -forall self rep . class self:Foo(rep) {} -forall self rep . class self:Bar(rep) {} +trait Foo {} +trait Bar {} -forall a rep . a:Foo(rep) => instance Wrap(a):Bar(rep) {} +impl Bar,rep> where a: Foo {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3525,20 +3503,18 @@ fn inference_improves_multi_parameter_result_through_local_given() { let module = parse_module( &db, r#" -data Wrap(a) = Wrap(a); +enum Wrap {Wrap(a)} -forall self rep . class self:Foo(rep) {} -forall self rep . class self:Bar(rep) {} +trait Foo {} +trait Bar {} -forall a rep . a:Foo(rep) => instance Wrap(a):Bar(rep) {} +impl Bar,rep> where a: Foo {} -forall a rep . Wrap(a):Bar(rep) => -function need_bar(x:Wrap(a)) -> () { +function need_bar(x:Wrap) returns () where Wrap: Bar { return (); } -forall a . a:Foo(word) => -function use_bar(x:Wrap(a)) -> () { +function use_bar(x:Wrap) returns () where a: Foo { need_bar(x); return (); } @@ -3569,8 +3545,8 @@ fn trait_solver_prefilter_preserves_comptime_correlated_instance_head() { let module = parse_module( &db, r#" -forall a . class a:Correlated {} -forall x . instance (comptime x, x):Correlated {} +trait Correlated {} +impl Correlated<(comptime, x)> {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3603,9 +3579,9 @@ fn trait_solver_prefers_specific_instance_over_default() { let module = parse_module( &db, r#" -forall a . class a:Test {} -forall a . default instance a:Test {} -instance word:Test {} +trait Test {} +default impl Test {} +impl Test {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3642,13 +3618,13 @@ fn trait_solver_uses_default_instance_for_non_default_clause_condition() { let module = parse_module( &db, r#" -data Wrap(a) = Wrap(a); +enum Wrap {Wrap(a)} -forall a . class a:DefaultDependency {} -forall a . default instance a:DefaultDependency {} +trait DefaultDependency {} +default impl DefaultDependency {} -forall a . class a:Outer {} -forall a . a:DefaultDependency => instance Wrap(a):Outer {} +trait Outer {} +impl Outer> where a: DefaultDependency {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3694,9 +3670,9 @@ fn trait_solver_reports_overlapping_non_default_instances_as_ambiguous() { let module = parse_module( &db, r#" -forall a . class a:C {} -instance word:C {} -instance word:C {} +trait C {} +impl C {} +impl C {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3720,14 +3696,14 @@ fn trait_solver_keeps_distinct_substitutions_from_the_same_instance() { let module = parse_module( &db, r#" -data Pair(a, b) = Pair(a, b); +enum Pair {Pair(a, b)} -forall a r . class a:D(r) {} -forall a . default instance a:D(word) {} -forall a . default instance a:D(bool) {} +trait D {} +default impl D {} +default impl D {} -forall a . class a:C {} -forall a r . a:D(r) => instance Pair(a, r):C {} +trait C {} +impl C> where a: D {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3763,15 +3739,15 @@ fn trait_solver_unifies_weak_class_args_across_conditions() { let module = parse_module( &db, r#" -data Uint = Uint(word); +enum Uint {Uint(word)} -forall abs rep . class abs:Typedef(rep) {} -instance Uint:Typedef(word) {} +trait Typedef {} +impl Typedef {} -forall a . class a:StorageSize {} -instance word:StorageSize {} +trait StorageSize {} +impl StorageSize {} -forall a b . a:Typedef(b), b:StorageSize => instance a:StorageSize {} +impl StorageSize where a: Typedef, b: StorageSize {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3801,9 +3777,9 @@ fn default_instance_is_blocked_by_unifying_normal_head() { let module = parse_module( &db, r#" -forall a . class a:C {} -instance word:C {} -forall a . default instance a:C {} +trait C {} +impl C {} +default impl C {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3823,25 +3799,25 @@ forall a . default instance a:C {} #[test] fn imported_class_origin_contributes_superclass_clauses() { let mut db = TestDb::default(); - let lib_path = PathBuf::from("/main/lib.solc"); - let main_path = PathBuf::from("/main/main.solc"); + let lib_path = PathBuf::from("/main/lib.sol"); + let main_path = PathBuf::from("/main/main.sol"); let lib_file = source_file_at_path( &db, &lib_path, r#" export { Eq, Ord }; -forall a . class a:Eq {} -forall a . a:Eq => class a:Ord {} +trait Eq {} +trait Ord where a: Eq {} "#, ); let main_file = source_file_at_path( &db, &main_path, r#" -import lib.{Eq, Ord}; +import {Eq, Ord} from lib; -instance word:Ord {} +impl Ord {} "#, ); let lib_key = module_key_for_path(LibraryId::Main, &PathBuf::from("/main"), &lib_path).unwrap(); @@ -3875,23 +3851,23 @@ instance word:Ord {} #[test] fn trait_env_from_module_resolution_and_imports_deduplicates_superclass_modules() { let mut db = TestDb::default(); - let lib_path = PathBuf::from("/main/lib.solc"); - let main_path = PathBuf::from("/main/main.solc"); + let lib_path = PathBuf::from("/main/lib.sol"); + let main_path = PathBuf::from("/main/main.sol"); let lib_file = source_file_at_path( &db, &lib_path, r#" export { Parent, Child }; -forall a . class a:Parent {} -forall a . a:Parent => class a:Child {} +trait Parent {} +trait Child where a: Parent {} "#, ); let main_file = source_file_at_path( &db, &main_path, r#" -import lib.{Parent, Child}; +import {Parent, Child} from lib; "#, ); let lib_key = module_key_for_path(LibraryId::Main, &PathBuf::from("/main"), &lib_path).unwrap(); @@ -3930,9 +3906,9 @@ fn superclass_solution_records_projection_evidence() { let module = parse_module( &db, r#" -forall a . class a:Eq {} -forall a . a:Eq => class a:Ord {} -instance word:Ord {} +trait Eq {} +trait Ord where a: Eq {} +impl Ord {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3964,10 +3940,10 @@ fn direct_instance_precedes_superclass_projection() { let module = parse_module( &db, r#" -forall a . class a:Eq {} -forall a . a:Eq => class a:Ord {} -instance word:Eq {} -instance word:Ord {} +trait Eq {} +trait Ord where a: Eq {} +impl Eq {} +impl Ord {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -3996,9 +3972,9 @@ fn local_givens_and_superclasses_precede_global_instances() { let module = parse_module( &db, r#" -forall a . class a:Eq {} -forall a . a:Eq => class a:Ord {} -instance word:Eq {} +trait Eq {} +trait Ord where a: Eq {} +impl Eq {} "#, ); let module_resolution = hir_nameres::resolve_module(&db, module); @@ -4039,12 +4015,12 @@ fn pragma_corpus_files_have_no_instance_soundness_diagnostics() { let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let corpus = manifest.join("../parser/tests/fixtures/corpus"); let files = [ - "pragmas/coverage.solc", - "cases/array.solc", - "cases/bound-with-pragma.solc", - "cases/tabled-left-recursive-fail.solc", - "cases/tabled-cycle-fail.solc", - "cases/mptc-partial-instance.solc", + "pragmas/coverage.sol", + "cases/array.sol", + "cases/bound-with-pragma.sol", + "cases/tabled-left-recursive-fail.sol", + "cases/tabled-cycle-fail.sol", + "cases/mptc-partial-instance.sol", ]; for file in files { @@ -4073,9 +4049,9 @@ fn pragma_corpus_files_have_no_instance_soundness_diagnostics() { fn structured_default_instance_head_is_allowed_only_when_it_contains_a_type_variable() { let (db, key) = db_with_main_typeck( r#" -data Box(a) = Box(a); -forall a . class a:Marker {} -forall a . default instance Box(a):Marker {} +enum Box {Box(a)} +trait Marker {} +default impl Marker> {} "#, ); let module_id = module_id_from_key(&db, &key); @@ -4090,9 +4066,9 @@ forall a . default instance Box(a):Marker {} let (db, key) = db_with_main_typeck( r#" -data Box(a) = Box(a); -forall a . class a:Marker {} -default instance Box(word):Marker {} +enum Box {Box(a)} +trait Marker {} +default impl Marker> {} "#, ); let module_id = module_id_from_key(&db, &key); diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 65bfdff0..403a93bf 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -446,7 +446,7 @@ impl<'db> TypeLowering<'db> { hir_nameres::Resolution::Def { def, kind: hir_nameres::DefResolutionKind::Class, - } => Some(def.name(self.db).unwrap_or_else(|| "class".to_owned())), + } => Some(def.name(self.db).unwrap_or_else(|| "trait".to_owned())), _ => None, } } diff --git a/crates/hir-ty/src/prepare.rs b/crates/hir-ty/src/prepare.rs index 95473a2e..74a47c91 100644 --- a/crates/hir-ty/src/prepare.rs +++ b/crates/hir-ty/src/prepare.rs @@ -1664,9 +1664,9 @@ mod tests { std_root.clone(), BTreeMap::new(), )); - let main_path = main_root.join("main.solc"); - let std_path = std_root.join("std.solc"); - let dispatch_path = std_root.join("dispatch.solc"); + let main_path = main_root.join("main.sol"); + let std_path = std_root.join("std.sol"); + let dispatch_path = std_root.join("dispatch.sol"); db.module_fs_snapshot = Some(ModuleFsSnapshot::new( &db, BTreeSet::from([main_path.clone(), std_path.clone(), dispatch_path.clone()]), @@ -1739,8 +1739,8 @@ mod tests { #[test] fn preserves_source_and_builds_effective_dispatch_overlay() { let src = r#" -import std.dispatch.{*}; -contract C { public function answer(x:uint256) -> uint256 { return x; } } +import * from std.dispatch; +contract C { function answer(x: uint256) public returns (uint256) { return x; } } "#; let (db, file) = db_with_main(src); let source = source_module(&db, file); @@ -1772,8 +1772,8 @@ contract C { public function answer(x:uint256) -> uint256 { return x; } } #[test] fn preparation_preserves_contract_and_field_comments() { let src = r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; // contract documentation contract C { // stored value documentation @@ -1781,7 +1781,7 @@ contract C { // constructor documentation constructor() {} // method documentation - public function answer(x:uint256) -> uint256 { return x; } + function answer(x:uint256) public returns (uint256) { return x; } } "#; let (db, file) = db_with_main(src); @@ -1872,7 +1872,7 @@ contract C { #[test] fn runtime_dispatch_is_implicit_and_existing_main_suppresses_it() { let (db, file) = db_with_main( - "contract C { public function answer() -> uint256 { return uint256(1); } }", + "contract C { function answer() public returns (uint256) { return uint256(1); } }", ); let source = source_module(&db, file); let prepared = prepare_module(&db, source); @@ -1891,8 +1891,8 @@ contract C { let (db, file) = db_with_main( r#" -import std.dispatch.{*}; -contract C { function main() -> () {} } +import * from std.dispatch; +contract C { function main() {} } "#, ); let source = source_module(&db, file); @@ -1913,8 +1913,7 @@ contract C { function main() -> () {} } #[test] fn nonempty_constructor_is_prepared_without_injecting_imports() { - let (db, file) = - db_with_main("contract C { constructor(x:word) {} function main() -> () {} }"); + let (db, file) = db_with_main("contract C { constructor(x:word) {} function main() {} }"); let source = source_module(&db, file); let prepared = prepare_module(&db, source); assert_ne!(prepared.module(&db), source); @@ -1933,11 +1932,11 @@ contract C { function main() -> () {} } fn constructor_overlay_preserves_source_and_generates_deployment_entry() { let (db, file) = db_with_main( r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract C { - payable constructor(x:word, y:word) { let z = x; } - function main() -> () { return (); } + constructor(x:word, y:word) payable { let z = x; } + function main() returns () { return (); } } "#, ); @@ -1990,10 +1989,10 @@ contract C { fn explicit_constructor_overlay_is_idempotent() { let (db, file) = db_with_main( r#" -import std.{*}; +import * from std; contract C { - payable constructor(x:word) { let saved = x; } - function main() -> () { return (); } + constructor(x:word) payable { let saved = x; } + function main() returns () { return (); } } "#, ); @@ -2030,19 +2029,19 @@ contract C { #[test] fn constructor_body_edit_keeps_generated_wrapper_identity() { let before = r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract C { constructor(x:word) { let z = 1; } - function main() -> () { return (); } + function main() returns () { return (); } } "#; let after = r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract C { constructor(x:word) { let z = 2; } - function main() -> () { return (); } + function main() returns () { return (); } } "#; let (mut db, file) = db_with_main(before); @@ -2084,10 +2083,10 @@ contract C { fn deduplicates_overloaded_method_name_declarations() { let (db, file) = db_with_main( r#" -import std.dispatch.{*}; +import * from std.dispatch; contract C { - public function get(x:uint256) -> uint256 { return x; } - public function get(x:bool) -> bool { return x; } + function get(x:uint256) public returns (uint256) { return x; } + function get(x:bool) public returns (bool) { return x; } } "#, ); @@ -2108,12 +2107,12 @@ contract C { fn dispatch_name_types_are_injective_across_contract_method_boundaries() { let (db, file) = db_with_main( r#" -import std.dispatch.{*}; +import * from std.dispatch; contract A { - public function B_C(x:uint256) -> uint256 { return x; } + function B_C(x:uint256) public returns (uint256) { return x; } } contract A_B { - public function C(x:uint256) -> uint256 { return x; } + function C(x:uint256) public returns (uint256) { return x; } } "#, ); @@ -2150,12 +2149,12 @@ contract A_B { #[test] fn omitted_return_uses_unit_and_body_edit_keeps_generated_identity() { let before = r#" -import std.dispatch.{*}; -contract C { public function ping() { let x = 1; } } +import * from std.dispatch; +contract C { function ping() public { let x = 1; } } "#; let after = r#" -import std.dispatch.{*}; -contract C { public function ping() { let x = 2; } } +import * from std.dispatch; +contract C { function ping() public { let x = 2; } } "#; let (mut db, file) = db_with_main(before); let source = source_module(&db, file); diff --git a/crates/hir-ty/src/solver/derived_class.rs b/crates/hir-ty/src/solver/derived_class.rs index 10ed2eab..841d8fda 100644 --- a/crates/hir-ty/src/solver/derived_class.rs +++ b/crates/hir-ty/src/solver/derived_class.rs @@ -109,7 +109,7 @@ pub(crate) fn class_derivation_diagnostics<'db>( span, ty, class: class_name, - reason: "only single-parameter classes can be derived".to_owned(), + reason: "only single-parameter traits can be derived".to_owned(), }); continue; } @@ -118,7 +118,7 @@ pub(crate) fn class_derivation_diagnostics<'db>( span, ty, class: class_name, - reason: "a contract-local data type cannot capture generic contract parameters" + reason: "a contract-local enum cannot capture generic contract parameters" .to_owned(), }); } diff --git a/crates/hir-ty/src/solver/derived_storage.rs b/crates/hir-ty/src/solver/derived_storage.rs index f708f943..29ab02ea 100644 --- a/crates/hir-ty/src/solver/derived_storage.rs +++ b/crates/hir-ty/src/solver/derived_storage.rs @@ -11,7 +11,7 @@ pub(super) struct DerivedStorageClauseSource<'db> { pub storage_size: DefId<'db>, /// `CanStore` class. pub can_store: DefId<'db>, - /// `storage(ty)` data type. + /// `storage` data type. pub storage: DefId<'db>, } @@ -25,7 +25,7 @@ pub(super) fn visible_storage_clause_source<'db>( /// Builds the storage obligation carried by a contract field declaration. /// -/// A field is addressed uniformly through `storage(field_ty)`, but mappings +/// A field is addressed uniformly through `storage`, but mappings /// and storage arrays load back as slot handles while strings and bytes load /// into memory. Keeping this distinction here mirrors expression inference /// and, importantly, makes an otherwise-unused ADT field validate the body of @@ -338,7 +338,7 @@ fn adt_named<'db>(db: &'db dyn Db, def: DefId<'db>, name: &str) -> Option<()> { } /// Adds the concrete `T:StorageSize` and -/// `storage(T):CanStore(T)` clauses emitted by upstream DeriveGeneric. +/// `storage: CanStore` clauses emitted by upstream DeriveGeneric. pub(super) fn push_derived_storage_clauses<'db>( db: &'db dyn Db, clauses: &mut Vec>, diff --git a/crates/hir-ty/src/solver/display.rs b/crates/hir-ty/src/solver/display.rs index b6d687a4..79507750 100644 --- a/crates/hir-ty/src/solver/display.rs +++ b/crates/hir-ty/src/solver/display.rs @@ -23,18 +23,18 @@ pub(super) fn display_scheme_source<'db>( .map(|pred| display_pred_source(db, *pred, &names)) .collect::>(); let ty = display_ty_source(db, body.ty(db), &names); - let qualified = if preds.is_empty() { + let mut displayed = if scheme.binder_count(db) == 0 { ty - } else { - format!("{} => {ty}", preds.join(", ")) - }; - if scheme.binder_count(db) == 0 { - qualified } else { let vars = (0..scheme.binder_count(db)) .map(|index| display_var_name(index, &names)) .collect::>() .join(", "); - format!("forall {vars}. {qualified}") + format!("<{vars}> {ty}") + }; + if !preds.is_empty() { + displayed.push_str(" where "); + displayed.push_str(&preds.join(", ")); } + displayed } diff --git a/crates/hir-ty/src/solver/evidence.rs b/crates/hir-ty/src/solver/evidence.rs index b3b9ef98..e765434f 100644 --- a/crates/hir-ty/src/solver/evidence.rs +++ b/crates/hir-ty/src/solver/evidence.rs @@ -19,10 +19,10 @@ impl<'db> Evidence<'db> { .collect::>() .join(", "); if sub_evidence.is_empty() { - format!("instance {name}({args})") + format!("impl {name}<{args}>") } else { format!( - "instance {name}({args}) with {} subproof(s)", + "impl {name}<{args}> with {} subproof(s)", sub_evidence.len() ) } @@ -34,7 +34,7 @@ impl<'db> Evidence<'db> { .filter(|name| !name.is_empty()) .unwrap_or_else(|| format!("{:?}", class.kind(db))); format!( - "superclass {name} => {} via {}", + "supertrait {name}: {} via {}", pred.display(db), child.display(db) ) diff --git a/crates/hir-ty/src/solver/mod.rs b/crates/hir-ty/src/solver/mod.rs index 5a4c0d25..e3b1bafd 100644 --- a/crates/hir-ty/src/solver/mod.rs +++ b/crates/hir-ty/src/solver/mod.rs @@ -262,7 +262,7 @@ pub enum DerivedClauseKind<'db> { /// ADT whose storage-size instance was synthesized. adt: DefId<'db>, }, - /// Concrete `storage(T):CanStore(T)` instance. + /// Concrete `storage: CanStore` impl. CanStore { /// ADT whose storage instance was synthesized. adt: DefId<'db>, diff --git a/crates/hir-ty/src/solver/soundness.rs b/crates/hir-ty/src/solver/soundness.rs index b9bd0a88..5045a4e0 100644 --- a/crates/hir-ty/src/solver/soundness.rs +++ b/crates/hir-ty/src/solver/soundness.rs @@ -506,7 +506,7 @@ fn check_instance_methods<'db>( .class .def_id_value(db) .name(db) - .unwrap_or_else(|| "".to_owned()); + .unwrap_or_else(|| "".to_owned()); let methods = instance.methods(db); let method_names = methods .iter() @@ -691,7 +691,7 @@ fn check_builtin_str_method_signature<'db>( span: LabelSpan::from_span(db, method.sig(db).span(db)), method: METHOD_NAME.to_owned(), reason: format!( - "expected (string) -> {}, got {}", + "expected function(string) returns ({}), got {}", display_ty_source(db, *main, &inherited_names), display_ty_source(db, actual, &inherited_names) ), diff --git a/crates/hir-ty/src/support.rs b/crates/hir-ty/src/support.rs index 0bc11db6..13558620 100644 --- a/crates/hir-ty/src/support.rs +++ b/crates/hir-ty/src/support.rs @@ -22,7 +22,7 @@ pub(crate) fn canonical_std_adt_defs<'db>(db: &'db dyn Db, name: &str) -> Vec SourceFile { - let url = format!("memory:///{name}.solc").parse().expect("valid url"); + let url = format!("memory:///{name}.sol").parse().expect("valid url"); SourceFile::new(db, url, Some(src.to_owned())) } @@ -126,8 +126,8 @@ fn db_with_main(src: &str) -> (TestDb, ModuleKey) { library: LibraryId::Main, logical_path: vec!["main".to_owned()], }; - let path = PathBuf::from("/main/main.solc"); - let file = source_file_at(&db, "/main/main.solc", src); + let path = PathBuf::from("/main/main.sol"); + let file = source_file_at(&db, "/main/main.sol", src); db.existing_files.insert(path); db.module_files.insert(key.clone(), file); db.sync_inputs(); @@ -143,45 +143,41 @@ fn insert_module_source(db: &mut TestDb, key: ModuleKey, path: &str, src: &str) fn insert_real_std_modules(db: &mut TestDb) { for (logical, path, source) in [ - ( - "std", - "/std/std.solc", - include_str!("../../../std/std.solc"), - ), + ("std", "/std/std.sol", include_str!("../../../std/std.sol")), ( "dispatch", - "/std/dispatch.solc", - include_str!("../../../std/dispatch.solc"), + "/std/dispatch.sol", + include_str!("../../../std/dispatch.sol"), ), ( "opcodes", - "/std/opcodes.solc", - include_str!("../../../std/opcodes.solc"), + "/std/opcodes.sol", + include_str!("../../../std/opcodes.sol"), ), ( "Generic", - "/std/Generic.solc", - include_str!("../../../std/Generic.solc"), + "/std/Generic.sol", + include_str!("../../../std/Generic.sol"), ), ( "ABIGeneric", - "/std/ABIGeneric.solc", - include_str!("../../../std/ABIGeneric.solc"), + "/std/ABIGeneric.sol", + include_str!("../../../std/ABIGeneric.sol"), ), ( "StorageGeneric", - "/std/StorageGeneric.solc", - include_str!("../../../std/StorageGeneric.solc"), + "/std/StorageGeneric.sol", + include_str!("../../../std/StorageGeneric.sol"), ), ( "eip712", - "/std/eip712.solc", - include_str!("../../../std/eip712.solc"), + "/std/eip712.sol", + include_str!("../../../std/eip712.sol"), ), ( "eip7951", - "/std/eip7951.solc", - include_str!("../../../std/eip7951.solc"), + "/std/eip7951.sol", + include_str!("../../../std/eip7951.sol"), ), ] { insert_module_source( @@ -285,7 +281,7 @@ fn yul_function_values_are_local_and_cannot_capture_sail_values() { "dynamic read", r#" contract C { - function main() -> word { + function main() returns (word) { let outer : word; assembly { outer := callvalue() @@ -301,7 +297,7 @@ contract C { "known write", r#" contract C { - function main() -> word { + function main() returns (word) { let outer : word = 7; assembly { function writeOuter() { outer := 9 } @@ -316,7 +312,7 @@ contract C { "outer Yul read", r#" contract C { - function main() -> word { + function main() returns (word) { let result : word; assembly { let outerYul := callvalue() @@ -342,7 +338,7 @@ contract C { let local = diagnostics( r#" contract C { - function main() -> word { + function main() returns (word) { let result : word; assembly { function localValue(input) -> output { @@ -364,7 +360,7 @@ fn yul_for_body_values_do_not_leak_into_the_post_block() { let diagnostics = diagnostics( r#" contract C { - function main() -> word { + function main() returns (word) { let result : word; assembly { let i := 0 @@ -395,7 +391,7 @@ fn generated_dispatch_is_synthesized_before_import_resolution() { &db, r#" contract Answer { - public function add(x: word) -> word { return x; } + function add(x: word) public returns (word) { return x; } } "#, ); @@ -414,7 +410,7 @@ contract Answer { &manual_db, r#" contract Answer { - function main() -> () { return (); } + function main() returns () { return (); } } "#, ); @@ -428,7 +424,7 @@ contract Answer { let parameterized_main = diagnostics( r#" contract Answer { - public function main(x: word) -> word { return x; } + function main(x: word) public returns (word) { return x; } } "#, ); @@ -444,11 +440,11 @@ contract Answer { fn prepared_dispatch_uses_its_synthetic_sigstring_instance_during_typeck() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract Answer { - public function ping(x: word) -> word { return x; } + function ping(x: word) public returns (word) { return x; } } "#, ); @@ -458,11 +454,11 @@ contract Answer { library: LibraryId::Std, logical_path: vec!["std".to_owned()], }, - "/std/std.solc", + "/std/std.sol", r#" export { Proxy(*), string }; -data Proxy(t) = Proxy; -data string; +enum Proxy {Proxy} +enum string {} "#, ); insert_module_source( @@ -471,9 +467,9 @@ data string; library: LibraryId::Std, logical_path: vec!["dispatch".to_owned()], }, - "/std/dispatch.solc", + "/std/dispatch.sol", r#" -import std.{*}; +import * from std; export { Contract(*), @@ -486,31 +482,29 @@ export { fallback_default_implementation }; -data Contract(methods, fb) = Contract(methods, fb); -data Method(name, payability, args, rets, fn) = - Method(Proxy(name), Proxy(payability), Proxy(args), Proxy(rets), fn); -data Fallback(payability, args, rets, fn) = - Fallback(Proxy(payability), Proxy(args), Proxy(rets), fn); -data Payable; -data NonPayable; +enum Contract {Contract(methods, fb)} +enum Method {Method(Proxy, Proxy, Proxy, Proxy, fn)} +enum Fallback {Fallback(Proxy, Proxy, Proxy, fn)} +enum Payable {} +enum NonPayable {} -forall t . class t:SigString { - function sigStr(value: Proxy(t)) -> string; +trait SigString { + function sigStr(value: Proxy) returns (string) ; } -forall c . class c:RunContract { - function exec(value: c) -> (); +trait RunContract { + function exec(value: c) returns () ; } -forall name payability args rets fn fb - . name:SigString -=> instance Contract(Method(name, payability, args, rets, fn), fb):RunContract { - function exec(value: Contract(Method(name, payability, args, rets, fn), fb)) -> () { +impl + RunContract, fb>> + where name: SigString { + function exec(value: Contract, fb>) returns () { return (); } } -function fallback_default_implementation() -> () { return (); } +function fallback_default_implementation() returns () { return (); } "#, ); @@ -543,15 +537,15 @@ function fallback_default_implementation() -> () { return (); } fn dispatch_names_and_selectors_distinguish_contract_method_boundaries() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract A { - public function B_C(x:uint256) -> uint256 { return x; } + function B_C(x:uint256) public returns (uint256) { return x; } } contract A_B { - public function C(x:uint256) -> uint256 { return x; } + function C(x:uint256) public returns (uint256) { return x; } } "#, ); @@ -581,15 +575,15 @@ fn dispatch_surface_tracks_public_private_constructor_and_fallback() { &db, r#" contract Token { - payable constructor(amount: word) {} + constructor(amount: word) payable {} - function hidden(x: word) -> word { return x; } + function hidden(x: word) returns (word) { return x; } - public payable function pay(to: word) -> (word, bool) { + function pay(to: word) public payable returns ((word, bool)) { return (to, true); } - payable fallback() -> () {} + fallback() payable {} } "#, ); @@ -626,8 +620,8 @@ fn abi_json_matches_reference_public_function_shape() { &db, r#" contract Sample { - public function get() -> word { return 1; } - function secret() -> word { return 0; } + function get() public returns (word) { return 1; } + function secret() returns (word) { return 0; } } "#, ); @@ -663,7 +657,7 @@ fn abi_json_matches_reference_constructor_payable_and_tuple_outputs() { contract Token { constructor(amount: word) {} - public payable function pay(to: word) -> (word, bool) { + function pay(to: word) public payable returns ((word, bool)) { return (to, true); } } @@ -685,10 +679,10 @@ fn abi_json_preserves_source_declaration_order() { &db, r#" contract Order { - public function a() -> word { return 1; } + function a() public returns (word) { return 1; } constructor(seed: word) {} - payable fallback() -> () {} - public function b(x: word) -> word { return x; } + fallback() payable {} + function b(x: word) public returns (word) { return x; } } "#, ); @@ -718,7 +712,7 @@ type UnitAlias = (); contract AliasDispatch { constructor(seed: U) {} - fallback() -> UnitAlias {} + fallback() {} } "#, ); @@ -747,20 +741,12 @@ contract AliasDispatch { fn dispatch_signature_spelling_matches_reference_sigstring_shape() { let (mut db, key) = db_with_main( r#" -import std.{*}; +import * from std; type U = word; contract Signatures { - public function spell( - a: word, - b: (word, bool), - c: memory(string), - d: memory(bytes), - e: bytes32, - f: address, - g: U - ) -> word { + function spell(a: word, b: (word, bool), c: memory, d: memory, e: bytes32, f: address, g: U) public returns (word) { return a; } } @@ -772,14 +758,14 @@ contract Signatures { library: LibraryId::Std, logical_path: vec!["std".to_owned()], }, - "/std/std.solc", + "/std/std.sol", r#" export { string, address(*), bytes, bytes32(*), memory(*) }; -data string; -data address = address(word); -data bytes; -data bytes32 = bytes32(word); -data memory(t) = memory(word); +enum string {} +enum address {address(word)} +enum bytes {} +enum bytes32 {bytes32(word)} +enum memory {memory(word)} "#, ); let file = db.module_files[&key]; @@ -797,10 +783,10 @@ data memory(t) = memory(word); fn bytes4_is_supported_by_the_e136_public_abi_surface() { let (mut db, key) = db_with_main( r#" -import std.{*}; +import * from std; contract Bytes4Echo { - public function echo(value: bytes4) -> bytes4 { return value; } + function echo(value: bytes4) public returns (bytes4) { return value; } } "#, ); @@ -832,14 +818,14 @@ contract Bytes4Echo { fn calldata_array_abi_uses_generic_signature_and_source_adt_json_name() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.Generic; +import * from std.ABIGeneric; -data Operation = Approve(uint256) | Reject(uint256); +enum Operation {Approve(uint256) , Reject(uint256)} contract Batch { - public function count(ops: calldata(array(Operation))) -> uint256 { + function count(ops: calldata>) public returns (uint256) { return uint256(0); } } @@ -874,10 +860,10 @@ contract Batch { fn calldata_tuple_array_json_preserves_components_and_runtime_sigstring_shape() { let (mut db, key) = db_with_main( r#" -import std.{*}; +import * from std; contract Tuples { - public function first(values: calldata(array((uint256, address)))) -> uint256 { + function first(values: calldata>) public returns (uint256) { return uint256(0); } } @@ -906,12 +892,10 @@ contract Tuples { fn nested_calldata_arrays_recurse_in_signatures_and_abi_json() { let (mut db, key) = db_with_main( r#" -import std.{*}; +import * from std; contract NestedArrays { - public function first( - values: calldata(array(calldata(array(uint256)))) - ) -> uint256 { + function first(values: calldata>>>) public returns (uint256) { return uint256(0); } } @@ -944,12 +928,10 @@ contract NestedArrays { fn calldata_arrays_are_rejected_from_nested_output_positions() { let (mut db, key) = db_with_main( r#" -import std.{*}; +import * from std; contract InputOnly { - public function keep( - values: calldata(array(uint256)) - ) -> (uint256, calldata(array(uint256))) { + function keep(values: calldata>) public returns (uint256, calldata>) { return (uint256(0), values); } } @@ -970,7 +952,7 @@ contract InputOnly { diagnostic.code.as_deref() == Some("SC0231") && diagnostic .message - .contains("calldata(array(t)) is input-only") + .contains("calldata> is input-only") && diagnostic.message.contains("no ABIEncode evidence") })); assert!(contract_abi_json(&db, module, contract).is_err()); @@ -980,15 +962,15 @@ contract InputOnly { fn calldata_array_signature_recurses_through_nested_derived_generic_reps() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.Generic; +import * from std.ABIGeneric; -data Inner = Number(uint256) | Account(address); -data Outer = Outer(Inner, bytes32); +enum Inner {Number(uint256) , Account(address)} +enum Outer {Outer(Inner, bytes32)} contract Nested { - public function inspect(values: calldata(array(Outer))) -> uint256 { + function inspect(values: calldata>) public returns (uint256) { return uint256(0); } } @@ -1012,25 +994,25 @@ contract Nested { fn calldata_array_supports_parameterized_and_rejects_recursive_and_manual_generic_adts() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.Generic; +import * from std.ABIGeneric; -data Box(a) = Box(a); -data Node = Node(uint256, Node); +enum Box {Box(a)} +enum Node {Node(uint256, Node)} pragma no-generic-instance-for Manual; -data Manual = Left(uint256) | Right(uint256); -instance Manual:Generic(sum(uint256, uint256)) {} +enum Manual {Left(uint256) , Right(uint256)} +impl Generic> {} contract Rejected { - public function boxed(values: calldata(array(Box(uint256)))) -> uint256 { + function boxed(values: calldata>>) public returns (uint256) { return uint256(0); } - public function recursive(values: calldata(array(Node))) -> uint256 { + function recursive(values: calldata>) public returns (uint256) { return uint256(0); } - public function manual(values: calldata(array(Manual))) -> uint256 { + function manual(values: calldata>) public returns (uint256) { return uint256(0); } } @@ -1046,7 +1028,7 @@ contract Rejected { assert_eq!(surface.methods[0].signature, "boxed(uint256[])"); assert_eq!( surface.methods[0].inputs[0].ty.to_string(), - "Box(uint256)[]" + "Box[]" ); assert!(surface.methods[1..].iter().all(|method| { method.signature.ends_with("()") @@ -1070,16 +1052,16 @@ contract Rejected { fn visible_orphan_generic_instance_rejects_calldata_adt_array_surface() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; -import model.{*}; +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; +import * from model; -instance Payload:Generic(word) {} +impl Generic {} contract C { - public function inspect(values:calldata(array(Payload))) -> uint256 { + function inspect(values:calldata>) public returns (uint256) { return uint256(0); } } @@ -1092,13 +1074,13 @@ contract C { library: LibraryId::Main, logical_path: vec!["model".to_owned()], }, - "/main/model.solc", + "/main/model.sol", r#" -import std.{*}; -import std.Generic.{*}; +import * from std; +import * from std.Generic; export { Payload(*) }; -data Payload = Left(uint256) | Right(uint256); +enum Payload {Left(uint256) , Right(uint256)} "#, ); @@ -1122,11 +1104,11 @@ fn same_named_user_calldata_and_array_types_do_not_gain_abi_meaning() { let module = parse_module( &db, r#" -data array(a) = array(word); -data calldata(a) = calldata(word); +enum array {array(word)} +enum calldata {calldata(word)} contract Fake { - public function inspect(values: calldata(array(word))) -> word { + function inspect(values: calldata>) public returns (word) { return 0; } } @@ -1144,14 +1126,14 @@ contract Fake { fn parameterized_single_constructor_adt_uses_its_generic_rep_in_the_public_abi() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.Generic; +import * from std.ABIGeneric; -data Point(a) = Point(a, bool); +enum Point {Point(a, bool)} contract Shapes { - public function roundtrip(p: Point(uint256)) -> Point(uint256) { return p; } + function roundtrip(p: Point) public returns (Point) { return p; } } "#, ); @@ -1171,32 +1153,32 @@ contract Shapes { ); let method = &surface.methods[0]; assert_eq!(method.signature, "roundtrip(uint256,bool)"); - assert_eq!(method.inputs[0].ty.to_string(), "Point(uint256)"); - assert_eq!(method.outputs[0].ty.to_string(), "Point(uint256)"); + assert_eq!(method.inputs[0].ty.to_string(), "Point"); + assert_eq!(method.outputs[0].ty.to_string(), "Point"); let abi = contract_abi_json(&db, module, contract).expect("direct parameterized ADT ABI"); assert!( - abi.contains("\"internalType\": \"Point(uint256)\""), + abi.contains("\"internalType\": \"Point\""), "{abi}" ); - assert!(abi.contains("\"type\": \"Point(uint256)\""), "{abi}"); + assert!(abi.contains("\"type\": \"Point\""), "{abi}"); } #[test] fn nested_parameterized_adt_instantiations_are_finite_but_recursive_plans_are_rejected() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.Generic; +import * from std.ABIGeneric; -data Box(a) = Box(a); -data Node = Node(Node); +enum Box {Box(a)} +enum Node {Node(Node)} contract Finite { - public function roundtrip(value:Box(Box(uint256))) -> Box(Box(uint256)) { return value; } + function roundtrip(value:Box>) public returns (Box>) { return value; } } contract Recursive { - public function recursive(value:Node) -> Node { return value; } + function recursive(value:Node) public returns (Node) { return value; } } "#, ); @@ -1209,14 +1191,14 @@ contract Recursive { assert_eq!(surface.methods[0].signature, "roundtrip(uint256)"); assert_eq!( surface.methods[0].inputs[0].ty.to_string(), - "Box(Box(uint256))" + "Box>" ); assert_eq!( surface.methods[0].outputs[0].ty.to_string(), - "Box(Box(uint256))" + "Box>" ); let abi = contract_abi_json(&db, module, finite).expect("finite nested ADT ABI"); - assert!(abi.contains("\"type\": \"Box(Box(uint256))\""), "{abi}"); + assert!(abi.contains("\"type\": \"Box>\""), "{abi}"); let recursive = contract_named(&db, module, "Recursive"); let surface = contract_dispatch_surface(&db, module, recursive); @@ -1235,17 +1217,17 @@ contract Recursive { fn phantom_adt_type_arguments_must_be_supported_by_the_derived_abi_context() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.Generic; +import * from std.ABIGeneric; -data Phantom(a) = Phantom(uint256); +enum Phantom {Phantom(uint256)} contract PhantomAbi { - public function take(value:Phantom(mapping(uint256, uint256))) -> uint256 { + function take(value:Phantom uint256)>) public returns (uint256) { return uint256(0); } - public function make() -> Phantom(mapping(uint256, uint256)) { + function make() public returns (Phantom uint256)>) { return Phantom(uint256(0)); } } @@ -1286,16 +1268,16 @@ contract PhantomAbi { fn calldata_arrays_nested_in_derived_adt_outputs_remain_input_only() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.Generic; +import * from std.ABIGeneric; -data Bag = Bag(calldata(array(uint256))); -data Outer = Outer(Bag); +enum Bag {Bag(calldata>)} +enum Outer {Outer(Bag)} contract InvalidOutputs { - public function bag(values:calldata(array(uint256))) -> Bag { return Bag(values); } - public function outer(values:calldata(array(uint256))) -> Outer { + function bag(values:calldata>) public returns (Bag) { return Bag(values); } + function outer(values:calldata>) public returns (Outer) { return Outer(Bag(values)); } } @@ -1319,7 +1301,7 @@ contract InvalidOutputs { diagnostic.code.as_deref() == Some("SC0231") && diagnostic .message - .contains("calldata(array(t)) is input-only") + .contains("calldata> is input-only") }) .count() >= 2, @@ -1333,14 +1315,14 @@ contract InvalidOutputs { fn tuple_typed_constructor_field_uses_the_structural_generic_signature() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.Generic; +import * from std.ABIGeneric; -data Wrap = Wrap((uint256, bool)); +enum Wrap {Wrap((uint256, bool))} contract Shapes { - public function roundtrip(value: Wrap) -> Wrap { return value; } + function roundtrip(value: Wrap) public returns (Wrap) { return value; } } "#, ); @@ -1370,11 +1352,11 @@ fn user_defined_location_name_does_not_make_an_adt_abi_safe() { let module = parse_module( &db, r#" -data memory(a) = memory(word); -data Wrap = Wrap(memory((word, bool))); +enum memory {memory(word)} +enum Wrap {Wrap(memory<(word, bool)>)} contract Shapes { - public function roundtrip(value: Wrap) -> Wrap { return value; } + function roundtrip(value: Wrap) public returns (Wrap) { return value; } } "#, ); @@ -1399,14 +1381,14 @@ contract Shapes { fn direct_dynamic_sum_adt_supports_input_output_and_roundtrip() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.Generic; +import * from std.ABIGeneric; -data D2 = L(uint256) | R(memory(bytes)); +enum D2 {L(uint256) , R(memory)} contract SumRoundtrip { - public function rtD2(x: D2) -> D2 { return x; } + function rtD2(x: D2) public returns (D2) { return x; } } "#, ); @@ -1437,12 +1419,12 @@ contract SumRoundtrip { fn imported_direct_adt_uses_definition_side_abi_derivation() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.dispatch.{*}; -import model.{*}; +import * from std; +import * from std.dispatch; +import * from model; contract Imported { - public function roundtrip(payload:Payload) -> Payload { return payload; } + function roundtrip(payload:Payload) public returns (Payload) { return payload; } } "#, ); @@ -1453,14 +1435,14 @@ contract Imported { library: LibraryId::Main, logical_path: vec!["model".to_owned()], }, - "/main/model.solc", + "/main/model.sol", r#" -import std.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.Generic; +import * from std.ABIGeneric; export { Payload(*) }; -data Payload = Left(uint256) | Right(uint256); +enum Payload {Left(uint256) , Right(uint256)} "#, ); @@ -1489,12 +1471,12 @@ data Payload = Left(uint256) | Right(uint256); fn imported_output_only_adt_requires_definition_side_abi_derivation() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.dispatch.{*}; -import model.{*}; +import * from std; +import * from std.dispatch; +import * from model; contract Imported { - public function make() -> Payload { return Payload.Left(uint256(1)); } + function make() public returns (Payload) { return Payload.Left(uint256(1)); } } "#, ); @@ -1505,13 +1487,13 @@ contract Imported { library: LibraryId::Main, logical_path: vec!["model".to_owned()], }, - "/main/model.solc", + "/main/model.sol", r#" -import std.{*}; -import std.Generic.{*}; +import * from std; +import * from std.Generic; export { Payload(*) }; -data Payload = Left(uint256) | Right(uint256); +enum Payload {Left(uint256) , Right(uint256)} "#, ); @@ -1537,12 +1519,12 @@ data Payload = Left(uint256) | Right(uint256); fn db_with_reexported_abi_adt(api_source: &str) -> (TestDb, ModuleKey) { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.dispatch.{*}; -import api.{Payload}; +import * from std; +import * from std.dispatch; +import {Payload} from api; contract Reexported { - public function roundtrip(payload:Payload) -> Payload { return payload; } + function roundtrip(payload:Payload) public returns (Payload) { return payload; } } "#, ); @@ -1553,14 +1535,14 @@ contract Reexported { library: LibraryId::Main, logical_path: vec!["base".to_owned()], }, - "/main/base.solc", + "/main/base.sol", r#" -import std.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.Generic; +import * from std.ABIGeneric; export { Payload(*) }; -data Payload = Left(uint256) | Right(uint256); +enum Payload {Left(uint256) , Right(uint256)} "#, ); insert_module_source( @@ -1569,7 +1551,7 @@ data Payload = Left(uint256) | Right(uint256); library: LibraryId::Main, logical_path: vec!["api".to_owned()], }, - "/main/api.solc", + "/main/api.sol", api_source, ); (db, key) @@ -1626,18 +1608,18 @@ fn instance_import_in_reexport_module_exposes_definition_side_abi_evidence() { fn visible_orphan_generic_instance_is_rejected_from_constructor_abi() { let (mut db, key) = db_with_main( r#" -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import model.{*}; +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from model; pragma no-generic-instance-for Payload; -instance Payload:Generic(word) {} +impl Generic {} contract C { constructor(payload:Payload) {} - public function roundtrip(payload:Payload) -> Payload { return payload; } + function roundtrip(payload:Payload) public returns (Payload) { return payload; } } "#, ); @@ -1647,7 +1629,7 @@ contract C { library: LibraryId::Std, logical_path: vec!["std".to_owned()], }, - "/std/std.solc", + "/std/std.sol", "", ); insert_module_source( @@ -1656,14 +1638,14 @@ contract C { library: LibraryId::Std, logical_path: vec!["Generic".to_owned()], }, - "/std/Generic.solc", + "/std/Generic.sol", r#" pragma no-patterson-condition; pragma no-bounded-variable-condition; export { Generic }; -forall a rep. class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; +trait Generic { + function from(x:a) returns (rep) ; + function to(x:rep) returns (a) ; } "#, ); @@ -1673,7 +1655,7 @@ forall a rep. class a:Generic(rep) { library: LibraryId::Std, logical_path: vec!["dispatch".to_owned()], }, - "/std/dispatch.solc", + "/std/dispatch.sol", "", ); insert_module_source( @@ -1682,11 +1664,11 @@ forall a rep. class a:Generic(rep) { library: LibraryId::Main, logical_path: vec!["model".to_owned()], }, - "/main/model.solc", + "/main/model.sol", r#" -import std.{*}; +import * from std; export { Payload(*) }; - data Payload = Payload(word, bool); + enum Payload {Payload(word, bool)} "#, ); @@ -1712,10 +1694,10 @@ export { Payload(*) }; fn unsupported_std_leaf_is_not_reinterpreted_as_a_structural_user_adt() { let (mut db, key) = db_with_main( r#" -import std.{*}; +import * from std; contract C { - public function echo(value:byte) -> word { return 0; } + function echo(value:byte) public returns (word) { return 0; } } "#, ); @@ -1741,10 +1723,10 @@ fn abi_like_user_type_names_are_not_treated_as_canonical_types() { let module = parse_module( &db, r#" -data bytes16 = bytes16(word); +enum bytes16 {bytes16(word)} contract C { - public function echo(value:bytes16) -> bytes16 { return value; } + function echo(value:bytes16) public returns (bytes16) { return value; } } "#, ); @@ -1770,10 +1752,10 @@ fn parameterized_abi_type_fails_loudly_and_duplicate_signatures_are_diagnosed() let module = parse_module( &db, r#" -data Mapping(a, b) = Mapping; +enum Mapping {Mapping} contract Store { - public function put(m: Mapping(word, word)) -> word { return 0; } + function put(m: Mapping) public returns (word) { return 0; } } "#, ); @@ -1795,10 +1777,10 @@ contract Store { assert!( diagnostics( r#" -data Mapping(a, b) = Mapping; +enum Mapping {Mapping} contract Store { - public function put(m: Mapping(word, word)) -> word { return 0; } + function put(m: Mapping) public returns (word) { return 0; } } "# ) @@ -1810,8 +1792,8 @@ contract Store { &db, r#" contract Dup { - public function f(x: word) -> word { return x; } - public function f(x: word) -> word { return x; } + function f(x: word) public returns (word) { return x; } + function f(x: word) public returns (word) { return x; } } "#, ); @@ -1836,9 +1818,9 @@ contract Dup { fn different_signatures_with_the_same_selector_are_diagnosed() { let src = r#" contract Collision { - public function collision_8764(x: word) -> () { return (); } - public function collision_99992(x: word) -> () { return (); } - function main() -> () { return (); } + function collision_8764(x: word) public returns () { return (); } + function collision_99992(x: word) public returns () { return (); } + function main() returns () { return (); } } "#; let db = TestDb::default(); @@ -1886,8 +1868,8 @@ fn frontend_desugar_plan_records_if_bool_and_storage_field_hooks() { contract C { flag: word; - public function f() -> word { - if true { + function f() public returns (word) { + if (true) { flag = 1; } else { return flag; @@ -1936,24 +1918,22 @@ fn pre_typeck_desugar_plan_records_tuple_product_shapes_and_origins() { &db, r#" contract C { - seed: (word, bool) = if (true) then (1, true) else (2, false); + seed: (word, bool) = ((true) ? (1, true) : (2, false)); - public function f(x : word, y : bool, z : word) -> (word, bool, word) { + function f(x : word, y : bool, z : word) public returns ((word, bool, word)) { let t : (word, bool, word) = (x, y, z); let b : bool = true; - match b { - | true => return (x, y, z); - | false => return (z, y, x); - } - let w : word = if (y) then x else z; + match (b) { + case true { return (x, y, z); } +case false { return (z, y, x); }} + let w : word = ((y) ? x : z); if (y) { return (w, y, z); } else { return (z, y, w); } - match t { - | (a, b, c) => return (a, b, c); - } + match (t) { + case (a, b, c) { return (a, b, c); }} } } "#, @@ -2117,7 +2097,7 @@ contract C { fn typeck_lowers_tuple_return_type_to_right_nested_product() { let (db, key) = db_with_main( r#" -function triple(x : word, y : bool, z : word) -> (word, bool, word) { +function triple(x : word, y : bool, z : word) returns ((word, bool, word)) { return (x, y, z); } "#, @@ -2162,8 +2142,7 @@ fn frontend_desugar_plan_records_indirect_call_shape_and_evidence() { let module = parse_module( &db, r#" -forall c . c : invokable(pair(word, word), word) => -function apply2(f : c, a : word, b : word) -> word { +function apply2(f : c, a : word, b : word) returns (word) where c : invokable, word> { return f(a, b); } "#, @@ -2203,7 +2182,7 @@ function apply2(f : c, a : word, b : word) -> word { #[test] fn frontend_desugar_plan_records_compose3_indirect_call() { let src = - include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/cases/Compose3.solc"); + include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/cases/Compose3.sol"); assert!(diagnostics(src).is_empty()); let db = TestDb::default(); @@ -2232,7 +2211,7 @@ fn frontend_desugar_plan_records_compose3_indirect_call() { #[test] fn frontend_desugar_plan_records_simple_lambda_pair_arg_call() { let src = - include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/cases/SimpleLambda.solc"); + include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/cases/SimpleLambda.sol"); assert!(diagnostics(src).is_empty()); let db = TestDb::default(); @@ -2264,7 +2243,7 @@ fn frontend_desugar_plan_records_captured_zero_arg_closure_call() { let module = parse_module( &db, r#" -function inc(x : word) -> word { +function inc(x : word) returns (word) { let f = lam () { return x; }; return f(); } @@ -2297,7 +2276,7 @@ fn derived_generic_plan_uses_right_nested_product_rep_for_tree() { let module = parse_module( &db, r#" -data Tree(a) = Leaf | Node(Tree(a), a, Tree(a)); +enum Tree {Leaf , Node(Tree, a, Tree)} "#, ); let tree = adt_named(&db, module, "Tree"); @@ -2332,13 +2311,13 @@ pragma no-patterson-condition; pragma no-bounded-variable-condition; pragma no-generic-instance-for Excluded; -forall a rep . class a:Generic(rep) {} +trait Generic {} -data Eligible = Eligible(word); -data Excluded = Excluded(word); -data Manual = Manual(word); +enum Eligible {Eligible(word)} +enum Excluded {Excluded(word)} +enum Manual {Manual(word)} -instance Manual:Generic(word) {} +impl Generic {} "#, ); let generic = module diff --git a/crates/hir-ty/tests/derived_abi_solver.rs b/crates/hir-ty/tests/derived_abi_solver.rs index dba819f4..9882a1ee 100644 --- a/crates/hir-ty/tests/derived_abi_solver.rs +++ b/crates/hir-ty/tests/derived_abi_solver.rs @@ -23,18 +23,18 @@ pragma no-patterson-condition; pragma no-bounded-variable-condition; pragma no-coverage-condition; -forall a rep . class a:Generic(rep) {} -forall self . class self:ABIDeriving {} -forall self . class self:ABIAttribs {} -forall decoder decoded . class decoder:ABIDecode(decoded) {} -forall reader . class reader:WordReader {} +trait Generic {} +trait ABIDeriving {} +trait ABIAttribs {} +trait ABIDecode {} +trait WordReader {} -data ABIDecoder(ty, reader) = ABIDecoder(reader); -data Reader = Reader; +enum ABIDecoder {ABIDecoder(reader)} +enum Reader {Reader} -instance Reader:WordReader {} -instance word:ABIAttribs {} -instance ABIDecoder(word, Reader):ABIDecode(word) {} +impl WordReader {} +impl ABIAttribs {} +impl ABIDecode,word> {} "#; fn class_def<'db>(db: &'db TestDb, module: Module<'db>, name: &str) -> DefId<'db> { @@ -86,13 +86,7 @@ fn decoder_ty<'db>( #[test] fn derives_parameterized_abi_evidence_once() { let mut db = TestDb::default(); - let key = load_main_source( - &mut db, - &format!( - "{ABI_SOURCE}\n\ - data Box(a) = Box(a);\n" - ), - ); + let key = load_main_source(&mut db, &format!("{ABI_SOURCE}\nenum Box {{Box(a)}}\n")); let module_id = module_id_from_key(&db, &key); let file = db.module_file(module_id).expect("main source file"); let module = parse_file_to_hir(&db, file).module(&db); @@ -171,13 +165,7 @@ fn excludes_recursive_no_generic_and_manual_generic_adts() { let key = load_main_source( &mut db, &format!( - "{ABI_SOURCE}\n\ - pragma no-generic-instance-for Excluded;\n\ - data Eligible = Eligible(word);\n\ - data Excluded = Excluded(word);\n\ - data Manual = Manual(word);\n\ - data Recursive = Recursive(Recursive);\n\ - instance Manual:Generic(word) {{}}\n" + "{ABI_SOURCE}\npragma no-generic-instance-for Excluded;\nenum Eligible {{Eligible(word)}}\nenum Excluded {{Excluded(word)}}\nenum Manual {{Manual(word)}}\nenum Recursive {{Recursive(Recursive)}}\nimpl Generic {{}}\n" ), ); let module_id = module_id_from_key(&db, &key); @@ -314,12 +302,7 @@ fn excludes_contract_local_adts_with_inherited_type_binders() { let mut db = TestDb::default(); let key = load_main_source( &mut db, - &format!( - "{ABI_SOURCE}\n\ - contract C(t) {{\n\ - data Local(a) = Local(a);\n\ - }}\n" - ), + &format!("{ABI_SOURCE}\ncontract C {{\nenum Local {{Local(a)}}\n}}\n"), ); let module_id = module_id_from_key(&db, &key); let file = db.module_file(module_id).expect("main source file"); diff --git a/crates/hir-ty/tests/derived_class_solver.rs b/crates/hir-ty/tests/derived_class_solver.rs index 9cfaf3eb..a885634b 100644 --- a/crates/hir-ty/tests/derived_class_solver.rs +++ b/crates/hir-ty/tests/derived_class_solver.rs @@ -59,9 +59,9 @@ fn derived_clause_constrains_every_declared_type_parameter() { let key = load_main_source( &mut db, r#" -forall a . class a:Marker {} -instance word:Marker {} -#[derive(Marker)] data Phantom(a) = Phantom(word); +trait Marker {} +impl Marker {} +#[derive(Marker)] enum Phantom { Phantom(word) } "#, ); let module_id = module_id_from_key(&db, &key); @@ -112,8 +112,8 @@ fn duplicate_derive_targets_remain_distinct_solver_candidates() { let key = load_main_source( &mut db, r#" -forall a . class a:Marker {} -#[derive(Marker, Marker)] data Target; +trait Marker {} +#[derive(Marker, Marker)] enum Target {} "#, ); let module_id = module_id_from_key(&db, &key); @@ -147,9 +147,9 @@ fn manual_and_derived_instances_report_the_usual_overlap() { let key = load_main_source( &mut db, r#" -forall a . class a:Marker {} -#[derive(Marker)] data Target; -instance Target:Marker {} +trait Marker {} +#[derive(Marker)] enum Target {} +impl Marker {} "#, ); let module = module_id_from_key(&db, &key); @@ -169,9 +169,9 @@ fn generic_contract_capture_does_not_create_an_unconditional_clause() { let key = load_main_source( &mut db, r#" -forall a . class a:Marker {} -contract C(t) { - #[derive(Marker)] data Local = Local(t); +trait Marker {} +contract C { + #[derive(Marker)] enum Local { Local(t) } } "#, ); @@ -194,8 +194,8 @@ fn multi_parameter_class_derive_is_rejected_at_the_declaration() { let key = load_main_source( &mut db, r#" -forall a r . class a:Convert(r) {} -#[derive(Convert)] data Target; +trait Convert {} +#[derive(Convert)] enum Target {} "#, ); let module = module_id_from_key(&db, &key); @@ -207,7 +207,7 @@ forall a r . class a:Convert(r) {} diagnostic, AnyDiagnostic::Typeck(diagnostic) if diagnostic.code.as_deref() == Some(DiagnosticCode::TYPECK_INVALID_DERIVE) - && diagnostic.message.contains("only single-parameter classes") + && diagnostic.message.contains("only single-parameter traits") )) ); } diff --git a/crates/hir-ty/tests/derived_storage_solver.rs b/crates/hir-ty/tests/derived_storage_solver.rs index 018d285a..796143b0 100644 --- a/crates/hir-ty/tests/derived_storage_solver.rs +++ b/crates/hir-ty/tests/derived_storage_solver.rs @@ -22,15 +22,15 @@ pragma no-patterson-condition; pragma no-bounded-variable-condition; pragma no-coverage-condition; -forall a rep . class a:Generic(rep) {} -forall self . class self:StorageDeriving {} -forall self . class self:StorageSize {} -forall slot value . class slot:CanStore(value) {} +trait Generic {} +trait StorageDeriving {} +trait StorageSize {} +trait CanStore {} -data storage(ty) = storage(word); +enum storage {storage(word)} -instance word:StorageSize {} -instance storage(word):CanStore(word) {} +impl StorageSize {} +impl CanStore,word> {} "#; fn class_def<'db>(db: &'db TestDb, module: Module<'db>, name: &str) -> DefId<'db> { @@ -79,7 +79,7 @@ fn derives_parameterized_storage_evidence_once() { let mut db = TestDb::default(); let key = load_main_source( &mut db, - &format!("{STORAGE_SOURCE}\ndata Box(a) = Box(a);\n"), + &format!("{STORAGE_SOURCE}\nenum Box {{Box(a)}}\n"), ); let module_id = module_id_from_key(&db, &key); let file = db.module_file(module_id).expect("main source file"); @@ -157,9 +157,7 @@ fn recursive_storage_derivation_is_a_per_type_skip() { let key = load_main_source( &mut db, &format!( - "{STORAGE_SOURCE}\n\ - data Point = Point(word, word);\n\ - data Recursive = Recursive(Recursive);\n" + "{STORAGE_SOURCE}\nenum Point {{Point(word, word)}}\nenum Recursive {{Recursive(Recursive)}}\n" ), ); let module_id = module_id_from_key(&db, &key); diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-mapping-field-fail/main.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-mapping-field-fail/main.sol new file mode 100644 index 00000000..15856de8 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-mapping-field-fail/main.sol @@ -0,0 +1,21 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.StorageGeneric; + +// A mapping cannot be a field of a data type. std only provides +// `storage(mapping(k,v)) : CanStore(storage(mapping(k,v)))` — the slot handle +// loads back as a handle, never as a mapping value — so the structural CanStore +// decomposition of `Wrapper` asks for `storage(mapping(uint256,uint256)) : +// CanStore(mapping(uint256,uint256))`, which does not exist. +// +// (Even if it did, that instance's store/load are `unimplemented()`: copying a +// mapping is not a meaningful storage operation.) + +enum Wrapper { Wrapper(mapping(uint256 => uint256)) } + +contract C { + w : Wrapper; + + constructor() {} +} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-mapping-field-fail/main.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-mapping-field-fail/main.solc deleted file mode 100644 index 15f842a6..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-mapping-field-fail/main.solc +++ /dev/null @@ -1,21 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -// A mapping cannot be a field of a data type. std only provides -// `storage(mapping(k,v)) : CanStore(storage(mapping(k,v)))` — the slot handle -// loads back as a handle, never as a mapping value — so the structural CanStore -// decomposition of `Wrapper` asks for `storage(mapping(uint256,uint256)) : -// CanStore(mapping(uint256,uint256))`, which does not exist. -// -// (Even if it did, that instance's store/load are `unimplemented()`: copying a -// mapping is not a meaningful storage operation.) - -data Wrapper = Wrapper(mapping(uint256, uint256)); - -contract C { - w : Wrapper; - - constructor() {} -} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-recursive-fail/main.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-recursive-fail/main.sol new file mode 100644 index 00000000..0895dd9a --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-recursive-fail/main.sol @@ -0,0 +1,22 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.StorageGeneric; + +// A recursive data type has no bounded slot footprint, so DeriveGeneric +// (isRecursiveData) deliberately skips deriving StorageSize and +// storage(T):CanStore(T) for it. Using one as a contract field must therefore +// fail: the field's CanStore obligation has no instance. +// +// The failure surfaces at the use site (the field assignment), not at +// derivation time, which is the design stated in DeriveGeneric. + +enum IntList { Nil, Cons(uint256, IntList) } + +contract C { + xs : IntList; + + constructor() { + xs = IntList.Nil; + } +} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-recursive-fail/main.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-recursive-fail/main.solc deleted file mode 100644 index d7739a57..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-recursive-fail/main.solc +++ /dev/null @@ -1,22 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -// A recursive data type has no bounded slot footprint, so DeriveGeneric -// (isRecursiveData) deliberately skips deriving StorageSize and -// storage(T):CanStore(T) for it. Using one as a contract field must therefore -// fail: the field's CanStore obligation has no instance. -// -// The failure surfaces at the use site (the field assignment), not at -// derivation time, which is the design stated in DeriveGeneric. - -data IntList = Nil | Cons(uint256, IntList); - -contract C { - xs : IntList; - - constructor() { - xs = IntList.Nil; - } -} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-recursive-ok/main.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-recursive-ok/main.sol new file mode 100644 index 00000000..cacf5e80 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-recursive-ok/main.sol @@ -0,0 +1,34 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.StorageGeneric; + +// The counterpart of storage-adt-recursive-fail.sol: skipping storage +// derivation for a recursive type is a SKIP, not a hard error. The type still +// gets its Generic instance and remains usable everywhere except storage. + +enum IntList { Nil, Cons(uint256, IntList) } + +function len(xs: IntList) returns (uint256) { + match (xs) { +case IntList.Nil { +return uint256(0); +} +case IntList.Cons(_, r) { +return uint256(1) + len(r); +} +} +} + +// A non-recursive neighbour in the same module still gets its storage +// instances, so the skip is per-type rather than per-module. +enum Point { Point(uint256, uint256) } + +contract C { + p : Point; + + constructor() { + p = Point(uint256(1), uint256(2)); + assert(StorageSize.size(@Point) == 2); + } +} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-recursive-ok/main.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-recursive-ok/main.solc deleted file mode 100644 index e28974fc..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-adt-recursive-ok/main.solc +++ /dev/null @@ -1,30 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -// The counterpart of storage-adt-recursive-fail.solc: skipping storage -// derivation for a recursive type is a SKIP, not a hard error. The type still -// gets its Generic instance and remains usable everywhere except storage. - -data IntList = Nil | Cons(uint256, IntList); - -function len(xs : IntList) -> uint256 { - match xs { - | IntList.Nil => return uint256(0); - | IntList.Cons(_, r) => return uint256(1) + len(r); - } -} - -// A non-recursive neighbour in the same module still gets its storage -// instances, so the skip is per-type rather than per-module. -data Point = Point(uint256, uint256); - -contract C { - p : Point; - - constructor() { - p = Point(uint256(1), uint256(2)); - assert(StorageSize.size(Proxy : Proxy(Point)) == 2); - } -} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-active-ok/main.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-active-ok/main.sol new file mode 100644 index 00000000..9dc2258c --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-active-ok/main.sol @@ -0,0 +1,8 @@ +import * from std; +import {Box} from types; + +function touchBox() { + let size : word = StorageSize.size(@Box); + let slot : storage> = storage(0); + let value : Box = CanStore.load(slot); +} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-active-ok/main.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-active-ok/main.solc deleted file mode 100644 index 48e697d7..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-active-ok/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -import std.{*}; -import types.{Box}; - -function touchBox() -> () { - let size : word = StorageSize.size(Proxy : Proxy(Box(uint256))); - let value : Box(uint256) = CanStore.load(storage(0) : storage(Box(uint256))); -} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-active-ok/types.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-active-ok/types.sol new file mode 100644 index 00000000..cf198a3a --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-active-ok/types.sol @@ -0,0 +1,7 @@ +import * from std; +import * from std.Generic; +import * from std.StorageGeneric; + +export { Box(*) }; + +enum Box { Box(a) } diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-active-ok/types.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-active-ok/types.solc deleted file mode 100644 index f20e7e56..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-active-ok/types.solc +++ /dev/null @@ -1,7 +0,0 @@ -import std.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -export { Box(*) }; - -data Box(a) = Box(a); diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-inactive-fail/main.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-inactive-fail/main.sol new file mode 100644 index 00000000..9dc2258c --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-inactive-fail/main.sol @@ -0,0 +1,8 @@ +import * from std; +import {Box} from types; + +function touchBox() { + let size : word = StorageSize.size(@Box); + let slot : storage> = storage(0); + let value : Box = CanStore.load(slot); +} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-inactive-fail/main.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-inactive-fail/main.solc deleted file mode 100644 index 48e697d7..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-inactive-fail/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -import std.{*}; -import types.{Box}; - -function touchBox() -> () { - let size : word = StorageSize.size(Proxy : Proxy(Box(uint256))); - let value : Box(uint256) = CanStore.load(storage(0) : storage(Box(uint256))); -} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-inactive-fail/types.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-inactive-fail/types.sol new file mode 100644 index 00000000..57b5be64 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-inactive-fail/types.sol @@ -0,0 +1,7 @@ +import * from std; +import * from std.Generic; + +export { Box(*) }; + +// StorageGeneric is deliberately not visible in this defining module. +enum Box { Box(a) } diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-inactive-fail/types.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-inactive-fail/types.solc deleted file mode 100644 index bbaa269e..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-inactive-fail/types.solc +++ /dev/null @@ -1,7 +0,0 @@ -import std.{*}; -import std.Generic.{*}; - -export { Box(*) }; - -// StorageGeneric is deliberately not visible in this defining module. -data Box(a) = Box(a); diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-qualified-ok/main.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-qualified-ok/main.sol new file mode 100644 index 00000000..5bf2469c --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-qualified-ok/main.sol @@ -0,0 +1,8 @@ +import * from std; +import types; + +function touchBox() { + let size : word = StorageSize.size(@types.Box); + let slot : storage> = storage(0); + let value : types.Box = CanStore.load(slot); +} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-qualified-ok/main.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-qualified-ok/main.solc deleted file mode 100644 index e1bc16bf..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-qualified-ok/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -import std.{*}; -import types; - -function touchBox() -> () { - let size : word = StorageSize.size(Proxy : Proxy(types.Box(uint256))); - let value : types.Box(uint256) = CanStore.load( - storage(0) : storage(types.Box(uint256)) - ); -} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-qualified-ok/types.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-qualified-ok/types.sol new file mode 100644 index 00000000..cf198a3a --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-qualified-ok/types.sol @@ -0,0 +1,7 @@ +import * from std; +import * from std.Generic; +import * from std.StorageGeneric; + +export { Box(*) }; + +enum Box { Box(a) } diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-qualified-ok/types.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-qualified-ok/types.solc deleted file mode 100644 index f20e7e56..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-body-only-qualified-ok/types.solc +++ /dev/null @@ -1,7 +0,0 @@ -import std.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -export { Box(*) }; - -data Box(a) = Box(a); diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-builtins-unused-ok/main.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-builtins-unused-ok/main.sol new file mode 100644 index 00000000..e1a5f9e6 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-builtins-unused-ok/main.sol @@ -0,0 +1,17 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.StorageGeneric; + +// Declaration-side validation must use the value each storage handle actually +// loads. Mappings and arrays load handles, strings and bytes load memory values, +// and scalar fields load their declared value. +contract C { + n : uint256; + m : mapping(uint256 => uint256); + a : array; + s : string; + b : bytes; + + constructor() {} +} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-builtins-unused-ok/main.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-builtins-unused-ok/main.solc deleted file mode 100644 index df44106f..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-builtins-unused-ok/main.solc +++ /dev/null @@ -1,17 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -// Declaration-side validation must use the value each storage handle actually -// loads. Mappings and arrays load handles, strings and bytes load memory values, -// and scalar fields load their declared value. -contract C { - n : uint256; - m : mapping(uint256, uint256); - a : array(uint256); - s : string; - b : bytes; - - constructor() {} -} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-active-no-marker-ok/main.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-active-no-marker-ok/main.sol new file mode 100644 index 00000000..e0ed6ce7 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-active-no-marker-ok/main.sol @@ -0,0 +1,12 @@ +import * from std; +import * from std.dispatch; +import {Box} from types; + +// The concrete storage instance belongs to Box's definition module. A +// consumer only needs the ordinary storage classes; it need not import +// Generic, StorageDeriving, or the structural implementation instances. +contract C { + value : Box; + + constructor() {} +} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-active-no-marker-ok/main.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-active-no-marker-ok/main.solc deleted file mode 100644 index 5ecce205..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-active-no-marker-ok/main.solc +++ /dev/null @@ -1,12 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import types.{Box}; - -// The concrete storage instance belongs to Box's definition module. A -// consumer only needs the ordinary storage classes; it need not import -// Generic, StorageDeriving, or the structural implementation instances. -contract C { - value : Box(uint256); - - constructor() {} -} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-active-no-marker-ok/types.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-active-no-marker-ok/types.sol new file mode 100644 index 00000000..cf198a3a --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-active-no-marker-ok/types.sol @@ -0,0 +1,7 @@ +import * from std; +import * from std.Generic; +import * from std.StorageGeneric; + +export { Box(*) }; + +enum Box { Box(a) } diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-active-no-marker-ok/types.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-active-no-marker-ok/types.solc deleted file mode 100644 index f20e7e56..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-active-no-marker-ok/types.solc +++ /dev/null @@ -1,7 +0,0 @@ -import std.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -export { Box(*) }; - -data Box(a) = Box(a); diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-inactive-no-marker-fail/main.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-inactive-no-marker-fail/main.sol new file mode 100644 index 00000000..5a25057b --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-inactive-no-marker-fail/main.sol @@ -0,0 +1,11 @@ +import * from std; +import * from std.dispatch; +import {Box} from types; + +// Importing an ordinary Generic ADT does not retroactively create storage +// instances when its definition module never enabled StorageGeneric. +contract C { + value : Box; + + constructor() {} +} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-inactive-no-marker-fail/main.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-inactive-no-marker-fail/main.solc deleted file mode 100644 index eea4b1e5..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-inactive-no-marker-fail/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import types.{Box}; - -// Importing an ordinary Generic ADT does not retroactively create storage -// instances when its definition module never enabled StorageGeneric. -contract C { - value : Box(uint256); - - constructor() {} -} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-inactive-no-marker-fail/types.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-inactive-no-marker-fail/types.sol new file mode 100644 index 00000000..b1f85bfc --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-inactive-no-marker-fail/types.sol @@ -0,0 +1,6 @@ +import * from std; +import * from std.Generic; + +export { Box(*) }; + +enum Box { Box(a) } diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-inactive-no-marker-fail/types.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-inactive-no-marker-fail/types.solc deleted file mode 100644 index 88af9f6f..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-inactive-no-marker-fail/types.solc +++ /dev/null @@ -1,6 +0,0 @@ -import std.{*}; -import std.Generic.{*}; - -export { Box(*) }; - -data Box(a) = Box(a); diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-invalid-no-marker-fail/main.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-invalid-no-marker-fail/main.sol new file mode 100644 index 00000000..ae40fdb5 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-invalid-no-marker-fail/main.sol @@ -0,0 +1,11 @@ +import * from std; +import * from std.dispatch; +import {Wrapper} from types; + +// Selecting Wrapper's derived instance must validate its structural body in +// the definition module even though this consumer does not import the marker. +contract C { + value : Wrapper; + + constructor() {} +} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-invalid-no-marker-fail/main.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-invalid-no-marker-fail/main.solc deleted file mode 100644 index 23cf325c..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-invalid-no-marker-fail/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import types.{Wrapper}; - -// Selecting Wrapper's derived instance must validate its structural body in -// the definition module even though this consumer does not import the marker. -contract C { - value : Wrapper; - - constructor() {} -} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-invalid-no-marker-fail/types.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-invalid-no-marker-fail/types.sol new file mode 100644 index 00000000..d782e003 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-invalid-no-marker-fail/types.sol @@ -0,0 +1,7 @@ +import * from std; +import * from std.Generic; +import * from std.StorageGeneric; + +export { Wrapper(*) }; + +enum Wrapper { Wrapper(mapping(uint256 => uint256)) } diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-invalid-no-marker-fail/types.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-invalid-no-marker-fail/types.solc deleted file mode 100644 index 43c3b895..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-imported-invalid-no-marker-fail/types.solc +++ /dev/null @@ -1,7 +0,0 @@ -import std.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -export { Wrapper(*) }; - -data Wrapper = Wrapper(mapping(uint256, uint256)); diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/api.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/api.sol similarity index 100% rename from crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/api.solc rename to crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/api.sol diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/base.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/base.sol new file mode 100644 index 00000000..e7c795f5 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/base.sol @@ -0,0 +1,7 @@ +import * from std; +import * from std.Generic; +import * from std.StorageGeneric; + +export { Inner(*) }; + +enum Inner { Inner(uint256) } diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/base.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/base.solc deleted file mode 100644 index 3bb1914a..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/base.solc +++ /dev/null @@ -1,7 +0,0 @@ -import std.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -export { Inner(*) }; - -data Inner = Inner(uint256); diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/main.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/main.sol new file mode 100644 index 00000000..748b8b73 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/main.sol @@ -0,0 +1,9 @@ +import * from std; +import * from std.dispatch; +import {Outer} from outer; + +contract C { + value : Outer; + + constructor() {} +} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/main.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/main.solc deleted file mode 100644 index d924c4be..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import outer.{Outer}; - -contract C { - value : Outer; - - constructor() {} -} diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/outer.sol b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/outer.sol new file mode 100644 index 00000000..44c713ac --- /dev/null +++ b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/outer.sol @@ -0,0 +1,8 @@ +import * from std; +import * from std.Generic; +import * from std.StorageGeneric; +import {Inner} from api; + +export { Outer(*) }; + +enum Outer { Outer(Inner) } diff --git a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/outer.solc b/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/outer.solc deleted file mode 100644 index b9352adf..00000000 --- a/crates/hir-ty/tests/fixtures/derived_storage_frontend/storage-nested-reexport-invalid-fail/outer.solc +++ /dev/null @@ -1,8 +0,0 @@ -import std.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; -import api.{Inner}; - -export { Outer(*) }; - -data Outer = Outer(Inner); diff --git a/crates/hir-ty/tests/fixtures/ok/comptime/class_method_runtime_body_deferred/main.sol b/crates/hir-ty/tests/fixtures/ok/comptime/class_method_runtime_body_deferred/main.sol new file mode 100644 index 00000000..6749c56f --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/comptime/class_method_runtime_body_deferred/main.sol @@ -0,0 +1,28 @@ +enum Box { Box(word) } + +trait Scale { + function scale(comptime factor: word, comptime x: a) returns (comptime) ; +} + +impl Scale { + function scale(comptime factor: word, comptime x: word) returns (comptime) { + return x; + } +} + +impl Scale { + function scale(comptime factor: word, comptime x: Box) returns (comptime) { + let y : word; + assembly { + y := sload(0) + } + return Box(y); + } +} + +contract C { + function main() returns (word) { + let a : comptime = Scale.scale(1, 2); + return a; + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/comptime/class_method_runtime_body_deferred/main.solc b/crates/hir-ty/tests/fixtures/ok/comptime/class_method_runtime_body_deferred/main.solc deleted file mode 100644 index f2040319..00000000 --- a/crates/hir-ty/tests/fixtures/ok/comptime/class_method_runtime_body_deferred/main.solc +++ /dev/null @@ -1,28 +0,0 @@ -data Box = Box(word); - -forall a. class a : Scale { - function scale(comptime factor : word, comptime x : a) -> comptime a; -} - -instance word : Scale { - function scale(comptime factor : word, comptime x : word) -> comptime word { - return x; - } -} - -instance Box : Scale { - function scale(comptime factor : word, comptime x : Box) -> comptime Box { - let y : word; - assembly { - y := sload(0) - } - return Box(y); - } -} - -contract C { - function main() -> word { - let a : comptime word = Scale.scale(1, 2); - return a; - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/comptime/frontend_call_classification/main.sol b/crates/hir-ty/tests/fixtures/ok/comptime/frontend_call_classification/main.sol new file mode 100644 index 00000000..724842e6 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/comptime/frontend_call_classification/main.sol @@ -0,0 +1,8 @@ +function id(x: word) returns (word) { + return x; +} + +function id_ct(x: word) returns (comptime) { + let y : comptime = id(x); + return id(x); +} diff --git a/crates/hir-ty/tests/fixtures/ok/comptime/frontend_call_classification/main.solc b/crates/hir-ty/tests/fixtures/ok/comptime/frontend_call_classification/main.solc deleted file mode 100644 index 68c48b63..00000000 --- a/crates/hir-ty/tests/fixtures/ok/comptime/frontend_call_classification/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -function id(x: word) -> word { - return x; -} - -function id_ct(x: word) -> comptime word { - let y : comptime word = id(x); - return id(x); -} diff --git a/crates/hir-ty/tests/fixtures/ok/comptime/polymorphic_param_defers_runtime_arg/main.sol b/crates/hir-ty/tests/fixtures/ok/comptime/polymorphic_param_defers_runtime_arg/main.sol new file mode 100644 index 00000000..bbc03c38 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/comptime/polymorphic_param_defers_runtime_arg/main.sol @@ -0,0 +1,7 @@ +trait Wrap { + function unwrap(comptime x: t) returns (comptime) ; +} + +function process(z: t) returns (word) where t: Wrap { + return Wrap.unwrap(z); +} diff --git a/crates/hir-ty/tests/fixtures/ok/comptime/polymorphic_param_defers_runtime_arg/main.solc b/crates/hir-ty/tests/fixtures/ok/comptime/polymorphic_param_defers_runtime_arg/main.solc deleted file mode 100644 index 248d013f..00000000 --- a/crates/hir-ty/tests/fixtures/ok/comptime/polymorphic_param_defers_runtime_arg/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -forall t. class t : Wrap { - function unwrap(comptime x : t) -> comptime word; -} - -forall t. t:Wrap => function process(z : t) -> word { - return Wrap.unwrap(z); -} diff --git a/crates/hir-ty/tests/fixtures/ok/comptime/return_params/main.sol b/crates/hir-ty/tests/fixtures/ok/comptime/return_params/main.sol new file mode 100644 index 00000000..074f6a04 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/comptime/return_params/main.sol @@ -0,0 +1,3 @@ +function id_ct(x: word) returns (comptime) { + return x; +} diff --git a/crates/hir-ty/tests/fixtures/ok/comptime/return_params/main.solc b/crates/hir-ty/tests/fixtures/ok/comptime/return_params/main.solc deleted file mode 100644 index 2af25936..00000000 --- a/crates/hir-ty/tests/fixtures/ok/comptime/return_params/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -function id_ct(x: word) -> comptime word { - return x; -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/p4-default-instance/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/p4-default-instance/main.sol new file mode 100644 index 00000000..e24cf383 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/p4-default-instance/main.sol @@ -0,0 +1,15 @@ +enum Name { Name(word) } + +trait Token { + function token(x: a) returns (word) ; +} + +default impl Token { + function token(x: a) returns (word) { + return 0; + } +} + +function main() returns (word) { + return Token.token(Name.Name(2)); +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/p4-default-instance/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/p4-default-instance/main.solc deleted file mode 100644 index cd383e3a..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/p4-default-instance/main.solc +++ /dev/null @@ -1,15 +0,0 @@ -data Name = Name(word); - -forall a . class a:Token { - function token(x:a) -> word; -} - -forall a . default instance a:Token { - function token(x:a) -> word { - return 0; - } -} - -function main() -> word { - return Token.token(Name.Name(2)); -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/p4-local-instance/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/p4-local-instance/main.sol new file mode 100644 index 00000000..0a168834 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/p4-local-instance/main.sol @@ -0,0 +1,19 @@ +enum Wrap { Wrap(word) } + +trait Boxed { + function unbox(x: a) returns (word) ; +} + +impl Boxed { + function unbox(x: Wrap) returns (word) { + match (x) { +case Wrap.Wrap(w) { +return w; +} +} + } +} + +function main() returns (word) { + return Boxed.unbox(Wrap.Wrap(1)); +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/p4-local-instance/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/p4-local-instance/main.solc deleted file mode 100644 index ed3a3253..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/p4-local-instance/main.solc +++ /dev/null @@ -1,17 +0,0 @@ -data Wrap = Wrap(word); - -forall a . class a:Boxed { - function unbox(x:a) -> word; -} - -instance Wrap:Boxed { - function unbox(x:Wrap) -> word { - match x { - | Wrap.Wrap(w) => return w; - } - } -} - -function main() -> word { - return Boxed.unbox(Wrap.Wrap(1)); -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-answer-reuse/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-answer-reuse/main.sol new file mode 100644 index 00000000..13f3124a --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-answer-reuse/main.sol @@ -0,0 +1,16 @@ +pragma no-patterson-condition Derived; + +trait Seed {} +trait Derived {} + +impl Seed {} + +impl Derived where a: Seed {} + +function needsDerivedTwice(x: a) where a: Derived, a: Derived { + return (); +} + +function main() { + return needsDerivedTwice(0); +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-answer-reuse/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-answer-reuse/main.solc deleted file mode 100644 index d815c67e..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-answer-reuse/main.solc +++ /dev/null @@ -1,16 +0,0 @@ -pragma no-patterson-condition Derived; - -forall a . class a:Seed {} -forall a . class a:Derived {} - -instance word:Seed {} - -forall a . a:Seed => instance a:Derived {} - -forall a . a:Derived, a:Derived => function needsDerivedTwice(x:a) -> () { - return (); -} - -function main() -> () { - return needsDerivedTwice(0); -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-given-order/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-given-order/main.sol new file mode 100644 index 00000000..4d688c8f --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-given-order/main.sol @@ -0,0 +1,23 @@ +pragma no-patterson-condition C; + +trait A {} +trait B {} +trait C {} + +impl C where a: A, a: B {} + +function needsC(x: a) where a: C { + return (); +} + +function fromAB(x: a) where a: A, a: B { + return needsC(x); +} + +function fromBA(x: a) where a: B, a: A { + return needsC(x); +} + +function main() { + return (); +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-given-order/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-given-order/main.solc deleted file mode 100644 index 689dee14..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-given-order/main.solc +++ /dev/null @@ -1,23 +0,0 @@ -pragma no-patterson-condition C; - -forall a . class a:A {} -forall a . class a:B {} -forall a . class a:C {} - -forall a . a:A, a:B => instance a:C {} - -forall a . a:C => function needsC(x:a) -> () { - return (); -} - -forall a . a:A, a:B => function fromAB(x:a) -> () { - return needsC(x); -} - -forall a . a:B, a:A => function fromBA(x:a) -> () { - return needsC(x); -} - -function main() -> () { - return (); -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-residual-given/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-residual-given/main.sol new file mode 100644 index 00000000..02beed75 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-residual-given/main.sol @@ -0,0 +1,18 @@ +pragma no-patterson-condition Wanted; + +trait Known {} +trait Wanted {} + +impl Wanted where a: Known {} + +function needsWanted(x: a) where a: Wanted { + return (); +} + +function passKnown(x: a) where a: Known { + return needsWanted(x); +} + +function main() { + return (); +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-residual-given/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-residual-given/main.solc deleted file mode 100644 index 29daa886..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/local-class/tabled-residual-given/main.solc +++ /dev/null @@ -1,18 +0,0 @@ -pragma no-patterson-condition Wanted; - -forall a . class a:Known {} -forall a . class a:Wanted {} - -forall a . a:Known => instance a:Wanted {} - -forall a . a:Wanted => function needsWanted(x:a) -> () { - return (); -} - -forall a . a:Known => function passKnown(x:a) -> () { - return needsWanted(x); -} - -function main() -> () { - return (); -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/00answer/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/spec/00answer/main.sol new file mode 100644 index 00000000..48c89978 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/spec/00answer/main.sol @@ -0,0 +1,5 @@ +contract Answer { + function main() public returns (word) { + return 42; + } +} \ No newline at end of file diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/00answer/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/spec/00answer/main.solc deleted file mode 100644 index ba55aa25..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/spec/00answer/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -contract Answer { - public function main() -> word { - return 42; - } -} \ No newline at end of file diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/021not/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/spec/021not/main.sol new file mode 100644 index 00000000..aeeb720c --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/spec/021not/main.sol @@ -0,0 +1,29 @@ +contract Not { + enum Bool { False, True } + + function main() public returns (word) { + return fromBool(bnot(Bool.False)); + } + + function fromBool(b: Bool) public returns (word) { + match (b) { +case Bool.False { +return 0; +} +case Bool.True { +return 1; +} +} + } + + function bnot(b: Bool) public returns (Bool) { + match (b) { +case Bool.False { +return Bool.True; +} +case Bool.True { +return Bool.False; +} +} + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/021not/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/spec/021not/main.solc deleted file mode 100644 index df5b9377..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/spec/021not/main.solc +++ /dev/null @@ -1,21 +0,0 @@ -contract Not { - data Bool = False | True; - - public function main() -> word { - return fromBool(bnot(Bool.False)); - } - - public function fromBool(b : Bool) -> word { - match(b) { - | Bool.False => return 0; - | Bool.True => return 1; - } - } - - public function bnot(b : Bool) -> Bool { - match b { - | Bool.False => return Bool.True; - | Bool.True => return Bool.False; - } - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/022add/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/spec/022add/main.sol new file mode 100644 index 00000000..85258483 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/spec/022add/main.sol @@ -0,0 +1,13 @@ +function add(x: word, y: word) returns (word) { + let res: word; + assembly { + res := add(x, y) + } + return res; +} + +contract Add1 { + function main() public returns (word) { + return add(40, 2); + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/022add/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/spec/022add/main.solc deleted file mode 100644 index 3ef65f35..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/spec/022add/main.solc +++ /dev/null @@ -1,13 +0,0 @@ -function add(x : word, y : word) -> word { - let res: word; - assembly { - res := add(x, y) - } - return res; -} - -contract Add1 { - public function main() -> word { - return add(40, 2); - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/024arith/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/spec/024arith/main.sol new file mode 100644 index 00000000..4043007d --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/spec/024arith/main.sol @@ -0,0 +1,64 @@ + + +function add(x: word, y: word) returns (word) { + let res: word; + assembly { + res := add(x, y) + } + return res; +} + +function sub(x: word, y: word) returns (word) { + let res: word; + assembly { + res := sub(x, y) + } + return res; +} + +function div(x: word, y: word) returns (word) { + let res: word; + assembly { + res := div(x, y) + } + return res; +} + +function sdiv(x: word, y: word) returns (word) { + let res: word; + assembly { + res := sdiv(x, y) + } + return res; +} + +function mod(x: word, y: word) returns (word) { + let res: word; + assembly { + res := mod(x, y) + } + return res; +} + +function smod(x: word, y: word) returns (word) { + let res: word; + assembly { + res := smod(x, y) + } + return res; +} + +function exp(x: word, y: word) returns (word) { + let res: word; + assembly { + res := exp(x, y) + } + return res; +} + + +contract Arith { + function main() public returns (word) { + return add(mod(sub(div(exp(2,18),4), 1), 16), 27); + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/024arith/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/spec/024arith/main.solc deleted file mode 100644 index a79ab49c..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/spec/024arith/main.solc +++ /dev/null @@ -1,64 +0,0 @@ - - -function add(x : word, y : word) -> word { - let res: word; - assembly { - res := add(x, y) - } - return res; -} - -function sub(x : word, y : word) -> word { - let res: word; - assembly { - res := sub(x, y) - } - return res; -} - -function div(x : word, y: word) -> word { - let res: word; - assembly { - res := div(x, y) - } - return res; -} - -function sdiv(x : word, y: word) -> word { - let res: word; - assembly { - res := sdiv(x, y) - } - return res; -} - -function mod(x : word, y: word) -> word { - let res: word; - assembly { - res := mod(x, y) - } - return res; -} - -function smod(x : word, y: word) -> word { - let res: word; - assembly { - res := smod(x, y) - } - return res; -} - -function exp(x : word, y: word) -> word { - let res: word; - assembly { - res := exp(x, y) - } - return res; -} - - -contract Arith { - public function main() -> word { - return add(mod(sub(div(exp(2,18),4), 1), 16), 27); - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/031maybe/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/spec/031maybe/main.sol new file mode 100644 index 00000000..379bb9a1 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/spec/031maybe/main.sol @@ -0,0 +1,20 @@ +contract Option { + enum Option { None, Some(a) } + + function just(x: word) public returns (Option) { return Option.Some(x); } + + function maybe(n: word, o: Option) public returns (word) { + match (o) { +case Option.None { +return n; +} +case Option.Some(x) { +return x; +} +} + } + + function main() public returns (word) { + return maybe(0, Option.Some(42)); + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/031maybe/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/spec/031maybe/main.solc deleted file mode 100644 index d1de1135..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/spec/031maybe/main.solc +++ /dev/null @@ -1,16 +0,0 @@ -contract Option { - data Option(a) = None | Some(a); - - public function just(x : word) -> Option(word) { return Option.Some(x); } - - public function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.None => return n; - | Option.Some(x) => return x; - } - } - - public function main() -> word { - return maybe(0, Option.Some(42)); - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/036wildcard/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/spec/036wildcard/main.sol new file mode 100644 index 00000000..635bfe52 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/spec/036wildcard/main.sol @@ -0,0 +1,18 @@ +contract Option { + enum Option { None, Some(a) } + + function maybe(n: word, o: Option) public returns (word) { + match (o) { +case Option.Some(x) { +return x; +} +default { +return n; +} +} + } + + function main() public returns (word) { + return maybe(7, Option.None); + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/036wildcard/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/spec/036wildcard/main.solc deleted file mode 100644 index 1e83f44f..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/spec/036wildcard/main.solc +++ /dev/null @@ -1,14 +0,0 @@ -contract Option { - data Option(a) = None | Some(a); - - public function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.Some(x) => return x; - | _ => return n; - } - } - - public function main() -> word { - return maybe(7, Option.None); - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/041pair/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/spec/041pair/main.sol new file mode 100644 index 00000000..f0f1e963 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/spec/041pair/main.sol @@ -0,0 +1,14 @@ +contract Pair { + + function fst(p: (word, word)) public returns (word) { + match (p) { +case (a,b) { +return a; +} +} + } + + function main() public returns (word) { + return fst((1,0)); + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/041pair/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/spec/041pair/main.solc deleted file mode 100644 index b8180a0a..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/spec/041pair/main.solc +++ /dev/null @@ -1,12 +0,0 @@ -contract Pair { - - public function fst(p : (word, word)) -> word { - match p { - | (a,b) => return a; - } - } - - public function main() -> word { - return fst((1,0)); - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/042triple/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/spec/042triple/main.sol new file mode 100644 index 00000000..d2013eca --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/spec/042triple/main.sol @@ -0,0 +1,14 @@ +contract Triple { + + function asel(t: (word, word, word)) public returns (word) { + match (t) { +case (a,b,c) { +return c; +} +} + } + + function main() public returns (word) { + return asel((1,21,42)); + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/042triple/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/spec/042triple/main.solc deleted file mode 100644 index 10c3724c..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/spec/042triple/main.solc +++ /dev/null @@ -1,12 +0,0 @@ -contract Triple { - - public function asel(t : (word, word, word)) -> word { - match t { - | (a,b,c) => return c; - } - } - - public function main() -> word { - return asel((1,21,42)); - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/047rgb/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/spec/047rgb/main.sol new file mode 100644 index 00000000..87529c81 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/spec/047rgb/main.sol @@ -0,0 +1,16 @@ +contract RGB { + enum Color { R, G, B } + function main() public returns (word) { + match (Color.B) { +case Color.R { +return 4; +} +case Color.G { +return 2; +} +case Color.B { +return 42; +} +} + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/047rgb/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/spec/047rgb/main.solc deleted file mode 100644 index 576182e5..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/spec/047rgb/main.solc +++ /dev/null @@ -1,10 +0,0 @@ -contract RGB { - data Color = R | G | B; - public function main() -> word { - match Color.B { - | Color.R => return 4; - | Color.G => return 2; - | Color.B => return 42; - } - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/048rgb2/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/spec/048rgb2/main.sol new file mode 100644 index 00000000..063a823e --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/spec/048rgb2/main.sol @@ -0,0 +1,19 @@ +contract RGB { + enum Color { R, G, B } + + function fromEnum(c: Color) public returns (word) { + match (c) { +case Color.R { +return 4; +} +case Color.G { +return 2; +} +case Color.B { +return 42; +} +} + } + + function main() public returns (word) { return fromEnum(Color.B); } +} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/048rgb2/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/spec/048rgb2/main.solc deleted file mode 100644 index 5e33af5d..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/spec/048rgb2/main.solc +++ /dev/null @@ -1,13 +0,0 @@ -contract RGB { - data Color = R | G | B; - - public function fromEnum(c : Color) -> word { - match c { - | Color.R => return 4; - | Color.G => return 2; - | Color.B => return 42; - } - } - - public function main() -> word { return fromEnum(Color.B); } -} diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/049rgb3/main.sol b/crates/hir-ty/tests/fixtures/ok/corpus/spec/049rgb3/main.sol new file mode 100644 index 00000000..2a7293b2 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/corpus/spec/049rgb3/main.sol @@ -0,0 +1,23 @@ +enum RGB { Red(word), Green(word), Blue(word) } + +contract RGB3 { + + function choose(c: RGB) public returns (word) { + let res : word; + match (c) { +case .Red(x) { +assembly { res := add(x,1) } +} +case .Green(x) { +assembly { res := add(x,2) } +} +case .Blue(x) { +assembly { res := add(x,3) } +} +} + return res; + } + function main() public returns (word) { + choose(RGB.Green(42)) + } +} \ No newline at end of file diff --git a/crates/hir-ty/tests/fixtures/ok/corpus/spec/049rgb3/main.solc b/crates/hir-ty/tests/fixtures/ok/corpus/spec/049rgb3/main.solc deleted file mode 100644 index 8cfbaeca..00000000 --- a/crates/hir-ty/tests/fixtures/ok/corpus/spec/049rgb3/main.solc +++ /dev/null @@ -1,17 +0,0 @@ -data RGB = Red(word) | Green(word) | Blue(word); - -contract RGB3 { - - public function choose(c:RGB) -> word { - let res : word; - match c { - | .Red(x) => assembly { res := add(x,1) } - | .Green(x) => assembly { res := add(x,2) } - | .Blue(x) => assembly { res := add(x,3) } - } - return res; - } - public function main() -> word { - choose(RGB.Green(42)) - } -} \ No newline at end of file diff --git a/crates/hir-ty/tests/fixtures/ok/solver/class_scoped_bounded_variable_pragma/main.sol b/crates/hir-ty/tests/fixtures/ok/solver/class_scoped_bounded_variable_pragma/main.sol new file mode 100644 index 00000000..abe459eb --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/solver/class_scoped_bounded_variable_pragma/main.sol @@ -0,0 +1,7 @@ +pragma no-bounded-variable-condition Container; + +enum Box { Box(word) } +trait Eq {} +trait Container {} + +impl Container, a> where c: Eq {} diff --git a/crates/hir-ty/tests/fixtures/ok/solver/class_scoped_bounded_variable_pragma/main.solc b/crates/hir-ty/tests/fixtures/ok/solver/class_scoped_bounded_variable_pragma/main.solc deleted file mode 100644 index be43f937..00000000 --- a/crates/hir-ty/tests/fixtures/ok/solver/class_scoped_bounded_variable_pragma/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -pragma no-bounded-variable-condition Container; - -data Box(a) = Box(word); -forall a . class a:Eq {} -forall a b . class a:Container(b) {} - -forall a c . c:Eq => instance Box(a):Container(a) {} diff --git a/crates/hir-ty/tests/fixtures/ok/solver/class_scoped_patterson_pragma/main.sol b/crates/hir-ty/tests/fixtures/ok/solver/class_scoped_patterson_pragma/main.sol new file mode 100644 index 00000000..99a6203c --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/solver/class_scoped_patterson_pragma/main.sol @@ -0,0 +1,6 @@ +pragma no-patterson-condition C1; + +trait C1 {} +trait C2 {} + +impl C1 where U: C1, U: C2 {} diff --git a/crates/hir-ty/tests/fixtures/ok/solver/class_scoped_patterson_pragma/main.solc b/crates/hir-ty/tests/fixtures/ok/solver/class_scoped_patterson_pragma/main.solc deleted file mode 100644 index fe6247f8..00000000 --- a/crates/hir-ty/tests/fixtures/ok/solver/class_scoped_patterson_pragma/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -pragma no-patterson-condition C1; - -forall a . class a:C1 {} -forall a . class a:C2 {} - -forall U . U:C1, U:C2 => instance U:C1 {} diff --git a/crates/hir-ty/tests/fixtures/ok/solver/global_coverage_pragma/main.sol b/crates/hir-ty/tests/fixtures/ok/solver/global_coverage_pragma/main.sol new file mode 100644 index 00000000..3a5f4124 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/solver/global_coverage_pragma/main.sol @@ -0,0 +1,6 @@ +pragma no-coverage-condition; + +enum Box { Box(word) } +trait MyClass {} + +impl MyClass, b> {} diff --git a/crates/hir-ty/tests/fixtures/ok/solver/global_coverage_pragma/main.solc b/crates/hir-ty/tests/fixtures/ok/solver/global_coverage_pragma/main.solc deleted file mode 100644 index d8991856..00000000 --- a/crates/hir-ty/tests/fixtures/ok/solver/global_coverage_pragma/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -pragma no-coverage-condition; - -data Box(a) = Box(word); -forall a b . class a:MyClass(b) {} - -forall a b . instance Box(a):MyClass(b) {} diff --git a/crates/hir-ty/tests/fixtures/ok/solver/obligation_order_improvement/main.sol b/crates/hir-ty/tests/fixtures/ok/solver/obligation_order_improvement/main.sol new file mode 100644 index 00000000..50ab5691 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/solver/obligation_order_improvement/main.sol @@ -0,0 +1,41 @@ +// Regression test for fixpoint obligation solving with class-argument +// improvement (mirrors the reference's TcSimplify `toHnfs` fixpoint). +// +// `Assign2.assign(Mk.mk(S), 7)` pushes the callee obligation +// `?lhs:Assign2(word)` BEFORE the argument obligation `S:Mk(?o)`. A single +// in-order pass rejects the var-headed Assign2 goal (SC0207); the fixpoint +// solver defers it, solves `S:Mk(?o)` (pinning ?o := R(word) via +// class-argument unification), and then discharges the improved goal +// `R(word):Assign2(word)` in the next round. The reference compiler accepts +// this program. + +trait Assign2 { + function assign(l: lhs, r: rhs) ; +} + +trait Mk { + function mk(x: s) returns (o) ; +} + +enum R { R(a) } + +impl Assign2, a> { + function assign(l: R, r: a) { + return (); + } +} + +enum S { S } + +impl Mk> { + function mk(x: S) returns (R) { + return R(0); + } +} + +contract Main { + function main() public returns (word) { + Assign2.assign(Mk.mk(S), 7); + return 1; + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/solver/obligation_order_improvement/main.solc b/crates/hir-ty/tests/fixtures/ok/solver/obligation_order_improvement/main.solc deleted file mode 100644 index 46094edc..00000000 --- a/crates/hir-ty/tests/fixtures/ok/solver/obligation_order_improvement/main.solc +++ /dev/null @@ -1,44 +0,0 @@ -// Regression test for fixpoint obligation solving with class-argument -// improvement (mirrors the reference's TcSimplify `toHnfs` fixpoint). -// -// `Assign2.assign(Mk.mk(S), 7)` pushes the callee obligation -// `?lhs:Assign2(word)` BEFORE the argument obligation `S:Mk(?o)`. A single -// in-order pass rejects the var-headed Assign2 goal (SC0207); the fixpoint -// solver defers it, solves `S:Mk(?o)` (pinning ?o := R(word) via -// class-argument unification), and then discharges the improved goal -// `R(word):Assign2(word)` in the next round. The reference compiler accepts -// this program. - -forall lhs rhs . -class lhs:Assign2(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -forall s o . -class s:Mk(o) { - function mk(x:s) -> o; -} - -data R(a) = R(a); - -forall a . -instance R(a):Assign2(a) { - function assign(l:R(a), r:a) -> () { - return (); - } -} - -data S = S; - -instance S:Mk(R(word)) { - function mk(x:S) -> R(word) { - return R(0); - } -} - -contract Main { - public function main() -> word { - Assign2.assign(Mk.mk(S), 7); - return 1; - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/abstract_data_wildcard_match/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/abstract_data_wildcard_match/main.sol new file mode 100644 index 00000000..4af52312 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/abstract_data_wildcard_match/main.sol @@ -0,0 +1,9 @@ +enum Opaque {} + +function keep(value: Opaque) returns (Opaque) { + match (value) { +default { +return value; +} +} +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/abstract_data_wildcard_match/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/abstract_data_wildcard_match/main.solc deleted file mode 100644 index 62427041..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/abstract_data_wildcard_match/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -data Opaque; - -function keep(value: Opaque) -> Opaque { - match value { - | _ => return value; - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/bytes_storage_roundtrip_full/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/bytes_storage_roundtrip_full/main.sol new file mode 100644 index 00000000..41faf427 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/bytes_storage_roundtrip_full/main.sol @@ -0,0 +1,17 @@ +import * from std; + +contract C { + value : bytes; + + constructor(x : memory) { + value = x; + } + + function get() public returns (memory) { + return value; + } + + function main() { + return (); + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/bytes_storage_roundtrip_full/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/bytes_storage_roundtrip_full/main.solc deleted file mode 100644 index 7d3e4a50..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/bytes_storage_roundtrip_full/main.solc +++ /dev/null @@ -1,17 +0,0 @@ -import std.{*}; - -contract C { - value : bytes; - - constructor(x : memory(bytes)) { - value = x; - } - - public function get() -> memory(bytes) { - return value; - } - - function main() -> () { - return (); - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/compiler_private_constructor_entry_name/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/compiler_private_constructor_entry_name/main.sol new file mode 100644 index 00000000..d5972494 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/compiler_private_constructor_entry_name/main.sol @@ -0,0 +1,8 @@ +import * from std; + +function init_(x: word) returns (word) { return x; } + +contract C { + constructor(x: uint256) { let saved: word = init_(Typedef.rep(x)); } + function main() { return (); } +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/compiler_private_constructor_entry_name/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/compiler_private_constructor_entry_name/main.solc deleted file mode 100644 index 69e5ac62..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/compiler_private_constructor_entry_name/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -import std.{*}; - -function init_(x: word) -> word { return x; } - -contract C { - constructor(x: uint256) { let saved: word = init_(Typedef.rep(x)); } - function main() -> () { return (); } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/compiler_private_dispatch_entry_name/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/compiler_private_dispatch_entry_name/main.sol new file mode 100644 index 00000000..296427ee --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/compiler_private_dispatch_entry_name/main.sol @@ -0,0 +1,9 @@ +import * from std; +import * from std.dispatch; + +function main(x: uint256) returns (uint256) { return x; } + +contract C { + function call_top() returns (uint256) { return main(uint256(1)); } + function ping(x: uint256) public returns (uint256) { return x; } +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/compiler_private_dispatch_entry_name/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/compiler_private_dispatch_entry_name/main.solc deleted file mode 100644 index b5872c69..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/compiler_private_dispatch_entry_name/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -function main(x: uint256) -> uint256 { return x; } - -contract C { - function call_top() -> uint256 { return main(uint256(1)); } - public function ping(x: uint256) -> uint256 { return x; } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/compound_assignment_uses_class_method/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/compound_assignment_uses_class_method/main.sol new file mode 100644 index 00000000..11e14a4f --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/compound_assignment_uses_class_method/main.sol @@ -0,0 +1,17 @@ +trait Add { + function add(l: t, r: t) returns (t) ; +} + +enum Choice { Choice(word) } + +impl Add { + function add(l: Choice, r: Choice) returns (Choice) { + return r; + } +} + +function choose_right(x: Choice, y: Choice) returns (Choice) { + let result: Choice = x; + result += y; + return result; +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/compound_assignment_uses_class_method/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/compound_assignment_uses_class_method/main.solc deleted file mode 100644 index 88b0fb61..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/compound_assignment_uses_class_method/main.solc +++ /dev/null @@ -1,17 +0,0 @@ -forall t . class t:Add { - function add(l: t, r: t) -> t; -} - -data Choice = Choice(word); - -instance Choice:Add { - function add(l: Choice, r: Choice) -> Choice { - return r; - } -} - -function choose_right(x: Choice, y: Choice) -> Choice { - let result: Choice = x; - result += y; - return result; -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/constructor_dynamic_string_full/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/constructor_dynamic_string_full/main.sol new file mode 100644 index 00000000..8183cadb --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/constructor_dynamic_string_full/main.sol @@ -0,0 +1,17 @@ +import * from std; + +contract C { + value : string; + + constructor(x : memory) { + value = x; + } + + function get() public returns (memory) { + return value; + } + + function main() { + return (); + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/constructor_dynamic_string_full/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/constructor_dynamic_string_full/main.solc deleted file mode 100644 index 713313bd..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/constructor_dynamic_string_full/main.solc +++ /dev/null @@ -1,17 +0,0 @@ -import std.{*}; - -contract C { - value : string; - - constructor(x : memory(string)) { - value = x; - } - - public function get() -> memory(string) { - return value; - } - - function main() -> () { - return (); - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/contract_field_access/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/contract_field_access/main.sol new file mode 100644 index 00000000..71bd32fe --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/contract_field_access/main.sol @@ -0,0 +1,11 @@ +contract Simple { + val : word; + + function getVal() public returns (word) { + return val; + } + + function main() { + return (); + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/contract_field_access/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/contract_field_access/main.solc deleted file mode 100644 index 3b15f848..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/contract_field_access/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -contract Simple { - val : word; - - public function getVal() -> word { - return val; - } - - function main() -> () { - return (); - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/contract_field_initializer/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/contract_field_initializer/main.sol new file mode 100644 index 00000000..217f5df1 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/contract_field_initializer/main.sol @@ -0,0 +1,4 @@ +contract C { + x: word = 1; + function main() { return (); } +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/contract_field_initializer/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/contract_field_initializer/main.solc deleted file mode 100644 index 94b19bc6..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/contract_field_initializer/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -contract C { - x: word = 1; - function main() -> () { return (); } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/dispatch_field_method_collision/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/dispatch_field_method_collision/main.sol new file mode 100644 index 00000000..ca388371 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/dispatch_field_method_collision/main.sol @@ -0,0 +1,16 @@ +import * from std; +import * from std.dispatch; + +contract C { + enum C { Foo } + + allowance: uint256; + + function allowance() public returns (uint256) { + return allowance; + } + + function Foo() public returns (uint256) { + return 1; + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/dispatch_field_method_collision/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/dispatch_field_method_collision/main.solc deleted file mode 100644 index 5f98b4b6..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/dispatch_field_method_collision/main.solc +++ /dev/null @@ -1,16 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract C { - data C = Foo; - - allowance: uint256; - - public function allowance() -> uint256 { - return allowance; - } - - public function Foo() -> uint256 { - return 1; - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/dot_constructors_nested_patterns/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/dot_constructors_nested_patterns/main.sol new file mode 100644 index 00000000..2fa6950a --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/dot_constructors_nested_patterns/main.sol @@ -0,0 +1,16 @@ +enum Option { None, Some(word) } + +function mkSome(x: word) returns (Option) { + return .Some(x); +} + +function fromOption(x: Option) returns (word) { + match (x) { +case .Some(v) { +return v; +} +case .None { +return 0; +} +} +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/dot_constructors_nested_patterns/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/dot_constructors_nested_patterns/main.solc deleted file mode 100644 index dac1fb48..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/dot_constructors_nested_patterns/main.solc +++ /dev/null @@ -1,12 +0,0 @@ -data Option = None | Some(word); - -function mkSome(x: word) -> Option { - return .Some(x); -} - -function fromOption(x: Option) -> word { - match x { - | .Some(v) => return v; - | .None => return 0; - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/generated_dispatch_explicit_imports/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/generated_dispatch_explicit_imports/main.sol new file mode 100644 index 00000000..80775386 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/generated_dispatch_explicit_imports/main.sol @@ -0,0 +1,6 @@ +import * from std; +import * from std.dispatch; + +contract C { + function echo(value: uint256) public returns (uint256) { return value; } +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/generated_dispatch_explicit_imports/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/generated_dispatch_explicit_imports/main.solc deleted file mode 100644 index 8ff53dab..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/generated_dispatch_explicit_imports/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract C { - public function echo(value: uint256) -> uint256 { return value; } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/import_same_name_ctor_unqualified/lib.sol b/crates/hir-ty/tests/fixtures/ok/typeck/import_same_name_ctor_unqualified/lib.sol new file mode 100644 index 00000000..fe63d647 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/import_same_name_ctor_unqualified/lib.sol @@ -0,0 +1,4 @@ +export { wrapper(wrapper), boxed(boxed) }; + +enum wrapper { wrapper(word) } +enum boxed { boxed(word) } diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/import_same_name_ctor_unqualified/lib.solc b/crates/hir-ty/tests/fixtures/ok/typeck/import_same_name_ctor_unqualified/lib.solc deleted file mode 100644 index 3cde2b1a..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/import_same_name_ctor_unqualified/lib.solc +++ /dev/null @@ -1,4 +0,0 @@ -export { wrapper(wrapper), boxed(boxed) }; - -data wrapper = wrapper(word); -data boxed = boxed(word); diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/import_same_name_ctor_unqualified/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/import_same_name_ctor_unqualified/main.sol new file mode 100644 index 00000000..55566562 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/import_same_name_ctor_unqualified/main.sol @@ -0,0 +1,23 @@ +import {wrapper, boxed} from lib; + +// Same-name constructors from a selective import stay legal unqualified in +// both pattern and expression position. +function unwrap(u: wrapper) returns (word) { + match (u) { +case wrapper(w) { +return w; +} +} +} + +function rebox(b: boxed) returns (boxed) { + match (b) { +case boxed(w) { +return boxed(w); +} +} +} + +function main() returns (word) { + return unwrap(wrapper(3)); +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/import_same_name_ctor_unqualified/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/import_same_name_ctor_unqualified/main.solc deleted file mode 100644 index 3c5cf062..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/import_same_name_ctor_unqualified/main.solc +++ /dev/null @@ -1,19 +0,0 @@ -import lib.{wrapper, boxed}; - -// Same-name constructors from a selective import stay legal unqualified in -// both pattern and expression position. -function unwrap(u: wrapper) -> word { - match u { - | wrapper(w) => return w; - } -} - -function rebox(b: boxed) -> boxed { - match b { - | boxed(w) => return boxed(w); - } -} - -function main() -> word { - return unwrap(wrapper(3)); -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/imported_derived_class/lib.sol b/crates/hir-ty/tests/fixtures/ok/typeck/imported_derived_class/lib.sol new file mode 100644 index 00000000..0bcfaff1 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/imported_derived_class/lib.sol @@ -0,0 +1,17 @@ +export { Marker, Box, Phantom }; + +trait Marker { + function mark(x: a) returns (word) ; +} + +impl Marker { + function mark(x: word) returns (word) { + return x; + } +} + +#[derive(Marker)] +enum Box { Box(a) } + +#[derive(Marker)] +enum Phantom { Phantom(word) } diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/imported_derived_class/lib.solc b/crates/hir-ty/tests/fixtures/ok/typeck/imported_derived_class/lib.solc deleted file mode 100644 index aac838be..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/imported_derived_class/lib.solc +++ /dev/null @@ -1,18 +0,0 @@ -export { Marker, Box, Phantom }; - -forall a . -class a:Marker { - function mark(x: a) -> word; -} - -instance word:Marker { - function mark(x: word) -> word { - return x; - } -} - -#[derive(Marker)] -data Box(a) = Box(a); - -#[derive(Marker)] -data Phantom(a) = Phantom(word); diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/imported_derived_class/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/imported_derived_class/main.sol new file mode 100644 index 00000000..5bef8513 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/imported_derived_class/main.sol @@ -0,0 +1,11 @@ +import {Marker, Box, Phantom} from lib; + +// The derived instance is declared in an imported module and recursively +// discharges the class constraint for every declared type parameter. +function markBox(x: Box) returns (word) { + return Marker.mark(x); +} + +function markPhantom(x: Phantom) returns (word) where a: Marker { + return Marker.mark(x); +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/imported_derived_class/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/imported_derived_class/main.solc deleted file mode 100644 index 7498b9a5..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/imported_derived_class/main.solc +++ /dev/null @@ -1,12 +0,0 @@ -import lib.{Marker, Box, Phantom}; - -// The derived instance is declared in an imported module and recursively -// discharges the class constraint for every declared type parameter. -function markBox(x: Box(word)) -> word { - return Marker.mark(x); -} - -forall a . a:Marker => -function markPhantom(x: Phantom(a)) -> word { - return Marker.mark(x); -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/integer_literal_pattern/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/integer_literal_pattern/main.sol new file mode 100644 index 00000000..420e8ada --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/integer_literal_pattern/main.sol @@ -0,0 +1,10 @@ +function classify(n: integer) returns (integer) { + match (n) { +case 0 { +return 1; +} +default { +return n; +} +} +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/integer_literal_pattern/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/integer_literal_pattern/main.solc deleted file mode 100644 index c72f6735..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/integer_literal_pattern/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -function classify(n : integer) -> integer { - match n { - | 0 => return 1; - | _ => return n; - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/lambda_expected_function_type/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/lambda_expected_function_type/main.sol new file mode 100644 index 00000000..4892ce61 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/lambda_expected_function_type/main.sol @@ -0,0 +1,9 @@ +enum Option { None, Some(word) } + +function apply(f: function(word) returns (Option)) returns (Option) { + return f(1); +} + +function main() returns (Option) { + return apply(lam(x) { return .Some(x); }); +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/lambda_expected_function_type/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/lambda_expected_function_type/main.solc deleted file mode 100644 index f583276c..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/lambda_expected_function_type/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -data Option = None | Some(word); - -function apply(f: (word) -> Option) -> Option { - return f(1); -} - -function main() -> Option { - return apply(lam(x) { return .Some(x); }); -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/literal_poly_noclass/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/literal_poly_noclass/main.sol new file mode 100644 index 00000000..f4408331 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/literal_poly_noclass/main.sol @@ -0,0 +1,4 @@ +function f() returns (word) { + let y : word = 7; + return y; +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/literal_poly_noclass/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/literal_poly_noclass/main.solc deleted file mode 100644 index 822b95b9..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/literal_poly_noclass/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -function f() -> word { - let y : word = 7; - return y; -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/nested_generic_adt_constructor/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/nested_generic_adt_constructor/main.sol new file mode 100644 index 00000000..9bb5db00 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/nested_generic_adt_constructor/main.sol @@ -0,0 +1,11 @@ +contract Box { + enum Option { None, Some(u) } + + function mk(x: word) returns (Option) { + return .Some(x); + } + + function main() { + return (); + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/nested_generic_adt_constructor/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/nested_generic_adt_constructor/main.solc deleted file mode 100644 index f23c1d5d..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/nested_generic_adt_constructor/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -contract Box(t) { - data Option(u) = None | Some(u); - - function mk(x: word) -> Option(word) { - return .Some(x); - } - - function main() -> () { - return (); - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/qualified_and_builtin_bool_patterns/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/qualified_and_builtin_bool_patterns/main.sol new file mode 100644 index 00000000..d476214b --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/qualified_and_builtin_bool_patterns/main.sol @@ -0,0 +1,27 @@ +enum flag { off, on } + +function pick(f: flag) returns (word) { + match (f) { +case flag.off { +return 0; +} +case flag.on { +return 1; +} +} +} + +function flip(b: bool) returns (word) { + match (b) { +case true { +return 1; +} +case false { +return 0; +} +} +} + +function main() returns (word) { + return primAddWord(pick(flag.on), flip(true)); +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/qualified_and_builtin_bool_patterns/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/qualified_and_builtin_bool_patterns/main.solc deleted file mode 100644 index 95235b0f..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/qualified_and_builtin_bool_patterns/main.solc +++ /dev/null @@ -1,19 +0,0 @@ -data flag = off | on; - -function pick(f: flag) -> word { - match f { - | flag.off => return 0; - | flag.on => return 1; - } -} - -function flip(b: bool) -> word { - match b { - | true => return 1; - | false => return 0; - } -} - -function main() -> word { - return primAddWord(pick(flag.on), flip(true)); -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/same_name_nullary_ctor_pattern/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/same_name_nullary_ctor_pattern/main.sol new file mode 100644 index 00000000..cda664f3 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/same_name_nullary_ctor_pattern/main.sol @@ -0,0 +1,25 @@ +enum thing { thing } +enum m { m, k } + +function pickThing(t: thing) returns (word) { + match (t) { +case thing { +return 7; +} +} +} + +function pickM(x: m) returns (word) { + match (x) { +case m { +return 1; +} +case m.k { +return 2; +} +} +} + +function main() returns (word) { + return primAddWord(pickThing(thing), pickM(m.k)); +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/same_name_nullary_ctor_pattern/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/same_name_nullary_ctor_pattern/main.solc deleted file mode 100644 index ce69e7dd..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/same_name_nullary_ctor_pattern/main.solc +++ /dev/null @@ -1,19 +0,0 @@ -data thing = thing; -data m = m | k; - -function pickThing(t: thing) -> word { - match t { - | thing => return 7; - } -} - -function pickM(x: m) -> word { - match x { - | m => return 1; - | m.k => return 2; - } -} - -function main() -> word { - return primAddWord(pickThing(thing), pickM(m.k)); -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/self_recursive_data/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/self_recursive_data/main.sol new file mode 100644 index 00000000..cac1c5a0 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/self_recursive_data/main.sol @@ -0,0 +1,5 @@ +enum A { A(A), Z } + +function f(x: A) returns (word) { + return 0; +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/self_recursive_data/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/self_recursive_data/main.solc deleted file mode 100644 index a6b0dc64..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/self_recursive_data/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data A = A(A) | Z; - -function f(x: A) -> word { - return 0; -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/std_universe_eq_ord/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/std_universe_eq_ord/main.sol new file mode 100644 index 00000000..abab0a4c --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/std_universe_eq_ord/main.sol @@ -0,0 +1,29 @@ +import {Eq, Ord, absurd} from std; + +function eqUnit(x: (), y: ()) returns (bool) { + return Eq.eq(x, y); +} + +function eqSum(x: sum, y: sum) returns (bool) { + return Eq.eq(x, y); +} + +function eqProduct(x: (word, word), y: (word, word)) returns (bool) { + return Eq.eq(x, y); +} + +function ordUnit(x: (), y: ()) returns (bool) { + return Ord.gt(x, y); +} + +function ordSum(x: sum, y: sum) returns (bool) { + return Ord.gt(x, y); +} + +function ordProduct(x: (word, word), y: (word, word)) returns (bool) { + return Ord.gt(x, y); +} + +function bottomWord() returns (word) { + return absurd(); +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/std_universe_eq_ord/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/std_universe_eq_ord/main.solc deleted file mode 100644 index d7f620fb..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/std_universe_eq_ord/main.solc +++ /dev/null @@ -1,29 +0,0 @@ -import std.{Eq, Ord, absurd}; - -function eqUnit(x : (), y : ()) -> bool { - return Eq.eq(x, y); -} - -function eqSum(x : sum(word, word), y : sum(word, word)) -> bool { - return Eq.eq(x, y); -} - -function eqProduct(x : (word, word), y : (word, word)) -> bool { - return Eq.eq(x, y); -} - -function ordUnit(x : (), y : ()) -> bool { - return Ord.gt(x, y); -} - -function ordSum(x : sum(word, word), y : sum(word, word)) -> bool { - return Ord.gt(x, y); -} - -function ordProduct(x : (word, word), y : (word, word)) -> bool { - return Ord.gt(x, y); -} - -function bottomWord() -> word { - return absurd(); -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/std_word_minmax/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/std_word_minmax/main.sol new file mode 100644 index 00000000..2a66e66a --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/std_word_minmax/main.sol @@ -0,0 +1,9 @@ +import {Typedef, maxWord, minWord, uint256} from std; + +function minUint(a: uint256, b: uint256) returns (uint256) { + return uint256(minWord(Typedef.rep(a), Typedef.rep(b))); +} + +function maxUint(a: uint256, b: uint256) returns (uint256) { + return uint256(maxWord(Typedef.rep(a), Typedef.rep(b))); +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/std_word_minmax/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/std_word_minmax/main.solc deleted file mode 100644 index 1279816c..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/std_word_minmax/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -import std.{Typedef, maxWord, minWord, uint256}; - -function minUint(a: uint256, b: uint256) -> uint256 { - return uint256(minWord(Typedef.rep(a), Typedef.rep(b))); -} - -function maxUint(a: uint256, b: uint256) -> uint256 { - return uint256(maxWord(Typedef.rep(a), Typedef.rep(b))); -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/storage_mapping_compound_add_uint256/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/storage_mapping_compound_add_uint256/main.sol new file mode 100644 index 00000000..437b70cf --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/storage_mapping_compound_add_uint256/main.sol @@ -0,0 +1,24 @@ +enum mapping { mapping(word) } +enum uint256 { uint256(word) } + +trait Add { + function add(l: t, r: t) returns (t) ; +} +trait Sub { + function sub(l: t, r: t) returns (t) ; +} +impl Add { + function add(l: word, r: word) returns (word) { return l; } +} +impl Sub { + function sub(l: word, r: word) returns (word) { return l; } +} +impl Add { + function add(l: uint256, r: uint256) returns (uint256) { return l; } +} + +contract C { + m: mapping(word => uint256); + function f(k: word, v: uint256) { m[k] += v; } + function main() { return (); } +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/storage_mapping_compound_add_uint256/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/storage_mapping_compound_add_uint256/main.solc deleted file mode 100644 index 6ab1d8c1..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/storage_mapping_compound_add_uint256/main.solc +++ /dev/null @@ -1,24 +0,0 @@ -data mapping(key, value) = mapping(word); -data uint256 = uint256(word); - -forall t . class t:Add { - function add(l: t, r: t) -> t; -} -forall t . class t:Sub { - function sub(l: t, r: t) -> t; -} -instance word:Add { - function add(l: word, r: word) -> word { return l; } -} -instance word:Sub { - function sub(l: word, r: word) -> word { return l; } -} -instance uint256:Add { - function add(l: uint256, r: uint256) -> uint256 { return l; } -} - -contract C { - m: mapping(word, uint256); - function f(k: word, v: uint256) -> () { m[k] += v; } - function main() -> () { return (); } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/storage_mapping_compound_add_word/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/storage_mapping_compound_add_word/main.sol new file mode 100644 index 00000000..0b3a1aa0 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/storage_mapping_compound_add_word/main.sol @@ -0,0 +1,24 @@ +enum mapping { mapping(word) } +enum uint256 { uint256(word) } + +trait Add { + function add(l: t, r: t) returns (t) ; +} +trait Sub { + function sub(l: t, r: t) returns (t) ; +} +impl Add { + function add(l: word, r: word) returns (word) { return l; } +} +impl Sub { + function sub(l: word, r: word) returns (word) { return l; } +} +impl Add { + function add(l: uint256, r: uint256) returns (uint256) { return l; } +} + +contract C { + m: mapping(word => word); + function f(k: word) { m[k] += 1; } + function main() { return (); } +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/storage_mapping_compound_add_word/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/storage_mapping_compound_add_word/main.solc deleted file mode 100644 index 7d22153f..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/storage_mapping_compound_add_word/main.solc +++ /dev/null @@ -1,24 +0,0 @@ -data mapping(key, value) = mapping(word); -data uint256 = uint256(word); - -forall t . class t:Add { - function add(l: t, r: t) -> t; -} -forall t . class t:Sub { - function sub(l: t, r: t) -> t; -} -instance word:Add { - function add(l: word, r: word) -> word { return l; } -} -instance word:Sub { - function sub(l: word, r: word) -> word { return l; } -} -instance uint256:Add { - function add(l: uint256, r: uint256) -> uint256 { return l; } -} - -contract C { - m: mapping(word, word); - function f(k: word) -> () { m[k] += 1; } - function main() -> () { return (); } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/storage_word_assignment_full/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/storage_word_assignment_full/main.sol new file mode 100644 index 00000000..a9fa2e0e --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/storage_word_assignment_full/main.sol @@ -0,0 +1,29 @@ +enum storage { storage(word) } + +trait CanStore { + function store(r: a, v: b) ; + function load(r: a) returns (b) ; +} + +impl CanStore, word> { + function store(dst: storage, src: word) { + return (); + } + + function load(src: storage) returns (word) { + return 0; + } +} + +contract StorageWordAssign { + x: word; + + function setx() { + x = 8; + } + + function main() public returns (word) { + setx(); + return x; + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/storage_word_assignment_full/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/storage_word_assignment_full/main.solc deleted file mode 100644 index c904787b..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/storage_word_assignment_full/main.solc +++ /dev/null @@ -1,30 +0,0 @@ -data storage(t) = storage(word); - -forall a b. -class a:CanStore(b) { - function store(r:a, v:b) -> (); - function load(r:a) -> b; -} - -instance storage(word):CanStore(word) { - function store(dst: storage(word), src: word) -> () { - return (); - } - - function load(src: storage(word)) -> word { - return 0; - } -} - -contract StorageWordAssign { - x: word; - - function setx() -> () { - x = 8; - } - - public function main() -> word { - setx(); - return x; - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/tuples_if_lambdas_for_loops_and_compound_assigns/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/tuples_if_lambdas_for_loops_and_compound_assigns/main.sol new file mode 100644 index 00000000..dc46aecc --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/tuples_if_lambdas_for_loops_and_compound_assigns/main.sol @@ -0,0 +1,91 @@ +trait Add { + function add(l: t, r: t) returns (t) ; +} + +trait Mod { + function mod(l: t, r: t) returns (t) ; +} + +trait BitAnd { + function band(l: t, r: t) returns (t) ; +} + +trait BitOr { + function bor(l: t, r: t) returns (t) ; +} + +trait BitXor { + function bxor(l: t, r: t) returns (t) ; +} + +trait Ord { + function gt(l: t, r: t) returns (bool) ; +} + +trait Eq { + function eq(l: t, r: t) returns (bool) ; +} + +impl Add { + function add(l: word, r: word) returns (word) { + return primAddWord(l, r); + } +} + +impl Mod { + function mod(l: word, r: word) returns (word) { + return l; + } +} + +impl BitAnd { + function band(l: word, r: word) returns (word) { + return l; + } +} + +impl BitOr { + function bor(l: word, r: word) returns (word) { + return l; + } +} + +impl BitXor { + function bxor(l: word, r: word) returns (word) { + return l; + } +} + +impl Ord { + function gt(l: word, r: word) returns (bool) { + return true; + } +} + +impl Eq { + function eq(l: word, r: word) returns (bool) { + return true; + } +} + +function lt(l: word, r: word) returns (bool) { + return Ord.gt(r, l); +} + +function main() returns (word) { + let f = lam(x: word) { return x; }; + let acc : word = 0; + for (let i : word = 0; i < 3; i = i + 1) { + acc += f(i); + acc ^= 1; + acc &= 7; + acc |= 2; + acc %= 5; + } + let t : (word, word) = (acc, 1); + match (t) { +case (x, _) { +return x == 0 ? 1 : x; +} +} +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/tuples_if_lambdas_for_loops_and_compound_assigns/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/tuples_if_lambdas_for_loops_and_compound_assigns/main.solc deleted file mode 100644 index a0f12c0d..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/tuples_if_lambdas_for_loops_and_compound_assigns/main.solc +++ /dev/null @@ -1,89 +0,0 @@ -forall t . class t:Add { - function add(l:t, r:t) -> t; -} - -forall t . class t:Mod { - function mod(l:t, r:t) -> t; -} - -forall t . class t:BitAnd { - function band(l:t, r:t) -> t; -} - -forall t . class t:BitOr { - function bor(l:t, r:t) -> t; -} - -forall t . class t:BitXor { - function bxor(l:t, r:t) -> t; -} - -forall t . class t:Ord { - function gt(l:t, r:t) -> bool; -} - -forall t . class t:Eq { - function eq(l:t, r:t) -> bool; -} - -instance word:Add { - function add(l:word, r:word) -> word { - return primAddWord(l, r); - } -} - -instance word:Mod { - function mod(l:word, r:word) -> word { - return l; - } -} - -instance word:BitAnd { - function band(l:word, r:word) -> word { - return l; - } -} - -instance word:BitOr { - function bor(l:word, r:word) -> word { - return l; - } -} - -instance word:BitXor { - function bxor(l:word, r:word) -> word { - return l; - } -} - -instance word:Ord { - function gt(l:word, r:word) -> bool { - return true; - } -} - -instance word:Eq { - function eq(l:word, r:word) -> bool { - return true; - } -} - -function lt(l:word, r:word) -> bool { - return Ord.gt(r, l); -} - -function main() -> word { - let f = lam(x: word) { return x; }; - let acc : word = 0; - for (let i : word = 0; i < 3; i = i + 1) { - acc += f(i); - acc ^= 1; - acc &= 7; - acc |= 2; - acc %= 5; - } - let t : (word, word) = (acc, 1); - match t { - | (x, _) => return if x == 0 then 1 else x; - } -} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/yul_keccak256/main.sol b/crates/hir-ty/tests/fixtures/ok/typeck/yul_keccak256/main.sol new file mode 100644 index 00000000..cd818abe --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/typeck/yul_keccak256/main.sol @@ -0,0 +1,8 @@ +function hash_word(value: word) returns (word) { + let result: word; + assembly { + mstore(0, value) + result := keccak256(0, 32) + } + return result; +} diff --git a/crates/hir-ty/tests/fixtures/ok/typeck/yul_keccak256/main.solc b/crates/hir-ty/tests/fixtures/ok/typeck/yul_keccak256/main.solc deleted file mode 100644 index a4ff7b09..00000000 --- a/crates/hir-ty/tests/fixtures/ok/typeck/yul_keccak256/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -function hash_word(value: word) -> word { - let result: word; - assembly { - mstore(0, value) - result := keccak256(0, 32) - } - return result; -} diff --git a/crates/hir-ty/tests/fixtures/ok/yul_polymorphic_terminators/main.sol b/crates/hir-ty/tests/fixtures/ok/yul_polymorphic_terminators/main.sol new file mode 100644 index 00000000..5bc872f6 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/ok/yul_polymorphic_terminators/main.sol @@ -0,0 +1,34 @@ +function viaStop() returns (a) { + assembly { + stop() + } +} + +function viaInvalid() returns (a) { + assembly { + invalid() + } +} + +function viaSelfdestruct(beneficiary: word) returns (a) { + assembly { + selfdestruct(beneficiary) + } +} + +function viaRevert() returns (a) { + assembly { + revert(0, 0) + } +} + +function useWord(value: word) {} + +contract Terminators { + function main() public { + useWord(viaStop()); + useWord(viaInvalid()); + useWord(viaSelfdestruct(0)); + useWord(viaRevert()); + } +} diff --git a/crates/hir-ty/tests/fixtures/ok/yul_polymorphic_terminators/main.solc b/crates/hir-ty/tests/fixtures/ok/yul_polymorphic_terminators/main.solc deleted file mode 100644 index f5d04b75..00000000 --- a/crates/hir-ty/tests/fixtures/ok/yul_polymorphic_terminators/main.solc +++ /dev/null @@ -1,34 +0,0 @@ -forall a . function viaStop() -> a { - assembly { - stop() - } -} - -forall a . function viaInvalid() -> a { - assembly { - invalid() - } -} - -forall a . function viaSelfdestruct(beneficiary : word) -> a { - assembly { - selfdestruct(beneficiary) - } -} - -forall a . function viaRevert() -> a { - assembly { - revert(0, 0) - } -} - -function useWord(value : word) -> () {} - -contract Terminators { - public function main() -> () { - useWord(viaStop()); - useWord(viaInvalid()); - useWord(viaSelfdestruct(0)); - useWord(viaRevert()); - } -} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/abi.sol b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/abi.sol new file mode 100644 index 00000000..af823e53 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/abi.sol @@ -0,0 +1,26 @@ +pragma no-patterson-condition; +pragma no-bounded-variable-condition; +pragma no-coverage-condition; + +export { + Generic, + ABIDeriving, + ABIAttribs, + ABIDecode, + WordReader, + ABIDecoder(*), + Reader +}; + +trait Generic {} +trait ABIDeriving {} +trait ABIAttribs {} +trait ABIDecode {} +trait WordReader {} + +enum ABIDecoder { ABIDecoder(reader) } +enum Reader { Reader } + +impl WordReader {} +impl ABIAttribs {} +impl ABIDecode, word> {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/abi.solc b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/abi.solc deleted file mode 100644 index 847dac79..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/abi.solc +++ /dev/null @@ -1,26 +0,0 @@ -pragma no-patterson-condition; -pragma no-bounded-variable-condition; -pragma no-coverage-condition; - -export { - Generic, - ABIDeriving, - ABIAttribs, - ABIDecode, - WordReader, - ABIDecoder(*), - Reader -}; - -forall a rep . class a:Generic(rep) {} -forall self . class self:ABIDeriving {} -forall self . class self:ABIAttribs {} -forall decoder decoded . class decoder:ABIDecode(decoded) {} -forall reader . class reader:WordReader {} - -data ABIDecoder(ty, reader) = ABIDecoder(reader); -data Reader = Reader; - -instance Reader:WordReader {} -instance word:ABIAttribs {} -instance ABIDecoder(word, Reader):ABIDecode(word) {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/main.sol b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/main.sol new file mode 100644 index 00000000..c8325e64 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/main.sol @@ -0,0 +1,6 @@ +import * from abi; +import {Box} from types; + +function keepBoxVisible(x: Box) returns (Box) { + return x; +} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/main.solc b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/main.solc deleted file mode 100644 index 3853b860..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -import abi.{*}; -import types.{Box}; - -function keepBoxVisible(x: Box(word)) -> Box(word) { - return x; -} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/types.sol b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/types.sol new file mode 100644 index 00000000..f7e4bc85 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/types.sol @@ -0,0 +1,5 @@ +import * from abi; + +export { Box(*) }; + +enum Box { Box(a) } diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/types.solc b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/types.solc deleted file mode 100644 index 472be27d..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported/types.solc +++ /dev/null @@ -1,5 +0,0 @@ -import abi.{*}; - -export { Box(*) }; - -data Box(a) = Box(a); diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/abi.sol b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/abi.sol new file mode 100644 index 00000000..44fa17bc --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/abi.sol @@ -0,0 +1,26 @@ +pragma no-patterson-condition; +pragma no-bounded-variable-condition; +pragma no-coverage-condition; + +import {Generic} from generic; + +export { + ABIDeriving, + ABIAttribs, + ABIDecode, + WordReader, + ABIDecoder(*), + Reader +}; + +trait ABIDeriving {} +trait ABIAttribs {} +trait ABIDecode {} +trait WordReader {} + +enum ABIDecoder { ABIDecoder(reader) } +enum Reader { Reader } + +impl WordReader {} +impl ABIAttribs {} +impl ABIDecode, word> {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/abi.solc b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/abi.solc deleted file mode 100644 index 5ec8c0a4..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/abi.solc +++ /dev/null @@ -1,26 +0,0 @@ -pragma no-patterson-condition; -pragma no-bounded-variable-condition; -pragma no-coverage-condition; - -import generic.{Generic}; - -export { - ABIDeriving, - ABIAttribs, - ABIDecode, - WordReader, - ABIDecoder(*), - Reader -}; - -forall self . class self:ABIDeriving {} -forall self . class self:ABIAttribs {} -forall decoder decoded . class decoder:ABIDecode(decoded) {} -forall reader . class reader:WordReader {} - -data ABIDecoder(ty, reader) = ABIDecoder(reader); -data Reader = Reader; - -instance Reader:WordReader {} -instance word:ABIAttribs {} -instance ABIDecoder(word, Reader):ABIDecode(word) {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/generic.sol b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/generic.sol new file mode 100644 index 00000000..8e6668fe --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/generic.sol @@ -0,0 +1,3 @@ +export { Generic }; + +trait Generic {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/generic.solc b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/generic.solc deleted file mode 100644 index ba757d4f..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/generic.solc +++ /dev/null @@ -1,3 +0,0 @@ -export { Generic }; - -forall a rep . class a:Generic(rep) {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/main.sol b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/main.sol new file mode 100644 index 00000000..7fd19909 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/main.sol @@ -0,0 +1,7 @@ +import {Generic} from generic; +import * from abi; +import {Box} from types; + +function keepBoxVisible(x: Box) returns (Box) { + return x; +} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/main.solc b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/main.solc deleted file mode 100644 index c5130dbc..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -import generic.{Generic}; -import abi.{*}; -import types.{Box}; - -function keepBoxVisible(x: Box(word)) -> Box(word) { - return x; -} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/types.sol b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/types.sol new file mode 100644 index 00000000..2d1d3bd8 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/types.sol @@ -0,0 +1,6 @@ +import {Generic} from generic; + +export { Box(*) }; + +// Generic is visible here, but the ABIDeriving marker deliberately is not. +enum Box { Box(a) } diff --git a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/types.solc b/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/types.solc deleted file mode 100644 index 949dd6da..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_abi_imported_inactive/types.solc +++ /dev/null @@ -1,6 +0,0 @@ -import generic.{Generic}; - -export { Box(*) }; - -// Generic is visible here, but the ABIDeriving marker deliberately is not. -data Box(a) = Box(a); diff --git a/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/api.solc b/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/api.sol similarity index 100% rename from crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/api.solc rename to crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/api.sol diff --git a/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/base.sol b/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/base.sol new file mode 100644 index 00000000..2175dde5 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/base.sol @@ -0,0 +1,5 @@ +import {Visible} from classes; + +export { Reexported }; + +#[derive(Visible)] enum Reexported {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/base.solc b/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/base.solc deleted file mode 100644 index 36c4cd8f..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/base.solc +++ /dev/null @@ -1,5 +0,0 @@ -import classes.{Visible}; - -export { Reexported }; - -#[derive(Visible)] data Reexported; diff --git a/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/classes.sol b/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/classes.sol new file mode 100644 index 00000000..f9b00433 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/classes.sol @@ -0,0 +1,3 @@ +export { Visible }; + +trait Visible {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/classes.solc b/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/classes.solc deleted file mode 100644 index 524a9f17..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/classes.solc +++ /dev/null @@ -1,3 +0,0 @@ -export { Visible }; - -forall a . class a:Visible {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/main.sol b/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/main.sol new file mode 100644 index 00000000..f6069135 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/main.sol @@ -0,0 +1,6 @@ +import {Reexported} from api; +import {Visible} from classes; + +function keepTypeVisible(x: Reexported) returns (Reexported) { + return x; +} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/main.solc b/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/main.solc deleted file mode 100644 index 68112c7f..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_reexport_visibility/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -import api.{Reexported}; -import classes.{Visible}; - -function keepTypeVisible(x: Reexported) -> Reexported { - return x; -} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/main.sol b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/main.sol new file mode 100644 index 00000000..ce1c8090 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/main.sol @@ -0,0 +1,2 @@ +import {StorageSize, CanStore, storage} from storage_support; +import types; diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/main.solc b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/main.solc deleted file mode 100644 index cc38035b..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/main.solc +++ /dev/null @@ -1,2 +0,0 @@ -import storage_support.{StorageSize, CanStore, storage}; -import types; diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/storage_support.sol b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/storage_support.sol new file mode 100644 index 00000000..f77343a6 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/storage_support.sol @@ -0,0 +1,15 @@ +pragma no-patterson-condition; +pragma no-bounded-variable-condition; +pragma no-coverage-condition; + +export { Generic, StorageDeriving, StorageSize, CanStore, storage(*) }; + +trait Generic {} +trait StorageDeriving {} +trait StorageSize {} +trait CanStore {} + +enum storage { storage(word) } + +impl StorageSize {} +impl CanStore, word> {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/storage_support.solc b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/storage_support.solc deleted file mode 100644 index 6af7d9db..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/storage_support.solc +++ /dev/null @@ -1,15 +0,0 @@ -pragma no-patterson-condition; -pragma no-bounded-variable-condition; -pragma no-coverage-condition; - -export { Generic, StorageDeriving, StorageSize, CanStore, storage(*) }; - -forall a rep . class a:Generic(rep) {} -forall self . class self:StorageDeriving {} -forall self . class self:StorageSize {} -forall slot value . class slot:CanStore(value) {} - -data storage(ty) = storage(word); - -instance word:StorageSize {} -instance storage(word):CanStore(word) {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/types.sol b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/types.sol new file mode 100644 index 00000000..b000e6cb --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/types.sol @@ -0,0 +1,5 @@ +import * from storage_support; + +export { Box(*) }; + +enum Box { Box(a) } diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/types.solc b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/types.solc deleted file mode 100644 index c8260a6c..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported/types.solc +++ /dev/null @@ -1,5 +0,0 @@ -import storage_support.{*}; - -export { Box(*) }; - -data Box(a) = Box(a); diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/generic.sol b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/generic.sol new file mode 100644 index 00000000..8e6668fe --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/generic.sol @@ -0,0 +1,3 @@ +export { Generic }; + +trait Generic {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/generic.solc b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/generic.solc deleted file mode 100644 index ba757d4f..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/generic.solc +++ /dev/null @@ -1,3 +0,0 @@ -export { Generic }; - -forall a rep . class a:Generic(rep) {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/main.sol b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/main.sol new file mode 100644 index 00000000..2ea17a06 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/main.sol @@ -0,0 +1,3 @@ +import {Generic} from generic; +import * from storage_support; +import {Box} from types; diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/main.solc b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/main.solc deleted file mode 100644 index 8005e46d..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -import generic.{Generic}; -import storage_support.{*}; -import types.{Box}; diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/storage_support.sol b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/storage_support.sol new file mode 100644 index 00000000..548104bd --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/storage_support.sol @@ -0,0 +1,16 @@ +pragma no-patterson-condition; +pragma no-bounded-variable-condition; +pragma no-coverage-condition; + +import {Generic} from generic; + +export { StorageDeriving, StorageSize, CanStore, storage(*) }; + +trait StorageDeriving {} +trait StorageSize {} +trait CanStore {} + +enum storage { storage(word) } + +impl StorageSize {} +impl CanStore, word> {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/storage_support.solc b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/storage_support.solc deleted file mode 100644 index 3dec0d35..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/storage_support.solc +++ /dev/null @@ -1,16 +0,0 @@ -pragma no-patterson-condition; -pragma no-bounded-variable-condition; -pragma no-coverage-condition; - -import generic.{Generic}; - -export { StorageDeriving, StorageSize, CanStore, storage(*) }; - -forall self . class self:StorageDeriving {} -forall self . class self:StorageSize {} -forall slot value . class slot:CanStore(value) {} - -data storage(ty) = storage(word); - -instance word:StorageSize {} -instance storage(word):CanStore(word) {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/types.sol b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/types.sol new file mode 100644 index 00000000..64f54c0b --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/types.sol @@ -0,0 +1,6 @@ +import {Generic} from generic; + +export { Box(*) }; + +// Generic is visible here, but StorageDeriving deliberately is not. +enum Box { Box(a) } diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/types.solc b/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/types.solc deleted file mode 100644 index 89fcb452..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_storage_imported_inactive/types.solc +++ /dev/null @@ -1,6 +0,0 @@ -import generic.{Generic}; - -export { Box(*) }; - -// Generic is visible here, but StorageDeriving deliberately is not. -data Box(a) = Box(a); diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/api.solc b/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/api.sol similarity index 100% rename from crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/api.solc rename to crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/api.sol diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/main.sol b/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/main.sol new file mode 100644 index 00000000..f40b6bf6 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/main.sol @@ -0,0 +1,2 @@ +import {Box} from api; +import {StorageSize, CanStore, storage} from storage_support; diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/main.solc b/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/main.solc deleted file mode 100644 index 382c459a..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/main.solc +++ /dev/null @@ -1,2 +0,0 @@ -import api.{Box}; -import storage_support.{StorageSize, CanStore, storage}; diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/storage_support.sol b/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/storage_support.sol new file mode 100644 index 00000000..f77343a6 --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/storage_support.sol @@ -0,0 +1,15 @@ +pragma no-patterson-condition; +pragma no-bounded-variable-condition; +pragma no-coverage-condition; + +export { Generic, StorageDeriving, StorageSize, CanStore, storage(*) }; + +trait Generic {} +trait StorageDeriving {} +trait StorageSize {} +trait CanStore {} + +enum storage { storage(word) } + +impl StorageSize {} +impl CanStore, word> {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/storage_support.solc b/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/storage_support.solc deleted file mode 100644 index 6af7d9db..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/storage_support.solc +++ /dev/null @@ -1,15 +0,0 @@ -pragma no-patterson-condition; -pragma no-bounded-variable-condition; -pragma no-coverage-condition; - -export { Generic, StorageDeriving, StorageSize, CanStore, storage(*) }; - -forall a rep . class a:Generic(rep) {} -forall self . class self:StorageDeriving {} -forall self . class self:StorageSize {} -forall slot value . class slot:CanStore(value) {} - -data storage(ty) = storage(word); - -instance word:StorageSize {} -instance storage(word):CanStore(word) {} diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/types.sol b/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/types.sol new file mode 100644 index 00000000..b000e6cb --- /dev/null +++ b/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/types.sol @@ -0,0 +1,5 @@ +import * from storage_support; + +export { Box(*) }; + +enum Box { Box(a) } diff --git a/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/types.solc b/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/types.solc deleted file mode 100644 index c8260a6c..00000000 --- a/crates/hir-ty/tests/fixtures/solver/derived_storage_reexport_visibility/types.solc +++ /dev/null @@ -1,5 +0,0 @@ -import storage_support.{*}; - -export { Box(*) }; - -data Box(a) = Box(a); diff --git a/crates/hir-ty/tests/frontend_smoke.rs b/crates/hir-ty/tests/frontend_smoke.rs index 73ce2240..42af22d7 100644 --- a/crates/hir-ty/tests/frontend_smoke.rs +++ b/crates/hir-ty/tests/frontend_smoke.rs @@ -187,11 +187,11 @@ fn std_solc_frontend_typecheck_triage() { let repo = repo_root(); let corpus_root = repo.join("crates/parser/tests/fixtures/corpus/ok"); let std_root = corpus_root.join("std"); - let outcome = run_frontend(&std_root.join("std.solc"), &std_root); + let outcome = run_frontend(&std_root.join("std.sol"), &std_root); let std_triage = std_solc_triage(&outcome); let mut report = String::new(); - writeln!(&mut report, "std.solc frontend triage").unwrap(); + writeln!(&mut report, "std.sol frontend triage").unwrap(); writeln!( &mut report, " unresolved-imports: {}", @@ -217,7 +217,7 @@ fn std_solc_frontend_typecheck_triage() { assert!( outcome.unresolved_imports.is_empty(), - "std.solc has unresolved imports:\n{report}" + "std.sol has unresolved imports:\n{report}" ); assert!( std_triage.unrecorded.is_empty() && std_triage.stale.is_empty(), @@ -231,9 +231,9 @@ fn curated_solver_files_execute_solver_and_soundness_queries() { let corpus_root = repo.join("crates/parser/tests/fixtures/corpus"); let std_root = corpus_root.join("ok/std"); let fixtures = [ - "examples/cases/tabled-default-instance.solc", - "examples/cases/tabled-given-order.solc", - "examples/cases/tabled-residual-given.solc", + "examples/cases/tabled-default-instance.sol", + "examples/cases/tabled-given-order.sol", + "examples/cases/tabled-residual-given.sol", ]; for fixture in fixtures { @@ -290,7 +290,7 @@ fn curated_solver_files_execute_solver_and_soundness_queries() { fn generated_dispatch_reuses_std_instance_facts_per_module() { let repo = repo_root(); let entry = repo.join( - "crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty_no_constructor.solc", + "crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty_no_constructor.sol", ); let std_root = repo.join("std"); let outcome = run_frontend(&entry, &std_root); @@ -321,9 +321,9 @@ fn match_coverage_conservative_cases_emit_no_false_diagnostics() { let std_root = corpus_root.join("ok/std"); for fixture in [ - "examples/cases/false-redundant-warning.solc", - "examples/comptime/match_labels.solc", - "examples/cases/polymatch-error.solc", + "examples/cases/false-redundant-warning.sol", + "examples/comptime/match_labels.sol", + "examples/cases/polymatch-error.sol", ] { let entry = corpus_entry(&corpus_root, fixture); let outcome = run_frontend_with_roots( @@ -735,7 +735,7 @@ fn relative_solc_paths(root: &Path) -> BTreeSet { let path = entry.path(); if path.is_dir() { walk(root, &path, paths); - } else if path.extension().and_then(|extension| extension.to_str()) == Some("solc") { + } else if path.extension().and_then(|extension| extension.to_str()) == Some("sol") { let relative = path .strip_prefix(root) .expect("walked path is below corpus root") @@ -980,7 +980,7 @@ fn collect_module_fs_snapshot( }; for entry in entries.flatten() { let path = entry.path(); - if path.extension().and_then(|extension| extension.to_str()) == Some("solc") { + if path.extension().and_then(|extension| extension.to_str()) == Some("sol") { if path.is_file() { existing_files.insert(path.clone()); } @@ -1094,7 +1094,7 @@ fn append_diagnostic_sample(report: &mut String, label: &str, diagnostics: &[Str fn append_std_solc_triage(report: &mut String, triage: &StdSolcTriage) { if !triage.known_by_reason.is_empty() { - writeln!(report, "\nstd.solc known diagnostic families").unwrap(); + writeln!(report, "\nstd.sol known diagnostic families").unwrap(); for (reason, diagnostics) in &triage.known_by_reason { writeln!(report, " {reason}: {}", diagnostics.len()).unwrap(); for diagnostic in diagnostics.iter().take(6) { @@ -1107,14 +1107,14 @@ fn append_std_solc_triage(report: &mut String, triage: &StdSolcTriage) { } if !triage.unrecorded.is_empty() { - writeln!(report, "\nstd.solc unrecorded diagnostic families").unwrap(); + writeln!(report, "\nstd.sol unrecorded diagnostic families").unwrap(); for diagnostic in triage.unrecorded.iter().take(20) { writeln!(report, " {}: {}", diagnostic.phase, diagnostic.diagnostic).unwrap(); } if triage.unrecorded.len() > 20 { writeln!( report, - " ... {} more unrecorded std.solc diagnostics", + " ... {} more unrecorded std.sol diagnostics", triage.unrecorded.len() - 20 ) .unwrap(); @@ -1122,7 +1122,7 @@ fn append_std_solc_triage(report: &mut String, triage: &StdSolcTriage) { } if !triage.stale.is_empty() { - writeln!(report, "\nstd.solc stale diagnostic families").unwrap(); + writeln!(report, "\nstd.sol stale diagnostic families").unwrap(); for known in &triage.stale { writeln!( report, diff --git a/crates/hir-ty/tests/incremental_cache.rs b/crates/hir-ty/tests/incremental_cache.rs index 6c448cf3..3323c49d 100644 --- a/crates/hir-ty/tests/incremental_cache.rs +++ b/crates/hir-ty/tests/incremental_cache.rs @@ -124,14 +124,14 @@ impl solcore_hir_ty::Db for TestDb {} #[test] fn unrelated_signature_edit_does_not_rerun_every_body_inference() { let before = r#" -function id(x: word) -> word { return x; } -function unrelated(x: word) -> word { return 0; } -function main() -> word { return id(1); } +function id(x: word) returns (word) { return x; } +function unrelated(x: word) returns (word) { return 0; } +function main() returns (word) { return id(1); } "#; let after = r#" -function id(x: word) -> word { return x; } -function unrelated(x: bool) -> word { return 0; } -function main() -> word { return id(1); } +function id(x: word) returns (word) { return x; } +function unrelated(x: bool) returns (word) { return 0; } +function main() returns (word) { return id(1); } "#; let (mut db, file, key) = db_with_main(before); @@ -165,21 +165,21 @@ function main() -> word { return id(1); } #[test] fn same_obligation_body_edit_does_not_resolve_solver_query() { let before = r#" -forall a . class a:C {} -instance word:C {} -forall a . a:C => function use(x: a) -> word { return 0; } +trait C {} +impl C {} +function use(x: a) returns (word) where a: C { return 0; } -function main() -> word { +function main() returns (word) { let y: word = 1; return use(1); } "#; let after = r#" -forall a . class a:C {} -instance word:C {} -forall a . a:C => function use(x: a) -> word { return 0; } +trait C {} +impl C {} +function use(x: a) returns (word) where a: C { return 0; } -function main() -> word { +function main() returns (word) { let y: word = 2; return use(1); } @@ -220,14 +220,14 @@ function main() -> word { #[test] fn instance_soundness_edit_is_backdated_into_module_diagnostics() { let before = r#" -data Box(a) = Box(word); -forall a b . class a:C(b) {} -forall a b . instance Box(a):C(b) {} +enum Box {Box(word)} +trait C {} +impl C,b> {} "#; let after = r#" -data Box(a) = Box(word); -forall a b . class a:C(b) {} -forall a . instance Box(a):C(word) {} +enum Box {Box(word)} +trait C {} +impl C,word> {} "#; let (mut db, file, key) = db_with_main(before); @@ -267,10 +267,10 @@ fn instance_soundness_reuses_scope_resolution_for_many_instances() { let mut source = String::new(); for index in 0..INSTANCE_COUNT { - writeln!(source, "forall a . class a:AuditClass{index} {{}}").unwrap(); - writeln!(source, "instance word:AuditClass{index} {{}}").unwrap(); + writeln!(source, "trait AuditClass{index} {{}}").unwrap(); + writeln!(source, "impl AuditClass{index} {{}}").unwrap(); } - source.push_str("function main() -> word { return 0; }\n"); + source.push_str("function main() returns (word) { return 0; }\n"); let (db, _file, key) = db_with_file_backed_main(&source); let module = module_id_from_key(&db, &key); @@ -289,8 +289,8 @@ fn instance_soundness_reuses_scope_resolution_for_many_instances() { #[test] fn generic_lookup_does_not_reresolve_all_item_types() { let source = r#" -forall a rep . class a:Generic(rep) {} -data Box(a) = Box(a); +trait Generic {} +enum Box {Box(a)} "#; let (db, file, _key) = db_with_main(source); @@ -312,12 +312,12 @@ data Box(a) = Box(a); fn contract_body_edit_does_not_rerun_dispatch_surface_query() { let before = r#" contract C { - public function get() -> word { return 1; } + function get() public returns (word) { return 1; } } "#; let after = r#" contract C { - public function get() -> word { return 2; } + function get() public returns (word) { return 2; } } "#; let (mut db, file, _key) = db_with_main(before); @@ -356,14 +356,14 @@ contract C { fn import_diagnostic_span_edit_does_not_rerun_unrelated_body_inference() { let before = concat!( "\n", - "import util.{f}; \x20\n", - "function f() -> word { return 1; }\n", - "function main() -> word { return f(); }\n", + "import {f} from util; \n", + "function f() returns (word) { return 1; }\n", + "function main() returns (word) { return f(); }\n", ); let after = r#" -import util.{f} ; -function f() -> word { return 1; } -function main() -> word { return f(); } +import {f} from util; +function f() returns (word) { return 1; } +function main() returns (word) { return f(); } "#; let (mut db, file, key) = db_with_selected_import_conflict(before); @@ -399,28 +399,26 @@ function main() -> word { return f(); } #[test] fn desugar_body_edit_does_not_rerun_unrelated_body_inference() { let before = r#" -function choose(b: bool, x: word, y: word) -> word { +function choose(b: bool, x: word, y: word) returns (word) { let p: (word, bool) = (x, true); - let selected: word = if b then x else y; - match p { - | (head, flag) => return selected; - } + let selected: word = (b ? x : y); + match (p) { + case (head, flag) { return selected; }} } -function stable(x: word) -> word { return x; } -function main() -> word { return stable(choose(false, 1, 2)); } +function stable(x: word) returns (word) { return x; } +function main() returns (word) { return stable(choose(false, 1, 2)); } "#; let after = r#" -function choose(b: bool, x: word, y: word) -> word { +function choose(b: bool, x: word, y: word) returns (word) { let p: (word, bool) = (x, false); - let selected: word = if b then x else y; - match p { - | (head, flag) => return selected; - } + let selected: word = (b ? x : y); + match (p) { + case (head, flag) { return selected; }} } -function stable(x: word) -> word { return x; } -function main() -> word { return stable(choose(false, 1, 2)); } +function stable(x: word) returns (word) { return x; } +function main() returns (word) { return stable(choose(false, 1, 2)); } "#; let (mut db, file, key) = db_with_main(before); @@ -461,11 +459,11 @@ function main() -> word { return stable(choose(false, 1, 2)); } } fn db_with_main(content: &str) -> (TestDb, SourceFile, ModuleKey) { - db_with_main_url(content, "memory:///main.solc") + db_with_main_url(content, "memory:///main.sol") } fn db_with_file_backed_main(content: &str) -> (TestDb, SourceFile, ModuleKey) { - db_with_main_url(content, "file:///memory/main.solc") + db_with_main_url(content, "file:///memory/main.sol") } fn db_with_main_url(content: &str, url: &str) -> (TestDb, SourceFile, ModuleKey) { @@ -506,14 +504,14 @@ fn db_with_selected_import_conflict(content: &str) -> (TestDb, SourceFile, Modul }; let util_file = SourceFile::new( &db, - "memory:///util.solc".parse().expect("valid URL"), - Some("function f() -> word { return 0; }\nexport { f };\n".to_owned()), + "memory:///util.sol".parse().expect("valid URL"), + Some("function f() returns (word) { return 0; }\nexport { f };\n".to_owned()), ); db.insert_module_file(util_key, util_file); let file = SourceFile::new( &db, - "memory:///main.solc".parse().expect("valid URL"), + "memory:///main.sol".parse().expect("valid URL"), Some(content.to_owned()), ); let key = ModuleKey { diff --git a/crates/hir-ty/tests/ok_fixtures.rs b/crates/hir-ty/tests/ok_fixtures.rs index 3a38f829..e6252e22 100644 --- a/crates/hir-ty/tests/ok_fixtures.rs +++ b/crates/hir-ty/tests/ok_fixtures.rs @@ -12,7 +12,7 @@ define_frontend_test_db!(TestDb, solcore_hir_ty); #[dir_test( dir: "$CARGO_MANIFEST_DIR/tests/fixtures/ok", - glob: "**/main.solc" + glob: "**/main.sol" )] fn hir_ty_ok_fixture_has_no_diagnostics(fixture: Fixture<&str>) { let case_dir = PathBuf::from(fixture.path()) diff --git a/crates/hir-ty/tests/properties.rs b/crates/hir-ty/tests/properties.rs index 9b9b7c5f..34ab6a55 100644 --- a/crates/hir-ty/tests/properties.rs +++ b/crates/hir-ty/tests/properties.rs @@ -13,8 +13,9 @@ fn run_frontend(source: &str) { } fn generated_program(literal: u64, depth: usize, result_kind: u8) -> String { - let mut source = - format!("function main(value : word) -> word {{\n let value0 : word = {literal};\n"); + let mut source = format!( + "function main(value : word) returns (word) {{\n let value0 : word = {literal};\n" + ); for index in 1..=depth { source.push_str(&format!( " let value{index} : word = value{};\n", @@ -25,7 +26,7 @@ fn generated_program(literal: u64, depth: usize, result_kind: u8) -> String { 0 => format!("value{depth}"), 1 => "true".to_owned(), 2 => "missing".to_owned(), - _ => format!("if true then value else value{depth}"), + _ => format!("true ? value : value{depth}"), }; source.push_str(&format!(" return {result};\n}}\n")); source diff --git a/crates/hir-ty/tests/scheme_cycle.rs b/crates/hir-ty/tests/scheme_cycle.rs index 1eb53746..65162141 100644 --- a/crates/hir-ty/tests/scheme_cycle.rs +++ b/crates/hir-ty/tests/scheme_cycle.rs @@ -5,13 +5,11 @@ use solcore_test_utils::{define_frontend_test_db, load_main_source, run_in_large define_frontend_test_db!(TestDb, hir_ty); -/// `return f` makes `f`'s inferred signature grow every fixpoint round; the -/// scheme query must converge through its cycle fallback instead of Salsa -/// panicking with "too many cycle iterations". The program is currently still -/// accepted under legacy signature inference (the reference meanwhile rejects -/// it with SC0220 "incomplete signature"), so only panic-freedom is asserted. +/// The missing `returns` clause intentionally gives `f` the canonical unit +/// result while its body returns `f` itself. Recovery from that recursive type +/// mismatch must not make the scheme query cycle or panic. #[test] -fn divergent_recursive_signature_does_not_panic() { +fn recursive_unit_return_mismatch_does_not_panic() { run_in_large_stack(|| { let mut db = TestDb::default(); let entry = load_main_source(&mut db, "function f(x: word) {\n return f;\n}\n"); diff --git a/crates/hir/src/diag/code.rs b/crates/hir/src/diag/code.rs index da2a1f3b..0d1490ac 100644 --- a/crates/hir/src/diag/code.rs +++ b/crates/hir/src/diag/code.rs @@ -490,11 +490,11 @@ impl DiagnosticCode { ), DiagnosticCodeAlias::new( Self::TYPECK_INCOMPLETE_METHOD_SIGNATURE, - "SC0221 covers incomplete method signatures and invalid instance method signatures.", + "SC0221 covers incomplete method signatures and invalid impl method signatures.", ), DiagnosticCodeAlias::new( Self::TYPECK_CLASS_AS_TYPE, - "SC0229 covers class-as-type errors and generated dispatch type collisions.", + "SC0229 covers trait-as-type errors and generated dispatch type collisions.", ), DiagnosticCodeAlias::new( Self::TYPECK_NON_EXHAUSTIVE_MATCH, diff --git a/crates/hir/src/diag/tests.rs b/crates/hir/src/diag/tests.rs index 27336c50..c4f0320a 100644 --- a/crates/hir/src/diag/tests.rs +++ b/crates/hir/src/diag/tests.rs @@ -35,7 +35,7 @@ impl crate::Db for TestDb { } fn source_file(db: &TestDb, name: &str, content: Option<&str>) -> SourceFile { - let url = format!("memory:///{name}.solc").parse().expect("valid url"); + let url = format!("memory:///{name}.sol").parse().expect("valid url"); SourceFile::new(db, url, content.map(ToOwned::to_owned)) } @@ -244,7 +244,7 @@ fn render_skips_contentless_def_labels_before_absolute_resolution() { #[test] fn render_decodes_file_urls_in_human_and_short_formats() { let db = TestDb::default(); - let file = file_source_file(&db, "/tmp/Solcore Project/日本語/main.solc", "missing\n"); + let file = file_source_file(&db, "/tmp/Solcore Project/日本語/main.sol", "missing\n"); let diagnostic = Diagnostic::error("undefined name") .with_primary_label_span(root_span(file, 0, 7), Some("not found")); @@ -252,7 +252,7 @@ fn render_decodes_file_urls_in_human_and_short_formats() { let short = diagnostic.render_short(&db); for rendered in [human, short] { - assert!(rendered.contains("/tmp/Solcore Project/日本語/main.solc")); + assert!(rendered.contains("/tmp/Solcore Project/日本語/main.sol")); assert!(!rendered.contains("%20")); assert!(!rendered.contains("%E6")); } @@ -261,7 +261,7 @@ fn render_decodes_file_urls_in_human_and_short_formats() { #[test] fn render_decodes_memory_urls_in_human_and_short_formats() { let db = TestDb::default(); - let url = url::Url::parse("memory:///Solcore%20Project/%E6%97%A5%E6%9C%AC%E8%AA%9E/main.solc") + let url = url::Url::parse("memory:///Solcore%20Project/%E6%97%A5%E6%9C%AC%E8%AA%9E/main.sol") .expect("valid memory URL"); let file = SourceFile::new(&db, url, Some("missing\n".to_owned())); let diagnostic = Diagnostic::error("undefined name") @@ -271,7 +271,7 @@ fn render_decodes_memory_urls_in_human_and_short_formats() { let short = diagnostic.render_short(&db); for rendered in [human, short] { - assert!(rendered.contains("/Solcore Project/日本語/main.solc")); + assert!(rendered.contains("/Solcore Project/日本語/main.sol")); assert!(!rendered.contains("memory:///")); assert!(!rendered.contains("%20")); assert!(!rendered.contains("%E6")); diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index dc644872..692e2a8a 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -36,7 +36,7 @@ pub mod visit; /// Solcore's virtual VFS paths are platform-neutral even though they are /// represented as `file:` URLs. Native builds prefer /// [`url::Url::to_file_path`], then decode a local URL directly when the native -/// conversion rejects a drive-less URL such as `file:///main/main.solc` on +/// conversion rejects a drive-less URL such as `file:///main/main.sol` on /// Windows. The `url` crate cfg-gates its native conversion API off for /// `wasm32-unknown-unknown`, so wasm builds use the direct form as well. pub fn url_to_file_path(url: &url::Url) -> Option { @@ -93,10 +93,10 @@ mod url_to_file_path_tests { #[test] fn virtual_file_urls_are_platform_neutral() { for (url, expected) in [ - ("file:///main/main.solc", "/main/main.solc"), - ("file:///std/std.solc", "/std/std.solc"), - ("file:///ext/math/lib.solc", "/ext/math/lib.solc"), - ("file:///main/space%20name.solc", "/main/space name.solc"), + ("file:///main/main.sol", "/main/main.sol"), + ("file:///std/std.sol", "/std/std.sol"), + ("file:///ext/math/lib.sol", "/ext/math/lib.sol"), + ("file:///main/space%20name.sol", "/main/space name.sol"), ] { let url = url::Url::parse(url).expect("virtual file URL"); assert_eq!( @@ -109,7 +109,7 @@ mod url_to_file_path_tests { #[test] fn direct_file_url_decoding_rejects_a_remote_host() { - let remote = url::Url::parse("file://server/main/file.solc").expect("remote URL"); + let remote = url::Url::parse("file://server/main/file.sol").expect("remote URL"); assert!(decoded_local_file_url_path(&remote).is_none()); } diff --git a/crates/hir/src/nameres/diagnostic.rs b/crates/hir/src/nameres/diagnostic.rs index 54630baa..b3d82b4a 100644 --- a/crates/hir/src/nameres/diagnostic.rs +++ b/crates/hir/src/nameres/diagnostic.rs @@ -180,9 +180,9 @@ impl NameresDiagnostic { diagnostic } NameresDiagnostic::UndefinedClass { name, span } => { - Diagnostic::error(format!("undefined class: {name}")) + Diagnostic::error(format!("undefined trait: {name}")) .with_code(DiagnosticCode::NAMERES_UNDEFINED_CLASS) - .with_primary_label_span(span.clone(), Some("undefined class")) + .with_primary_label_span(span.clone(), Some("undefined trait")) } NameresDiagnostic::UnqualifiedConstructor { name, diff --git a/crates/hir/src/sema/ty.rs b/crates/hir/src/sema/ty.rs index aa48f368..882da7b8 100644 --- a/crates/hir/src/sema/ty.rs +++ b/crates/hir/src/sema/ty.rs @@ -358,7 +358,7 @@ impl<'db> Ty<'db> { name } else { format!( - "{name}({})", + "{name}<{}>", args.iter() .map(|arg| arg.display(db)) .collect::>() @@ -372,7 +372,7 @@ impl<'db> Ty<'db> { .map(|param| param.display(db)) .collect::>() .join(", "); - format!("({params}) -> {}", ret.display(db)) + format!("function({params}) returns ({})", ret.display(db)) } TyKind::Tuple(elems) => { if elems.is_empty() { @@ -388,7 +388,7 @@ impl<'db> Ty<'db> { ) } } - TyKind::Comptime(inner) => format!("comptime {}", inner.display(db)), + TyKind::Comptime(inner) => format!("comptime<{}>", inner.display(db)), } } } @@ -431,19 +431,15 @@ impl<'db> Pred<'db> { PredKind::InClass { class, main, args } => { let class = match class { ClassId::Builtin(class) => class.name().to_owned(), - ClassId::User(def) => { - format!( - "class:{}", - def.name(db) - .unwrap_or_else(|| format!("{:?}", def.kind(db))) - ) - } + ClassId::User(def) => def + .name(db) + .unwrap_or_else(|| format!("{:?}", def.kind(db))), }; if args.is_empty() { - format!("{}:{class}", main.display(db)) + format!("{}: {class}", main.display(db)) } else { format!( - "{}:{class}({})", + "{}: {class}<{}>", main.display(db), args.iter() .map(|arg| arg.display(db)) @@ -479,20 +475,21 @@ impl<'db> TyScheme<'db> { .iter() .map(|pred| pred.display(db)) .collect::>(); - let qualified = if preds.is_empty() { - body.ty(db).display(db) - } else { - format!("{} => {}", preds.join(", "), body.ty(db).display(db)) - }; - if self.binder_count(db) == 0 { - qualified + let ty = body.ty(db).display(db); + let mut displayed = if self.binder_count(db) == 0 { + ty } else { let vars = (0..self.binder_count(db)) .map(|_| "_".to_owned()) .collect::>() .join(", "); - format!("forall {vars}. {qualified}") + format!("<{vars}> {ty}") + }; + if !preds.is_empty() { + displayed.push_str(" where "); + displayed.push_str(&preds.join(", ")); } + displayed } } diff --git a/crates/hull/src/emit/mod.rs b/crates/hull/src/emit/mod.rs index a18da6c4..d2c7b517 100644 --- a/crates/hull/src/emit/mod.rs +++ b/crates/hull/src/emit/mod.rs @@ -64,7 +64,7 @@ const STORAGE_ARRAY_SLOT_HELPER: &str = "__solcore_storage_array_slot"; const STORAGE_MAPPING_VALUE_HELPER: &str = "__solcore_storage_mapping_value"; const MEMORY_ARRAY_INDEX_HELPER: &str = "__solcore_memory_array_index"; /// Error selector of the reference std's `Unimplemented` error -/// (`Error(0x6e128399)` raised by `unimplemented()` in std.solc). +/// (`Error(0x6e128399)` raised by `unimplemented()` in std.sol). const UNIMPLEMENTED_SELECTOR: &str = "0x6e128399"; const OUT_OF_BOUNDS_SELECTOR: &str = "0xb4120f14"; diff --git a/crates/hull/src/emit/storage.rs b/crates/hull/src/emit/storage.rs index ed34854b..9c990406 100644 --- a/crates/hull/src/emit/storage.rs +++ b/crates/hull/src/emit/storage.rs @@ -256,7 +256,7 @@ impl<'db> Emitter<'db> { } } - /// Mirrors the reference std's `storage(mapping(k, v)) : CanStore` + /// Mirrors the reference std's `storage v)>: CanStore` /// instance, whose `load`/`store` bodies are `unimplemented()`: touching a /// whole mapping field as a value compiles, but reverts at runtime with /// the std `Unimplemented` error, nominally yielding the field's base diff --git a/crates/hull/tests/smoke.rs b/crates/hull/tests/smoke.rs index c41d5abd..65f2337f 100644 --- a/crates/hull/tests/smoke.rs +++ b/crates/hull/tests/smoke.rs @@ -100,27 +100,27 @@ fn specialization_corpus_subset_emits_and_checks() { let cases = [ ( "spec/01id", - include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/spec/01id.solc"), + include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/spec/01id.sol"), ), ( "spec/00answer", - include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/spec/00answer.solc"), + include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/spec/00answer.sol"), ), ( "spec/022add", - include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/spec/022add.solc"), + include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/spec/022add.sol"), ), ( "spec/024arith", - include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/spec/024arith.solc"), + include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/spec/024arith.sol"), ), ( "spec/031maybe", - include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/spec/031maybe.solc"), + include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/spec/031maybe.sol"), ), ( "spec/047rgb", - include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/spec/047rgb.solc"), + include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/spec/047rgb.sol"), ), ]; let mut failures = Vec::new(); @@ -181,11 +181,11 @@ fn objectless_string_materializers_are_content_deduplicated() { let (db, output) = specialize_src_with_std( "objectless_string_materializer", r#" -import std.{memory, string}; +import {memory, string} from std; -function alpha() -> memory(string) { return "alpha"; } -function beta() -> memory(string) { return "beta"; } -function main() -> memory(string) { +function alpha() returns (memory) { return "alpha"; } +function beta() returns (memory) { return "beta"; } +function main() returns (memory) { alpha(); beta(); return "alpha"; @@ -215,10 +215,10 @@ fn contract_objects_receive_their_reachable_string_materializer() { let (db, output) = specialize_src_with_std( "contract_string_materializers", r#" -import std.{memory, string}; +import {memory, string} from std; -contract A { function main() -> memory(string) { return "shared"; } } -contract B { function main() -> memory(string) { return "shared"; } } +contract A { function main() returns (memory) { return "shared"; } } +contract B { function main() returns (memory) { return "shared"; } } "#, ); assert_eq!(output.diagnostics, Vec::new()); @@ -243,13 +243,13 @@ fn canonical_revert_literal_lowers_to_message_revert() { let hull = pretty_src_hull_with_std( "revert_literal", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract WithFallback { - public function answer() -> uint256 { return uint256(42); } + function answer() public returns (uint256) { return uint256(42); } - fallback() -> () { + fallback() { revertLit("fallback-was-called"); } } @@ -268,11 +268,11 @@ fn let_initializer_revert_literal_lowers_to_message_revert() { let hull = pretty_src_hull_with_std( "let_revert_literal", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract C { - fallback() -> () { + fallback() { let unreachable : () = revertLit("let-initializer"); return unreachable; } @@ -289,14 +289,14 @@ fn nested_revert_literal_lowers_before_its_containing_expression() { let hull = pretty_src_hull_with_std( "nested_revert_literal", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract C { - fallback() -> () { + fallback() { let raw : word; assembly { raw := callvalue() } - let result : () = if (raw == 0) then revertLit("nested") else (); + let result : () = ((raw == 0) ? revertLit("nested") : ()); return result; } } @@ -317,7 +317,7 @@ fn contract_without_runtime_main_defers_dispatch_to_specialization() { "dispatch_word", r#" contract C { - function main() -> () {} + function main() returns () {} } "#, ); @@ -348,7 +348,7 @@ contract C { fn dispatch_basic_fixture_uses_std_dispatch_main() { solcore_test_utils::run_in_large_stack(|| { let fixture = repo_root() - .join("crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/basic.solc"); + .join("crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/basic.sol"); let (db, output) = specialize_fixture(&fixture); assert_eq!(output.diagnostics, Vec::new()); let emitted = emit_module(db, &output.module, EmitOptions::default()); @@ -373,7 +373,7 @@ fn dispatch_basic_fixture_uses_std_dispatch_main() { fn deployment_objects_copy_runtime_and_guard_constructor_value() { let repo = repo_root(); let fixture = repo.join( - "crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty_no_constructor.solc", + "crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty_no_constructor.sol", ); let (db, output) = specialize_fixture(&fixture); assert_eq!(output.diagnostics, Vec::new()); @@ -389,7 +389,7 @@ fn deployment_objects_copy_runtime_and_guard_constructor_value() { ); let fixture = repo - .join("crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/nonpayable_ctor.solc"); + .join("crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/nonpayable_ctor.sol"); let (db, output) = specialize_fixture(&fixture); assert_eq!(output.diagnostics, Vec::new()); let emitted = emit_module(db, &output.module, EmitOptions::default()); @@ -420,8 +420,8 @@ fn deployment_objects_copy_runtime_and_guard_constructor_value() { .expect("runtime object"); assert!(!runtime.contains("_start"), "{hull}"); - let fixture = repo - .join("crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable_ctor.solc"); + let fixture = + repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable_ctor.sol"); let (db, output) = specialize_fixture(&fixture); assert_eq!(output.diagnostics, Vec::new()); let emitted = emit_module(db, &output.module, EmitOptions::default()); @@ -442,7 +442,7 @@ fn importless_nullary_constructor_uses_overlay_deployment_entry() { contract C { constructor() {} - function main() -> () { + function main() returns () { return (); } } @@ -468,15 +468,15 @@ fn std_constructor_overlay_decodes_appended_arguments_in_deployment_closure() { let (db, output) = specialize_src_with_std( "std_ctor_overlay_args", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract C { constructor(config : uint256) { let saved_config = config; } - public function echo(config : uint256) -> uint256 { return config; } + function echo(config : uint256) public returns (uint256) { return config; } } "#, ); @@ -519,11 +519,11 @@ fn std_dispatch_address_decode_rejects_dirty_high_bits() { let (db, output) = specialize_src_with_std( "std_address_dispatch", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract C { - public function id_address(a : address) -> address { return a; } + function id_address(a : address) public returns (address) { return a; } } "#, ); @@ -547,12 +547,12 @@ fn std_dispatch_explicit_fallback_stops_after_execution() { let (db, output) = specialize_src_with_std( "std_fallback_dispatch", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract C { - public function answer() -> uint256 { return uint256(42); } - fallback() -> () {} + function answer() public returns (uint256) { return uint256(42); } + fallback() {} } "#, ); @@ -568,7 +568,7 @@ contract C { fn for_loop_emits_hull_for_and_loop_control() { let repo = repo_root(); let fixture = - repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-break.solc"); + repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-break.sol"); let (db, output) = specialize_fixture(&fixture); assert_eq!(output.diagnostics, Vec::new()); let emitted = emit_module(db, &output.module, EmitOptions::default()); @@ -599,7 +599,7 @@ fn for_loop_emits_hull_for_and_loop_control() { fn word_storage_fixture_reaches_word_slot_ops() { let repo = repo_root(); let fixture = - repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/spec/120basicCounter.solc"); + repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/spec/120basicCounter.sol"); let (db, output) = specialize_fixture(&fixture); assert_eq!(output.diagnostics, Vec::new()); let emitted = emit_module(db, &output.module, EmitOptions::default()); @@ -620,22 +620,22 @@ fn word_storage_fixture_reaches_word_slot_ops() { #[test] fn single_constructor_matches_project_payloads_from_scrutinee() { - assert_fixture_emits_and_checks("cases/encoder1.solc"); - assert_fixture_has_no_unbound_alt("cases/mptc-multi-instance.solc"); + assert_fixture_emits_and_checks("cases/encoder1.sol"); + assert_fixture_has_no_unbound_alt("cases/mptc-multi-instance.sol"); } #[test] fn decision_tree_match_lowering_preserves_priority_nested_and_multi_scrutinee_cases() { for fixture in [ - "spec/033join.solc", - "spec/038food0.solc", - "cases/Option.solc", - "cases/option2.solc", - "cases/dot-pattern-nested-constructor.solc", - "cases/Logic.solc", - "cases/Ackermann.solc", - "cases/false-redundant-warning.solc", - "cases/super-class.solc", + "spec/033join.sol", + "spec/038food0.sol", + "cases/Option.sol", + "cases/option2.sol", + "cases/dot-pattern-nested-constructor.sol", + "cases/Logic.sol", + "cases/Ackermann.sol", + "cases/false-redundant-warning.sol", + "cases/super-class.sol", ] { assert_fixture_emits_without_match_lowering_regressions(fixture); } @@ -647,21 +647,25 @@ fn decision_tree_shape_preserves_specific_constructors_before_wildcard_defaults( "dwarves_runtime_shape", r#" contract Dwarves { - data Dwarf = Doc | Grumpy | Sleepy | Bashful | Happy | Sneezy | Dopey; + enum Dwarf {Doc , Grumpy , Sleepy , Bashful , Happy , Sneezy , Dopey} - public function fromEnum(c : Dwarf) -> word { + function fromEnum(c : Dwarf) public returns (word) { assembly { mstore(0, 0) } - match c { - | Dwarf.Doc => return 1; - | Dwarf.Grumpy => return 2; - | Dwarf.Sleepy => return 3; - | Dwarf.Bashful => return 4; - | Dwarf.Happy => return 5; - | _ => return 0; + match (c) { + case Dwarf.Doc { + return 1; + } + case Dwarf.Grumpy { return 2; } + case Dwarf.Sleepy { return 3; } + case Dwarf.Bashful { return 4; } + case Dwarf.Happy { return 5; } + default { + return 0; + } } } - function main() -> word { return fromEnum(Dwarf.Happy); } + function main() returns (word) { return fromEnum(Dwarf.Happy); } } "#, ); @@ -683,7 +687,7 @@ contract Dwarves { ], ); - let food0_actual = pretty_fixture_hull("spec/038food0.solc"); + let food0_actual = pretty_fixture_hull("spec/038food0.sol"); assert!( food0_actual.contains("function 038food0_FoodContract_main"), "{food0_actual}" @@ -693,20 +697,24 @@ contract Dwarves { let food0_shape = pretty_src_hull( "food0_runtime_shape", r#" -data Food = Curry | Beans | Other; -data CFood = Red(Food) | Green(Food) | Nocolor; +enum Food {Curry , Beans , Other} +enum CFood {Red(Food) , Green(Food) , Nocolor} -function fromEnum(x : CFood) -> word { +function fromEnum(x : CFood) returns (word) { assembly { mstore(0, 0) } - match x { - | CFood.Red(Food.Curry) => return 1; - | CFood.Green(Food.Beans) => return 42; - | _ => return 3; + match (x) { + case CFood.Red(Food.Curry) { + return 1; + } + case CFood.Green(Food.Beans) { return 42; } + default { + return 3; + } } } contract FoodContract { - function main() -> word { return fromEnum(CFood.Green(Food.Beans)); } + function main() returns (word) { return fromEnum(CFood.Green(Food.Beans)); } } "#, ); @@ -723,7 +731,7 @@ contract FoodContract { ], ); - let food = pretty_fixture_hull("spec/039food.solc"); + let food = pretty_fixture_hull("spec/039food.sol"); assert!( food.contains("function 039food_FoodContract_main") && food.contains("return 42"), "{food}" @@ -732,18 +740,22 @@ contract FoodContract { let wildcard_after_ctor = pretty_src_hull( "wildcard_after_ctor", r#" -data Tiny = A | B | C; +enum Tiny {A , B , C} contract C { - public function pick(t : Tiny) -> word { + function pick(t : Tiny) public returns (word) { assembly { mstore(0, 0) } - match t { - | Tiny.B => return 2; - | _ => return 9; + match (t) { + case Tiny.B { + return 2; + } + default { + return 9; + } } } - function main() -> word { return pick(Tiny.B); } + function main() returns (word) { return pick(Tiny.B); } } "#, ); @@ -757,9 +769,9 @@ contract C { #[test] fn cited_terminal_yul_fixtures_do_not_fail_missing_terminator() { for fixture in [ - "cases/yul-return.solc", - "cases/undefined.solc", - "cases/copytomem.solc", + "cases/yul-return.sol", + "cases/undefined.sol", + "cases/copytomem.sol", ] { let kinds = check_fixture_kinds(fixture); assert!( @@ -773,7 +785,7 @@ fn cited_terminal_yul_fixtures_do_not_fail_missing_terminator() { #[test] fn recursive_adt_layouts_are_cycle_safe() { - for fixture in ["cases/PeanoMatch.solc", "cases/listid.solc"] { + for fixture in ["cases/PeanoMatch.sol", "cases/listid.sol"] { assert_fixture_emits_and_checks(fixture); } } @@ -783,10 +795,14 @@ fn runtime_string_match_is_rejected_before_emission() { let (_db, output) = specialize_src( "string_literal_match", r#" -function main(s : string) -> word { - match s { - | "a" => return 1; - | _ => return 2; +function main(s : string) returns (word) { + match (s) { + case "a" { + return 1; + } + default { + return 2; + } } } "#, @@ -813,27 +829,33 @@ fn out_of_range_word_literals_wrap_in_hull_exprs_and_patterns() { "word_literal_wrap", &format!( r#" -import std.{{*}}; -import std.dispatch.{{*}}; +import * from std; +import * from std.dispatch; contract C {{ - function exact() -> word {{ + function exact() returns (word) {{ return {TWO_256}; }} - function plus() -> word {{ + function plus() returns (word) {{ return {TWO_256_PLUS_ONE}; }} - function pick(x : word) -> word {{ - match x {{ - | {TWO_256} => return 10; - | {TWO_256_PLUS_ONE} => return 11; - | _ => return 12; + function pick(x : word) returns (word) {{ + match (x) {{ + case {TWO_256} {{ + return 10; + }} + case {TWO_256_PLUS_ONE} {{ + return 11; + }} + default {{ + return 12; + }} }} }} - public function main() -> word {{ + function main() public returns (word) {{ let x : word = 0; assembly {{ x := calldataload(0) }} return exact() + plus() + pick(x); @@ -872,15 +894,19 @@ fn value_equal_word_patterns_share_one_canonical_switch_branch() { "equal_literal_spellings", r#" contract C { - function pick(x : word) -> word { - match x { - | 0x2a => return 111; - | 0042 => return 222; - | _ => return 333; + function pick(x : word) returns (word) { + match (x) { + case 0x2a { + return 111; + } + case 0042 { return 222; } + default { + return 333; + } } } - function main() -> word { + function main() returns (word) { let x : word = 0; assembly { x := calldataload(0) } return pick(x); @@ -907,23 +933,27 @@ fn evaluator_does_not_fold_past_unknown_return() { let hull = pretty_src_hull_with_std( "eval_return_unknown_abort", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract RetUnknown { - function pick(flag: bool, y: word) -> word { - match flag { - | true => return y; - | false => return 5; + function pick(flag: bool, y: word) returns (word) { + match (flag) { + case true { + return y; + } + case false { + return 5; + } } return 0; } - function get(x: word) -> word { + function get(x: word) returns (word) { return pick(true, x); } - public function main() -> word { + function main() public returns (word) { let x : word = 0; assembly { x := calldataload(0) } return get(x); @@ -942,17 +972,17 @@ fn evaluator_does_not_inline_storage_writing_helpers() { let mapping_hull = pretty_src_hull_with_std( "eval_storage_writer_mapping", r#" -import std.{*}; +import * from std; contract MappingWriter { - m: mapping(word, word); + m: mapping(word => word); - function set(k: word, v: word) -> word { + function set(k: word, v: word) returns (word) { m[k] = v; return v; } - public function main() -> word { + function main() public returns (word) { let a : word = set(1, 42); return m[1]; } @@ -974,17 +1004,17 @@ contract MappingWriter { let direct_hull = pretty_src_hull_with_std( "eval_storage_writer_direct", r#" -import std.{*}; +import * from std; contract DirectWriter { x: word; - function setv(v: word) -> word { + function setv(v: word) returns (word) { x = v; return v; } - public function main() -> word { + function main() public returns (word) { let a : word = setv(9); return x; } @@ -1012,13 +1042,13 @@ fn storage_index_assignment_materializes_slot_before_rhs() { let hull = pretty_src_hull_with_std( "storage_index_order", r#" -import std.{*}; +import * from std; contract StorageIndexOrder { counter: word; - m: mapping(word, word); + m: mapping(word => word); - function next() -> word { + function next() returns (word) { let cur: word = counter; let res: word; assembly { @@ -1028,7 +1058,7 @@ contract StorageIndexOrder { return res; } - public function main() -> word { + function main() public returns (word) { counter = 0; m[next()] = next(); return m[1]; @@ -1057,13 +1087,13 @@ contract StorageIndexOrder { let compound_hull = pretty_src_hull_with_std( "storage_index_compound", r#" -import std.{*}; +import * from std; contract StorageIndexCompound { counter: word; - m: mapping(word, word); + m: mapping(word => word); - function next() -> word { + function next() returns (word) { let cur: word = counter; let res: word; assembly { @@ -1073,7 +1103,7 @@ contract StorageIndexCompound { return res; } - public function main() -> word { + function main() public returns (word) { counter = 0; m[1] = 10; m[next()] += next(); @@ -1110,13 +1140,13 @@ fn new_compound_assignments_evaluate_storage_lhs_once() { let hull = pretty_src_hull_with_std( "storage_index_bit_not_compound", r#" -import std.{*}; +import * from std; contract StorageIndexBitNotCompound { counter: word; - m: mapping(word, word); + m: mapping(word => word); - function next() -> word { + function next() returns (word) { let cur: word = counter; let res: word; assembly { @@ -1126,7 +1156,7 @@ contract StorageIndexBitNotCompound { return res; } - public function main() -> word { + function main() public returns (word) { counter = 0; m[1] = 10; m[next()] ~=; @@ -1146,13 +1176,13 @@ contract StorageIndexBitNotCompound { for (name, operator) in [("mul", "*="), ("div", "/=")] { let source = format!( r#" -import std.{{*}}; +import * from std; contract StorageIndexBinaryCompound {{ counter: word; - m: mapping(word, word); + m: mapping(word => word); - function next() -> word {{ + function next() returns (word) {{ let cur: word = counter; let res: word; assembly {{ res := add(cur, 1) }} @@ -1160,7 +1190,7 @@ contract StorageIndexBinaryCompound {{ return res; }} - public function main() -> word {{ + function main() public returns (word) {{ counter = 0; m[1] = 12; m[next()] {operator} next(); @@ -1188,11 +1218,11 @@ fn evaluator_invalidates_storage_bindings_after_residual_calls() { contract StaleCall { x: word; - function setx() -> () { + function setx() returns () { x = 8; } - public function main() -> word { + function main() public returns (word) { x = 7; setx(); return x; @@ -1214,23 +1244,23 @@ fn audit_p0_match_scrutinees_are_materialized_exactly_once_even_for_default_bind for (name, arms) in [ ( "match_call_default_binding", - "| 0 => return 0; | n => return n;", + "case 0 { return 0; } case n { return n; }", ), - ("match_call_wildcard", "| _ => return 7;"), + ("match_call_wildcard", "default { return 7; }"), ] { let hull = pretty_src_hull( name, &format!( r#" -function read(x: word) -> word {{ +function read(x: word) returns (word) {{ let value: word; assembly {{ value := sload(x) }} return value; }} contract C {{ - public function main() -> word {{ - match read(0) {{ {arms} }} + function main() public returns (word) {{ + match (read(0)) {{ {arms} }} }} }} "# @@ -1250,7 +1280,7 @@ fn audit_p0_shadowing_let_materializes_its_initializer_before_declaration() { contract C { balance: word; - public function main() -> word { + function main() public returns (word) { let balance: word = balance; return balance; } @@ -1279,7 +1309,7 @@ fn audit_p0_for_initializer_let_remains_visible_after_the_loop() { contract C { i: word; - public function main() -> word { + function main() public returns (word) { for (let i: word; false; ) {} return i; } @@ -1296,19 +1326,19 @@ fn audit_p0_if_branch_let_is_hoisted_and_remains_a_local() { let hull = pretty_src_hull_with_std( "if_branch_let_scope", r#" -import std.{*}; +import * from std; contract C { x: word; - function f(flag: bool) -> word { + function f(flag: bool) returns (word) { if (flag && true) { let x: word = 7; } return x; } - public function main() -> word { return f(tobool(x)); } + function main() public returns (word) { return f(tobool(x)); } } "#, ); @@ -1345,11 +1375,11 @@ fn evaluator_invalidates_residual_assembly_branch_assignments() { let if_hull = pretty_src_hull_with_std( "eval_if_asm_assignment", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract IfAsm { - function f(b: bool) -> word { + function f(b: bool) returns (word) { let x : word = 1; if (b) { assembly { x := 5 } @@ -1357,7 +1387,7 @@ contract IfAsm { return x; } - public function main() -> word { + function main() public returns (word) { let raw : word = 0; assembly { raw := calldataload(0) } let b : bool = tobool(raw); @@ -1375,20 +1405,22 @@ contract IfAsm { let match_hull = pretty_src_hull_with_std( "eval_match_asm_assignment", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract MatchAsm { - function g(b: bool) -> word { + function g(b: bool) returns (word) { let x : word = 1; - match b { - | true => assembly { x := 5 } - | false => {} + match (b) { + case true { + assembly { x := 5 } + } + case false {} } return x; } - public function main() -> word { + function main() public returns (word) { let raw : word = 0; assembly { raw := calldataload(0) } let b : bool = tobool(raw); @@ -1407,9 +1439,9 @@ contract MatchAsm { #[test] fn cited_nested_layout_fixtures_check_cleanly() { for fixture in [ - "spec/032simplejoin.solc", - "spec/034cojoin.solc", - "spec/043fstsnd.solc", + "spec/032simplejoin.sol", + "spec/034cojoin.sol", + "spec/043fstsnd.sol", ] { let kinds = check_fixture_kinds(fixture); assert!(kinds.is_empty(), "{fixture}: {kinds:?}"); @@ -1454,24 +1486,24 @@ fn mapping_field_in_value_position_lowers_to_unimplemented_trap() { // `unimplemented()` runtime traps. This must not escape as an internal // hull-check error (previously: UndefinedVariable { name: "bal" }). let read_src = r#" -data mapping(key, value) = mapping(word); +enum mapping {mapping(word)} contract C { - bal : mapping(word, word); + bal : mapping(word => word); - public function main() -> word { + function main() public returns (word) { let b = bal; return 7; } } "#; let store_src = r#" -data mapping(key, value) = mapping(word); +enum mapping {mapping(word)} contract C { - bal : mapping(word, word); + bal : mapping(word => word); - public function main() -> word { + function main() public returns (word) { bal = bal; return 7; } @@ -1504,15 +1536,15 @@ fn aliased_mapping_field_keeps_the_storage_hash_helper_reachable() { let hull = pretty_src_hull_with_std( "aliased_mapping_field", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; -type Balances = mapping(uint256, uint256); +type Balances = mapping(uint256 => uint256); contract C { balances : Balances; - public function roundtrip(k:uint256, v:uint256) -> uint256 { + function roundtrip(k:uint256, v:uint256) public returns (uint256) { balances[k] = v; return balances[k]; } @@ -1529,27 +1561,27 @@ fn contract_field_offsets_honor_custom_storage_size_instances() { let hull = pretty_src_hull_with_std( "custom_contract_field_offset", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; -data Wide = Wide(word); +enum Wide {Wide(word)} -instance Wide:StorageSize { - function size(x:Proxy(Wide)) -> word { return 7; } +impl StorageSize { + function size(x:Proxy) returns (word) { return 7; } } -instance storage(Wide):CanStore(Wide) { - function store(r:storage(Wide), v:Wide) -> () { +impl CanStore,Wide> { + function store(r:storage, v:Wide) returns () { let slot:word; let value:word; - match r { | storage(x) => slot = x; } - match v { | Wide(x) => value = x; } + match (r) { case storage(x) { slot = x; }} + match (v) { case Wide(x) { value = x; }} assembly { sstore(slot, value) } } - function load(r:storage(Wide)) -> Wide { + function load(r:storage) returns (Wide) { let slot:word; let value:word; - match r { | storage(x) => slot = x; } + match (r) { case storage(x) { slot = x; }} assembly { value := sload(slot) } return Wide(value); } @@ -1559,7 +1591,7 @@ contract C { first : Wide; second : uint256; - public function setAndGet(v:uint256) -> uint256 { + function setAndGet(v:uint256) public returns (uint256) { second = v; return second; } @@ -1575,31 +1607,31 @@ fn compound_contract_field_access_replays_effectful_storage_size() { let hull = pretty_src_hull_with_std( "effectful_contract_field_offset", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; -data Wide = Wide(word); +enum Wide {Wide(word)} -instance Wide:StorageSize { - function size(x:Proxy(Wide)) -> word { +impl StorageSize { + function size(x:Proxy) returns (word) { let result:word; assembly { result := sload(99) } return result; } } -instance storage(Wide):CanStore(Wide) { - function store(r:storage(Wide), v:Wide) -> () { +impl CanStore,Wide> { + function store(r:storage, v:Wide) returns () { let slot:word; let value:word; - match r { | storage(x) => slot = x; } - match v { | Wide(x) => value = x; } + match (r) { case storage(x) { slot = x; }} + match (v) { case Wide(x) { value = x; }} assembly { sstore(slot, value) } } - function load(r:storage(Wide)) -> Wide { + function load(r:storage) returns (Wide) { let slot:word; let value:word; - match r { | storage(x) => slot = x; } + match (r) { case storage(x) { slot = x; }} assembly { value := sload(slot) } return Wide(value); } @@ -1610,7 +1642,7 @@ contract C { first : Wide; second : uint256; - public function bump(v:uint256) -> uint256 { + function bump(v:uint256) public returns (uint256) { second += v; return v; } @@ -1633,7 +1665,7 @@ fn specialize_src(name: &str, src: &str) -> (&'static TestDb, SpecializeOutput<' } fn parse_module<'db>(db: &'db TestDb, name: &str, src: &str) -> Module<'db> { - let url = format!("memory:///{name}.solc").parse().expect("valid URL"); + let url = format!("memory:///{name}.sol").parse().expect("valid URL"); let file = SourceFile::new(db, url, Some(src.to_owned())); parse_file_to_hir(db, file).module(db) } @@ -1645,7 +1677,7 @@ fn parse_module<'db>(db: &'db TestDb, name: &str, src: &str) -> Module<'db> { fn specialize_src_with_std(name: &str, src: &str) -> (&'static TestDb, SpecializeOutput<'static>) { let main_root = repo_root().join("target/hull-smoke-tmp").join(name); fs::create_dir_all(&main_root).expect("create temp main root"); - let path = main_root.join("main.solc"); + let path = main_root.join("main.sol"); fs::write(&path, src).expect("write temp source"); specialize_fixture(&path) } @@ -1707,7 +1739,7 @@ fn collect_module_fs_snapshot( }; for entry in entries.flatten() { let path = entry.path(); - if path.extension().and_then(|extension| extension.to_str()) == Some("solc") { + if path.extension().and_then(|extension| extension.to_str()) == Some("sol") { if path.is_file() { existing_files.insert(path.clone()); } @@ -1861,34 +1893,34 @@ fn dynamic_array_helpers_check_bounds_and_preserve_typedef_representations() { let hull = pretty_src_hull_with_std( "array-checked-typedef", r#" -import std.{*}; +import * from std; -data Shifted = Shifted(word); -instance Shifted:Typedef(word) { - function rep(x:Shifted) -> word { - match x { | Shifted(w) => return w + 100; } +enum Shifted {Shifted(word)} +impl Typedef { + function rep(x:Shifted) returns (word) { + match (x) { case Shifted(w) { return w + 100; }} } - function abs(w:word) -> Shifted { return Shifted(w - 100); } + function abs(w:word) returns (Shifted) { return Shifted(w - 100); } } -data Second = Second(word); -instance Second:Typedef(word) { - function rep(x:Second) -> word { - match x { | Second(w) => return w + 1; } +enum Second {Second(word)} +impl Typedef { + function rep(x:Second) returns (word) { + match (x) { case Second(w) { return w + 1; }} } - function abs(w:word) -> Second { return Second(w - 1); } + function abs(w:word) returns (Second) { return Second(w - 1); } } -type Numbers = array(uint256); +type Numbers = array; contract CheckedArrays { xs : Numbers; seed : word; - function main() -> word { - let m : memory(DynArray(Shifted)) = [Shifted(3), Shifted(4)]; + function main() returns (word) { + let m : memory> = [Shifted(3), Shifted(4)]; xs = [10, 20]; - let p : storage(Numbers) = xs; + let p : storage = xs; let idx : Second = Second(seed); p[idx] += uint256(1); let picked : Shifted = m[idx]; @@ -1926,10 +1958,10 @@ fn storage_array_slot_helper_is_reachable_without_array_fields() { let hull = pretty_src_hull_with_std( "array-local-storage-ref", r#" -import std.{*}; +import * from std; -function main() -> uint256 { - let xs : storage(array(uint256)) = storage(0x100); +function main() returns (uint256) { + let xs : storage> = storage(0x100); return xs[uint256(0)]; } "#, @@ -1947,15 +1979,15 @@ fn nested_and_dynamic_storage_array_values_emit_deep_conversion_paths() { let hull = pretty_src_hull_with_std( "array-nested-dynamic", r#" -import std.{*}; +import * from std; contract CollectionArray { - flags : array(bool); - grid : array(array(uint256)); - names : array(string); - backup : array(string); + flags : array; + grid : array>; + names : array; + backup : array; - function main() -> uint256 { + function main() returns (uint256) { Array.setLength(flags, uint256(0)); ArrayPush.push(flags, true); let flag : bool = flags[uint256(0)]; @@ -1963,17 +1995,17 @@ contract CollectionArray { Array.setLength(grid, uint256(1)); ArrayPush.push(grid[uint256(0)], uint256(7)); grid[uint256(0)][uint256(0)] = uint256(9); - let row : storage(array(uint256)) = grid[uint256(0)]; + let row : storage> = grid[uint256(0)]; ArrayPush.push(row, uint256(11)); - let s : memory(string) = "hello"; + let s : memory = "hello"; ArrayPush.push(names, s); names[uint256(0)] = s; - let loaded : memory(string) = names[uint256(0)]; + let loaded : memory = names[uint256(0)]; backup = names; - let copied : memory(string) = backup[uint256(0)]; + let copied : memory = backup[uint256(0)]; - if flag { + if (flag) { return row[uint256(1)] + uint256(strlen(loaded)) + uint256(strlen(copied)); } return uint256(0); @@ -1994,13 +2026,13 @@ fn public_dynamic_array_return_emits_abi_copy() { let hull = pretty_src_hull_with_std( "array-public-return", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract PublicArray { constructor() {} - public function values() -> memory(DynArray(uint256)) { + function values() public returns (memory>) { return [1, 2, 3]; } } @@ -2012,24 +2044,23 @@ contract PublicArray { } const OPERATOR_CUSTOM_UINT_ADD: &str = r#" -import std.{*}; +import * from std; -data uint = u(word); +enum uint {u(word)} -instance uint:Add { - function add(x:uint, y:uint) -> uint { +impl Add { + function add(x:uint, y:uint) returns (uint) { return uint.u(42); } } -function unwrap(x:uint) -> word { - match x { - | uint.u(w) => return w; - } +function unwrap(x:uint) returns (word) { + match (x) { + case uint.u(w) { return w; }} } contract C { - public function main() -> word { + function main() public returns (word) { let a:uint = uint.u(1); let b:uint = uint.u(2); let c:uint = a + b; @@ -2039,34 +2070,33 @@ contract C { "#; const OPERATOR_CUSTOM_BIT_NOT: &str = r#" -import std.{*}; +import * from std; -data mask = mask(word); +enum mask {mask(word)} -instance mask:BitNot { - function bnot(x:mask) -> mask { +impl BitNot { + function bnot(x:mask) returns (mask) { return mask(42); } } -function unwrap(x:mask) -> word { - match x { - | mask(w) => return w; - } +function unwrap(x:mask) returns (word) { + match (x) { + case mask(w) { return w; }} } contract C { - public function main() -> word { + function main() public returns (word) { return unwrap(~mask(0)); } } "#; const OPERATOR_ALL_COMPOUND: &str = r#" -import std.{*}; +import * from std; contract C { - public function main() -> word { + function main() public returns (word) { let acc:word = 6; acc += 4; acc -= 3; @@ -2084,26 +2114,24 @@ contract C { "#; const OPERATOR_METERS_ADD: &str = r#" -import std.{*}; +import * from std; -data meters = meters(word); +enum meters {meters(word)} -instance meters:Add { - function add(x:meters, y:meters) -> meters { - match x, y { - | meters(xw), meters(yw) => return meters(addWord(xw, yw)); - } +impl Add { + function add(x:meters, y:meters) returns (meters) { + match (x, y) { + case (meters(xw), meters(yw)) { return meters(addWord(xw, yw)); }} } } -function unwrap(x:meters) -> word { - match x { - | meters(w) => return w; - } +function unwrap(x:meters) returns (word) { + match (x) { + case meters(w) { return w; }} } contract C { - public function main() -> word { + function main() public returns (word) { let a:meters = meters(1); let b:meters = meters(2); let c:meters = a + b; @@ -2113,28 +2141,26 @@ contract C { "#; const OPERATOR_METERS_ORD: &str = r#" -import std.{*}; +import * from std; -data meters = meters(word); +enum meters {meters(word)} -instance meters:Eq { - function eq(x:meters, y:meters) -> bool { - match x, y { - | meters(xw), meters(yw) => return eqWord(xw, yw); - } +impl Eq { + function eq(x:meters, y:meters) returns (bool) { + match (x, y) { + case (meters(xw), meters(yw)) { return eqWord(xw, yw); }} } } -instance meters:Ord { - function gt(x:meters, y:meters) -> bool { - match x, y { - | meters(xw), meters(yw) => return gtWord(xw, yw); - } +impl Ord { + function gt(x:meters, y:meters) returns (bool) { + match (x, y) { + case (meters(xw), meters(yw)) { return gtWord(xw, yw); }} } } contract C { - public function main() -> word { + function main() public returns (word) { let a:meters = meters(1); let b:meters = meters(2); if (a < b) { @@ -2147,10 +2173,10 @@ contract C { "#; const OPERATOR_WORD_ADD: &str = r#" -import std.{*}; +import * from std; contract C { - public function main() -> word { + function main() public returns (word) { return 1 + 2; } } diff --git a/crates/hull/tests/snapshots.rs b/crates/hull/tests/snapshots.rs index 34e1006c..82ecc0c2 100644 --- a/crates/hull/tests/snapshots.rs +++ b/crates/hull/tests/snapshots.rs @@ -36,7 +36,7 @@ impl parser::Db for TestDb {} fn test_span<'db>(db: &'db TestDb) -> Span<'db> { let file = SourceFile::new( db, - "memory:///hull_snapshots.solc".parse().expect("valid URL"), + "memory:///hull_snapshots.sol".parse().expect("valid URL"), Some(String::new()), ); Span::new(AnchorId::root(db, file), Offset::new(0), Offset::new(0)) @@ -70,6 +70,7 @@ fn identity_function_snapshot() { assert_eq!(check_program_with_db(&db, &program), Vec::new()); assert_eq!( pretty_program(&db, &program), + // syntax-migration: preserve-next-literal "function id (x : word) -> word {\n return x\n}\n" ); } @@ -410,6 +411,7 @@ fn add1_contract_object_snapshot() { " }\n", " object \"Add1_deployed\" {\n", " code {\n", + // syntax-migration: preserve-next-literal " function main () -> word {\n", " let res : word\n", " assembly {\n", diff --git a/crates/lsp/src/code_actions.rs b/crates/lsp/src/code_actions.rs index 48b2d265..854b4547 100644 --- a/crates/lsp/src/code_actions.rs +++ b/crates/lsp/src/code_actions.rs @@ -634,7 +634,7 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } @@ -671,8 +671,7 @@ mod tests { #[test] fn typo_diagnostic_becomes_nonpreferred_quick_fix() { - let source = - "function value() -> word { return 1; }\nfunction main() -> word { return vaue(); }\n"; + let source = "function value() returns (word) { return 1; }\nfunction main() returns (word) { return vaue(); }\n"; let (world, uri) = world_with_main(source); let diagnostic = undefined_name_diagnostic(&world, &uri); let requested_range = diagnostic.range; @@ -699,10 +698,10 @@ mod tests { #[test] fn real_uri_and_utf16_range_are_preserved() { - let source = "// 😀\nfunction value() -> word { return 1; }\nfunction main() -> word { return vaue(); }\n"; + let source = "// 😀\nfunction value() returns (word) { return 1; }\nfunction main() returns (word) { return vaue(); }\n"; let root = Url::parse("file:///tmp/solcore%20project/").expect("root uri"); let uri = - Url::parse("file:///tmp/solcore%20project/src/%E6%95%B0.solc").expect("document uri"); + Url::parse("file:///tmp/solcore%20project/src/%E6%95%B0.sol").expect("document uri"); let mut world = WorldState::new(); assert_eq!( world.load_workspace_documents(root, [(uri.clone(), source.to_owned())]), @@ -726,8 +725,7 @@ mod tests { #[test] fn stale_code_or_range_does_not_receive_a_fix() { - let source = - "function value() -> word { return 1; }\nfunction main() -> word { return vaue(); }\n"; + let source = "function value() returns (word) { return 1; }\nfunction main() returns (word) { return vaue(); }\n"; let (world, uri) = world_with_main(source); let diagnostic = undefined_name_diagnostic(&world, &uri); @@ -748,7 +746,7 @@ mod tests { #[test] fn typed_missing_import_lookup_requires_the_same_diagnostic_code() { - let (world, uri) = world_with_main("function main() -> word { return missing; }\n"); + let (world, uri) = world_with_main("function main() returns (word) { return missing; }\n"); let db = world.db(); let module = module_id_for_uri(&world, db, &uri).expect("main module"); let mut diagnostic = compute_vfs_diagnostics(&world, &uri) @@ -773,8 +771,7 @@ mod tests { #[test] fn request_range_and_only_filter_are_respected() { - let source = - "function value() -> word { return 1; }\nfunction main() -> word { return vaue(); }\n"; + let source = "function value() returns (word) { return 1; }\nfunction main() returns (word) { return vaue(); }\n"; let (world, uri) = world_with_main(source); let diagnostic = undefined_name_diagnostic(&world, &uri); @@ -813,11 +810,12 @@ mod tests { #[test] fn unknown_import_item_uses_compiler_suggestion() { - let main = "import math.{doubl};\nfunction main() -> word { return 1; }\n"; - let math = "function double(x: word) -> word { return x + x; }\nexport { double };\n"; + let main = "import {doubl} from math;\nfunction main() returns (word) { return 1; }\n"; + let math = + "function double(x: word) returns (word) { return x + x; }\nexport { double };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(math_uri, math.to_owned())); let diagnostic = compute_diagnostics(&world, &main_uri) @@ -856,11 +854,12 @@ mod tests { #[test] fn module_path_typo_is_nonpreferred() { - let main = "import mth;\nfunction main() -> word { return 1; }\n"; - let math = "function double(x: word) -> word { return x + x; }\nexport { double };\n"; + let main = "import mth;\nfunction main() returns (word) { return 1; }\n"; + let math = + "function double(x: word) returns (word) { return x + x; }\nexport { double };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(math_uri, math.to_owned())); let diagnostic = compute_diagnostics(&world, &main_uri) @@ -899,11 +898,11 @@ mod tests { #[test] fn qualified_name_suggestion_replaces_only_the_leaf() { - let main = "import math;\nfunction main(x: math.Vaue) -> word { return 1; }\n"; - let math = "data Value = Value(word);\nexport { Value(*) };\n"; + let main = "import math;\nfunction main(x: math.Vaue) returns (word) { return 1; }\n"; + let math = "enum Value {Value(word)}\nexport { Value(*) };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(math_uri, math.to_owned())); let diagnostic = compute_diagnostics(&world, &main_uri) @@ -942,11 +941,12 @@ mod tests { #[test] fn qualified_name_with_wrong_qualifier_has_no_partial_fix() { - let main = "import math as M;\nfunction main(x: N.Value) -> word { return 1; }\n"; - let math = "data Value = Value(word);\nexport { Value(*) };\n"; + let main = + "import * as M from math;\nfunction main(x: N.Value) returns (word) { return 1; }\n"; + let math = "enum Value {Value(word)}\nexport { Value(*) };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(math_uri, math.to_owned())); let diagnostic = compute_diagnostics(&world, &main_uri) @@ -973,11 +973,12 @@ mod tests { #[test] fn qualified_name_with_wrong_qualifier_and_leaf_has_no_partial_fix() { - let main = "import math as M;\nfunction main(x: N.Vaue) -> word { return 1; }\n"; - let math = "data Value = Value(word);\nexport { Value(*) };\n"; + let main = + "import * as M from math;\nfunction main(x: N.Vaue) returns (word) { return 1; }\n"; + let math = "enum Value {Value(word)}\nexport { Value(*) };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(math_uri, math.to_owned())); let diagnostic = compute_diagnostics(&world, &main_uri) @@ -1004,7 +1005,7 @@ mod tests { #[test] fn exact_constructor_qualification_is_preferred() { - let source = "data Option = None | Some(word);\nfunction main(x: word) -> Option { return Some(x); }\n"; + let source = "enum Option {None , Some(word)}\nfunction main(x: word) returns (Option) { return Some(x); }\n"; let (world, uri) = world_with_main(source); let diagnostic = compute_diagnostics(&world, &uri) .into_iter() @@ -1038,7 +1039,7 @@ mod tests { #[test] fn no_op_suggestion_edits_are_not_emitted() { - let source = "function main() -> word { return 1; }\n"; + let source = "function main() returns (word) { return 1; }\n"; let (world, uri) = world_with_main(source); let suggestion = DiagnosticSuggestion { title: "No change".to_owned(), @@ -1058,11 +1059,11 @@ mod tests { #[test] fn unique_exported_term_gets_a_preferred_auto_import() { - let main = "function main() -> word { return value(); }\n"; - let math = "function value() -> word { return 1; }\nexport { value };\n"; + let main = "function main() returns (word) { return value(); }\n"; + let math = "function value() returns (word) { return 1; }\nexport { value };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(math_uri.clone(), math.to_owned())); let diagnostic = undefined_name_diagnostic(&world, &main_uri); @@ -1086,11 +1087,11 @@ mod tests { .and_then(|changes| changes.get(&main_uri)), Some(&vec![TextEdit { range: Range::new(Position::new(0, 0), Position::new(0, 0)), - new_text: "import lib.math.{value};\n".to_owned(), + new_text: "import {value} from lib.math;\n".to_owned(), }]) ); - let fixed = format!("import lib.math.{{value}};\n{main}"); + let fixed = format!("import {{value}} from lib.math;\n{main}"); let mut fixed_world = WorldState::new(); assert!(fixed_world.open_document(main_uri.clone(), fixed)); assert!(fixed_world.open_document(math_uri, math.to_owned())); @@ -1104,17 +1105,17 @@ mod tests { #[test] fn multiple_auto_import_providers_are_sorted_and_nonpreferred() { - let main = "function main() -> word { return value(); }\n"; - let provider = "function value() -> word { return 1; }\nexport { value };\n"; + let main = "function main() returns (word) { return value(); }\n"; + let provider = "function value() returns (word) { return 1; }\nexport { value };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document( - Url::parse("file:///main/math.solc").expect("math uri"), + Url::parse("file:///main/math.sol").expect("math uri"), provider.to_owned() )); assert!(world.open_document( - Url::parse("file:///main/util.solc").expect("util uri"), + Url::parse("file:///main/util.sol").expect("util uri"), provider.to_owned() )); let diagnostic = undefined_name_diagnostic(&world, &main_uri); @@ -1157,13 +1158,14 @@ mod tests { } fn auto_import_extends_an_existing_selective_import_inner() { - let main = "import lib.math.{other};\nfunction main() -> word { return value(); }\n"; - let math = "function other() -> word { return 0; }\nfunction value() -> word { return 1; }\nexport { other, value };\n"; + let main = + "import {other} from lib.math;\nfunction main() returns (word) { return value(); }\n"; + let math = "function other() returns (word) { return 0; }\nfunction value() returns (word) { return 1; }\nexport { other, value };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document( - Url::parse("file:///main/math.solc").expect("math uri"), + Url::parse("file:///main/math.sol").expect("math uri"), math.to_owned() )); let diagnostic = undefined_name_diagnostic(&world, &main_uri); @@ -1192,14 +1194,14 @@ mod tests { } #[test] - fn exported_types_and_classes_are_auto_importable() { - let type_main = "function keep(x: Token) -> Token { return x; }\n"; - let type_provider = "data Token = Token(word);\nexport { Token };\n"; + fn exported_types_and_traits_are_auto_importable() { + let type_main = "function keep(x: Token) returns (Token) { return x; }\n"; + let type_provider = "enum Token {Token(word)}\nexport { Token };\n"; let mut type_world = WorldState::new(); - let type_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let type_uri = Url::parse("file:///main/main.sol").expect("main uri"); assert!(type_world.open_document(type_uri.clone(), type_main.to_owned())); assert!(type_world.open_document( - Url::parse("file:///main/model.solc").expect("model uri"), + Url::parse("file:///main/model.sol").expect("model uri"), type_provider.to_owned() )); let type_diagnostic = diagnostic_with_code( @@ -1219,13 +1221,13 @@ mod tests { "Import `Token` from `lib.model`" ); - let class_main = "forall a. a:Comparable =>\nfunction keep(x: a) -> a { return x; }\n"; - let class_provider = "forall a. class a:Comparable {\n function compare(x: a, y: a) -> word;\n}\nexport { Comparable };\n"; + let class_main = "function keep(x: a) returns (a) where a: Comparable { return x; }\n"; + let class_provider = "trait Comparable {\n function compare(x: a, y: a) returns (word) ;\n}\nexport { Comparable };\n"; let mut class_world = WorldState::new(); - let class_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let class_uri = Url::parse("file:///main/main.sol").expect("main uri"); assert!(class_world.open_document(class_uri.clone(), class_main.to_owned())); assert!(class_world.open_document( - Url::parse("file:///main/classes.solc").expect("classes uri"), + Url::parse("file:///main/classes.sol").expect("classes uri"), class_provider.to_owned() )); let class_diagnostic = diagnostic_with_code( @@ -1239,7 +1241,7 @@ mod tests { class_diagnostic.range, &context(class_diagnostic), ) - .expect("class code actions"); + .expect("trait code actions"); assert_eq!( action(&class_actions).title, "Import `Comparable` from `lib.classes`" @@ -1247,13 +1249,13 @@ mod tests { } #[test] - fn generated_dispatch_missing_type_and_class_have_auto_import_candidates() { - let source = r#"import std.{*}; -import std.opcodes.{address as address_}; + fn generated_dispatch_missing_type_and_trait_have_auto_import_candidates() { + let source = r#"import * from std; +import {address as address_} from std.opcodes; contract C { constructor() {} - public function nothing() -> () {} + function nothing() public returns () {} } "#; let (world, uri) = world_with_main(source); @@ -1279,13 +1281,13 @@ contract C { #[test] fn generated_dispatch_missing_terms_have_auto_import_candidates() { - let source = r#"import std.{*}; -import std.opcodes.{address as address_}; -import std.dispatch.{NonPayable, SigString}; + let source = r#"import * from std; +import {address as address_} from std.opcodes; +import {NonPayable, SigString} from std.dispatch; contract C { constructor() {} - public function nothing() -> () {} + function nothing() public returns () {} } "#; let (world, uri) = world_with_main(source); @@ -1303,10 +1305,30 @@ contract C { "expected generated term diagnostics" ); + for message in [ + "undefined name: Contract", + "undefined name: Fallback", + "undefined name: Method", + ] { + let diagnostic = diagnostics + .iter() + .find(|diagnostic| diagnostic.message.starts_with(message)) + .unwrap_or_else(|| panic!("missing diagnostic `{message}`")) + .clone(); + let actions = + handle_code_action(&world, &uri, diagnostic.range, &context(diagnostic.clone())) + .expect("code actions"); + assert!( + actions.iter().all(|action| !matches!( + action, + CodeActionOrCommand::CodeAction(action) + if action.title == "Import all from `std.dispatch`" + )), + "a selective import must not be rewritten into a mixed name/wildcard selector: {actions:#?}" + ); + } + for (message, expected_title) in [ - ("undefined name: Contract", "Import all from `std.dispatch`"), - ("undefined name: Fallback", "Import all from `std.dispatch`"), - ("undefined name: Method", "Import all from `std.dispatch`"), ( "undefined name: RunContract", "Import `RunContract` from `std.dispatch`", @@ -1345,13 +1367,14 @@ contract C { #[test] fn resolved_member_errors_do_not_offer_term_imports() { let field_main = - "data Local = Present;\nfunction main() -> word { return Local.missing; }\n"; - let exported_missing = "function missing() -> word { return 1; }\nexport { missing };\n"; + "enum Local {Present}\nfunction main() returns (word) { return Local.missing; }\n"; + let exported_missing = + "function missing() returns (word) { return 1; }\nexport { missing };\n"; let mut field_world = WorldState::new(); - let field_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let field_uri = Url::parse("file:///main/main.sol").expect("main uri"); assert!(field_world.open_document(field_uri.clone(), field_main.to_owned())); assert!(field_world.open_document( - Url::parse("file:///main/symbols.solc").expect("symbols uri"), + Url::parse("file:///main/symbols.sol").expect("symbols uri"), exported_missing.to_owned() )); let field_diagnostic = undefined_name_diagnostic(&field_world, &field_uri); @@ -1368,17 +1391,17 @@ contract C { #[test] fn resolved_module_member_does_not_offer_a_constructor_import() { - let main = "import lib.foo as Math;\nfunction main() -> word { return Math.Value(1); }\n"; + let main = "import * as Math from lib.foo;\nfunction main() returns (word) { return Math.Value(1); }\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document( - Url::parse("file:///main/foo.solc").expect("foo uri"), - "function other() -> word { return 0; }\nexport { other };\n".to_owned() + Url::parse("file:///main/foo.sol").expect("foo uri"), + "function other() returns (word) { return 0; }\nexport { other };\n".to_owned() )); assert!(world.open_document( - Url::parse("file:///main/model.solc").expect("model uri"), - "data Math = Value(word);\nexport { Math(*) };\n".to_owned() + Url::parse("file:///main/model.sol").expect("model uri"), + "enum Math {Value(word)}\nexport { Math(*) };\n".to_owned() )); let diagnostic = undefined_name_diagnostic(&world, &main_uri); @@ -1392,11 +1415,11 @@ contract C { #[test] fn qualified_constructor_expression_imports_the_visible_type() { - let main = "function main() -> word { let option = Option.Some(1); return 1; }\n"; - let provider = "data Option = None | Some(word);\nexport { Option(*) };\n"; + let main = "function main() returns (word) { let option = Option.Some(1); return 1; }\n"; + let provider = "enum Option {None , Some(word)}\nexport { Option(*) };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let model_uri = Url::parse("file:///main/model.solc").expect("model uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let model_uri = Url::parse("file:///main/model.sol").expect("model uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(model_uri.clone(), provider.to_owned())); let diagnostic = undefined_name_diagnostic(&world, &main_uri); @@ -1420,14 +1443,14 @@ contract C { .and_then(|changes| changes.get(&main_uri)), Some(&vec![TextEdit { range: Range::new(Position::new(0, 0), Position::new(0, 0)), - new_text: "import lib.model.{Option};\n".to_owned(), + new_text: "import {Option} from lib.model;\n".to_owned(), }]) ); let mut fixed_world = WorldState::new(); assert!(fixed_world.open_document( main_uri.clone(), - format!("import lib.model.{{Option}};\n{main}"), + format!("import {{Option}} from lib.model;\n{main}"), )); assert!(fixed_world.open_document(model_uri, provider.to_owned())); assert!(compute_diagnostics(&fixed_world, &main_uri).iter().all( @@ -1440,11 +1463,11 @@ contract C { #[test] fn qualified_constructor_pattern_imports_the_visible_type() { - let main = "function main(x: word) -> word {\n match x {\n | Option.Some(value) => return value;\n | _ => return 0;\n }\n}\n"; - let provider = "data Option = None | Some(word);\nexport { Option(*) };\n"; + let main = "function main(x: word) returns (word) {\n match (x) {\n case Option.Some(value) { return value; }\ndefault { return 0; }}\n}\n"; + let provider = "enum Option {None , Some(word)}\nexport { Option(*) };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let model_uri = Url::parse("file:///main/model.solc").expect("model uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let model_uri = Url::parse("file:///main/model.sol").expect("model uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(model_uri, provider.to_owned())); let diagnostic = undefined_name_diagnostic(&world, &main_uri); @@ -1457,13 +1480,13 @@ contract C { #[test] fn resolved_pattern_type_does_not_import_a_conflicting_constructor_owner() { - let main = "data Option = None;\nfunction main(x: word) -> word {\n match x {\n | Option.Some(value) => return value;\n | _ => return 0;\n }\n}\n"; - let provider = "data Option = None | Some(word);\nexport { Option(*) };\n"; + let main = "enum Option {None}\nfunction main(x: word) returns (word) {\n match (x) {\n case Option.Some(value) { return value; }\ndefault { return 0; }}\n}\n"; + let provider = "enum Option {None , Some(word)}\nexport { Option(*) };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document( - Url::parse("file:///main/model.solc").expect("model uri"), + Url::parse("file:///main/model.sol").expect("model uri"), provider.to_owned() )); let diagnostic = undefined_name_diagnostic(&world, &main_uri); @@ -1478,13 +1501,13 @@ contract C { #[test] fn qualified_constructor_import_requires_that_constructor_to_be_exported() { - let main = "function main() -> word { let option = Option.Some(1); return 1; }\n"; - let provider = "data Option = None | Some(word);\nexport { Option(None) };\n"; + let main = "function main() returns (word) { let option = Option.Some(1); return 1; }\n"; + let provider = "enum Option {None , Some(word)}\nexport { Option(None) };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document( - Url::parse("file:///main/model.solc").expect("model uri"), + Url::parse("file:///main/model.sol").expect("model uri"), provider.to_owned() )); let diagnostic = undefined_name_diagnostic(&world, &main_uri); @@ -1497,13 +1520,13 @@ contract C { #[test] fn module_import_requires_an_immediate_term_member() { - let main = "function main() -> word { return math.Value; }\n"; - let provider = "data Value = Value(word);\nexport { Value };\n"; + let main = "function main() returns (word) { return math.Value; }\n"; + let provider = "enum Value {Value(word)}\nexport { Value };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document( - Url::parse("file:///main/math.solc").expect("math uri"), + Url::parse("file:///main/math.sol").expect("math uri"), provider.to_owned() )); let diagnostic = undefined_name_diagnostic(&world, &main_uri); @@ -1518,11 +1541,11 @@ contract C { #[test] fn missing_module_qualifier_gets_a_plain_module_import() { - let main = "function main() -> word { return math.value(); }\n"; - let provider = "function value() -> word { return 1; }\nexport { value };\n"; + let main = "function main() returns (word) { return math.value(); }\n"; + let provider = "function value() returns (word) { return 1; }\nexport { value };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(math_uri.clone(), provider.to_owned())); let diagnostic = undefined_name_diagnostic(&world, &main_uri); @@ -1563,13 +1586,13 @@ contract C { #[test] fn module_import_stays_separate_from_an_existing_selective_import() { - let main = "import lib.math.{other};\nfunction main() -> word { return math.value(); }\n"; - let provider = "function other() -> word { return 0; }\nfunction value() -> word { return 1; }\nexport { other, value };\n"; + let main = "import {other} from lib.math;\nfunction main() returns (word) { return math.value(); }\n"; + let provider = "function other() returns (word) { return 0; }\nfunction value() returns (word) { return 1; }\nexport { other, value };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document( - Url::parse("file:///main/math.solc").expect("math uri"), + Url::parse("file:///main/math.sol").expect("math uri"), provider.to_owned() )); let diagnostic = undefined_name_diagnostic(&world, &main_uri); @@ -1597,13 +1620,13 @@ contract C { #[test] fn module_import_does_not_conflict_with_an_unqualified_term() { - let main = "import lib.math.{other};\nfunction math() -> word { return 0; }\nfunction main() -> word { return math.value(); }\n"; - let provider = "function other() -> word { return 0; }\nfunction value() -> word { return 1; }\nexport { other, value };\n"; + let main = "import {other} from lib.math;\nfunction math() returns (word) { return 0; }\nfunction main() returns (word) { return math.value(); }\n"; + let provider = "function other() returns (word) { return 0; }\nfunction value() returns (word) { return 1; }\nexport { other, value };\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document( - Url::parse("file:///main/math.solc").expect("math uri"), + Url::parse("file:///main/math.sol").expect("math uri"), provider.to_owned() )); let diagnostic = undefined_name_diagnostic(&world, &main_uri); @@ -1616,17 +1639,18 @@ contract C { #[test] fn module_import_does_not_override_an_existing_path_prefix() { - let main = "import lib.math.deep;\nfunction main() -> word { return math.value(); }\n"; + let main = + "import lib.math.deep;\nfunction main() returns (word) { return math.value(); }\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document( - Url::parse("file:///main/math/deep.solc").expect("deep uri"), - "function old() -> word { return 0; }\nexport { old };\n".to_owned() + Url::parse("file:///main/math/deep.sol").expect("deep uri"), + "function old() returns (word) { return 0; }\nexport { old };\n".to_owned() )); assert!(world.open_document( - Url::parse("file:///main/other/math.solc").expect("candidate uri"), - "function value() -> word { return 1; }\nexport { value };\n".to_owned() + Url::parse("file:///main/other/math.sol").expect("candidate uri"), + "function value() returns (word) { return 1; }\nexport { value };\n".to_owned() )); let diagnostic = undefined_name_diagnostic(&world, &main_uri); @@ -1649,11 +1673,11 @@ contract C { let right_path = base.join("right"); let left_root = Url::from_directory_path(&left_path).expect("left root"); let right_root = Url::from_directory_path(&right_path).expect("right root"); - let left_main = Url::from_file_path(left_path.join("main.solc")).expect("left main"); - let left_math = Url::from_file_path(left_path.join("math.solc")).expect("left math"); - let right_extra = Url::from_file_path(right_path.join("extra.solc")).expect("right extra"); - let main = "function main() -> word { return value(); }\n"; - let provider = "function value() -> word { return 1; }\nexport { value };\n"; + let left_main = Url::from_file_path(left_path.join("main.sol")).expect("left main"); + let left_math = Url::from_file_path(left_path.join("math.sol")).expect("left math"); + let right_extra = Url::from_file_path(right_path.join("extra.sol")).expect("right extra"); + let main = "function main() returns (word) { return value(); }\n"; + let provider = "function value() returns (word) { return 1; }\nexport { value };\n"; let mut world = WorldState::new(); world.load_workspace_roots([ ( diff --git a/crates/lsp/src/completion.rs b/crates/lsp/src/completion.rs index 925d7e20..36aadaba 100644 --- a/crates/lsp/src/completion.rs +++ b/crates/lsp/src/completion.rs @@ -23,16 +23,19 @@ use crate::{ const KEYWORDS: &[&str] = &[ "contract", "import", + "from", + "hiding", "export", "as", "let", - "data", - "class", - "forall", - "instance", + "enum", + "trait", + "impl", + "where", "if", "else", "for", + "while", "switch", "type", "case", @@ -41,6 +44,7 @@ const KEYWORDS: &[&str] = &[ "public", "payable", "function", + "returns", "constructor", "fallback", "return", @@ -50,6 +54,8 @@ const KEYWORDS: &[&str] = &[ "lam", "assembly", "pragma", + "comptime", + "derive", "true", "false", ]; @@ -520,7 +526,7 @@ fn detail_for_resolution(resolution: &Resolution<'_>) -> &'static str { Resolution::Def { kind: DefResolutionKind::Adt, .. - } => "data", + } => "enum", Resolution::Def { kind: DefResolutionKind::TypeAlias, .. @@ -528,23 +534,23 @@ fn detail_for_resolution(resolution: &Resolution<'_>) -> &'static str { Resolution::Def { kind: DefResolutionKind::Class, .. - } => "class", + } => "trait", Resolution::Def { kind: DefResolutionKind::Instance, .. - } => "instance", + } => "impl", Resolution::Ctor { .. } => "constructor", Resolution::Local(LocalBinding::TypeVar(_)) => "type parameter", Resolution::Local(_) => "local", Resolution::Param(_) => "parameter", Resolution::Field(_) => "field", - Resolution::ClassMethod { .. } => "class method", + Resolution::ClassMethod { .. } => "trait method", Resolution::Module(_) => "module", Resolution::Builtin(BuiltinKind::Type(_)) => "builtin type", - Resolution::Builtin(BuiltinKind::Class(_)) => "builtin class", + Resolution::Builtin(BuiltinKind::Class(_)) => "builtin trait", Resolution::Builtin(BuiltinKind::Constructor(_)) => "builtin constructor", Resolution::Builtin(BuiltinKind::Function(_)) => "builtin function", - Resolution::Builtin(BuiltinKind::ClassMethod(_)) => "builtin class method", + Resolution::Builtin(BuiltinKind::ClassMethod(_)) => "builtin trait method", Resolution::DotCtorDeferred => "constructor", Resolution::Err => "unresolved", } @@ -589,23 +595,14 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } #[test] fn function_body_completion_includes_params_locals_and_top_level_items() { - let source = "\ -function helper() -> word { - return 1; -} - -function main(input: word) -> word { - let local = input; - return local; -} -"; + let source = "function helper() returns (word) {\n return 1;\n}\n\nfunction main(input: word) returns (word) {\n let local = input;\n return local;\n}\n"; let (world, uri) = world_with_main(source); let offset = (source.find("return local").expect("return local") + "return ".len()) as u32; let position = world @@ -623,7 +620,7 @@ function main(input: word) -> word { #[test] fn completion_includes_language_keywords() { - let source = "function main() -> word {\n return 1;\n}\n"; + let source = "function main() returns (word) {\n return 1;\n}\n"; let (world, uri) = world_with_main(source); let offset = source.find('1').expect("literal") as u32; let position = world @@ -635,18 +632,18 @@ function main(input: word) -> word { completion_items(handle_completion(&world, &uri, position).expect("completion")); assert_completion(&items, "function", CompletionItemKind::KEYWORD); + assert_completion(&items, "hiding", CompletionItemKind::KEYWORD); + assert_completion(&items, "derive", CompletionItemKind::KEYWORD); } #[test] fn completion_uses_requested_module_when_unrelated_document_opened_first() { - let unrelated = "function unrelated() -> word { return 0; }\n"; - let math = - "function combine(a: word, b: word) -> word { return a + b; }\n\nexport { combine };\n"; - let main = - "import math.{combine};\n\nfunction main() -> word {\n return combine(1, 2);\n}\n"; - let unrelated_uri = Url::parse("file:///main/unrelated.solc").expect("unrelated uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let unrelated = "function unrelated() returns (word) { return 0; }\n"; + let math = "function combine(a: word, b: word) returns (word) { return a + b; }\n\nexport { combine };\n"; + let main = "import {combine} from math;\n\nfunction main() returns (word) {\n return combine(1, 2);\n}\n"; + let unrelated_uri = Url::parse("file:///main/unrelated.sol").expect("unrelated uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); let mut world = WorldState::new(); assert!(world.open_document(unrelated_uri, unrelated.to_owned())); assert!(world.open_document(math_uri, math.to_owned())); @@ -666,19 +663,9 @@ function main(input: word) -> word { #[test] fn trailing_dot_module_completion_is_member_only_and_respects_exports() { - let math = "\ -function visible() -> word { return 1; } -function hidden() -> word { return 2; } -data Color = Red | Green; -export { visible, Color(Red, Green) }; -"; - let main = "\ -import math; -function main() -> word { - return math.; -} -"; - let (world, main_uri) = world_with_module(main, "math.solc", math); + let math = "function visible() returns (word) { return 1; }\nfunction hidden() returns (word) { return 2; }\nenum Color {Red , Green}\nexport { visible, Color(Red, Green) };\n"; + let main = "import math;\nfunction main() returns (word) {\n return math.;\n}\n"; + let (world, main_uri) = world_with_module(main, "math.sol", math); let items = completion_at(&world, &main_uri, main, "math."); assert_completion(&items, "visible", CompletionItemKind::FUNCTION); @@ -694,18 +681,9 @@ function main() -> word { #[test] fn qualified_completion_filters_a_typed_member_prefix() { - let math = "\ -function visible() -> word { return 1; } -function value() -> word { return 2; } -export { visible, value }; -"; - let main = "\ -import math; -function main() -> word { - return math.vis; -} -"; - let (world, main_uri) = world_with_module(main, "math.solc", math); + let math = "function visible() returns (word) { return 1; }\nfunction value() returns (word) { return 2; }\nexport { visible, value };\n"; + let main = "import math;\nfunction main() returns (word) {\n return math.vis;\n}\n"; + let (world, main_uri) = world_with_module(main, "math.sol", math); let items = completion_at(&world, &main_uri, main, "math.vis"); assert_completion(&items, "visible", CompletionItemKind::FUNCTION); @@ -714,15 +692,7 @@ function main() -> word { #[test] fn qualified_completion_includes_contract_local_adt_constructors() { - let source = "\ -contract Palette { - data Color = Red | Green; - - function main() -> word { - return Color.; - } -} -"; + let source = "contract Palette {\n enum Color {Red , Green}\n\n function main() returns (word) {\n return Color.;\n }\n}\n"; let (world, uri) = world_with_main(source); let items = completion_at(&world, &uri, source, "Color."); @@ -732,21 +702,11 @@ contract Palette { } #[test] - fn qualified_completion_includes_imported_class_methods() { - let classes = "\ -forall a . class a : Eq { - function eq(x: a, y: a) -> bool; - function unequal(x: a, y: a) -> bool; -} -export { Eq }; -"; - let main = "\ -import classes.{Eq}; -function main() -> word { - return Eq.; -} -"; - let (world, main_uri) = world_with_module(main, "classes.solc", classes); + fn qualified_completion_includes_imported_trait_methods() { + let classes = "trait Eq {\n function eq(x: a, y: a) returns (bool) ;\n function unequal(x: a, y: a) returns (bool) ;\n}\nexport { Eq };\n"; + let main = + "import {Eq} from classes;\nfunction main() returns (word) {\n return Eq.;\n}\n"; + let (world, main_uri) = world_with_module(main, "classes.sol", classes); let items = completion_at(&world, &main_uri, main, "Eq."); assert_completion(&items, "eq", CompletionItemKind::METHOD); @@ -777,7 +737,7 @@ function main() -> word { fn world_with_module(main: &str, module_path: &str, module_source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); let module_uri = Url::parse(&format!("file:///main/{module_path}")).expect("module uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); assert!(world.open_document(module_uri, module_source.to_owned())); assert!(world.open_document(main_uri.clone(), main.to_owned())); (world, main_uri) diff --git a/crates/lsp/src/definition.rs b/crates/lsp/src/definition.rs index c7629cd0..ed659ac6 100644 --- a/crates/lsp/src/definition.rs +++ b/crates/lsp/src/definition.rs @@ -489,15 +489,15 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } fn world_with_main_and_math(main: &str, math: &str) -> (WorldState, Url, Url) { let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(math_uri.clone(), math.to_owned())); (world, main_uri, math_uri) @@ -509,9 +509,9 @@ mod tests { nested: &str, ) -> (WorldState, Url, Url) { let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); let nested_uri = - Url::parse(&format!("file:///main/{nested_path}.solc")).expect("nested uri"); + Url::parse(&format!("file:///main/{nested_path}.sol")).expect("nested uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(nested_uri.clone(), nested.to_owned())); (world, main_uri, nested_uri) @@ -529,7 +529,7 @@ mod tests { #[test] fn definition_of_parameter_use_points_to_parameter_name() { - let source = "function id(x: word) -> word {\n return x;\n}\n"; + let source = "function id(x: word) returns (word) {\n return x;\n}\n"; let (world, uri) = world_with_main(source); let use_offset = (source.find("return x").expect("return") + "return ".len()) as u32; let param_offset = source.find("x: word").expect("param") as u32; @@ -550,8 +550,10 @@ mod tests { #[test] fn definition_of_import_selector_name_points_to_imported_declaration() { - let main = "import math.{double};\nfunction main() -> word { return double(21); }\n"; - let math = "function double(x: word) -> word { return x + x; }\nexport { double };\n"; + let main = + "import {double} from math;\nfunction main() returns (word) { return double(21); }\n"; + let math = + "function double(x: word) returns (word) { return x + x; }\nexport { double };\n"; let (world, main_uri, math_uri) = world_with_main_and_math(main, math); let main_index = world.line_index(&main_uri).expect("main line index"); let math_index = world.line_index(&math_uri).expect("math line index"); @@ -573,7 +575,7 @@ mod tests { #[test] fn definition_in_embedded_std_is_not_returned_as_an_unopenable_uri() { - let source = "import std.{addWord};\nfunction main() -> word { return addWord(1, 2); }\n"; + let source = "import {addWord} from std;\nfunction main() returns (word) { return addWord(1, 2); }\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let call = source.rfind("addWord").expect("call") as u32; @@ -586,14 +588,8 @@ mod tests { #[test] fn definition_of_cross_file_type_ref_points_to_type_declaration() { - let main = "\ -import models.{Box}; -function wrap(value: word) -> Box { - let boxed: Box = Box(value); - return boxed; -} -"; - let models = "data Box = Box(word);\nexport { Box };\n"; + let main = "import {Box} from models;\nfunction wrap(value: word) returns (Box) {\n let boxed: Box = Box(value);\n return boxed;\n}\n"; + let models = "enum Box {Box(word)}\nexport { Box };\n"; let (world, main_uri, models_uri) = world_with_main_and_nested(main, "models", models); let models_index = world.line_index(&models_uri).expect("models line index"); let type_ref = (main.find("boxed: Box").expect("local type") + "boxed: ".len()) as u32; @@ -610,7 +606,7 @@ function wrap(value: word) -> Box { #[test] fn definition_on_type_declaration_points_to_itself() { - let source = "data Choice = Left | Right;\n"; + let source = "enum Choice {Left , Right}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let declaration = source.find("Choice").expect("declaration") as u32; @@ -625,18 +621,11 @@ function wrap(value: word) -> Box { } #[test] - fn definition_of_predicate_points_to_class_declaration() { - let source = "\ -forall a. class a:Comparable { - function compare(x: a, y: a) -> word; -} - -forall a. a:Comparable => -function keep(x: a) -> a { return x; } -"; + fn definition_of_predicate_points_to_trait_declaration() { + let source = "trait Comparable {\n function compare(x: a, y: a) returns (word) ;\n}\n\nfunction keep(x: a) returns (a) where a: Comparable { return x; }\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); - let declaration = source.find("Comparable").expect("class declaration") as u32; + let declaration = source.find("Comparable").expect("trait declaration") as u32; let predicate = source.rfind("Comparable").expect("predicate") as u32; let location = scalar_definition(&world, &uri, predicate); @@ -650,16 +639,7 @@ function keep(x: a) -> a { return x; } #[test] fn definition_of_constructor_pattern_points_to_constructor_declaration() { - let source = "\ -data Choice = Left(word) | Right; - -function unwrap(value: Choice) -> word { - match value { - | Choice.Left(x) => return x; - | Choice.Right => return 0; - } -} -"; + let source = "enum Choice {Left(word) , Right}\n\nfunction unwrap(value: Choice) returns (word) {\n match (value) {\n case Choice.Left(x) { return x; }\ncase Choice.Right { return 0; }}\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let declaration = source.find("Left").expect("constructor declaration") as u32; @@ -676,8 +656,8 @@ function unwrap(value: Choice) -> word { #[test] fn definition_of_import_path_and_module_qualifier_points_to_module_start() { - let main = "import foo.bar;\nfunction main() -> word { return foo.bar.value(); }\n"; - let bar = "export { value };\nfunction value() -> word { return 7; }\n"; + let main = "import foo.bar;\nfunction main() returns (word) { return foo.bar.value(); }\n"; + let bar = "export { value };\nfunction value() returns (word) { return 7; }\n"; let (world, main_uri, bar_uri) = world_with_main_and_nested(main, "foo/bar", bar); let bar_index = world.line_index(&bar_uri).expect("bar line index"); let expected = bar_index.range(0, 0); @@ -695,14 +675,13 @@ function unwrap(value: Choice) -> word { #[test] fn definition_of_exact_module_qualifier_wins_over_shared_navigation_origin() { - let main = - "import foo.bar;\nimport foo;\nfunction main() -> word { return foo.value(); }\n"; - let foo = "export { value };\nfunction value() -> word { return 1; }\n"; - let bar = "export { value };\nfunction value() -> word { return 2; }\n"; + let main = "import foo.bar;\nimport foo;\nfunction main() returns (word) { return foo.value(); }\n"; + let foo = "export { value };\nfunction value() returns (word) { return 1; }\n"; + let bar = "export { value };\nfunction value() returns (word) { return 2; }\n"; let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let foo_uri = Url::parse("file:///main/foo.solc").expect("foo uri"); - let bar_uri = Url::parse("file:///main/foo/bar.solc").expect("bar uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let foo_uri = Url::parse("file:///main/foo.sol").expect("foo uri"); + let bar_uri = Url::parse("file:///main/foo/bar.sol").expect("bar uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(foo_uri.clone(), foo.to_owned())); assert!(world.open_document(bar_uri, bar.to_owned())); diff --git a/crates/lsp/src/diagnostics.rs b/crates/lsp/src/diagnostics.rs index 74e05e8c..b03c4245 100644 --- a/crates/lsp/src/diagnostics.rs +++ b/crates/lsp/src/diagnostics.rs @@ -164,7 +164,7 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } @@ -208,14 +208,14 @@ mod tests { #[test] fn clean_program_has_no_diagnostics() { - let (world, uri) = world_with_main("function main() -> word {\n return 1;\n}\n"); + let (world, uri) = world_with_main("function main() returns (word) {\n return 1;\n}\n"); assert!(compute_diagnostics(&world, &uri).is_empty()); } #[test] fn type_error_maps_to_lsp_error_with_range() { - let source = "function f() -> word {\n return true;\n}\n"; + let source = "function f() returns (word) {\n return true;\n}\n"; let (world, uri) = world_with_main(source); let diagnostics = compute_diagnostics(&world, &uri); @@ -234,10 +234,10 @@ mod tests { #[test] fn sibling_import_open_in_workspace_has_no_module_not_found_diagnostic() { let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); - let main = "import math.{double};\n\nfunction main() -> word {\n return double(21);\n}\n"; - let math = "function double(x: word) -> word {\n let res: word;\n assembly {\n res := add(x, x)\n }\n return res;\n}\n\nexport { double };\n"; + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); + let main = "import {double} from math;\n\nfunction main() returns (word) {\n return double(21);\n}\n"; + let math = "function double(x: word) returns (word) {\n let res: word;\n assembly {\n res := add(x, x)\n }\n return res;\n}\n\nexport { double };\n"; assert!(world.open_document(main_uri.clone(), main.to_owned())); let _ = compute_diagnostics(&world, &main_uri); @@ -250,10 +250,10 @@ mod tests { #[test] fn sibling_import_opened_before_importer_has_no_module_not_found_diagnostic() { let mut world = WorldState::new(); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math = "function double(x: word) -> word { return x; }\n\nexport { double };\n"; - let main = "import math.{double};\n\nfunction main() -> word {\n return double(21);\n}\n"; + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math = "function double(x: word) returns (word) { return x; }\n\nexport { double };\n"; + let main = "import {double} from math;\n\nfunction main() returns (word) {\n return double(21);\n}\n"; assert!(world.open_document(math_uri, math.to_owned())); assert!(world.open_document(main_uri.clone(), main.to_owned())); @@ -265,12 +265,12 @@ mod tests { #[test] fn fallback_diagnostics_for_unreachable_importer_update_after_sibling_opens() { let mut world = WorldState::new(); - let entry_uri = Url::parse("file:///main/entry.solc").expect("entry uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); - let entry = "function entry() -> word { return 0; }\n"; - let main = "import math.{double};\n\nfunction main() -> word {\n return double(21);\n}\n"; - let math = "function double(x: word) -> word { return x; }\n\nexport { double };\n"; + let entry_uri = Url::parse("file:///main/entry.sol").expect("entry uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); + let entry = "function entry() returns (word) { return 0; }\n"; + let main = "import {double} from math;\n\nfunction main() returns (word) {\n return double(21);\n}\n"; + let math = "function double(x: word) returns (word) { return x; }\n\nexport { double };\n"; assert!(world.open_document(entry_uri, entry.to_owned())); assert!(world.open_document(main_uri.clone(), main.to_owned())); @@ -295,13 +295,14 @@ mod tests { use std::{sync::mpsc, time::Duration}; let mut world = WorldState::new(); - let entry_uri = Url::parse("file:///main/entry.solc").expect("entry uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); + let entry_uri = Url::parse("file:///main/entry.sol").expect("entry uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); let shadow_uri = Url::parse("file:///main/math.txt").expect("shadow uri"); - let entry = "function entry() -> word { return 0; }\n"; - let main = "import math.{double};\nfunction main() -> word { return double(21); }\n"; - let math = "function double(x: word) -> word { return x; }\nexport { double };\n"; + let entry = "function entry() returns (word) { return 0; }\n"; + let main = + "import {double} from math;\nfunction main() returns (word) { return double(21); }\n"; + let math = "function double(x: word) returns (word) { return x; }\nexport { double };\n"; assert!(world.open_document(entry_uri, entry.to_owned())); assert!(world.open_document(main_uri.clone(), main.to_owned())); @@ -326,8 +327,8 @@ mod tests { let result = std::thread::Builder::new() .stack_size(1024 * 1024) .spawn(|| { - let mut source = "function main() -> word { return ".to_owned(); - source.push_str(&"if true then 0 else ".repeat(130)); + let mut source = "function main() returns (word) { return ".to_owned(); + source.push_str(&"true ? 0 : ".repeat(130)); source.push_str("0; }\n"); let (world, uri) = world_with_main(&source); @@ -349,14 +350,14 @@ mod tests { #[test] fn open_document_diagnostics_refresh_importer_when_sibling_changes() { let mut world = WorldState::new(); - let entry_uri = Url::parse("file:///main/entry.solc").expect("entry uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); - let entry = "function entry() -> word { return 0; }\n"; - let main = "import math.{double};\n\nfunction main() -> word {\n return double(21);\n}\n"; - let math_no_export = "function double(x: word) -> word { return x; }\n"; + let entry_uri = Url::parse("file:///main/entry.sol").expect("entry uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); + let entry = "function entry() returns (word) { return 0; }\n"; + let main = "import {double} from math;\n\nfunction main() returns (word) {\n return double(21);\n}\n"; + let math_no_export = "function double(x: word) returns (word) { return x; }\n"; let math_with_export = - "function double(x: word) -> word { return x; }\n\nexport { double };\n"; + "function double(x: word) returns (word) { return x; }\n\nexport { double };\n"; assert!(world.open_document(entry_uri, entry.to_owned())); assert!(world.open_document(main_uri.clone(), main.to_owned())); @@ -387,12 +388,12 @@ mod tests { #[test] fn adding_export_via_change_clears_unknown_import_item() { let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); - let main = "import math.{double};\n\nfunction main() -> word {\n return double(21);\n}\n"; - let math_no_export = "function double(x: word) -> word { return x; }\n"; + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); + let main = "import {double} from math;\n\nfunction main() returns (word) {\n return double(21);\n}\n"; + let math_no_export = "function double(x: word) returns (word) { return x; }\n"; let math_with_export = - "function double(x: word) -> word { return x; }\n\nexport { double };\n"; + "function double(x: word) returns (word) { return x; }\n\nexport { double };\n"; assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(math_uri.clone(), math_no_export.to_owned())); @@ -417,14 +418,14 @@ mod tests { #[test] fn adding_export_via_change_clears_unknown_import_item_entry_drift() { let mut world = WorldState::new(); - let entry_uri = Url::parse("file:///main/entry.solc").expect("entry uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); - let entry = "function entry() -> word { return 0; }\n"; - let main = "import math.{double};\n\nfunction main() -> word {\n return double(21);\n}\n"; - let math_no_export = "function double(x: word) -> word { return x; }\n"; + let entry_uri = Url::parse("file:///main/entry.sol").expect("entry uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); + let entry = "function entry() returns (word) { return 0; }\n"; + let main = "import {double} from math;\n\nfunction main() returns (word) {\n return double(21);\n}\n"; + let math_no_export = "function double(x: word) returns (word) { return x; }\n"; let math_with_export = - "function double(x: word) -> word { return x; }\n\nexport { double };\n"; + "function double(x: word) returns (word) { return x; }\n\nexport { double };\n"; assert!(world.open_document(entry_uri, entry.to_owned())); assert!(world.open_document(main_uri.clone(), main.to_owned())); diff --git a/crates/lsp/src/document_highlight.rs b/crates/lsp/src/document_highlight.rs index 9cfbc5d7..e2bc7be1 100644 --- a/crates/lsp/src/document_highlight.rs +++ b/crates/lsp/src/document_highlight.rs @@ -46,14 +46,14 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } #[test] fn parameter_highlights_declaration_and_uses_in_current_file() { - let source = "function id(x: word) -> word {\n let y = x;\n return x;\n}\n"; + let source = "function id(x: word) returns (word) {\n let y = x;\n return x;\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let first_use = (source.find("let y = x").expect("first use") + "let y = ".len()) as u32; @@ -85,7 +85,7 @@ mod tests { #[test] fn whitespace_returns_none() { - let source = "function id(x: word) -> word {\n let y = x;\n return x;\n}\n"; + let source = "function id(x: word) returns (word) {\n let y = x;\n return x;\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let whitespace = (source.find("let y = x").expect("let statement") + "let".len()) as u32; diff --git a/crates/lsp/src/folding.rs b/crates/lsp/src/folding.rs index 928d86da..b24af393 100644 --- a/crates/lsp/src/folding.rs +++ b/crates/lsp/src/folding.rs @@ -350,14 +350,14 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } #[test] fn folds_imports_comments_items_and_nested_blocks() { - let source = "// first\n// second\nimport alpha;\nimport beta;\n\n/* block\n comment */\ncontract Box {\n function get() -> word {\n if true {\n return 1;\n }\n }\n}\n"; + let source = "// first\n// second\nimport alpha;\nimport beta;\n\n/* block\n comment */\ncontract Box {\n function get() returns (word) {\n if (true) {\n return 1;\n }\n }\n}\n"; let (world, uri) = world_with_main(source); let folds = handle_folding_range(&world, &uri).expect("folding ranges"); @@ -385,7 +385,7 @@ mod tests { #[test] fn lexical_folding_ignores_delimiters_in_unicode_strings_and_comments() { - let source = "function main() {\n let label = \"😀 { not a block }\";\n /* { ignored } */\n {\n return 1;\n }\n}\n"; + let source = "function main() returns (word) {\n let label = \"😀 { not a block }\";\n /* { ignored } */\n {\n return 1;\n }\n}\n"; let (world, uri) = world_with_main(source); let folds = handle_folding_range(&world, &uri).expect("folding ranges"); @@ -401,7 +401,7 @@ mod tests { #[test] fn malformed_source_still_returns_balanced_inner_blocks() { - let source = "function main() {\n {\n return 1;\n }\n"; + let source = "function main() returns (word) {\n {\n return 1;\n }\n"; let (world, uri) = world_with_main(source); let folds = handle_folding_range(&world, &uri).expect("folding ranges"); @@ -414,7 +414,7 @@ mod tests { #[test] fn nested_blocks_with_the_same_line_extent_remain_distinct() { - let source = "function main() { if true {\n return 1;\n} }\n"; + let source = "function main() returns (word) { if (true) {\n return 1;\n} }\n"; let (world, uri) = world_with_main(source); let folds = handle_folding_range(&world, &uri).expect("folding ranges"); let structural = folds @@ -429,7 +429,7 @@ mod tests { #[test] fn unknown_document_has_no_folding_result() { let world = WorldState::new(); - let uri = Url::parse("file:///main/missing.solc").expect("uri"); + let uri = Url::parse("file:///main/missing.sol").expect("uri"); assert_eq!(handle_folding_range(&world, &uri), None); } } diff --git a/crates/lsp/src/formatting.rs b/crates/lsp/src/formatting.rs index cf88ee28..c39b5ada 100644 --- a/crates/lsp/src/formatting.rs +++ b/crates/lsp/src/formatting.rs @@ -286,15 +286,15 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } #[test] fn formats_whole_document_without_touching_braces_in_trivia() { - let source = "function main() -> word { \nreturn \"{\";\n/* } { */\nif true {\nreturn 1; // }\n}\n}\n\n"; - let expected = "function main() -> word {\n return \"{\";\n /* } { */\n if true {\n return 1; // }\n }\n}\n"; + let source = "function main() returns (word) { \nreturn \"{\";\n/* } { */\nif (true) {\nreturn 1; // }\n}\n}\n\n"; + let expected = "function main() returns (word) {\n return \"{\";\n /* } { */\n if (true) {\n return 1; // }\n }\n}\n"; let (world, uri) = world_with_main(source); let edits = handle_formatting(&world, &uri, &options(2, true)).expect("formatting"); @@ -311,8 +311,8 @@ mod tests { #[test] fn respects_tabs_and_preserves_crlf() { - let source = "function main() {\r\nreturn \"😀\";\r\n}"; - let expected = "function main() {\r\n\treturn \"😀\";\r\n}\r\n"; + let source = "function main() returns (string) {\r\nreturn \"😀\";\r\n}"; + let expected = "function main() returns (string) {\r\n\treturn \"😀\";\r\n}\r\n"; let (world, uri) = world_with_main(source); let edits = handle_formatting(&world, &uri, &options(8, false)).expect("formatting"); @@ -328,7 +328,7 @@ mod tests { #[test] fn already_formatted_document_needs_no_edit() { - let source = "function main() {\n return 1;\n}\n"; + let source = "function main() returns (word) {\n return 1;\n}\n"; let (world, uri) = world_with_main(source); assert_eq!( @@ -340,15 +340,15 @@ mod tests { #[test] fn formatting_requires_an_open_document() { let world = WorldState::new(); - let uri = Url::parse("file:///main/missing.solc").expect("uri"); + let uri = Url::parse("file:///main/missing.sol").expect("uri"); assert_eq!(handle_formatting(&world, &uri, &options(2, true)), None); } #[test] fn preserves_multiline_string_and_block_comment_payload_whitespace() { for source in [ - "function main() {\nreturn \"first\n second \";\n}\n", - "function main() {\n/* markdown\n indented code \n*/\nreturn 1;\n}\n", + "function main() returns (string) {\nreturn \"first\n second \";\n}\n", + "function main() returns (word) {\n/* markdown\n indented code \n*/\nreturn 1;\n}\n", ] { let (world, uri) = world_with_main(source); assert_eq!( @@ -375,8 +375,8 @@ mod tests { #[test] fn honors_disabled_trailing_whitespace_trimming() { - let source = "function main() { \n \nreturn 1; \n}\n"; - let expected = "function main() { \n \n return 1; \n}\n"; + let source = "function main() returns (word) { \n \nreturn 1; \n}\n"; + let expected = "function main() returns (word) { \n \n return 1; \n}\n"; let (world, uri) = world_with_main(source); let mut options = options(2, true); options.trim_trailing_whitespace = Some(false); @@ -387,8 +387,8 @@ mod tests { #[test] fn dedents_adjacent_leading_closing_braces() { - let source = "function main() {\n{\nreturn 1;\n }}\n"; - let expected = "function main() {\n {\n return 1;\n}}\n"; + let source = "function main() returns (word) {\n{\nreturn 1;\n }}\n"; + let expected = "function main() returns (word) {\n {\n return 1;\n}}\n"; let (world, uri) = world_with_main(source); let edits = handle_formatting(&world, &uri, &options(2, true)).expect("formatting"); diff --git a/crates/lsp/src/hover.rs b/crates/lsp/src/hover.rs index 67ac5a62..56ee2365 100644 --- a/crates/lsp/src/hover.rs +++ b/crates/lsp/src/hover.rs @@ -230,11 +230,11 @@ fn definition_hover<'db>(db: &'db vfs::AnalysisHost, def: DefId<'db>) -> Option< documentation: comments_markdown(found.adt.leading_comments(db)), }), Definition::Class(class) => Some(HoverInfo { - code: format!("class {}", display_pred_ref(db, class.head(db))), + code: format_trait_header(db, class), documentation: comments_markdown(class.leading_comments(db)), }), Definition::Instance(instance) => Some(HoverInfo { - code: format!("instance {}", display_pred_ref(db, instance.head(db))), + code: format_impl_header(db, instance), documentation: comments_markdown(instance.leading_comments(db)), }), Definition::Contract(contract) => { @@ -377,11 +377,30 @@ fn format_source_function_signature<'db>(db: &'db dyn hir_ty::Db, sig: &FuncSig< .map(|param| format_source_param(db, param)) .collect::>() .join(", "); - let ret = sig - .ret - .map(|ret| display_type_ref(db, ret)) - .unwrap_or_else(|| "_".to_owned()); - format!("{}({params}) -> {ret}", sig.name.atom().text(db)) + let type_params = type_parameter_list(db, &sig.type_vars); + let mut signature = format!("{}{type_params}({params})", sig.name.atom().text(db)); + if sig.public.is_some() { + signature.push_str(" public"); + } + if sig.payable.is_some() { + signature.push_str(" payable"); + } + if let Some(ret) = sig.ret { + signature.push_str(" returns ("); + signature.push_str(&display_type_ref(db, ret)); + signature.push(')'); + } + if !sig.preds.is_empty() { + signature.push_str(" where "); + signature.push_str( + &sig.preds + .iter() + .map(|pred| display_pred_ref(db, *pred)) + .collect::>() + .join(", "), + ); + } + signature } fn format_source_param<'db>(db: &'db dyn hir_ty::Db, param: &FuncParam<'db>) -> String { @@ -422,11 +441,11 @@ fn format_adt_declaration<'db>(db: &'db dyn hir_ty::Db, adt: AdtDef<'db>) -> Str } }) .collect::>() - .join(" | "); + .join(", "); if ctors.is_empty() { - format!("data {name}{params}") + format!("enum {name}{params} {{}}") } else { - format!("data {name}{params} = {ctors}") + format!("enum {name}{params} {{ {ctors} }}") } } @@ -437,8 +456,43 @@ fn type_parameter_list<'db>( if params.is_empty() { String::new() } else { - format!("({})", ident_names(db, params).join(", ")) + format!("<{}>", ident_names(db, params).join(", ")) + } +} + +fn format_trait_header<'db>(db: &'db dyn hir_ty::Db, trait_def: ClassDef<'db>) -> String { + let mut header = format!("trait {}", display_trait_ref(db, trait_def.head(db))); + append_where_clause(db, &mut header, trait_def.super_preds(db)); + header +} + +fn format_impl_header<'db>(db: &'db dyn hir_ty::Db, impl_def: InstanceDef<'db>) -> String { + let default = if impl_def.default_kw(db).is_some() { + "default " + } else { + "" + }; + let params = type_parameter_list(db, impl_def.type_var_elems(db)); + let mut header = format!( + "{default}impl{params} {}", + display_trait_ref(db, impl_def.head(db)) + ); + append_where_clause(db, &mut header, impl_def.preds(db)); + header +} + +fn append_where_clause<'db>(db: &'db dyn hir_ty::Db, header: &mut String, preds: &[PredRef<'db>]) { + if preds.is_empty() { + return; } + header.push_str(" where "); + header.push_str( + &preds + .iter() + .map(|pred| display_pred_ref(db, *pred)) + .collect::>() + .join(", "), + ); } fn comments_markdown(comments: &[SourceComment]) -> Option { @@ -886,7 +940,7 @@ fn format_callable_scheme<'db>( .collect::>() .join(", "); let mut signature = format!( - "{name}({params}) -> {}", + "{name}({params}) returns ({})", display_ty(db, ret, type_var_names) ); let predicates = scheme @@ -928,9 +982,15 @@ fn display_ty<'db>(db: &'db dyn hir_ty::Db, ty: Ty<'db>, names: &[String]) -> St }; if args.is_empty() { name + } else if name == "mapping" && args.len() == 2 { + format!( + "mapping({} => {})", + display_ty(db, args[0], names), + display_ty(db, args[1], names) + ) } else { format!( - "{name}({})", + "{name}<{}>", args.iter() .map(|arg| display_ty(db, *arg, names)) .collect::>() @@ -939,7 +999,7 @@ fn display_ty<'db>(db: &'db dyn hir_ty::Db, ty: Ty<'db>, names: &[String]) -> St } } TyKind::Function { params, ret } => format!( - "({}) -> {}", + "function({}) returns ({})", params .iter() .map(|param| display_ty(db, *param, names)) @@ -961,7 +1021,7 @@ fn display_ty<'db>(db: &'db dyn hir_ty::Db, ty: Ty<'db>, names: &[String]) -> St ) } } - TyKind::Comptime(inner) => format!("comptime {}", display_ty(db, *inner, names)), + TyKind::Comptime(inner) => format!("comptime<{}>", display_ty(db, *inner, names)), } } @@ -978,7 +1038,7 @@ fn display_pred<'db>(db: &'db dyn hir_ty::Db, pred: hir_ty::Pred<'db>, names: &[ format!("{}: {class}", display_ty(db, *main, names)) } else { format!( - "{}: {class}({})", + "{}: {class}<{}>", display_ty(db, *main, names), args.iter() .map(|arg| display_ty(db, *arg, names)) @@ -1008,23 +1068,32 @@ fn display_type_ref<'db>(db: &'db dyn hir_ty::Db, ty: TypeRef<'db>) -> String { out.push_str(qualifier.atom().text(db)); out.push('.'); } - out.push_str(name.atom().text(db)); + let name_text = name.atom().text(db); + out.push_str(name_text); if !args.atom().is_empty() { - out.push('('); - out.push_str( - &args - .atom() - .iter() - .map(|arg| display_type_ref(db, *arg)) - .collect::>() - .join(", "), - ); - out.push(')'); + if name_text == "mapping" && args.atom().len() == 2 { + out.push('('); + out.push_str(&display_type_ref(db, args.atom()[0])); + out.push_str(" => "); + out.push_str(&display_type_ref(db, args.atom()[1])); + out.push(')'); + } else { + out.push('<'); + out.push_str( + &args + .atom() + .iter() + .map(|arg| display_type_ref(db, *arg)) + .collect::>() + .join(", "), + ); + out.push('>'); + } } out } TypeRefKind::Fn { params, ret } => format!( - "({}) -> {}", + "function({}) returns ({})", params .atom() .iter() @@ -1034,7 +1103,7 @@ fn display_type_ref<'db>(db: &'db dyn hir_ty::Db, ty: TypeRef<'db>) -> String { display_type_ref(db, *ret) ), TypeRefKind::Comptime { inner, .. } => { - format!("comptime {}", display_type_ref(db, *inner)) + format!("comptime<{}>", display_type_ref(db, *inner)) } TypeRefKind::Tuple { elems } => format!( "({})", @@ -1057,7 +1126,7 @@ fn display_pred_ref<'db>(db: &'db dyn hir_ty::Db, pred: PredRef<'db>) -> String format!("{ty}: {class}") } else { format!( - "{ty}: {class}({})", + "{ty}: {class}<{}>", kind.args .atom() .iter() @@ -1068,6 +1137,18 @@ fn display_pred_ref<'db>(db: &'db dyn hir_ty::Db, pred: PredRef<'db>) -> String } } +fn display_trait_ref<'db>(db: &'db dyn hir_ty::Db, pred: PredRef<'db>) -> String { + let kind = pred.kind(db); + let mut args = vec![display_type_ref(db, kind.ty)]; + args.extend( + kind.args + .atom() + .iter() + .map(|arg| display_type_ref(db, *arg)), + ); + format!("{}<{}>", kind.class.atom().text(db), args.join(", ")) +} + #[cfg(test)] mod tests { use lsp_types::{HoverContents, MarkedString}; @@ -1076,7 +1157,7 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } @@ -1122,7 +1203,7 @@ mod tests { #[test] fn hovers_integer_literal_type() { - let source = "function main() -> word {\n return 42;\n}\n"; + let source = "function main() returns (word) {\n return 42;\n}\n"; let (world, uri) = world_with_main(source); let literal_offset = source.find("42").expect("literal"); @@ -1146,23 +1227,14 @@ mod tests { #[test] fn function_and_parameter_references_show_signatures_and_identifier_ranges() { - let source = "\ -// Returns its input. -function id(x: word) -> word { - return x; -} - -function main() -> word { - return id(42); -} -"; + let source = "// Returns its input.\nfunction id(x: word) returns (word) {\n return x;\n}\n\nfunction main() returns (word) {\n return id(42);\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let call = source.rfind("id(42)").expect("call"); let function_hover = hover_at(source, &world, &uri, call); assert!( - hover_code(&function_hover).contains("id(x: word) -> word"), + hover_code(&function_hover).contains("id(x: word) returns (word)"), "unexpected function hover: {:?}", function_hover.contents ); @@ -1186,12 +1258,7 @@ function main() -> word { #[test] fn inferred_local_reference_hover_uses_local_name_range() { - let source = "\ -function main() -> word { - let result = 42; - return result; -} -"; + let source = "function main() returns (word) {\n let result = 42;\n return result;\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let reference = source.rfind("result").expect("local reference"); @@ -1211,20 +1278,14 @@ function main() -> word { #[test] fn type_and_constructor_references_have_rich_hover_and_leaf_ranges() { - let source = "\ -data Maybe = None | Some(word); - -function main() -> Maybe { - return Maybe.Some(42); -} -"; + let source = "enum Maybe {None , Some(word)}\n\nfunction main() returns (Maybe) {\n return Maybe.Some(42);\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let ty_reference = source.rfind("Maybe").expect("type reference"); let ty_hover = hover_at(source, &world, &uri, ty_reference); assert!( - hover_code(&ty_hover).contains("data Maybe = None | Some(word)"), + hover_code(&ty_hover).contains("enum Maybe { None, Some(word) }"), "unexpected type hover: {:?}", ty_hover.contents ); @@ -1237,7 +1298,7 @@ function main() -> Maybe { let ctor_hover = hover_at(source, &world, &uri, ctor_reference); let ctor_code = hover_code(&ctor_hover); assert!( - ctor_code.contains("Some(word) -> Maybe"), + ctor_code.contains("Some(word) returns (Maybe)"), "unexpected constructor hover: {ctor_code}" ); assert_eq!( diff --git a/crates/lsp/src/import_edits.rs b/crates/lsp/src/import_edits.rs index 302c1a7a..a8cf34be 100644 --- a/crates/lsp/src/import_edits.rs +++ b/crates/lsp/src/import_edits.rs @@ -26,7 +26,7 @@ pub struct ImportEdit { /// Plans one deterministic edit that brings `public_name` into scope. /// -/// When the target already has a safe explicit `.{...}` import, the name is +/// When the target already has a safe explicit `{...} from` import, the name is /// appended to that selector. Otherwise a separate selective import is placed /// after the existing import block, or after leading pragmas/header comments. /// Malformed source, stale parse metadata, and text that cannot be represented @@ -148,10 +148,10 @@ pub fn plan_import_edit<'db>( /// Plans an import that exposes every public name from `target_import_path`. /// -/// When a selective import for the same target already exists, `*` is appended -/// to its selector. The parser treats a selector containing `*` as a wildcard, -/// which preserves comments and formatting inside the existing declaration. -/// Otherwise a new `import path.{*};` declaration is inserted. +/// A wildcard import already present needs no edit. A selective import for the +/// same module is left untouched because the canonical grammar does not mix +/// names and `*` in one selector; callers may offer a separate rewrite in that +/// case. Otherwise a new `import * from path;` declaration is inserted. pub fn plan_wildcard_import_edit<'db>( db: &'db dyn parser::Db, source: &str, @@ -184,12 +184,7 @@ pub fn plan_wildcard_import_edit<'db>( { match import.selector(db) { Some(ImportSelector::Wildcard) => return None, - Some(ImportSelector::Names(names)) - if import.alias_elem(db).is_none() && import.hiding(db).is_empty() => - { - let offset = selector_append_offset(db, source, import, names)?; - return Some(insertion(offset, ", *".to_owned())); - } + Some(ImportSelector::Names(_)) => return None, _ => {} } } @@ -199,7 +194,7 @@ pub fn plan_wildcard_import_edit<'db>( source, module, &imports, - &format!("import {target_import_path}.{{*}};"), + &format!("import * from {target_import_path};"), ) } @@ -384,7 +379,7 @@ fn plan_new_import( target_import_path: &str, public_name: &str, ) -> Option { - let declaration = format!("import {target_import_path}.{{{public_name}}};"); + let declaration = format!("import {{{public_name}}} from {target_import_path};"); plan_new_import_declaration(db, source, module, imports, &declaration) } @@ -587,7 +582,7 @@ mod tests { fn plan(source: &str, target: &str, name: &str) -> Option { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); let db = world.db(); let path = world.vfs_path_for_uri(&uri).expect("VFS path"); @@ -598,7 +593,7 @@ mod tests { fn plan_module(source: &str, target: &str) -> Option { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); let db = world.db(); let path = world.vfs_path_for_uri(&uri).expect("VFS path"); @@ -609,7 +604,7 @@ mod tests { fn plan_wildcard(source: &str, target: &str) -> Option { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); let db = world.db(); let path = world.vfs_path_for_uri(&uri).expect("VFS path"); @@ -626,190 +621,188 @@ mod tests { #[test] fn appends_to_matching_selective_import() { - let source = "import lib.math.{old};\nfunction main() { value; }\n"; + let source = "import {old} from lib.math;\nfunction main() { value; }\n"; let edit = plan(source, "lib.math", "value").expect("edit"); assert_eq!(edit.start, edit.end); assert_eq!(edit.replacement, ", value"); assert_eq!( apply(source, &edit), - "import lib.math.{old, value};\nfunction main() { value; }\n" + "import {old, value} from lib.math;\nfunction main() { value; }\n" ); } #[test] - fn wildcard_upgrade_preserves_an_existing_selective_import() { - let source = "import std.dispatch.{NonPayable, SigString};\nfunction main() {}\n"; - let edit = plan_wildcard(source, "std.dispatch").expect("edit"); - - assert_eq!( - apply(source, &edit), - "import std.dispatch.{NonPayable, SigString, *};\nfunction main() {}\n" - ); + fn wildcard_upgrade_does_not_create_an_invalid_mixed_selector() { + let source = "import {NonPayable, SigString} from std.dispatch;\nfunction main() {}\n"; + assert_eq!(plan_wildcard(source, "std.dispatch"), None); } #[test] fn wildcard_import_is_inserted_when_target_is_not_selected() { - let source = "import std.{*};\nfunction main() {}\n"; + let source = "import * from std;\nfunction main() {}\n"; let edit = plan_wildcard(source, "std.dispatch").expect("edit"); assert_eq!( apply(source, &edit), - "import std.{*};\nimport std.dispatch.{*};\nfunction main() {}\n" + "import * from std;\nimport * from std.dispatch;\nfunction main() {}\n" ); } #[test] fn existing_wildcard_import_needs_no_edit() { - let source = "import std.dispatch.{*};\nfunction main() {}\n"; + let source = "import * from std.dispatch;\nfunction main() {}\n"; assert_eq!(plan_wildcard(source, "std.dispatch"), None); } #[test] fn appends_after_the_last_alias_without_disturbing_operator_or_hiding() { - let source = - "import lib.{(^^), source as local} hiding {hidden};\nfunction main() { value; }\n"; + let source = "import {(^^), source as local} from lib hiding {hidden};\nfunction main() { value; }\n"; let edit = plan(source, "lib", "value").expect("edit"); assert_eq!( apply(source, &edit), - "import lib.{(^^), source as local, value} hiding {hidden};\nfunction main() { value; }\n" + "import {(^^), source as local, value} from lib hiding {hidden};\nfunction main() { value; }\n" ); } #[test] fn appending_keeps_selector_comments_and_crlf_layout() { - let source = "import lib.{old // keep old\r\n}; // keep import\r\n\r\nfunction main() { value; }\r\n"; + let source = "import {old // keep old\r\n} from lib; // keep import\r\n\r\nfunction main() { value; }\r\n"; let edit = plan(source, "lib", "value").expect("edit"); assert_eq!( apply(source, &edit), - "import lib.{old, value // keep old\r\n}; // keep import\r\n\r\nfunction main() { value; }\r\n" + "import {old, value // keep old\r\n} from lib; // keep import\r\n\r\nfunction main() { value; }\r\n" ); } #[test] fn appending_skips_a_nested_selector_comment() { - let source = - "import lib.{old /* outer /* inner */ still outer */};\nfunction main() { value; }\n"; + let source = "import {old /* outer /* inner */ still outer */} from lib;\nfunction main() { value; }\n"; let edit = plan(source, "lib", "value").expect("edit"); assert_eq!( apply(source, &edit), - "import lib.{old, value /* outer /* inner */ still outer */};\nfunction main() { value; }\n" + "import {old, value /* outer /* inner */ still outer */} from lib;\nfunction main() { value; }\n" ); } #[test] fn does_not_duplicate_an_existing_unaliased_name() { - let source = "import lib.{value};\nfunction main() { value; }\n"; + let source = "import {value} from lib;\nfunction main() { value; }\n"; assert_eq!(plan(source, "lib", "value"), None); } #[test] fn existing_source_alias_gets_a_separate_import() { - let source = "import lib.{value as renamed};\nfunction main() { value; }\n"; + let source = "import {value as renamed} from lib;\nfunction main() { value; }\n"; let edit = plan(source, "lib", "value").expect("edit"); assert_eq!( apply(source, &edit), - "import lib.{value as renamed};\nimport lib.{value};\nfunction main() { value; }\n" + "import {value as renamed} from lib;\nimport {value} from lib;\nfunction main() { value; }\n" ); } #[test] fn selector_hiding_the_name_gets_a_separate_import() { - let source = "import lib.{old} hiding {value};\nfunction main() { value; }\n"; + let source = "import {old} from lib hiding {value};\nfunction main() { value; }\n"; let edit = plan(source, "lib", "value").expect("edit"); assert_eq!( apply(source, &edit), - "import lib.{old} hiding {value};\nimport lib.{value};\nfunction main() { value; }\n" + "import {old} from lib hiding {value};\nimport {value} from lib;\nfunction main() { value; }\n" ); } #[test] fn hidden_selected_name_does_not_suppress_a_clean_import() { - let source = "import lib.{Option} hiding {Option};\nfunction main() { Option; }\n"; + let source = "import {Option} from lib hiding {Option};\nfunction main() { Option; }\n"; let edit = plan(source, "lib", "Option").expect("edit"); assert_eq!( apply(source, &edit), - "import lib.{Option} hiding {Option};\nimport lib.{Option};\nfunction main() { Option; }\n" + "import {Option} from lib hiding {Option};\nimport {Option} from lib;\nfunction main() { Option; }\n" ); } #[test] fn hidden_aliased_source_does_not_create_a_local_name_collision() { - let source = "import lib.{Other as Option} hiding {Other};\nfunction main() { Option; }\n"; + let source = + "import {Other as Option} from lib hiding {Other};\nfunction main() { Option; }\n"; let edit = plan(source, "lib", "Option").expect("edit"); assert_eq!( apply(source, &edit), - "import lib.{Other as Option} hiding {Other};\nimport lib.{Option};\nfunction main() { Option; }\n" + "import {Other as Option} from lib hiding {Other};\nimport {Option} from lib;\nfunction main() { Option; }\n" ); } #[test] fn active_alias_still_suppresses_an_ambiguous_selective_import() { - let source = "import lib.{Other as Option};\nfunction main() { Option; }\n"; + let source = "import {Other as Option} from lib;\nfunction main() { Option; }\n"; assert_eq!(plan(source, "lib", "Option"), None); } #[test] fn wildcard_plain_and_module_alias_imports_get_separate_imports() { - for existing in ["import lib.{*};", "import lib;", "import lib as L;"] { + for existing in [ + "import * from lib;", + "import lib;", + "import * as L from lib;", + ] { let source = format!("{existing}\nfunction main() {{ value; }}\n"); let edit = plan(&source, "lib", "value").expect("edit"); assert_eq!( apply(&source, &edit), - format!("{existing}\nimport lib.{{value}};\nfunction main() {{ value; }}\n") + format!("{existing}\nimport {{value}} from lib;\nfunction main() {{ value; }}\n") ); } } #[test] fn new_import_follows_the_complete_import_block_and_keeps_blank_lines() { - let source = - "import first.{a};\nimport second.{b}; // second\n\nfunction main() { value; }\n"; + let source = "import {a} from first;\nimport {b} from second; // second\n\nfunction main() { value; }\n"; let edit = plan(source, "lib", "value").expect("edit"); assert_eq!( apply(source, &edit), - "import first.{a};\nimport second.{b}; // second\nimport lib.{value};\n\nfunction main() { value; }\n" + "import {a} from first;\nimport {b} from second; // second\nimport {value} from lib;\n\nfunction main() { value; }\n" ); } #[test] fn new_import_does_not_split_a_multiline_trailing_block_comment() { - let source = "import first.{a}; /* trailing\n block */\nfunction main() { value; }\n"; + let source = + "import {a} from first; /* trailing\n block */\nfunction main() { value; }\n"; let edit = plan(source, "lib", "value").expect("edit"); assert_eq!( apply(source, &edit), - "import first.{a}; /* trailing\n block */\nimport lib.{value};\nfunction main() { value; }\n" + "import {a} from first; /* trailing\n block */\nimport {value} from lib;\nfunction main() { value; }\n" ); } #[test] fn new_import_does_not_split_a_nested_trailing_block_comment() { - let source = "import first.{a}; /* outer\n /* inner */\n still outer */\nfunction main() { value; }\n"; + let source = "import {a} from first; /* outer\n /* inner */\n still outer */\nfunction main() { value; }\n"; let edit = plan(source, "lib", "value").expect("edit"); assert_eq!( apply(source, &edit), - "import first.{a}; /* outer\n /* inner */\n still outer */\nimport lib.{value};\nfunction main() { value; }\n" + "import {a} from first; /* outer\n /* inner */\n still outer */\nimport {value} from lib;\nfunction main() { value; }\n" ); } #[test] fn new_import_preserves_crlf_and_trailing_line_comment() { - let source = "import first.{a}; // first\r\n\r\nfunction main() { value; }\r\n"; + let source = "import {a} from first; // first\r\n\r\nfunction main() { value; }\r\n"; let edit = plan(source, "lib", "value").expect("edit"); assert_eq!( apply(source, &edit), - "import first.{a}; // first\r\nimport lib.{value};\r\n\r\nfunction main() { value; }\r\n" + "import {a} from first; // first\r\nimport {value} from lib;\r\n\r\nfunction main() { value; }\r\n" ); } @@ -820,7 +813,7 @@ mod tests { assert_eq!( apply(source, &edit), - "// license\npragma no-patterson-condition;\nimport lib.{value};\n\nfunction main() { value; }\n" + "// license\npragma no-patterson-condition;\nimport {value} from lib;\n\nfunction main() { value; }\n" ); } @@ -831,7 +824,7 @@ mod tests { assert_eq!( apply(source, &edit), - "// Copyright\n/* License */\nimport lib.{value};\n\nfunction main() { value; }\n" + "// Copyright\n/* License */\nimport {value} from lib;\n\nfunction main() { value; }\n" ); } @@ -842,23 +835,26 @@ mod tests { assert_eq!( apply(source, &edit), - "/* outer /* inner */ still outer */\nimport lib.{value};\n\nfunction main() { value; }\n" + "/* outer /* inner */ still outer */\nimport {value} from lib;\n\nfunction main() { value; }\n" ); } #[test] fn empty_source_gets_a_top_level_import() { let edit = plan("", "lib.math", "value").expect("edit"); - assert_eq!(edit, insertion(0, "import lib.math.{value};\n".to_owned())); + assert_eq!( + edit, + insertion(0, "import {value} from lib.math;\n".to_owned()) + ); } #[test] fn import_at_eof_stays_on_its_own_line() { - let source = "import first.{a}; // first"; + let source = "import {a} from first; // first"; let edit = plan(source, "lib", "value").expect("edit"); assert_eq!( apply(source, &edit), - "import first.{a}; // first\nimport lib.{value};" + "import {a} from first; // first\nimport {value} from lib;" ); } @@ -868,7 +864,7 @@ mod tests { let edit = plan(source, "@dep.util", "value").expect("edit"); assert_eq!( apply(source, &edit), - "import @dep.util.{value};\nfunction main() { value; }\n" + "import {value} from @dep.util;\nfunction main() { value; }\n" ); } @@ -892,10 +888,10 @@ mod tests { #[test] fn selected_wildcard_and_aliased_imports_do_not_count_as_plain() { for existing in [ - "import lib.math.{value};", - "import lib.math.{other} hiding {other};", - "import lib.math.{*};", - "import lib.math as Math;", + "import {value} from lib.math;", + "import {other} from lib.math hiding {other};", + "import * from lib.math;", + "import * as Math from lib.math;", ] { let source = format!("{existing}\nfunction main() {{ lib.value; }}\n"); let edit = @@ -937,7 +933,7 @@ mod tests { assert_eq!(plan_module(source, ""), None); let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); let db = world.db(); let path = world.vfs_path_for_uri(&uri).expect("VFS path"); @@ -965,7 +961,7 @@ mod tests { fn rejects_stale_parse_metadata() { let source = "function main() { value; }\n"; let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); let db = world.db(); let path = world.vfs_path_for_uri(&uri).expect("VFS path"); diff --git a/crates/lsp/src/inlay_hints.rs b/crates/lsp/src/inlay_hints.rs index dcc20c86..f30a6b98 100644 --- a/crates/lsp/src/inlay_hints.rs +++ b/crates/lsp/src/inlay_hints.rs @@ -251,14 +251,14 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } #[test] fn unannotated_let_gets_type_hint() { - let source = "function main() -> word {\n let x = 42;\n return x;\n}\n"; + let source = "function main() returns (word) {\n let x = 42;\n return x;\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let range = line_index.range(0, source.len() as u32); @@ -285,7 +285,7 @@ mod tests { #[test] fn annotated_let_gets_no_type_hint() { - let source = "function main() -> word {\n let y: word = 42;\n return y;\n}\n"; + let source = "function main() returns (word) {\n let y: word = 42;\n return y;\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let range = line_index.range(0, source.len() as u32); @@ -297,13 +297,8 @@ mod tests { #[test] fn range_filters_binding_names() { - let source = "\ -function main() -> word { - let a = 1; - let b = 2; - return b; -} -"; + let source = + "function main() returns (word) {\n let a = 1;\n let b = 2;\n return b;\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let start = line_index.byte_to_position(source.find("let b").expect("let b") as u32); diff --git a/crates/lsp/src/native.rs b/crates/lsp/src/native.rs index 868dc7cc..3ff0d378 100644 --- a/crates/lsp/src/native.rs +++ b/crates/lsp/src/native.rs @@ -492,12 +492,12 @@ fn initial_workspace_roots(params: &InitializeParams) -> Vec { fn watched_files_registration() -> Registration { let options = DidChangeWatchedFilesRegistrationOptions { watchers: vec![FileSystemWatcher { - glob_pattern: GlobPattern::String("**/*.solc".to_owned()), + glob_pattern: GlobPattern::String("**/*.sol".to_owned()), kind: Some(WatchKind::Create | WatchKind::Change | WatchKind::Delete), }], }; Registration { - id: "solcore-watch-solc".to_owned(), + id: "solcore-watch-sol".to_owned(), method: "workspace/didChangeWatchedFiles".to_owned(), register_options: serde_json::to_value(options).ok(), } @@ -566,7 +566,7 @@ fn is_solcore_uri(uri: &Url) -> bool { } fn is_solcore_path(path: &Path) -> bool { - path.extension().and_then(|extension| extension.to_str()) == Some("solc") + path.extension().and_then(|extension| extension.to_str()) == Some("sol") } fn is_ignored_directory(path: &Path) -> bool { diff --git a/crates/lsp/src/references.rs b/crates/lsp/src/references.rs index 6753d0cf..99cb635f 100644 --- a/crates/lsp/src/references.rs +++ b/crates/lsp/src/references.rs @@ -32,8 +32,8 @@ use crate::{ /// Semantic identity used by references, highlights, and future rename support. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ReferenceTarget<'db> { - /// A named user definition such as a function, type, contract, class, or - /// instance. + /// A named user definition such as a function, type, contract, trait, or + /// impl. Def(DefId<'db>), /// A data constructor identified by its owning type and constructor index. Ctor { @@ -48,16 +48,16 @@ pub enum ReferenceTarget<'db> { Local(LocalBinding<'db>), /// A contract field. Field(FieldId<'db>), - /// A type-class method. + /// A trait method. ClassMethod { - /// The class that declares the method. + /// The trait that declares the method. class: DefId<'db>, /// The method name. name: String, }, /// A module qualifier binding local to one source module. Module(ModuleRef<'db>), - /// A local alias introduced by `import m.{source as alias}`. + /// A local alias introduced by `import {source as alias} from m;`. ImportAlias { /// Module definition that owns the import declaration. owner: DefId<'db>, @@ -2207,15 +2207,15 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } fn world_with_main_and_math(main: &str, math: &str) -> (WorldState, Url, Url) { let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(math_uri.clone(), math.to_owned())); (world, main_uri, math_uri) @@ -2223,7 +2223,7 @@ mod tests { #[test] fn parameter_references_include_uses_and_optional_declaration() { - let source = "function id(x: word) -> word {\n let y = x;\n return x;\n}\n"; + let source = "function id(x: word) returns (word) {\n let y = x;\n return x;\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let first_use = (source.find("let y = x").expect("first use") + "let y = ".len()) as u32; @@ -2254,15 +2254,7 @@ mod tests { #[test] fn top_level_function_declaration_finds_call_site() { - let source = "\ -function target() -> word { - return 1; -} - -function caller() -> word { - return target(); -} -"; + let source = "function target() returns (word) {\n return 1;\n}\n\nfunction caller() returns (word) {\n return target();\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let declaration = source.find("target").expect("declaration") as u32; @@ -2278,7 +2270,7 @@ function caller() -> word { #[test] fn std_references_exclude_the_unopenable_embedded_declaration() { - let source = "import std.{addWord};\nfunction main() -> word { return addWord(1, 2); }\n"; + let source = "import {addWord} from std;\nfunction main() returns (word) { return addWord(1, 2); }\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let import = source.find("addWord").expect("import") as u32; @@ -2299,8 +2291,10 @@ function caller() -> word { #[test] fn import_and_export_names_are_references_to_exported_item() { - let main = "import math.{double};\nfunction main() -> word { return double(21); }\n"; - let math = "function double(x: word) -> word { return x + x; }\nexport { double };\n"; + let main = + "import {double} from math;\nfunction main() returns (word) { return double(21); }\n"; + let math = + "function double(x: word) returns (word) { return x + x; }\nexport { double };\n"; let (world, main_uri, math_uri) = world_with_main_and_math(main, math); let main_index = world.line_index(&main_uri).expect("main line index"); let math_index = world.line_index(&math_uri).expect("math line index"); @@ -2331,9 +2325,8 @@ function caller() -> word { #[test] fn selected_import_alias_references_do_not_rename_the_source_symbol() { - let main = - "import math.{double as twice};\nfunction main() -> word { return twice(21); }\n"; - let math = "function double(x: word) -> word { return x; }\nexport { double };\n"; + let main = "import {double as twice} from math;\nfunction main() returns (word) { return twice(21); }\n"; + let math = "function double(x: word) returns (word) { return x; }\nexport { double };\n"; let (world, main_uri, math_uri) = world_with_main_and_math(main, math); let main_index = world.line_index(&main_uri).expect("main line index"); let math_index = world.line_index(&math_uri).expect("math line index"); @@ -2380,11 +2373,12 @@ function caller() -> word { #[test] fn module_alias_references_include_declaration_and_qualifier() { - let main = "import math as M;\nfunction main() -> word { return M.value(); }\n"; - let math = "function value() -> word { return 1; }\nexport { value };\n"; + let main = + "import * as M from math;\nfunction main() returns (word) { return M.value(); }\n"; + let math = "function value() returns (word) { return 1; }\nexport { value };\n"; let (world, main_uri, _) = world_with_main_and_math(main, math); let index = world.line_index(&main_uri).expect("main line index"); - let declaration = main.find("M;").expect("module alias") as u32; + let declaration = main.find("M from").expect("module alias") as u32; let qualifier = main.rfind("M.value").expect("module qualifier") as u32; let references = @@ -2402,19 +2396,11 @@ function caller() -> word { #[test] fn module_alias_references_include_type_and_pattern_qualifiers() { - let main = "\ -import math as M; -function unwrap(token: M.Token) -> word { - match token { - | M.Token.Ok(value) => return value; - | M.Token.Err(value) => return value; - } -} -"; - let model = "data Token = Ok(word) | Err(word);\nexport { Token(Ok, Err) };\n"; + let main = "import * as M from math;\nfunction unwrap(token: M.Token) returns (word) {\n match (token) {\n case M.Token.Ok(value) { return value; }\ncase M.Token.Err(value) { return value; }}\n}\n"; + let model = "enum Token {Ok(word) , Err(word)}\nexport { Token(Ok, Err) };\n"; let (world, main_uri, _) = world_with_main_and_math(main, model); let index = world.line_index(&main_uri).expect("main line index"); - let declaration = main.find("M;").expect("module alias") as u32; + let declaration = main.find("M from").expect("module alias") as u32; let type_qualifier = main.find("M.Token").expect("type qualifier") as u32; let ok_qualifier = main.find("M.Token.Ok").expect("Ok qualifier") as u32; let err_qualifier = main.find("M.Token.Err").expect("Err qualifier") as u32; @@ -2440,12 +2426,8 @@ function unwrap(token: M.Token) -> word { #[test] fn local_reexport_of_selected_alias_is_a_local_reference() { - let main = "\ -import math.{double as twice}; -export { twice }; -function main() -> word { return twice(21); } -"; - let math = "function double(x: word) -> word { return x; }\nexport { double };\n"; + let main = "import {double as twice} from math;\nexport { twice };\nfunction main() returns (word) { return twice(21); }\n"; + let math = "function double(x: word) returns (word) { return x; }\nexport { double };\n"; let (world, main_uri, _) = world_with_main_and_math(main, math); let index = world.line_index(&main_uri).expect("main index"); let declaration = main.find("twice").expect("alias declaration") as u32; @@ -2479,15 +2461,16 @@ function main() -> word { return twice(21); } #[test] fn exported_module_alias_references_include_downstream_qualifiers() { let mut world = WorldState::new(); - let util_uri = Url::parse("file:///main/util.solc").expect("util uri"); - let facade_uri = Url::parse("file:///main/facade.solc").expect("facade uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let consumer_uri = Url::parse("file:///main/consumer.solc").expect("consumer uri"); - let util = "function value() -> word { return 1; }\nexport { value };\n"; + let util_uri = Url::parse("file:///main/util.sol").expect("util uri"); + let facade_uri = Url::parse("file:///main/facade.sol").expect("facade uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let consumer_uri = Url::parse("file:///main/consumer.sol").expect("consumer uri"); + let util = "function value() returns (word) { return 1; }\nexport { value };\n"; let facade = "export util as Tools;\n"; - let main = "import facade;\nfunction main() -> word { return facade.Tools.value(); }\n"; + let main = + "import facade;\nfunction main() returns (word) { return facade.Tools.value(); }\n"; let consumer = - "import facade;\nfunction consume() -> word { return facade.Tools.value(); }\n"; + "import facade;\nfunction consume() returns (word) { return facade.Tools.value(); }\n"; assert!(world.open_document(util_uri, util.to_owned())); assert!(world.open_document(facade_uri.clone(), facade.to_owned())); assert!(world.open_document(main_uri.clone(), main.to_owned())); @@ -2523,12 +2506,8 @@ function main() -> word { return twice(21); } #[test] fn ambiguous_term_and_type_selector_has_no_single_reference_target() { - let main = "import math.{Thing};\nfunction use(x: Thing) -> word { return Thing(); }\n"; - let math = "\ -data Thing = MakeThing; -function Thing() -> word { return 1; } -export { Thing }; -"; + let main = "import {Thing} from math;\nfunction use(x: Thing) returns (word) { return Thing(); }\n"; + let math = "enum Thing {MakeThing}\nfunction Thing() returns (word) { return 1; }\nexport { Thing };\n"; let (world, main_uri, _) = world_with_main_and_math(main, math); let index = world.line_index(&main_uri).expect("main index"); let selector = main.find("Thing").expect("selector") as u32; @@ -2542,15 +2521,14 @@ export { Thing }; #[test] fn exported_module_alias_identity_survives_unaliased_reexport() { let mut world = WorldState::new(); - let util_uri = Url::parse("file:///main/util.solc").expect("util uri"); - let facade_uri = Url::parse("file:///main/facade.solc").expect("facade uri"); - let bridge_uri = Url::parse("file:///main/bridge.solc").expect("bridge uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let util = "function value() -> word { return 1; }\nexport { value };\n"; + let util_uri = Url::parse("file:///main/util.sol").expect("util uri"); + let facade_uri = Url::parse("file:///main/facade.sol").expect("facade uri"); + let bridge_uri = Url::parse("file:///main/bridge.sol").expect("bridge uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let util = "function value() returns (word) { return 1; }\nexport { value };\n"; let facade = "export util as Tools;\n"; let bridge = "export facade;\n"; - let main = - "import bridge;\nfunction main() -> word { return bridge.facade.Tools.value(); }\n"; + let main = "import bridge;\nfunction main() returns (word) { return bridge.facade.Tools.value(); }\n"; assert!(world.open_document(util_uri, util.to_owned())); assert!(world.open_document(facade_uri.clone(), facade.to_owned())); assert!(world.open_document(bridge_uri.clone(), bridge.to_owned())); @@ -2581,13 +2559,12 @@ export { Thing }; #[test] fn constructor_selectors_and_reexports_are_references() { let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let bridge_uri = Url::parse("file:///main/bridge.solc").expect("bridge uri"); - let model_uri = Url::parse("file:///main/model.solc").expect("model uri"); - let main = - "import bridge.{Token};\nfunction make(x: word) -> Token { return Token.Ok(x); }\n"; + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let bridge_uri = Url::parse("file:///main/bridge.sol").expect("bridge uri"); + let model_uri = Url::parse("file:///main/model.sol").expect("model uri"); + let main = "import {Token} from bridge;\nfunction make(x: word) returns (Token) { return Token.Ok(x); }\n"; let bridge = "export model.{Token(Ok)};\n"; - let model = "data Token = Ok(word) | Err(word);\nexport { Token(Ok, Err) };\n"; + let model = "enum Token {Ok(word) , Err(word)}\nexport { Token(Ok, Err) };\n"; assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(bridge_uri.clone(), bridge.to_owned())); assert!(world.open_document(model_uri.clone(), model.to_owned())); diff --git a/crates/lsp/src/rename.rs b/crates/lsp/src/rename.rs index 4d7fb0e6..2942848b 100644 --- a/crates/lsp/src/rename.rs +++ b/crates/lsp/src/rename.rs @@ -141,15 +141,15 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } fn world_with_main_and_math(main: &str, math: &str) -> (WorldState, Url, Url) { let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(math_uri.clone(), math.to_owned())); (world, main_uri, math_uri) @@ -157,7 +157,7 @@ mod tests { #[test] fn renaming_parameter_edits_declaration_and_uses() { - let source = "function id(x: word) -> word {\n let y = x;\n return x;\n}\n"; + let source = "function id(x: word) returns (word) {\n let y = x;\n return x;\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let declaration = source.find("x: word").expect("declaration") as u32; @@ -185,7 +185,7 @@ mod tests { #[test] fn prepare_rename_returns_user_symbol_range_but_not_builtin_or_keyword() { - let source = "function id(x: word) -> word {\n return x;\n}\n"; + let source = "function id(x: word) returns (word) {\n return x;\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let use_offset = (source.find("return x").expect("use") + "return ".len()) as u32; @@ -213,7 +213,7 @@ mod tests { #[test] fn rename_rejects_invalid_new_name() { - let source = "function id(x: word) -> word {\n return x;\n}\n"; + let source = "function id(x: word) returns (word) {\n return x;\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let use_offset = (source.find("return x").expect("use") + "return ".len()) as u32; @@ -229,8 +229,10 @@ mod tests { #[test] fn renaming_exported_function_edits_import_and_export_names() { - let main = "import math.{double};\nfunction main() -> word { return double(21); }\n"; - let math = "function double(x: word) -> word { return x + x; }\nexport { double };\n"; + let main = + "import {double} from math;\nfunction main() returns (word) { return double(21); }\n"; + let math = + "function double(x: word) returns (word) { return x + x; }\nexport { double };\n"; let (world, main_uri, math_uri) = world_with_main_and_math(main, math); let main_index = world.line_index(&main_uri).expect("main line index"); let math_index = world.line_index(&math_uri).expect("math line index"); @@ -271,7 +273,7 @@ mod tests { #[test] fn embedded_std_symbol_is_not_offered_for_rename() { - let source = "import std.{addWord};\nfunction main() -> word { return addWord(1, 2); }\n"; + let source = "import {addWord} from std;\nfunction main() returns (word) { return addWord(1, 2); }\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let call = source.rfind("addWord").expect("call") as u32; @@ -284,14 +286,11 @@ mod tests { #[test] fn renaming_exported_function_from_defining_module_edits_importer() { let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); - let main = "import math.{double};\nfunction main() -> word { return double(21); }\n"; - let math = "\ -function double(x: word) -> word { return x + x; } -function local() -> word { return double(2); } -export { double }; -"; + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); + let main = + "import {double} from math;\nfunction main() returns (word) { return double(21); }\n"; + let math = "function double(x: word) returns (word) { return x + x; }\nfunction local() returns (word) { return double(2); }\nexport { double };\n"; assert!(world.open_document(math_uri.clone(), math.to_owned())); assert!(world.open_document(main_uri.clone(), main.to_owned())); let main_index = world.line_index(&main_uri).expect("main line index"); @@ -335,9 +334,8 @@ export { double }; #[test] fn renaming_selected_import_alias_only_edits_local_alias_uses() { - let main = - "import math.{double as twice};\nfunction main() -> word { return twice(21); }\n"; - let math = "function double(x: word) -> word { return x; }\nexport { double };\n"; + let main = "import {double as twice} from math;\nfunction main() returns (word) { return twice(21); }\n"; + let math = "function double(x: word) returns (word) { return x; }\nexport { double };\n"; let (world, main_uri, math_uri) = world_with_main_and_math(main, math); let index = world.line_index(&main_uri).expect("main index"); let alias = main.find("twice").expect("alias declaration") as u32; @@ -368,11 +366,12 @@ export { double }; #[test] fn renaming_explicit_module_alias_edits_alias_and_qualifiers() { - let main = "import math as M;\nfunction main() -> word { return M.value(); }\n"; - let math = "function value() -> word { return 1; }\nexport { value };\n"; + let main = + "import * as M from math;\nfunction main() returns (word) { return M.value(); }\n"; + let math = "function value() returns (word) { return 1; }\nexport { value };\n"; let (world, main_uri, _) = world_with_main_and_math(main, math); let index = world.line_index(&main_uri).expect("main index"); - let alias = main.find("M;").expect("alias declaration") as u32; + let alias = main.find("M from").expect("alias declaration") as u32; let use_offset = main.rfind("M.value").expect("alias use") as u32; let edit = handle_rename( @@ -398,19 +397,11 @@ export { double }; #[test] fn renaming_module_alias_updates_type_and_pattern_qualifiers() { - let main = "\ -import math as M; -function unwrap(token: M.Token) -> word { - match token { - | M.Token.Ok(value) => return value; - | M.Token.Err(value) => return value; - } -} -"; - let model = "data Token = Ok(word) | Err(word);\nexport { Token(Ok, Err) };\n"; + let main = "import * as M from math;\nfunction unwrap(token: M.Token) returns (word) {\n match (token) {\n case M.Token.Ok(value) { return value; }\ncase M.Token.Err(value) { return value; }}\n}\n"; + let model = "enum Token {Ok(word) , Err(word)}\nexport { Token(Ok, Err) };\n"; let (world, main_uri, _) = world_with_main_and_math(main, model); let index = world.line_index(&main_uri).expect("main index"); - let declaration = main.find("M;").expect("alias declaration") as u32; + let declaration = main.find("M from").expect("alias declaration") as u32; let type_qualifier = main.find("M.Token").expect("type qualifier") as u32; let ok_qualifier = main.find("M.Token.Ok").expect("Ok qualifier") as u32; let err_qualifier = main.find("M.Token.Err").expect("Err qualifier") as u32; @@ -440,12 +431,8 @@ function unwrap(token: M.Token) -> word { #[test] fn exported_selected_alias_is_not_offered_an_incomplete_text_rename() { - let main = "\ -import math.{double as twice}; -export { twice }; -function main() -> word { return twice(21); } -"; - let math = "function double(x: word) -> word { return x; }\nexport { double };\n"; + let main = "import {double as twice} from math;\nexport { twice };\nfunction main() returns (word) { return twice(21); }\n"; + let math = "function double(x: word) returns (word) { return x; }\nexport { double };\n"; let (world, main_uri, _) = world_with_main_and_math(main, math); let index = world.line_index(&main_uri).expect("main index"); let use_offset = main.rfind("twice").expect("alias use") as u32; @@ -458,15 +445,16 @@ function main() -> word { return twice(21); } #[test] fn renaming_exported_module_alias_updates_downstream_qualifiers() { let mut world = WorldState::new(); - let util_uri = Url::parse("file:///main/util.solc").expect("util uri"); - let facade_uri = Url::parse("file:///main/facade.solc").expect("facade uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let consumer_uri = Url::parse("file:///main/consumer.solc").expect("consumer uri"); - let util = "function value() -> word { return 1; }\nexport { value };\n"; + let util_uri = Url::parse("file:///main/util.sol").expect("util uri"); + let facade_uri = Url::parse("file:///main/facade.sol").expect("facade uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let consumer_uri = Url::parse("file:///main/consumer.sol").expect("consumer uri"); + let util = "function value() returns (word) { return 1; }\nexport { value };\n"; let facade = "export util as Tools;\n"; - let main = "import facade;\nfunction main() -> word { return facade.Tools.value(); }\n"; + let main = + "import facade;\nfunction main() returns (word) { return facade.Tools.value(); }\n"; let consumer = - "import facade;\nfunction consume() -> word { return facade.Tools.value(); }\n"; + "import facade;\nfunction consume() returns (word) { return facade.Tools.value(); }\n"; assert!(world.open_document(util_uri, util.to_owned())); assert!(world.open_document(facade_uri.clone(), facade.to_owned())); assert!(world.open_document(main_uri.clone(), main.to_owned())); @@ -517,12 +505,13 @@ function main() -> word { return twice(21); } #[test] fn source_definition_rename_is_rejected_across_exported_selected_alias() { let mut world = WorldState::new(); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); - let bridge_uri = Url::parse("file:///main/bridge.solc").expect("bridge uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let math = "function double(x: word) -> word { return x; }\nexport { double };\n"; - let bridge = "import math.{double as twice};\nexport { twice };\n"; - let main = "import bridge.{twice};\nfunction main() -> word { return twice(1); }\n"; + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); + let bridge_uri = Url::parse("file:///main/bridge.sol").expect("bridge uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let math = "function double(x: word) returns (word) { return x; }\nexport { double };\n"; + let bridge = "import {double as twice} from math;\nexport { twice };\n"; + let main = + "import {twice} from bridge;\nfunction main() returns (word) { return twice(1); }\n"; assert!(world.open_document(math_uri.clone(), math.to_owned())); assert!(world.open_document(bridge_uri, bridge.to_owned())); assert!(world.open_document(main_uri, main.to_owned())); @@ -541,15 +530,16 @@ function main() -> word { return twice(21); } let right_path = base.join("right"); let left_root = Url::from_directory_path(&left_path).expect("left root"); let right_root = Url::from_directory_path(&right_path).expect("right root"); - let left_main = Url::from_file_path(left_path.join("main.solc")).expect("left main"); - let left_math = Url::from_file_path(left_path.join("math.solc")).expect("left math"); - let right_main = Url::from_file_path(right_path.join("main.solc")).expect("right main"); - let right_math = Url::from_file_path(right_path.join("math.solc")).expect("right math"); - let left_source = "import lib.math.{value};\nfunction left() -> word { return value(); }\n"; + let left_main = Url::from_file_path(left_path.join("main.sol")).expect("left main"); + let left_math = Url::from_file_path(left_path.join("math.sol")).expect("left math"); + let right_main = Url::from_file_path(right_path.join("main.sol")).expect("right main"); + let right_math = Url::from_file_path(right_path.join("math.sol")).expect("right math"); + let left_source = + "import {value} from lib.math;\nfunction left() returns (word) { return value(); }\n"; let right_source = - "import lib.math.{value};\nfunction right() -> word { return value(); }\n"; - let left_library = "function value() -> word { return 1; }\nexport { value };\n"; - let right_library = "function value() -> word { return 2; }\nexport { value };\n"; + "import {value} from lib.math;\nfunction right() returns (word) { return value(); }\n"; + let left_library = "function value() returns (word) { return 1; }\nexport { value };\n"; + let right_library = "function value() returns (word) { return 2; }\nexport { value };\n"; let mut world = WorldState::new(); world.load_workspace_roots([ ( @@ -589,15 +579,14 @@ function main() -> word { return twice(21); } #[test] fn renaming_exported_module_alias_updates_unaliased_reexport_chain() { let mut world = WorldState::new(); - let util_uri = Url::parse("file:///main/util.solc").expect("util uri"); - let facade_uri = Url::parse("file:///main/facade.solc").expect("facade uri"); - let bridge_uri = Url::parse("file:///main/bridge.solc").expect("bridge uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let util = "function value() -> word { return 1; }\nexport { value };\n"; + let util_uri = Url::parse("file:///main/util.sol").expect("util uri"); + let facade_uri = Url::parse("file:///main/facade.sol").expect("facade uri"); + let bridge_uri = Url::parse("file:///main/bridge.sol").expect("bridge uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let util = "function value() returns (word) { return 1; }\nexport { value };\n"; let facade = "export util as Tools;\n"; let bridge = "export facade;\n"; - let main = - "import bridge;\nfunction main() -> word { return bridge.facade.Tools.value(); }\n"; + let main = "import bridge;\nfunction main() returns (word) { return bridge.facade.Tools.value(); }\n"; assert!(world.open_document(util_uri, util.to_owned())); assert!(world.open_document(facade_uri.clone(), facade.to_owned())); assert!(world.open_document(bridge_uri.clone(), bridge.to_owned())); @@ -635,12 +624,13 @@ function main() -> word { return twice(21); } #[test] fn default_module_reexport_without_alias_is_not_text_renameable() { let mut world = WorldState::new(); - let util_uri = Url::parse("file:///main/util.solc").expect("util uri"); - let facade_uri = Url::parse("file:///main/facade.solc").expect("facade uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let util = "function value() -> word { return 1; }\nexport { value };\n"; + let util_uri = Url::parse("file:///main/util.sol").expect("util uri"); + let facade_uri = Url::parse("file:///main/facade.sol").expect("facade uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let util = "function value() returns (word) { return 1; }\nexport { value };\n"; let facade = "export util;\n"; - let main = "import facade;\nfunction main() -> word { return facade.util.value(); }\n"; + let main = + "import facade;\nfunction main() returns (word) { return facade.util.value(); }\n"; assert!(world.open_document(util_uri, util.to_owned())); assert!(world.open_document(facade_uri, facade.to_owned())); assert!(world.open_document(main_uri.clone(), main.to_owned())); @@ -655,11 +645,10 @@ function main() -> word { return twice(21); } #[test] fn renaming_constructor_updates_import_and_export_selectors() { let mut world = WorldState::new(); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); - let model_uri = Url::parse("file:///main/model.solc").expect("model uri"); - let main = - "import model.{Token};\nfunction make(x: word) -> Token { return Token.Ok(x); }\n"; - let model = "data Token = Ok(word) | Err(word);\nexport { Token(Ok, Err) };\n"; + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); + let model_uri = Url::parse("file:///main/model.sol").expect("model uri"); + let main = "import {Token} from model;\nfunction make(x: word) returns (Token) { return Token.Ok(x); }\n"; + let model = "enum Token {Ok(word) , Err(word)}\nexport { Token(Ok, Err) };\n"; assert!(world.open_document(main_uri.clone(), main.to_owned())); assert!(world.open_document(model_uri.clone(), model.to_owned())); let main_index = world.line_index(&main_uri).expect("main index"); diff --git a/crates/lsp/src/selection_range.rs b/crates/lsp/src/selection_range.rs index 198270f6..55b4e96f 100644 --- a/crates/lsp/src/selection_range.rs +++ b/crates/lsp/src/selection_range.rs @@ -259,10 +259,13 @@ fn is_two_byte_operator(bytes: Option<&[u8]>) -> bool { | b"||" | b"+=" | b"-=" + | b"*=" + | b"/=" | b"^=" | b"&=" | b"|=" | b"%=" + | b"~=" ) ) } @@ -376,7 +379,7 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } @@ -393,8 +396,7 @@ mod tests { #[test] fn builds_unicode_safe_leaf_to_module_chain() { - let source = - "function main(value: word) -> word {\n let café = (value + 1);\n return café;\n}\n"; + let source = "function main(value: word) returns (word) {\n let café = (value + 1);\n return café;\n}\n"; let (world, uri) = world_with_main(source); let line_index = world.line_index(&uri).expect("line index"); let leaf_start = source.find("café").expect("unicode identifier"); @@ -453,14 +455,7 @@ mod tests { #[test] fn overlapping_source_line_does_not_hide_multiline_call_selection() { - let source = "\ -function main() -> word { - let x = add( - 1, - 2); // trailing - return x; -} -"; + let source = "function main() returns (word) {\n let x = add(\n 1,\n 2); // trailing\n return x;\n}\n"; let (world, uri) = world_with_main(source); let position = Position::new(3, 4); let ranges = handle_selection_range(&world, &uri, &[position]).expect("selection ranges"); @@ -479,7 +474,7 @@ function main() -> word { #[test] fn leaf_ranges_follow_identifier_and_operator_token_boundaries() { - let source = "pragma no-bounded-variable-condition;\nfunction main() -> word {\n let value = 1;\n return value-1;\n}\n"; + let source = "pragma no-bounded-variable-condition;\nfunction main() returns (word) {\n let value = 1;\n return value-1;\n}\n"; let (world, uri) = world_with_main(source); let index = world.line_index(&uri).unwrap(); let pragma = source.find("no-bounded").unwrap(); @@ -503,6 +498,21 @@ function main() -> word { ); } + #[test] + fn compound_assignment_leaf_ranges_include_every_canonical_operator() { + let source = "left *= right; left /= right; left ~=;"; + for operator in ["*=", "/=", "~="] { + let start = source.find(operator).expect("operator"); + assert_eq!( + leaf_range_at(source, start + 1), + Some(ByteRange { + start, + end: start + operator.len(), + }) + ); + } + } + #[test] fn rejects_out_of_range_and_mid_surrogate_positions() { let source = "// 😀\n"; @@ -523,7 +533,7 @@ function main() -> word { let (world, uri) = world_with_main(""); assert_eq!(handle_selection_range(&world, &uri, &[]), Some(Vec::new())); - let missing = Url::parse("file:///main/missing.solc").expect("uri"); + let missing = Url::parse("file:///main/missing.sol").expect("uri"); assert_eq!(handle_selection_range(&world, &missing, &[]), None); } } diff --git a/crates/lsp/src/semantic_tokens.rs b/crates/lsp/src/semantic_tokens.rs index ba822d23..b08fb28e 100644 --- a/crates/lsp/src/semantic_tokens.rs +++ b/crates/lsp/src/semantic_tokens.rs @@ -676,14 +676,14 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } #[test] fn semantic_tokens_are_non_empty_ordered_and_start_at_first_named_entity() { - let source = "function main(x: word) -> word {\n let y = x;\n return y;\n}\n"; + let source = "function main(x: word) returns (word) {\n let y = x;\n return y;\n}\n"; let (world, uri) = world_with_main(source); let result = handle_semantic_tokens_full(&world, &uri).expect("semantic tokens"); @@ -704,17 +704,7 @@ mod tests { #[test] fn emitted_token_type_indexes_are_covered_by_the_legend() { - let source = "\ -data Maybe = None | Some(word); - -contract Box { - value: word; - function get(x: word) -> word { - let current = value; - return current + x; - } -} -"; + let source = "enum Maybe {None , Some(word)}\n\ncontract Box {\n value: word;\n function get(x: word) returns (word) {\n let current = value;\n return current + x;\n }\n}\n"; let (world, uri) = world_with_main(source); let result = handle_semantic_tokens_full(&world, &uri).expect("semantic tokens"); diff --git a/crates/lsp/src/signature_help.rs b/crates/lsp/src/signature_help.rs index 3ad0360a..46777d21 100644 --- a/crates/lsp/src/signature_help.rs +++ b/crates/lsp/src/signature_help.rs @@ -324,7 +324,11 @@ fn signature_from_scheme<'db>( .unwrap_or(ty) }) .collect::>(); - let label = format!("{name}({}) -> {}", parameters.join(", "), ret.display(db)); + let label = format!( + "{name}({}) returns ({})", + parameters.join(", "), + ret.display(db) + ); Some(CallableSignature { label, parameters }) } @@ -478,7 +482,7 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } @@ -493,7 +497,7 @@ mod tests { #[test] fn highlights_first_argument() { - let source = "function f(a: word, b: word) -> word {\n return a;\n}\n\nfunction main() -> word {\n return f(1, 2);\n}\n"; + let source = "function f(a: word, b: word) returns (word) {\n return a;\n}\n\nfunction main() returns (word) {\n return f(1, 2);\n}\n"; let (world, uri) = world_with_main(source); let position = position_at(source, &world, &uri, "1, 2"); @@ -506,7 +510,7 @@ mod tests { #[test] fn highlights_second_argument_and_labels_signature() { - let source = "function f(a: word, b: word) -> word {\n return a;\n}\n\nfunction main() -> word {\n return f(1, 2);\n}\n"; + let source = "function f(a: word, b: word) returns (word) {\n return a;\n}\n\nfunction main() returns (word) {\n return f(1, 2);\n}\n"; let (world, uri) = world_with_main(source); let comma_offset = source.find(", 2").expect("comma") as u32 + 1; let position = world @@ -535,7 +539,7 @@ mod tests { signature.label ); assert!( - signature.label.contains("-> word"), + signature.label.contains("returns (word)"), "expected return type in label, got {}", signature.label ); @@ -543,10 +547,10 @@ mod tests { #[test] fn signature_help_uses_requested_module_when_unrelated_document_opened_first() { - let unrelated = "function unrelated() -> word { return 0; }\n"; - let main = "function combine(a: word, b: word) -> word { return a; }\n\nfunction main() -> word {\n return combine(1, 2);\n}\n"; - let unrelated_uri = Url::parse("file:///main/unrelated.solc").expect("unrelated uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let unrelated = "function unrelated() returns (word) { return 0; }\n"; + let main = "function combine(a: word, b: word) returns (word) { return a; }\n\nfunction main() returns (word) {\n return combine(1, 2);\n}\n"; + let unrelated_uri = Url::parse("file:///main/unrelated.sol").expect("unrelated uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); let mut world = WorldState::new(); assert!(world.open_document(unrelated_uri, unrelated.to_owned())); assert!(world.open_document(main_uri.clone(), main.to_owned())); @@ -572,14 +576,12 @@ mod tests { #[test] fn signature_help_resolves_imported_function_in_defining_module() { - let unrelated = "function unrelated() -> word { return 0; }\n"; - let math = - "function combine(a: word, b: word) -> word { return a; }\n\nexport { combine };\n"; - let main = - "import math.{combine};\n\nfunction main() -> word {\n return combine(1, 2);\n}\n"; - let unrelated_uri = Url::parse("file:///main/unrelated.solc").expect("unrelated uri"); - let math_uri = Url::parse("file:///main/math.solc").expect("math uri"); - let main_uri = Url::parse("file:///main/main.solc").expect("main uri"); + let unrelated = "function unrelated() returns (word) { return 0; }\n"; + let math = "function combine(a: word, b: word) returns (word) { return a; }\n\nexport { combine };\n"; + let main = "import {combine} from math;\n\nfunction main() returns (word) {\n return combine(1, 2);\n}\n"; + let unrelated_uri = Url::parse("file:///main/unrelated.sol").expect("unrelated uri"); + let math_uri = Url::parse("file:///main/math.sol").expect("math uri"); + let main_uri = Url::parse("file:///main/main.sol").expect("main uri"); let mut world = WorldState::new(); assert!(world.open_document(unrelated_uri, unrelated.to_owned())); assert!(world.open_document(math_uri, math.to_owned())); diff --git a/crates/lsp/src/state.rs b/crates/lsp/src/state.rs index 74c1c3f3..49bf74e6 100644 --- a/crates/lsp/src/state.rs +++ b/crates/lsp/src/state.rs @@ -557,7 +557,7 @@ impl WorldState { .file_name() .and_then(|name| name.to_str()) .filter(|name| !name.is_empty()) - .unwrap_or("document.solc") + .unwrap_or("document.sol") .to_owned(); (hex_encode(identity.as_bytes()), filename) }); @@ -605,7 +605,7 @@ impl WorldState { .extension() .and_then(|extension| extension.to_str()) .filter(|extension| !extension.is_empty()) - .unwrap_or("solc"); + .unwrap_or("sol"); Some(format!("/main/__virtual__/{id}.{extension}")) } } @@ -731,22 +731,22 @@ mod tests { #[test] fn maps_main_file_uris_to_vfs_paths() { - let uri = Url::parse("file:///main/main.solc").expect("uri"); - assert_eq!(uri_to_vfs_path(&uri), Some("/main/main.solc".to_owned())); + let uri = Url::parse("file:///main/main.sol").expect("uri"); + assert_eq!(uri_to_vfs_path(&uri), Some("/main/main.sol".to_owned())); - let std_uri = Url::parse("file:///std/std.solc").expect("uri"); + let std_uri = Url::parse("file:///std/std.sol").expect("uri"); assert_eq!(uri_to_vfs_path(&std_uri), None); - let memory_uri = Url::parse("memory:///main/main.solc").expect("uri"); + let memory_uri = Url::parse("memory:///main/main.sol").expect("uri"); assert_eq!(uri_to_vfs_path(&memory_uri), None); } #[test] fn open_change_and_close_document() { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); - let clean = "function main() -> word {\n return 1;\n}\n"; - let changed = "function main() -> word {\n return 2;\n}\n"; + let uri = Url::parse("file:///main/main.sol").expect("uri"); + let clean = "function main() returns (word) {\n return 1;\n}\n"; + let changed = "function main() returns (word) {\n return 2;\n}\n"; assert!(world.open_document(uri.clone(), clean.to_owned())); assert_eq!(world.document_text(&uri), Some(clean)); @@ -763,7 +763,7 @@ mod tests { use lsp_types::{Position, Range}; let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), "a😀c\n".to_owned())); assert!(world.apply_document_changes( @@ -799,8 +799,8 @@ mod tests { let mut world = WorldState::new(); let root_path = std::env::temp_dir().join("solcore-lsp-state-project"); let root = Url::from_directory_path(&root_path).expect("root uri"); - let main_uri = Url::from_file_path(root_path.join("src/main.solc")).expect("main uri"); - let util_uri = Url::from_file_path(root_path.join("src/util.solc")).expect("util uri"); + let main_uri = Url::from_file_path(root_path.join("src/main.sol")).expect("main uri"); + let util_uri = Url::from_file_path(root_path.join("src/util.sol")).expect("util uri"); assert_eq!( world.load_workspace_documents( @@ -808,11 +808,11 @@ mod tests { [ ( main_uri.clone(), - "function main() -> word { return 1; }\n".to_owned() + "function main() returns (word) { return 1; }\n".to_owned() ), ( util_uri.clone(), - "function util() -> word { return 2; }\n".to_owned() + "function util() returns (word) { return 2; }\n".to_owned() ), ], ), @@ -820,15 +820,15 @@ mod tests { ); assert_eq!( world.vfs_path_for_uri(&main_uri), - Some("/main/src/main.solc".to_owned()) + Some("/main/src/main.sol".to_owned()) ); assert_eq!( - world.client_uri_for_vfs_url("file:///main/src/util.solc"), + world.client_uri_for_vfs_url("file:///main/src/util.sol"), Some(util_uri) ); assert!(world.open_document( main_uri.clone(), - "function main() -> word { return 1; }\n".to_owned() + "function main() returns (word) { return 1; }\n".to_owned() )); assert_eq!(world.open_document_uris(), vec![main_uri]); assert_eq!(world.workspace_document_uris().len(), 2); @@ -839,23 +839,23 @@ mod tests { let mut world = WorldState::new(); let root_path = std::env::temp_dir().join("solcore-lsp-state-encoded-project"); let root = Url::from_directory_path(&root_path).expect("root uri"); - let uri = Url::from_file_path(root_path.join("src/数 学.solc")).expect("encoded uri"); + let uri = Url::from_file_path(root_path.join("src/数 学.sol")).expect("encoded uri"); assert_eq!( world.load_workspace_documents( root, [( uri.clone(), - "function value() -> word { return 1; }\n".to_owned() + "function value() returns (word) { return 1; }\n".to_owned() )] ), 1 ); assert_eq!( world.vfs_path_for_uri(&uri), - Some("/main/src/数 学.solc".to_owned()) + Some("/main/src/数 学.sol".to_owned()) ); assert_eq!( - world.client_uri_for_vfs_url("file:///main/src/%E6%95%B0%20%E5%AD%A6.solc"), + world.client_uri_for_vfs_url("file:///main/src/%E6%95%B0%20%E5%AD%A6.sol"), Some(uri) ); } @@ -867,9 +867,9 @@ mod tests { let right_path = base.join("right"); let left_root = Url::from_directory_path(&left_path).expect("left root uri"); let right_root = Url::from_directory_path(&right_path).expect("right root uri"); - let left_uri = Url::from_file_path(left_path.join("src/main.solc")).expect("left uri"); - let right_uri = Url::from_file_path(right_path.join("src/main.solc")).expect("right uri"); - let source = "function value() -> word { return 1; }\n"; + let left_uri = Url::from_file_path(left_path.join("src/main.sol")).expect("left uri"); + let right_uri = Url::from_file_path(right_path.join("src/main.sol")).expect("right uri"); + let source = "function value() returns (word) { return 1; }\n"; let mut world = WorldState::new(); assert_eq!( @@ -890,8 +890,8 @@ mod tests { let right_vfs = world.vfs_path_for_uri(&right_uri).expect("right vfs path"); assert!(left_vfs.starts_with("/main/__solcore_workspace__/")); assert!(right_vfs.starts_with("/main/__solcore_workspace__/")); - assert!(left_vfs.ends_with("/src/main.solc")); - assert!(right_vfs.ends_with("/src/main.solc")); + assert!(left_vfs.ends_with("/src/main.sol")); + assert!(right_vfs.ends_with("/src/main.sol")); assert_ne!(left_vfs, right_vfs); assert_eq!(world.workspace_root_count(), 2); assert_eq!( @@ -916,8 +916,8 @@ mod tests { fn configured_main_file_root_uses_multi_root_namespace_before_virtual_mapping() { let main_root = Url::parse("file:///main/").expect("main root"); let other_root = Url::parse("file:///workspace/other/").expect("other root"); - let main_uri = Url::parse("file:///main/project.solc").expect("main uri"); - let other_uri = Url::parse("file:///workspace/other/project.solc").expect("other uri"); + let main_uri = Url::parse("file:///main/project.sol").expect("main uri"); + let other_uri = Url::parse("file:///workspace/other/project.sol").expect("other uri"); let mut world = WorldState::new(); world.load_workspace_roots([ @@ -942,15 +942,15 @@ mod tests { fn rootless_main_document_is_remapped_when_workspace_folders_arrive() { let main_root = Url::parse("file:///main/").expect("main root"); let other_root = Url::parse("file:///workspace/other/").expect("other root"); - let main_uri = Url::parse("file:///main/project.solc").expect("main uri"); + let main_uri = Url::parse("file:///main/project.sol").expect("main uri"); let mut world = WorldState::new(); assert!(world.open_document( main_uri.clone(), - "function value() -> word { return 1; }\n".to_owned() + "function value() returns (word) { return 1; }\n".to_owned() )); assert_eq!( world.vfs_path_for_uri(&main_uri), - Some("/main/project.solc".to_owned()) + Some("/main/project.sol".to_owned()) ); world.update_workspace_roots( @@ -973,16 +973,16 @@ mod tests { let right_path = base.join("right"); let left_root = Url::from_directory_path(&left_path).expect("left root uri"); let right_root = Url::from_directory_path(&right_path).expect("right root uri"); - let left_main = Url::from_file_path(left_path.join("main.solc")).expect("left main uri"); - let left_math = Url::from_file_path(left_path.join("math.solc")).expect("left math uri"); - let right_main = Url::from_file_path(right_path.join("main.solc")).expect("right main uri"); - let right_math = Url::from_file_path(right_path.join("math.solc")).expect("right math uri"); - let left_source = - "import lib.math.{leftValue};\nfunction runLeft() -> word { return leftValue(); }\n"; - let left_library = "function leftValue() -> word { return 1; }\nexport { leftValue };\n"; - let right_source = - "import lib.math.{rightValue};\nfunction runRight() -> word { return rightValue(); }\n"; - let right_library = "function rightValue() -> word { return 2; }\nexport { rightValue };\n"; + let left_main = Url::from_file_path(left_path.join("main.sol")).expect("left main uri"); + let left_math = Url::from_file_path(left_path.join("math.sol")).expect("left math uri"); + let right_main = Url::from_file_path(right_path.join("main.sol")).expect("right main uri"); + let right_math = Url::from_file_path(right_path.join("math.sol")).expect("right math uri"); + let left_source = "import {leftValue} from lib.math;\nfunction runLeft() returns (word) { return leftValue(); }\n"; + let left_library = + "function leftValue() returns (word) { return 1; }\nexport { leftValue };\n"; + let right_source = "import {rightValue} from lib.math;\nfunction runRight() returns (word) { return rightValue(); }\n"; + let right_library = + "function rightValue() returns (word) { return 2; }\nexport { rightValue };\n"; let mut world = WorldState::new(); world.load_workspace_roots([ @@ -1036,10 +1036,10 @@ mod tests { let right_path = base.join("right"); let left_root = Url::from_directory_path(&left_path).expect("left root uri"); let right_root = Url::from_directory_path(&right_path).expect("right root uri"); - let left_uri = Url::from_file_path(left_path.join("shared.solc")).expect("left uri"); - let right_uri = Url::from_file_path(right_path.join("shared.solc")).expect("right uri"); + let left_uri = Url::from_file_path(left_path.join("shared.sol")).expect("left uri"); + let right_uri = Url::from_file_path(right_path.join("shared.sol")).expect("right uri"); let generated_uri = - Url::from_file_path(right_path.join("generated.solc")).expect("generated uri"); + Url::from_file_path(right_path.join("generated.sol")).expect("generated uri"); let mut world = WorldState::new(); world.load_workspace_roots([ @@ -1083,11 +1083,11 @@ mod tests { let right_path = base.join("right"); let left_root = Url::from_directory_path(&left_path).expect("left root uri"); let right_root = Url::from_directory_path(&right_path).expect("right root uri"); - let left_main = Url::from_file_path(left_path.join("main.solc")).expect("left main uri"); - let left_util = Url::from_file_path(left_path.join("util.solc")).expect("left util uri"); - let right_main = Url::from_file_path(right_path.join("main.solc")).expect("right main uri"); - let disk_source = "function value() -> word { return 1; }\n"; - let unsaved_source = "function value() -> word { return 99; }\n"; + let left_main = Url::from_file_path(left_path.join("main.sol")).expect("left main uri"); + let left_util = Url::from_file_path(left_path.join("util.sol")).expect("left util uri"); + let right_main = Url::from_file_path(right_path.join("main.sol")).expect("right main uri"); + let disk_source = "function value() returns (word) { return 1; }\n"; + let unsaved_source = "function value() returns (word) { return 99; }\n"; let mut world = WorldState::new(); world.load_workspace_roots([ @@ -1164,14 +1164,15 @@ mod tests { let left_root = Url::from_directory_path(&left_path).expect("left root"); let right_root = Url::from_directory_path(&right_path).expect("right root"); let third_root = Url::from_directory_path(&third_path).expect("third root"); - let left_main = Url::from_file_path(left_path.join("main.solc")).expect("left main"); - let left_math = Url::from_file_path(left_path.join("math.solc")).expect("left math"); - let right_math = Url::from_file_path(right_path.join("math.solc")).expect("right math"); - let third_file = Url::from_file_path(third_path.join("third.solc")).expect("third file"); - let main_source = - "import lib.math.{leftValue};\nfunction main() -> word { return leftValue(); }\n"; - let left_source = "function leftValue() -> word { return 1; }\nexport { leftValue };\n"; - let right_source = "function rightValue() -> word { return 2; }\nexport { rightValue };\n"; + let left_main = Url::from_file_path(left_path.join("main.sol")).expect("left main"); + let left_math = Url::from_file_path(left_path.join("math.sol")).expect("left math"); + let right_math = Url::from_file_path(right_path.join("math.sol")).expect("right math"); + let third_file = Url::from_file_path(third_path.join("third.sol")).expect("third file"); + let main_source = "import {leftValue} from lib.math;\nfunction main() returns (word) { return leftValue(); }\n"; + let left_source = + "function leftValue() returns (word) { return 1; }\nexport { leftValue };\n"; + let right_source = + "function rightValue() returns (word) { return 2; }\nexport { rightValue };\n"; let mut world = WorldState::new(); world.load_workspace_roots([ @@ -1207,7 +1208,7 @@ mod tests { third_root, vec![( third_file, - "function third() -> word { return 3; }\n".to_owned(), + "function third() returns (word) { return 3; }\n".to_owned(), )], )], ); @@ -1240,13 +1241,13 @@ mod tests { fn file_uri_drive_letters_are_normalized_without_folding_path_case() { let root = Url::parse("file:///c:/CaseSensitive/Project").expect("root uri"); let matching = - Url::parse("file:///C:/CaseSensitive/Project/main.solc").expect("matching uri"); + Url::parse("file:///C:/CaseSensitive/Project/main.sol").expect("matching uri"); let wrong_case = - Url::parse("file:///C:/casesensitive/Project/main.solc").expect("wrong-case uri"); + Url::parse("file:///C:/casesensitive/Project/main.sol").expect("wrong-case uri"); assert_eq!( workspace_relative_path(&root, &matching).as_deref(), - Some("main.solc") + Some("main.sol") ); assert_eq!(workspace_relative_path(&root, &wrong_case), None); } @@ -1256,18 +1257,18 @@ mod tests { let mut world = WorldState::new(); let file = std::env::temp_dir() .join("solcore-lsp-inferred-root") - .join("main.solc"); + .join("main.sol"); let uri = Url::from_file_path(file).expect("real file uri"); assert!(world.open_document( uri.clone(), - "function main() -> word { return 1; }\n".to_owned() + "function main() returns (word) { return 1; }\n".to_owned() )); assert!(world.has_workspace_root()); assert_eq!( world.vfs_path_for_uri(&uri), - Some("/main/main.solc".to_owned()) + Some("/main/main.sol".to_owned()) ); } @@ -1277,14 +1278,14 @@ mod tests { let uri = Url::parse("untitled:Untitled-1").expect("untitled uri"); assert!(world.open_document( uri.clone(), - "function main() -> word { return 1; }\n".to_owned() + "function main() returns (word) { return 1; }\n".to_owned() )); assert_eq!( world.vfs_path_for_uri(&uri), - Some("/main/__virtual__/0.solc".to_owned()) + Some("/main/__virtual__/0.sol".to_owned()) ); assert_eq!( - world.client_uri_for_vfs_url("file:///main/__virtual__/0.solc"), + world.client_uri_for_vfs_url("file:///main/__virtual__/0.sol"), Some(uri) ); } @@ -1295,7 +1296,7 @@ mod tests { let uri = Url::parse("untitled:Untitled-1").expect("untitled uri"); assert!(world.open_document( uri.clone(), - "function main() -> word { return 1; }\n".to_owned() + "function main() returns (word) { return 1; }\n".to_owned() )); world.close_document(&uri); @@ -1308,8 +1309,8 @@ mod tests { #[test] fn unix_backslash_in_filename_does_not_become_a_path_separator() { assert_eq!( - relative_url_path("src/name\\part.solc"), - Some("src/name\\part.solc".to_owned()) + relative_url_path("src/name\\part.sol"), + Some("src/name\\part.sol".to_owned()) ); } } diff --git a/crates/lsp/src/symbols.rs b/crates/lsp/src/symbols.rs index 5c5be4ce..e8297834 100644 --- a/crates/lsp/src/symbols.rs +++ b/crates/lsp/src/symbols.rs @@ -166,7 +166,7 @@ fn instance_symbol<'db>( Some(document_symbol( db, line_index, - format!("instance {}", class.atom().text(db)), + format!("impl {}", class.atom().text(db)), SymbolKind::OBJECT, instance.span(db), class.span(db), @@ -214,29 +214,14 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } #[test] fn document_symbols_include_top_level_items_and_contract_children() { - let source = "\ -function foo(x: word) -> word { - return x; -} - -type Pair = pair(word, word); - -data Maybe = None | Some(word); - -contract Box { - item: word; - function get() -> word { - return item; - } -} -"; + let source = "function foo(x: word) returns (word) {\n return x;\n}\n\ntype Pair = pair;\n\nenum Maybe {None , Some(word)}\n\ncontract Box {\n item: word;\n function get() returns (word) {\n return item;\n }\n}\n"; let (world, uri) = world_with_main(source); let response = handle_document_symbol(&world, &uri).expect("symbols"); let DocumentSymbolResponse::Nested(symbols) = response else { diff --git a/crates/lsp/src/wasm.rs b/crates/lsp/src/wasm.rs index ec619b41..9c7c1ac6 100644 --- a/crates/lsp/src/wasm.rs +++ b/crates/lsp/src/wasm.rs @@ -582,8 +582,8 @@ fn json_string(value: Value) -> String { mod tests { use super::*; - const URI: &str = "file:///main/main.solc"; - const MATH_URI: &str = "file:///main/math.solc"; + const URI: &str = "file:///main/main.sol"; + const MATH_URI: &str = "file:///main/math.sol"; #[test] fn initialize_returns_capabilities_response() { @@ -660,7 +660,7 @@ mod tests { #[test] fn did_open_publishes_diagnostics() { let mut world = WorldState::new(); - let source = "function f() -> word {\n return true;\n}\n"; + let source = "function f() returns (word) {\n return true;\n}\n"; let outgoing = dispatch(&mut world, &did_open_message(source)); assert_eq!(outgoing.len(), 1); @@ -679,10 +679,10 @@ mod tests { #[test] fn did_change_republishes_importer_diagnostics_when_sibling_exports_change() { let mut world = WorldState::new(); - let main = "import math.{double};\n\nfunction main() -> word {\n return double(21);\n}\n"; - let math_no_export = "function double(x: word) -> word { return x; }\n"; + let main = "import {double} from math;\n\nfunction main() returns (word) {\n return double(21);\n}\n"; + let math_no_export = "function double(x: word) returns (word) { return x; }\n"; let math_with_export = - "function double(x: word) -> word { return x; }\n\nexport { double };\n"; + "function double(x: word) returns (word) { return x; }\n\nexport { double };\n"; let _ = dispatch(&mut world, &did_open_uri_message(URI, main)); let opened_math = dispatch(&mut world, &did_open_uri_message(MATH_URI, math_no_export)); @@ -719,7 +719,7 @@ mod tests { #[test] fn hover_and_document_symbol_requests_return_results() { let mut world = WorldState::new(); - let source = "function main() -> word {\n return 42;\n}\n"; + let source = "function main() returns (word) {\n return 42;\n}\n"; let outgoing = dispatch(&mut world, &did_open_message(source)); assert_eq!(outgoing.len(), 1); @@ -768,7 +768,7 @@ mod tests { #[test] fn completion_request_returns_items() { let mut world = WorldState::new(); - let source = "function helper() -> word { return 1; }\nfunction main(x: word) -> word { return x; }\n"; + let source = "function helper() returns (word) { return 1; }\nfunction main(x: word) returns (word) { return x; }\n"; let outgoing = dispatch(&mut world, &did_open_message(source)); assert_eq!(outgoing.len(), 1); let character = source @@ -807,7 +807,7 @@ mod tests { #[test] fn references_request_returns_locations() { let mut world = WorldState::new(); - let source = "function id(x: word) -> word {\n return x;\n}\n"; + let source = "function id(x: word) returns (word) {\n return x;\n}\n"; let outgoing = dispatch(&mut world, &did_open_message(source)); assert_eq!(outgoing.len(), 1); @@ -842,7 +842,7 @@ mod tests { #[test] fn signature_help_request_returns_active_parameter() { let mut world = WorldState::new(); - let source = "function f(a: word, b: word) -> word {\n return a;\n}\n\nfunction main() -> word {\n return f(1, 2);\n}\n"; + let source = "function f(a: word, b: word) returns (word) {\n return a;\n}\n\nfunction main() returns (word) {\n return f(1, 2);\n}\n"; let outgoing = dispatch(&mut world, &did_open_message(source)); assert_eq!(outgoing.len(), 1); @@ -877,7 +877,7 @@ mod tests { #[test] fn semantic_tokens_full_request_returns_tokens() { let mut world = WorldState::new(); - let source = "function main(x: word) -> word {\n return x;\n}\n"; + let source = "function main(x: word) returns (word) {\n return x;\n}\n"; let outgoing = dispatch(&mut world, &did_open_message(source)); assert_eq!(outgoing.len(), 1); @@ -910,7 +910,7 @@ mod tests { #[test] fn inlay_hint_request_returns_results() { let mut world = WorldState::new(); - let source = "function main() -> word {\n let x = 42;\n return x;\n}\n"; + let source = "function main() returns (word) {\n let x = 42;\n return x;\n}\n"; let outgoing = dispatch(&mut world, &did_open_message(source)); assert_eq!(outgoing.len(), 1); @@ -941,7 +941,7 @@ mod tests { #[test] fn workspace_symbol_request_returns_matching_symbols() { let mut world = WorldState::new(); - let source = "function target() -> word {\n return 42;\n}\n"; + let source = "function target() returns (word) {\n return 42;\n}\n"; let outgoing = dispatch(&mut world, &did_open_message(source)); assert_eq!(outgoing.len(), 1); @@ -971,7 +971,7 @@ mod tests { #[test] fn code_action_formatting_folding_and_selection_requests_return_results() { let mut world = WorldState::new(); - let source = "function value() -> word { return 1; }\nfunction main() -> word {\n/* 😀 */ return vaue();\n}\n"; + let source = "function value() returns (word) { return 1; }\nfunction main() returns (word) {\n/* 😀 */ return vaue();\n}\n"; let opened = dispatch(&mut world, &did_open_message(source)); let notification = diagnostic_notification_for_uri(&opened, URI); let diagnostic = notification["params"]["diagnostics"] @@ -1077,8 +1077,8 @@ mod tests { #[test] fn missing_import_code_action_round_trips_over_wasm_dispatch() { let mut world = WorldState::new(); - let provider = "function value() -> word { return 1; }\n\nexport { value };\n"; - let main = "function main() -> word { return value(); }\n"; + let provider = "function value() returns (word) { return 1; }\n\nexport { value };\n"; + let main = "function main() returns (word) { return value(); }\n"; let _ = dispatch(&mut world, &did_open_uri_message(MATH_URI, provider)); let opened = dispatch(&mut world, &did_open_uri_message(URI, main)); @@ -1124,7 +1124,7 @@ mod tests { "start": { "line": 0, "character": 0 }, "end": { "line": 0, "character": 0 } }, - "newText": "import lib.math.{value};\n" + "newText": "import {value} from lib.math;\n" }) ); } @@ -1133,14 +1133,14 @@ mod tests { fn qualified_import_code_actions_round_trip_over_wasm_dispatch() { let cases = [ ( - "data Option = None | Some(word);\nexport { Option(*) };\n", - "function main() -> word { let option = Option.Some(1); return 1; }\n", + "enum Option {None , Some(word)}\nexport { Option(*) };\n", + "function main() returns (word) { let option = Option.Some(1); return 1; }\n", "Import `Option` from `lib.math`", - "import lib.math.{Option};\n", + "import {Option} from lib.math;\n", ), ( - "function value() -> word { return 1; }\nexport { value };\n", - "function main() -> word { return math.value(); }\n", + "function value() returns (word) { return 1; }\nexport { value };\n", + "function main() returns (word) { return math.value(); }\n", "Import module `math` from `lib.math`", "import lib.math;\n", ), @@ -1193,7 +1193,7 @@ mod tests { #[test] fn standard_library_missing_import_round_trips_over_wasm_dispatch() { let mut world = WorldState::new(); - let source = "function main() -> word { assert(true); return 1; }\n"; + let source = "function main() returns (word) { assert(true); return 1; }\n"; let opened = dispatch(&mut world, &did_open_message(source)); let notification = diagnostic_notification_for_uri(&opened, URI); let diagnostic = notification["params"]["diagnostics"] @@ -1232,7 +1232,7 @@ mod tests { assert_eq!(actions[0]["title"], "Import `assert` from `std`"); assert_eq!( actions[0]["edit"]["changes"][URI][0]["newText"], - "import std.{assert};\n" + "import {assert} from std;\n" ); } @@ -1240,7 +1240,7 @@ mod tests { fn closing_untitled_document_removes_it_from_workspace_symbols() { let mut world = WorldState::new(); let uri = "untitled:Untitled-1"; - let source = "function ghost() -> word { return 42; }\n"; + let source = "function ghost() returns (word) { return 42; }\n"; let _ = dispatch(&mut world, &did_open_uri_message(uri, source)); let _ = dispatch( @@ -1275,8 +1275,8 @@ mod tests { #[test] fn closing_workspace_document_removes_it_from_workspace_symbols() { let mut world = WorldState::new(); - let uri = "file:///main/ghost.solc"; - let source = "function ghost() -> word { return 42; }\n"; + let uri = "file:///main/ghost.sol"; + let source = "function ghost() returns (word) { return 42; }\n"; let _ = dispatch(&mut world, &did_open_uri_message(uri, source)); let _ = dispatch( @@ -1312,7 +1312,7 @@ mod tests { fn closing_file_detached_from_removed_workspace_discards_it() { let mut world = WorldState::new(); let root = "file:///main/"; - let uri = "file:///main/ghost.solc"; + let uri = "file:///main/ghost.sol"; let _ = dispatch( &mut world, &serde_json::json!({ @@ -1328,7 +1328,7 @@ mod tests { ); let _ = dispatch( &mut world, - &did_open_uri_message(uri, "function ghost() -> word { return 42; }\n"), + &did_open_uri_message(uri, "function ghost() returns (word) { return 42; }\n"), ); let _ = dispatch( &mut world, @@ -1367,7 +1367,7 @@ mod tests { #[test] fn document_highlight_request_returns_highlights() { let mut world = WorldState::new(); - let source = "function id(x: word) -> word {\n return x;\n}\n"; + let source = "function id(x: word) returns (word) {\n return x;\n}\n"; let outgoing = dispatch(&mut world, &did_open_message(source)); assert_eq!(outgoing.len(), 1); @@ -1405,7 +1405,7 @@ mod tests { #[test] fn rename_requests_return_workspace_edit_and_prepare_range() { let mut world = WorldState::new(); - let source = "function id(x: word) -> word {\n let y = x;\n return x;\n}\n"; + let source = "function id(x: word) returns (word) {\n let y = x;\n return x;\n}\n"; let outgoing = dispatch(&mut world, &did_open_message(source)); assert_eq!(outgoing.len(), 1); diff --git a/crates/lsp/src/workspace_symbols.rs b/crates/lsp/src/workspace_symbols.rs index 2636328e..943b3c44 100644 --- a/crates/lsp/src/workspace_symbols.rs +++ b/crates/lsp/src/workspace_symbols.rs @@ -208,7 +208,7 @@ fn instance_symbol<'db>( db, line_index, uri, - format!("instance {}", class.atom().text(db)), + format!("impl {}", class.atom().text(db)), SymbolKind::OBJECT, class.span(db), None, @@ -267,17 +267,17 @@ mod tests { fn world_with_main(source: &str) -> (WorldState, Url) { let mut world = WorldState::new(); - let uri = Url::parse("file:///main/main.solc").expect("uri"); + let uri = Url::parse("file:///main/main.sol").expect("uri"); assert!(world.open_document(uri.clone(), source.to_owned())); (world, uri) } #[test] fn query_returns_matching_functions_from_each_open_document() { - let main_source = "function target_main() -> word {\n return 1;\n}\n"; - let util_source = "function target_util() -> word {\n return 2;\n}\n"; + let main_source = "function target_main() returns (word) {\n return 1;\n}\n"; + let util_source = "function target_util() returns (word) {\n return 2;\n}\n"; let (mut world, main_uri) = world_with_main(main_source); - let util_uri = Url::parse("file:///main/util.solc").expect("uri"); + let util_uri = Url::parse("file:///main/util.sol").expect("uri"); assert!(world.open_document(util_uri.clone(), util_source.to_owned())); let symbols = handle_workspace_symbol(&world, "TARGET").expect("workspace symbols"); @@ -305,19 +305,19 @@ mod tests { let mut world = WorldState::new(); let root_path = std::env::temp_dir().join("solcore-lsp-symbol-project"); let root = Url::from_directory_path(&root_path).expect("root uri"); - let main_uri = Url::from_file_path(root_path.join("main.solc")).expect("main uri"); - let util_uri = Url::from_file_path(root_path.join("util.solc")).expect("util uri"); + let main_uri = Url::from_file_path(root_path.join("main.sol")).expect("main uri"); + let util_uri = Url::from_file_path(root_path.join("util.sol")).expect("util uri"); assert_eq!( world.load_workspace_documents( root, [ ( main_uri, - "function main_symbol() -> word { return 1; }\n".to_owned() + "function main_symbol() returns (word) { return 1; }\n".to_owned() ), ( util_uri.clone(), - "function unopened_symbol() -> word { return 2; }\n".to_owned() + "function unopened_symbol() returns (word) { return 2; }\n".to_owned() ), ] ), @@ -335,17 +335,7 @@ mod tests { #[test] fn empty_query_returns_top_level_symbols_and_non_matching_query_is_empty() { - let source = "\ -function alpha() -> word { - return 1; -} - -type Alias = word; - -data Choice = One | Two; - -contract Vault {} -"; + let source = "function alpha() returns (word) {\n return 1;\n}\n\ntype Alias = word;\n\nenum Choice {One , Two}\n\ncontract Vault {}\n"; let (world, uri) = world_with_main(source); let symbols = handle_workspace_symbol(&world, "").expect("workspace symbols"); @@ -372,14 +362,7 @@ contract Vault {} #[test] fn contract_member_symbols_keep_container_name() { - let source = "\ -contract Vault { - balance: word; - function read() -> word { - return balance; - } -} -"; + let source = "contract Vault {\n balance: word;\n function read() returns (word) {\n return balance;\n }\n}\n"; let (world, uri) = world_with_main(source); let field = handle_workspace_symbol(&world, "balance") diff --git a/crates/lsp/tests/stdio_smoke.rs b/crates/lsp/tests/stdio_smoke.rs index 0b820951..72e06b1b 100644 --- a/crates/lsp/tests/stdio_smoke.rs +++ b/crates/lsp/tests/stdio_smoke.rs @@ -13,21 +13,11 @@ use std::{ use lsp_types::Url; use serde_json::{Value, json}; -const MAIN_SOURCE: &str = "\ -import math.{double}; - -function f() -> word { - return double(true); -} -"; -const MATH_SOURCE: &str = "\ -function double(x: word) -> word { - return x; -} - -export { double }; -"; -const SECONDARY_SOURCE: &str = "function secondaryValue() -> word { return 2; }\n"; +const MAIN_SOURCE: &str = + "import {double} from math;\n\nfunction f() returns (word) {\n return double(true);\n}\n"; +const MATH_SOURCE: &str = + "function double(x: word) returns (word) {\n return x;\n}\n\nexport { double };\n"; +const SECONDARY_SOURCE: &str = "function secondaryValue() returns (word) { return 2; }\n"; struct TestWorkspace { root: PathBuf, @@ -50,8 +40,8 @@ impl TestWorkspace { std::process::id() )); fs::create_dir_all(&root).expect("create test workspace"); - let main = root.join("main.solc"); - let math = root.join("math.solc"); + let main = root.join("main.sol"); + let math = root.join("math.sol"); fs::write(&main, MAIN_SOURCE).expect("write main source"); fs::write(&math, MATH_SOURCE).expect("write math source"); let secondary_root = std::env::temp_dir().join(format!( @@ -59,7 +49,7 @@ impl TestWorkspace { std::process::id() )); fs::create_dir_all(&secondary_root).expect("create secondary workspace"); - let secondary = secondary_root.join("secondary.solc"); + let secondary = secondary_root.join("secondary.sol"); fs::write(&secondary, SECONDARY_SOURCE).expect("write secondary source"); Self { @@ -344,8 +334,8 @@ fn run_lsp_smoke( Some(&workspace.secondary_uri), )?; - fs::remove_file(workspace.root.join("math.solc")) - .map_err(|error| format!("failed to remove watched math.solc: {error}"))?; + fs::remove_file(workspace.root.join("math.sol")) + .map_err(|error| format!("failed to remove watched math.sol: {error}"))?; send_message( stdin, &json!({ diff --git a/crates/nameres/src/item_refs.rs b/crates/nameres/src/item_refs.rs index 94526f5b..e3e0c169 100644 --- a/crates/nameres/src/item_refs.rs +++ b/crates/nameres/src/item_refs.rs @@ -412,12 +412,19 @@ pub(super) fn select_import_refs<'db>( .iter() .map(|hidden| spanned_name_text(db, &hidden.name)) .collect(); - let mut selected = match selector { - ImportSelector::Wildcard => available.to_vec(), + let selected = match selector { + ImportSelector::Wildcard => available + .iter() + .filter(|item_ref| !hidden.contains(&item_ref.public_name)) + .cloned() + .collect(), ImportSelector::Names(names) => names .iter() - .flat_map(|selected| { + .filter_map(|selected| { let source_name = spanned_name_text(db, &selected.name); + (!hidden.contains(&source_name)).then_some((selected, source_name)) + }) + .flat_map(|(selected, source_name)| { let local_name = selected .alias .as_ref() @@ -449,7 +456,6 @@ pub(super) fn select_import_refs<'db>( }) .collect(), }; - selected.retain(|item_ref| !hidden.contains(&item_ref.public_name)); let selected = unique_import_bindings(selected); tracing::trace!( target: "nameres::imports", diff --git a/crates/nameres/src/model.rs b/crates/nameres/src/model.rs index 85bafc98..9d1026ed 100644 --- a/crates/nameres/src/model.rs +++ b/crates/nameres/src/model.rs @@ -40,11 +40,11 @@ pub struct ModuleTree { /// expected to use the same normalized roots as [`ModuleTree`]. #[salsa::input(debug)] pub struct ModuleFsSnapshot { - /// Absolute `.solc` source files observed on disk. + /// Absolute `.sol` source files observed on disk. #[returns(ref)] pub existing_files: BTreeSet, - /// Sibling `.solc` file stems by parent directory. + /// Sibling `.sol` file stems by parent directory. #[returns(ref)] pub sibling_stems: BTreeMap>, } diff --git a/crates/nameres/src/util.rs b/crates/nameres/src/util.rs index 3dab1c8e..a856b82f 100644 --- a/crates/nameres/src/util.rs +++ b/crates/nameres/src/util.rs @@ -194,13 +194,13 @@ pub(super) fn main_workspace_prefix(logical_path: &[String]) -> &[String] { /// Converts a logical module path into the conventional source file path. /// /// Each logical segment becomes a path component and the file extension is -/// `.solc`. +/// `.sol`. pub fn module_file_path(logical_path: &[String]) -> PathBuf { let mut path = PathBuf::new(); for segment in logical_path { path.push(segment); } - path.set_extension("solc"); + path.set_extension("sol"); path } @@ -288,7 +288,7 @@ fn virtual_module_id_for_source_file<'db>( _ => return None, }; let last = logical_path.last_mut()?; - *last = last.strip_suffix(".solc")?.to_owned(); + *last = last.strip_suffix(".sol")?.to_owned(); if last.is_empty() { return None; } @@ -505,7 +505,7 @@ fn namespace_name(namespace: Namespace) -> &'static str { match namespace { Namespace::Term => "term", Namespace::Type => "type", - Namespace::Class => "class", + Namespace::Class => "trait", } } diff --git a/crates/nameres/tests/fixtures/ok/alias/main.sol b/crates/nameres/tests/fixtures/ok/alias/main.sol new file mode 100644 index 00000000..a2a878d8 --- /dev/null +++ b/crates/nameres/tests/fixtures/ok/alias/main.sol @@ -0,0 +1,3 @@ +import * as U from util; + +export util as PublicUtil; diff --git a/crates/nameres/tests/fixtures/ok/alias/main.solc b/crates/nameres/tests/fixtures/ok/alias/main.solc deleted file mode 100644 index 985ec04a..00000000 --- a/crates/nameres/tests/fixtures/ok/alias/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -import util as U; - -export util as PublicUtil; diff --git a/crates/nameres/tests/fixtures/ok/alias/util.solc b/crates/nameres/tests/fixtures/ok/alias/util.sol similarity index 100% rename from crates/nameres/tests/fixtures/ok/alias/util.solc rename to crates/nameres/tests/fixtures/ok/alias/util.sol diff --git a/crates/nameres/tests/fixtures/ok/cycle/a.solc b/crates/nameres/tests/fixtures/ok/cycle/a.sol similarity index 100% rename from crates/nameres/tests/fixtures/ok/cycle/a.solc rename to crates/nameres/tests/fixtures/ok/cycle/a.sol diff --git a/crates/nameres/tests/fixtures/ok/cycle/b.solc b/crates/nameres/tests/fixtures/ok/cycle/b.sol similarity index 100% rename from crates/nameres/tests/fixtures/ok/cycle/b.solc rename to crates/nameres/tests/fixtures/ok/cycle/b.sol diff --git a/crates/nameres/tests/fixtures/ok/cycle/main.sol b/crates/nameres/tests/fixtures/ok/cycle/main.sol new file mode 100644 index 00000000..f88cb703 --- /dev/null +++ b/crates/nameres/tests/fixtures/ok/cycle/main.sol @@ -0,0 +1,3 @@ +import {fb} from a; + +function main() {} diff --git a/crates/nameres/tests/fixtures/ok/cycle/main.solc b/crates/nameres/tests/fixtures/ok/cycle/main.solc deleted file mode 100644 index 9b89b000..00000000 --- a/crates/nameres/tests/fixtures/ok/cycle/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -import a.{fb}; - -function main() {} diff --git a/crates/nameres/tests/fixtures/ok/external/extroot/extmod.solc b/crates/nameres/tests/fixtures/ok/external/extroot/extmod.sol similarity index 100% rename from crates/nameres/tests/fixtures/ok/external/extroot/extmod.solc rename to crates/nameres/tests/fixtures/ok/external/extroot/extmod.sol diff --git a/crates/nameres/tests/fixtures/ok/external/main.sol b/crates/nameres/tests/fixtures/ok/external/main.sol new file mode 100644 index 00000000..24eb4225 --- /dev/null +++ b/crates/nameres/tests/fixtures/ok/external/main.sol @@ -0,0 +1,3 @@ +import {ext} from @pkg.extmod; + +function main() {} diff --git a/crates/nameres/tests/fixtures/ok/external/main.solc b/crates/nameres/tests/fixtures/ok/external/main.solc deleted file mode 100644 index a84946ad..00000000 --- a/crates/nameres/tests/fixtures/ok/external/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -import @pkg.extmod.{ext}; - -function main() {} diff --git a/crates/nameres/tests/fixtures/ok/local_std_subpath/main.sol b/crates/nameres/tests/fixtures/ok/local_std_subpath/main.sol new file mode 100644 index 00000000..53699d87 --- /dev/null +++ b/crates/nameres/tests/fixtures/ok/local_std_subpath/main.sol @@ -0,0 +1,5 @@ +import {value} from std.a.b; + +function main(x: word) returns (word) { + return value(x); +} diff --git a/crates/nameres/tests/fixtures/ok/local_std_subpath/main.solc b/crates/nameres/tests/fixtures/ok/local_std_subpath/main.solc deleted file mode 100644 index f1af761a..00000000 --- a/crates/nameres/tests/fixtures/ok/local_std_subpath/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import std.a.b.{value}; - -function main(x: word) -> word { - return value(x); -} diff --git a/crates/nameres/tests/fixtures/ok/local_std_subpath/std/a/b.sol b/crates/nameres/tests/fixtures/ok/local_std_subpath/std/a/b.sol new file mode 100644 index 00000000..36dd500c --- /dev/null +++ b/crates/nameres/tests/fixtures/ok/local_std_subpath/std/a/b.sol @@ -0,0 +1,5 @@ +function value(x: word) returns (word) { + return x; +} + +export { value }; diff --git a/crates/nameres/tests/fixtures/ok/local_std_subpath/std/a/b.solc b/crates/nameres/tests/fixtures/ok/local_std_subpath/std/a/b.solc deleted file mode 100644 index 0d203179..00000000 --- a/crates/nameres/tests/fixtures/ok/local_std_subpath/std/a/b.solc +++ /dev/null @@ -1,5 +0,0 @@ -function value(x: word) -> word { - return x; -} - -export { value }; diff --git a/crates/nameres/tests/fixtures/ok/plain/main.sol b/crates/nameres/tests/fixtures/ok/plain/main.sol new file mode 100644 index 00000000..d8afb553 --- /dev/null +++ b/crates/nameres/tests/fixtures/ok/plain/main.sol @@ -0,0 +1,3 @@ +import {value} from util; + +function main() {} diff --git a/crates/nameres/tests/fixtures/ok/plain/main.solc b/crates/nameres/tests/fixtures/ok/plain/main.solc deleted file mode 100644 index 47d7583c..00000000 --- a/crates/nameres/tests/fixtures/ok/plain/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -import util.{value}; - -function main() {} diff --git a/crates/nameres/tests/fixtures/ok/plain/util.solc b/crates/nameres/tests/fixtures/ok/plain/util.sol similarity index 100% rename from crates/nameres/tests/fixtures/ok/plain/util.solc rename to crates/nameres/tests/fixtures/ok/plain/util.sol diff --git a/crates/nameres/tests/fixtures/ok/reexport_chain/a.solc b/crates/nameres/tests/fixtures/ok/reexport_chain/a.sol similarity index 100% rename from crates/nameres/tests/fixtures/ok/reexport_chain/a.solc rename to crates/nameres/tests/fixtures/ok/reexport_chain/a.sol diff --git a/crates/nameres/tests/fixtures/ok/reexport_chain/b.solc b/crates/nameres/tests/fixtures/ok/reexport_chain/b.sol similarity index 100% rename from crates/nameres/tests/fixtures/ok/reexport_chain/b.solc rename to crates/nameres/tests/fixtures/ok/reexport_chain/b.sol diff --git a/crates/nameres/tests/fixtures/ok/reexport_chain/main.sol b/crates/nameres/tests/fixtures/ok/reexport_chain/main.sol new file mode 100644 index 00000000..253d0f2f --- /dev/null +++ b/crates/nameres/tests/fixtures/ok/reexport_chain/main.sol @@ -0,0 +1,3 @@ +import {value} from b; + +function main() {} diff --git a/crates/nameres/tests/fixtures/ok/reexport_chain/main.solc b/crates/nameres/tests/fixtures/ok/reexport_chain/main.solc deleted file mode 100644 index 882df435..00000000 --- a/crates/nameres/tests/fixtures/ok/reexport_chain/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -import b.{value}; - -function main() {} diff --git a/crates/nameres/tests/fixtures/ok/selective_hiding/main.sol b/crates/nameres/tests/fixtures/ok/selective_hiding/main.sol new file mode 100644 index 00000000..a7c9bc0c --- /dev/null +++ b/crates/nameres/tests/fixtures/ok/selective_hiding/main.sol @@ -0,0 +1,3 @@ +import * from util hiding {hidden}; + +function main() {} diff --git a/crates/nameres/tests/fixtures/ok/selective_hiding/main.solc b/crates/nameres/tests/fixtures/ok/selective_hiding/main.solc deleted file mode 100644 index 282f035e..00000000 --- a/crates/nameres/tests/fixtures/ok/selective_hiding/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -import util.{*} hiding {hidden}; - -function main() {} diff --git a/crates/nameres/tests/fixtures/ok/selective_hiding/util.solc b/crates/nameres/tests/fixtures/ok/selective_hiding/util.sol similarity index 100% rename from crates/nameres/tests/fixtures/ok/selective_hiding/util.solc rename to crates/nameres/tests/fixtures/ok/selective_hiding/util.sol diff --git a/crates/nameres/tests/incremental_cache.rs b/crates/nameres/tests/incremental_cache.rs index 5f40a924..2fd6b891 100644 --- a/crates/nameres/tests/incremental_cache.rs +++ b/crates/nameres/tests/incremental_cache.rs @@ -107,8 +107,8 @@ impl solcore_nameres::Db for TestDb { #[test] fn module_diagnostics_backdates_after_same_module_body_literal_edit() { - let before = "function main() -> word {\n return 1;\n}\n"; - let after = "function main() -> word {\n return 2;\n}\n"; + let before = "function main() returns (word) {\n return 1;\n}\n"; + let after = "function main() returns (word) {\n return 2;\n}\n"; let (mut db, file, key) = db_with_main(before); { @@ -150,7 +150,7 @@ fn module_diagnostics_backdates_after_same_module_body_literal_edit() { #[test] fn body_diagnostics_key_excludes_module_env_diagnostics() { - let (db, file, key) = db_with_main("function main() -> word { return 1; }\n"); + let (db, file, key) = db_with_main("function main() returns (word) { return 1; }\n"); let module = module_id_from_key(&db, &key); let hir_module = parse_file_to_hir(&db, file).module(&db); let body = hir_module @@ -206,9 +206,9 @@ fn body_diagnostics_key_excludes_module_env_diagnostics() { #[test] fn duplicate_export_diagnostics_backdate_after_unrelated_body_length_edit() { - let before = "export a.{f};\nexport b.{f};\n\nfunction unrelated() -> word {\n return 1;\n}\n"; - let after = - "export a.{f};\nexport b.{f};\n\nfunction unrelated() -> word {\n return 123456789;\n}\n"; + let before = + "export a.{f};\nexport b.{f};\n\nfunction unrelated() returns (word) {\n return 1;\n}\n"; + let after = "export a.{f};\nexport b.{f};\n\nfunction unrelated() returns (word) {\n return 123456789;\n}\n"; let (mut db, file, key) = db_with_duplicate_export_main(before); let before_ids = { @@ -299,7 +299,7 @@ fn db_with_main(content: &str) -> (TestDb, SourceFile, ModuleKey) { db.module_fs_snapshot = Some(empty_module_fs_snapshot(&db)); let file = SourceFile::new( &db, - "memory:///main.solc".parse().expect("valid URL"), + "memory:///main.sol".parse().expect("valid URL"), Some(content.to_owned()), ); let key = ModuleKey { @@ -322,11 +322,11 @@ fn db_with_duplicate_export_main(content: &str) -> (TestDb, SourceFile, ModuleKe for (path, source) in [ ( vec!["a"], - "function f() -> word { return 0; }\nexport { f };\n", + "function f() returns (word) { return 0; }\nexport { f };\n", ), ( vec!["b"], - "function f() -> word { return 0; }\nexport { f };\n", + "function f() returns (word) { return 0; }\nexport { f };\n", ), ] { let key = ModuleKey { @@ -339,7 +339,7 @@ fn db_with_duplicate_export_main(content: &str) -> (TestDb, SourceFile, ModuleKe let file = SourceFile::new( &db, - "memory:///main.solc".parse().expect("valid URL"), + "memory:///main.sol".parse().expect("valid URL"), Some(content.to_owned()), ); let key = ModuleKey { @@ -355,7 +355,7 @@ fn empty_module_fs_snapshot(db: &TestDb) -> ModuleFsSnapshot { } fn source_file(db: &TestDb, key: &ModuleKey, content: &str) -> SourceFile { - let url = format!("memory:///{}.solc", key.logical_path.join("/")) + let url = format!("memory:///{}.sol", key.logical_path.join("/")) .parse() .expect("valid URL"); SourceFile::new(db, url, Some(content.to_owned())) diff --git a/crates/nameres/tests/module_system.rs b/crates/nameres/tests/module_system.rs index 727995c8..a59e3ddc 100644 --- a/crates/nameres/tests/module_system.rs +++ b/crates/nameres/tests/module_system.rs @@ -98,7 +98,7 @@ impl solcore_nameres::Db for TestDb { #[test] fn module_keys_reject_parent_directory_components() { let root = Path::new("workspace"); - let spelled_with_parent = Path::new("workspace/src/../src/main.solc"); + let spelled_with_parent = Path::new("workspace/src/../src/main.sol"); assert!( module_key_for_path(LibraryId::Main, root, spelled_with_parent).is_none(), @@ -130,17 +130,20 @@ fn auto_imports_index_unreachable_public_symbols_and_rank_direct_exports_first() let (db, entry) = load_sources([ ( vec!["main"], - "export { wanted }; function wanted() -> word { return 0; }", + "export { wanted }; function wanted() returns (word) { return 0; }", ), ( vec!["direct"], - "export { wanted, Thing, Eqish }; function wanted() -> word { return 1; } data Thing = Thing; class a:Eqish {}", + "export { wanted, Thing, Eqish }; function wanted() returns (word) { return 1; } enum Thing { Thing } trait Eqish {}", ), (vec!["wrapper"], "export direct.{wanted};"), - (vec!["private"], "function wanted() -> word { return 2; }"), + ( + vec!["private"], + "function wanted() returns (word) { return 2; }", + ), ( vec!["broken"], - "export { wanted }; lost(x: word) -> word { return 0; } function wanted() -> word { return 3; }", + "export { wanted }; lost(x) returns (word) { return 0; } function wanted() returns (word) { return 3; }", ), (vec!["broken_wrapper"], "export broken.{wanted};"), ( @@ -149,15 +152,15 @@ fn auto_imports_index_unreachable_public_symbols_and_rank_direct_exports_first() ), ( vec!["other"], - "export { wanted }; function wanted() -> word { return 4; }", + "export { wanted }; function wanted() returns (word) { return 4; }", ), ( vec!["term_collision"], - "export { Clash }; function Clash() -> word { return 5; }", + "export { Clash }; function Clash() returns (word) { return 5; }", ), ( vec!["type_collision"], - "export { Clash }; data Clash = Clash;", + "export { Clash }; enum Clash { Clash }", ), ( vec!["namespace_ambiguous"], @@ -246,15 +249,15 @@ fn constructor_auto_imports_require_the_requested_constructor_to_be_visible() { (vec!["main"], "function main() {}"), ( vec!["full"], - "export { Option(*) }; data Option = None | Some(word);", + "export { Option(*) }; enum Option { None, Some(word) }", ), ( vec!["opaque"], - "export { Option }; data Option = None | Some(word);", + "export { Option }; enum Option { None, Some(word) }", ), ( vec!["partial"], - "export { Option(Some) }; data Option = None | Some(word);", + "export { Option(Some) }; enum Option { None, Some(word) }", ), (vec!["wrapper"], "export full.{Option(Some)};"), ]); @@ -281,24 +284,24 @@ fn module_auto_imports_match_the_default_qualifier_and_public_member() { (vec!["main"], "function main() {}"), ( vec!["one", "math"], - "export { value }; function value() -> word { return 1; }", + "export { value }; function value() returns (word) { return 1; }", ), ( vec!["two", "math"], - "export { value }; function value() -> word { return 2; }", + "export { value }; function value() returns (word) { return 2; }", ), (vec!["aaa", "math"], "export lib.one.math.{value};"), ( vec!["private", "math"], - "function value() -> word { return 3; }", + "function value() returns (word) { return 3; }", ), ( vec!["broken", "math"], - "export { value }; lost(x: word) -> word { return 0; } function value() -> word { return 4; }", + "export { value }; lost(x) returns (word) { return 0; } function value() returns (word) { return 4; }", ), ( vec!["other"], - "export { value }; function value() -> word { return 5; }", + "export { value }; function value() returns (word) { return 5; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -324,12 +327,12 @@ fn module_auto_imports_require_an_immediate_term_member() { (vec!["main"], "function main() {}"), ( vec!["types", "math"], - "export { Value }; data Value = Value(word);", + "export { Value }; enum Value { Value(word) }", ), (vec!["aliases", "math"], "export lib.target as nested;"), ( vec!["target"], - "export { value }; function value() -> word { return 1; }", + "export { value }; function value() returns (word) { return 1; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -344,11 +347,11 @@ fn module_auto_imports_do_not_create_duplicate_default_qualifiers() { (vec!["main"], "import lib.existing.math; function main() {}"), ( vec!["existing", "math"], - "export { old }; function old() -> word { return 1; }", + "export { old }; function old() returns (word) { return 1; }", ), ( vec!["candidate", "math"], - "export { value }; function value() -> word { return 2; }", + "export { value }; function value() returns (word) { return 2; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -357,15 +360,15 @@ fn module_auto_imports_do_not_create_duplicate_default_qualifiers() { let (db, entry) = load_sources([ ( vec!["main"], - "import lib.existing as math; function main() {}", + "import * as math from lib.existing; function main() {}", ), ( vec!["existing"], - "export { old }; function old() -> word { return 1; }", + "export { old }; function old() returns (word) { return 1; }", ), ( vec!["candidate", "math"], - "export { value }; function value() -> word { return 2; }", + "export { value }; function value() returns (word) { return 2; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -375,11 +378,11 @@ fn module_auto_imports_do_not_create_duplicate_default_qualifiers() { (vec!["main"], "import lib.math.deep; function main() {}"), ( vec!["math", "deep"], - "export { old }; function old() -> word { return 1; }", + "export { old }; function old() returns (word) { return 1; }", ), ( vec!["other", "math"], - "export { value }; function value() -> word { return 2; }", + "export { value }; function value() returns (word) { return 2; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -391,25 +394,28 @@ fn module_auto_imports_do_not_conflict_with_unqualified_bindings() { let (db, entry) = load_sources([ ( vec!["main"], - "function math() -> word { return 0; } function main() {}", + "function math() returns (word) { return 0; } function main() {}", ), ( vec!["candidate", "math"], - "export { value }; function value() -> word { return 1; }", + "export { value }; function value() returns (word) { return 1; }", ), ]); let importing = module_id_from_key(&db, &entry); assert!(auto_import_module_candidates(&db, importing, "math", "value").is_empty()); let (db, entry) = load_sources([ - (vec!["main"], "import lib.names.{math}; function main() {}"), + ( + vec!["main"], + "import {math} from lib.names; function main() {}", + ), ( vec!["names"], - "export { math }; function math() -> word { return 0; }", + "export { math }; function math() returns (word) { return 0; }", ), ( vec!["candidate", "math"], - "export { value }; function value() -> word { return 1; }", + "export { value }; function value() returns (word) { return 1; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -419,16 +425,19 @@ fn module_auto_imports_do_not_conflict_with_unqualified_bindings() { #[test] fn module_qualifier_conflicts_with_selected_term_in_either_import_order() { for imports in [ - "import util; import other.{util};", - "import other.{util}; import util;", + "import util; import {util} from other;", + "import {util} from other; import util;", ] { let main = format!("{imports} function main() {{}}"); let (db, entry) = load_sources([ (vec!["main"], main.as_str()), - (vec!["util"], "function value() -> word { return 0; }"), + ( + vec!["util"], + "function value() returns (word) { return 0; }", + ), ( vec!["other"], - "export { util }; function util() -> word { return 1; }", + "export { util }; function util() returns (word) { return 1; }", ), ]); let module = module_id_from_key(&db, &entry); @@ -445,15 +454,15 @@ fn module_auto_imports_check_every_generated_prefix_binding() { let (db, entry) = load_sources([ ( vec!["main"], - "function one() -> word { return 0; } function main() {}", + "function one() returns (word) { return 0; } function main() {}", ), ( vec!["one", "math"], - "export { value }; function value() -> word { return 1; }", + "export { value }; function value() returns (word) { return 1; }", ), ( vec!["two", "math"], - "export { value }; function value() -> word { return 2; }", + "export { value }; function value() returns (word) { return 2; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -464,24 +473,27 @@ fn module_auto_imports_check_every_generated_prefix_binding() { assert_eq!(paths, ["lib.two.math"]); let (db, entry) = load_sources([ - (vec!["main"], "data one = One; function main() {}"), + (vec!["main"], "enum one { One } function main() {}"), ( vec!["one", "math"], - "export { value }; function value() -> word { return 1; }", + "export { value }; function value() returns (word) { return 1; }", ), ]); let importing = module_id_from_key(&db, &entry); assert!(auto_import_module_candidates(&db, importing, "math", "value").is_empty()); let (db, entry) = load_sources([ - (vec!["main"], "import lib.names.{one}; function main() {}"), + ( + vec!["main"], + "import {one} from lib.names; function main() {}", + ), ( vec!["names"], - "export { one }; function one() -> word { return 0; }", + "export { one }; function one() returns (word) { return 0; }", ), ( vec!["one", "math"], - "export { value }; function value() -> word { return 1; }", + "export { value }; function value() returns (word) { return 1; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -493,24 +505,19 @@ fn module_auto_imports_check_contract_local_prefix_bindings() { let (db, entry) = load_sources([ ( vec!["main"], - "contract C { - one: word; - data two = Two; - function three() -> word { return 0; } - function main() {} - }", + "contract C {\n one: word;\n enum two {Two}\n function three() returns (word) { return 0; }\n function main() {}\n }", ), ( vec!["one", "math"], - "export { value }; function value() -> word { return 1; }", + "export { value }; function value() returns (word) { return 1; }", ), ( vec!["two", "math"], - "export { value }; function value() -> word { return 2; }", + "export { value }; function value() returns (word) { return 2; }", ), ( vec!["three", "math"], - "export { value }; function value() -> word { return 3; }", + "export { value }; function value() returns (word) { return 3; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -523,11 +530,11 @@ fn module_auto_imports_check_resolved_and_unresolved_plain_import_prefixes() { (vec!["main"], "import lib.one.deep; function main() {}"), ( vec!["one", "deep"], - "export { old }; function old() -> word { return 0; }", + "export { old }; function old() returns (word) { return 0; }", ), ( vec!["one", "math"], - "export { value }; function value() -> word { return 1; }", + "export { value }; function value() returns (word) { return 1; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -537,7 +544,7 @@ fn module_auto_imports_check_resolved_and_unresolved_plain_import_prefixes() { (vec!["main"], "import lib.missing.deep; function main() {}"), ( vec!["missing", "math"], - "export { value }; function value() -> word { return 1; }", + "export { value }; function value() returns (word) { return 1; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -549,11 +556,11 @@ fn module_auto_imports_allow_a_separate_plain_import_after_a_selective_import() let (db, entry) = load_sources([ ( vec!["main"], - "import lib.one.math.{other}; function main() {}", + "import {other} from lib.one.math; function main() {}", ), ( vec!["one", "math"], - "export { other, value }; function other() -> word { return 0; } function value() -> word { return 1; }", + "export { other, value }; function other() returns (word) { return 0; } function value() returns (word) { return 1; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -568,11 +575,7 @@ fn auto_imports_exclude_namespace_blind_selector_collisions_within_one_provider( (vec!["main"], "function main() {}"), ( vec!["provider"], - "export { Shared, term_only, TypeOnly }; - function Shared() -> word { return 1; } - data Shared = Shared; - function term_only() -> word { return 2; } - data TypeOnly = TypeOnly;", + "export { Shared, term_only, TypeOnly };\n function Shared() returns (word) { return 1; }\n enum Shared {Shared}\n function term_only() returns (word) { return 2; }\n enum TypeOnly {TypeOnly}", ), ]); let importing = module_id_from_key(&db, &entry); @@ -595,14 +598,14 @@ fn auto_imports_exclude_namespace_blind_selector_collisions_within_one_provider( #[test] fn auto_imports_suppress_different_target_for_explicit_selector_but_keep_same_target() { let (db, entry) = load_sources([ - (vec!["main"], "import lib.a.{Foo}; function main() {}"), + (vec!["main"], "import {Foo} from lib.a; function main() {}"), ( vec!["a"], - "export { Foo }; function Foo() -> word { return 1; }", + "export { Foo }; function Foo() returns (word) { return 1; }", ), ( vec!["b"], - "export { Foo }; function Foo() -> word { return 2; }", + "export { Foo }; function Foo() returns (word) { return 2; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -620,15 +623,15 @@ fn auto_imports_consider_selector_aliases_by_their_local_name() { let (db, entry) = load_sources([ ( vec!["main"], - "import lib.a.{Original as Foo}; function main() {}", + "import {Original as Foo} from lib.a; function main() {}", ), ( vec!["a"], - "export { Original }; function Original() -> word { return 1; }", + "export { Original }; function Original() returns (word) { return 1; }", ), ( vec!["b"], - "export { Foo }; function Foo() -> word { return 2; }", + "export { Foo }; function Foo() returns (word) { return 2; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -639,14 +642,14 @@ fn auto_imports_consider_selector_aliases_by_their_local_name() { #[test] fn auto_imports_consider_bindings_from_wildcard_selectors() { let (db, entry) = load_sources([ - (vec!["main"], "import lib.a.{*}; function main() {}"), + (vec!["main"], "import * from lib.a; function main() {}"), ( vec!["a"], - "export { Foo }; function Foo() -> word { return 1; }", + "export { Foo }; function Foo() returns (word) { return 1; }", ), ( vec!["b"], - "export { Foo }; function Foo() -> word { return 2; }", + "export { Foo }; function Foo() returns (word) { return 2; }", ), ]); let importing = module_id_from_key(&db, &entry); @@ -659,12 +662,12 @@ fn auto_imports_consider_bindings_from_wildcard_selectors() { #[test] fn auto_imports_suppress_cross_namespace_collisions_from_different_targets() { let (db, entry) = load_sources([ - (vec!["main"], "import lib.a.{Foo}; function main() {}"), + (vec!["main"], "import {Foo} from lib.a; function main() {}"), ( vec!["a"], - "export { Foo }; function Foo() -> word { return 1; }", + "export { Foo }; function Foo() returns (word) { return 1; }", ), - (vec!["b"], "export { Foo }; data Foo = Foo;"), + (vec!["b"], "export { Foo }; enum Foo { Foo }"), ]); let importing = module_id_from_key(&db, &entry); @@ -683,11 +686,11 @@ fn auto_imports_keep_main_workspace_namespaces_isolated() { ), ( vec!["__solcore_workspace__", workspace_a, "nested", "util"], - "export { wanted }; function wanted() -> word { return 1; }", + "export { wanted }; function wanted() returns (word) { return 1; }", ), ( vec!["__solcore_workspace__", workspace_b, "nested", "util"], - "export { wanted }; function wanted() -> word { return 2; }", + "export { wanted }; function wanted() returns (word) { return 2; }", ), ( vec!["__solcore_detached__", detached, "main"], @@ -695,7 +698,7 @@ fn auto_imports_keep_main_workspace_namespaces_isolated() { ), ( vec!["__solcore_detached__", detached, "nested", "util"], - "export { wanted }; function wanted() -> word { return 3; }", + "export { wanted }; function wanted() returns (word) { return 3; }", ), ]); let importing = module_id_from_key( @@ -788,8 +791,8 @@ fn source_import_paths_use_canonical_library_syntax() { let sources = [ "function main() {}", "function local_only() {}", - "export { std_value }; function std_value() -> word { return 1; }", - "export { external_value }; function external_value() -> word { return 2; }", + "export { std_value }; function std_value() returns (word) { return 1; }", + "export { external_value }; function external_value() returns (word) { return 2; }", ]; for (key, source) in keys.iter().zip(sources) { let file = SourceFile::new(&db, fixture_url(key), Some(source.to_owned())); @@ -823,7 +826,7 @@ fn source_import_paths_use_canonical_library_syntax() { { let file = SourceFile::new( &db, - format!("memory:///roundtrip-{index}.solc") + format!("memory:///roundtrip-{index}.sol") .parse() .expect("round-trip test URL"), Some(format!("import {path};")), @@ -928,18 +931,15 @@ fn glob_hiding_uses_the_renamed_reexport_name() { let (db, entry) = load_sources([ ( vec!["main"], - "import lib.wrapper.{*} hiding {renamed};\n\ - function renamed() -> word { return 1; }", + "import * from lib.wrapper hiding {renamed};\nfunction renamed() returns (word) { return 1; }", ), ( vec!["base"], - "export { original };\n\ - function original() -> word { return 0; }", + "export { original };\nfunction original() returns (word) { return 0; }", ), ( vec!["wrapper"], - "import lib.base.{original as renamed};\n\ - export { renamed };", + "import {original as renamed} from lib.base;\nexport { renamed };", ), ]); @@ -990,11 +990,29 @@ fn wildcard_hiding_validates_against_source_interface() { assert_no_diagnostics(&db, &diagnostics); } +#[test] +fn selective_alias_hiding_uses_the_source_name() { + let (db, entry) = load_sources([ + ( + vec!["main"], + "import {original as renamed} from lib hiding {original};\n\ + function renamed() returns (word) { return 1; }\n\ + function main() returns (word) { return renamed(); }", + ), + ( + vec!["lib"], + "export {original}; function original() returns (word) { return 0; }", + ), + ]); + + let (_, diagnostics) = run(&db, &entry); + assert_no_diagnostics(&db, &diagnostics); +} + #[test] fn parse_broken_selected_import_does_not_blame_importer() { let (db, entry) = load_sources(parse_broken_provider_sources( - "import util.{lost}; - function main() -> word { return lost(0); }", + "import {lost} from util;\n function main() returns (word) { return lost(0); }", )); let main = module_id_from_key(&db, &entry); assert_eq!(module_diagnostic_codes(&db, main), Vec::::new()); @@ -1011,8 +1029,7 @@ fn parse_broken_selected_import_does_not_blame_importer() { #[test] fn parse_broken_qualified_import_does_not_blame_importer() { let (db, entry) = load_sources(parse_broken_provider_sources( - "import util; - function main() -> word { return util.lost(0); }", + "import util;\n function main() returns (word) { return util.lost(0); }", )); let main = module_id_from_key(&db, &entry); assert_eq!(module_diagnostic_codes(&db, main), Vec::::new()); @@ -1023,13 +1040,16 @@ fn parse_broken_leaf_does_not_mark_unrelated_module_prefixes_incomplete() { let (db, entry) = load_sources([ ( vec!["main"], - "import lib.a.b.c; import lib.a.x; function main() -> word { return a.missing(); }", + "import lib.a.b.c; import lib.a.x; function main() returns (word) { return a.missing(); }", ), ( vec!["a", "b", "c"], - "function value() -> word { let broken = ; return 1; }", + "function value() returns (word) { let broken = ; return 1; }", + ), + ( + vec!["a", "x"], + "function other() returns (word) { return 2; }", ), - (vec!["a", "x"], "function other() -> word { return 2; }"), ]); let main = module_id_from_key(&db, &entry); let leaf = module_id_from_key(&db, &module_key(["a", "b", "c"])); @@ -1061,10 +1081,7 @@ fn parse_broken_leaf_does_not_mark_unrelated_module_prefixes_incomplete() { fn parse_broken_module_diagnostics_publish_only_parse_errors() { let (db, entry) = load_sources([( vec!["main"], - "function main() -> word { - let x = ; - return missing; - }", + "function main() returns (word) {\n let x = ;\n return missing;\n }", )]); let main = module_id_from_key(&db, &entry); let diagnostics = lowered_module_diagnostics(&db, main); @@ -1186,7 +1203,7 @@ fn load_fixture(root: &Path, external_roots: BTreeMap) -> (Test ); } - let entry_path = root.join("main.solc"); + let entry_path = root.join("main.sol"); let entry_key = module_key_for_path(LibraryId::Main, root, &entry_path).expect("entry key"); (db, entry_key) } @@ -1227,8 +1244,7 @@ fn parse_broken_provider_sources(main: &str) -> [(Vec<&str>, &str); 2] { (vec!["main"], main), ( vec!["util"], - "lost(x: word) -> word { return 0; } - function other() {}", + "lost(x) returns (word) { return 0; }\n function other() {}", ), ] } @@ -1352,7 +1368,7 @@ fn collect_module_fs_snapshot( }; for entry in entries.flatten() { let path = entry.path(); - if path.extension().and_then(|extension| extension.to_str()) == Some("solc") { + if path.extension().and_then(|extension| extension.to_str()) == Some("sol") { if path.is_file() { existing_files.insert(path.clone()); } @@ -1374,7 +1390,7 @@ fn load_library_files(db: &mut TestDb, library: LibraryId, root: &Path, dir: &Pa let path = entry.expect("fixture entry").path(); if path.is_dir() { load_library_files(db, library.clone(), root, &path); - } else if path.extension().and_then(|ext| ext.to_str()) == Some("solc") { + } else if path.extension().and_then(|ext| ext.to_str()) == Some("sol") { let key = module_key_for_path(library.clone(), root, &path).expect("module key"); let source = fs::read_to_string(&path).expect("fixture source"); let url = fixture_url(&key); @@ -1391,7 +1407,7 @@ fn fixture_url(key: &ModuleKey) -> Url { LibraryId::External(name) => format!("external/{name}"), }; let path = key.logical_path.join("/"); - format!("memory:///{library}/{path}.solc") + format!("memory:///{library}/{path}.sol") .parse() .expect("fixture memory URL") } @@ -1476,398 +1492,398 @@ fn known_divergence(path: &str) -> Option { const KNOWN_DIVERGENCES: &[KnownDivergence] = &[ KnownDivergence { - path: "hidden_ctor_nonexhaustive_fail.solc", + path: "hidden_ctor_nonexhaustive_fail.sol", reason: "reference fails later exhaustiveness checking for partial constructor visibility; Rust nameres records partial-data metadata but does not run exhaustiveness", }, KnownDivergence { - path: "symlink_identity_fail.solc", + path: "symlink_identity_fail.sol", reason: "reference rejects distinct module identities for equivalent helper sources; Rust nameres does not canonicalize/symlink-check type identity in this pass", }, KnownDivergence { - path: "private_bad_main.solc", + path: "private_bad_main.sol", reason: "reference type-checks private helper bodies and rejects the unexported broken function; Rust nameres intentionally reports only name-resolution diagnostics", }, KnownDivergence { - path: "pragma_scope_main.solc", + path: "pragma_scope_main.sol", reason: "reference fails pragma-scoped typeclass/termination validation; Rust nameres does not implement that semantic check", }, ]; const IMPORT_CORPUS_CASES: &[ImportCorpusCase] = &[ ImportCorpusCase { - path: "booldef.solc", + path: "booldef.sol", expected_failure: false, }, ImportCorpusCase { - path: "boolmain.solc", + path: "boolmain.sol", expected_failure: false, }, ImportCorpusCase { - path: "unordered_imports_main.solc", + path: "unordered_imports_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "boolalias.solc", + path: "boolalias.sol", expected_failure: false, }, ImportCorpusCase { - path: "alias_hides_original_fail.solc", + path: "alias_hides_original_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "boolalias_open_fail.solc", + path: "boolalias_open_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "boolqualified.solc", + path: "boolqualified.sol", expected_failure: false, }, ImportCorpusCase { - path: "boolqualifiedtype.solc", + path: "boolqualifiedtype.sol", expected_failure: false, }, ImportCorpusCase { - path: "boolaliastype.solc", + path: "boolaliastype.sol", expected_failure: false, }, ImportCorpusCase { - path: "module_unqualified_fun_fail.solc", + path: "module_unqualified_fun_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "alias_unqualified_fun_fail.solc", + path: "alias_unqualified_fun_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "module_unqualified_type_fail.solc", + path: "module_unqualified_type_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "alias_unqualified_type_fail.solc", + path: "alias_unqualified_type_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "module_unqualified_constr_fail.solc", + path: "module_unqualified_constr_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "alias_unqualified_constr_fail.solc", + path: "alias_unqualified_constr_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "selective_unqualified_fun_ok.solc", + path: "selective_unqualified_fun_ok.sol", expected_failure: false, }, ImportCorpusCase { - path: "transitive_dep_main_module.solc", + path: "transitive_dep_main_module.sol", expected_failure: false, }, ImportCorpusCase { - path: "transitive_dep_main_select.solc", + path: "transitive_dep_main_select.sol", expected_failure: false, }, ImportCorpusCase { - path: "opaque_alias_main.solc", + path: "opaque_alias_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "opaque_select_alias_main.solc", + path: "opaque_select_alias_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "opaque_alias_leak_fail.solc", + path: "opaque_alias_leak_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "opaque_alias_qualifier_leak_fail.solc", + path: "opaque_alias_qualifier_leak_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "opaque_select_direct_leak_fail.solc", + path: "opaque_select_direct_leak_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "module_name_shadow.solc", + path: "module_name_shadow.sol", expected_failure: true, }, ImportCorpusCase { - path: "wrapper_shadow_success.solc", + path: "wrapper_shadow_success.sol", expected_failure: false, }, ImportCorpusCase { - path: "ns_cross_ok.solc", + path: "ns_cross_ok.sol", expected_failure: false, }, ImportCorpusCase { - path: "ns_constr_dup.solc", + path: "ns_constr_dup.sol", expected_failure: false, }, ImportCorpusCase { - path: "strict_open_fail.solc", + path: "strict_open_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "boolselect.solc", + path: "boolselect.sol", expected_failure: false, }, ImportCorpusCase { - path: "boolconselect_ok.solc", + path: "boolconselect_ok.sol", expected_failure: false, }, ImportCorpusCase { - path: "boolconselect_fail.solc", + path: "boolconselect_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "nested_alias.solc", + path: "nested_alias.sol", expected_failure: false, }, ImportCorpusCase { - path: "nested_select.solc", + path: "nested_select.sol", expected_failure: false, }, ImportCorpusCase { - path: "nested_foo_and_bar.solc", + path: "nested_foo_and_bar.sol", expected_failure: false, }, ImportCorpusCase { - path: "nested_direct_qualifier.solc", + path: "nested_direct_qualifier.sol", expected_failure: false, }, ImportCorpusCase { - path: "nested_deep_qualifier.solc", + path: "nested_deep_qualifier.sol", expected_failure: false, }, ImportCorpusCase { - path: "glob_import_ok.solc", + path: "glob_import_ok.sol", expected_failure: false, }, ImportCorpusCase { - path: "glob_import_mixed.solc", + path: "glob_import_mixed.sol", expected_failure: false, }, ImportCorpusCase { - path: "glob_import_hiding.solc", + path: "glob_import_hiding.sol", expected_failure: false, }, ImportCorpusCase { - path: "glob_hiding_amb_ok.solc", + path: "glob_hiding_amb_ok.sol", expected_failure: false, }, ImportCorpusCase { - path: "glob_import_dup.solc", + path: "glob_import_dup.sol", expected_failure: false, }, ImportCorpusCase { - path: "glob_export_mixed.solc", + path: "glob_export_mixed.sol", expected_failure: false, }, ImportCorpusCase { - path: "glob_amb_main_fail.solc", + path: "glob_amb_main_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "glob_import_hiding_unknown_fail.solc", + path: "glob_import_hiding_unknown_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "select_hiding_ok.solc", + path: "select_hiding_ok.sol", expected_failure: false, }, ImportCorpusCase { - path: "select_hiding_fail.solc", + path: "select_hiding_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "export_item_dup_fail.solc", + path: "export_item_dup_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "export_module_dup_fail.solc", + path: "export_module_dup_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "select_ok.solc", + path: "select_ok.sol", expected_failure: false, }, ImportCorpusCase { - path: "select_shadow_local.solc", + path: "select_shadow_local.sol", expected_failure: true, }, ImportCorpusCase { - path: "select_shadow_param_ok.solc", + path: "select_shadow_param_ok.sol", expected_failure: false, }, ImportCorpusCase { - path: "select_fail.solc", + path: "select_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "select_unknown.solc", + path: "select_unknown.sol", expected_failure: true, }, ImportCorpusCase { - path: "select_dup_item.solc", + path: "select_dup_item.sol", expected_failure: true, }, ImportCorpusCase { - path: "alias_dup.solc", + path: "alias_dup.sol", expected_failure: true, }, ImportCorpusCase { - path: "amb_main.solc", + path: "amb_main.sol", expected_failure: true, }, ImportCorpusCase { - path: "amb_ok.solc", + path: "amb_ok.sol", expected_failure: false, }, ImportCorpusCase { - path: "dupqual_main.solc", + path: "dupqual_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "dupqual_module_main.solc", + path: "dupqual_module_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "private_helper_main.solc", + path: "private_helper_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "module_qualified_constructor.solc", + path: "module_qualified_constructor.sol", expected_failure: false, }, ImportCorpusCase { - path: "module_qualified_constructor_pattern.solc", + path: "module_qualified_constructor_pattern.sol", expected_failure: false, }, ImportCorpusCase { - path: "module_qualified_constructor_alias.solc", + path: "module_qualified_constructor_alias.sol", expected_failure: false, }, ImportCorpusCase { - path: "type_collision_main.solc", + path: "type_collision_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "dot_context_expr.solc", + path: "dot_context_expr.sol", expected_failure: false, }, ImportCorpusCase { - path: "reexport_items_main.solc", + path: "reexport_items_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "reexport_select_main.solc", + path: "reexport_select_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "reexport_select_alias_main.solc", + path: "reexport_select_alias_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "reexport_module_main.solc", + path: "reexport_module_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "reexport_module_alias_main.solc", + path: "reexport_module_alias_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "reexport_ctor_pattern.solc", + path: "reexport_ctor_pattern.sol", expected_failure: false, }, ImportCorpusCase { - path: "reexport_ctor_expr_ok.solc", + path: "reexport_ctor_expr_ok.sol", expected_failure: false, }, ImportCorpusCase { - path: "reexport_ctor_expr_hidden_fail.solc", + path: "reexport_ctor_expr_hidden_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "reexport_ctor_hidden_fail.solc", + path: "reexport_ctor_hidden_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "hidden_ctor_expr_fail.solc", + path: "hidden_ctor_expr_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "hidden_ctor_dot_fail.solc", + path: "hidden_ctor_dot_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "hidden_ctor_pattern_fail.solc", + path: "hidden_ctor_pattern_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "hidden_ctor_nonexhaustive_fail.solc", + path: "hidden_ctor_nonexhaustive_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "hidden_ctor_wildcard_ok.solc", + path: "hidden_ctor_wildcard_ok.sol", expected_failure: false, }, ImportCorpusCase { - path: "rootcheck/nested/main.solc", + path: "rootcheck/nested/main.sol", expected_failure: false, }, ImportCorpusCase { - path: "rootcheck/nested/relative_and_lib_main.solc", + path: "rootcheck/nested/relative_and_lib_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "external_lib_main.solc", + path: "external_lib_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "external_lib_alias_main.solc", + path: "external_lib_alias_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "import_std_minimal.solc", + path: "import_std_minimal.sol", expected_failure: false, }, ImportCorpusCase { - path: "select_alias_item_ok.solc", + path: "select_alias_item_ok.sol", expected_failure: false, }, ImportCorpusCase { - path: "select_alias_multi_ok.solc", + path: "select_alias_multi_ok.sol", expected_failure: false, }, ImportCorpusCase { - path: "external_lib_missing_fail.solc", + path: "external_lib_missing_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "symlink_identity_fail.solc", + path: "symlink_identity_fail.sol", expected_failure: true, }, ImportCorpusCase { - path: "private_bad_main.solc", + path: "private_bad_main.sol", expected_failure: true, }, ImportCorpusCase { - path: "pragma_scope_main.solc", + path: "pragma_scope_main.sol", expected_failure: true, }, ImportCorpusCase { - path: "selfcycle.solc", + path: "selfcycle.sol", expected_failure: false, }, ImportCorpusCase { - path: "cycle_main.solc", + path: "cycle_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "wild_main.solc", + path: "wild_main.sol", expected_failure: false, }, ImportCorpusCase { - path: "leak_main.solc", + path: "leak_main.sol", expected_failure: true, }, ]; diff --git a/crates/parser/src/lexer.rs b/crates/parser/src/lexer.rs index 891653e8..c77841f8 100644 --- a/crates/parser/src/lexer.rs +++ b/crates/parser/src/lexer.rs @@ -421,11 +421,12 @@ mod tests { assert_eq!(tokenize("export"), vec![Token::Export]); assert_eq!(tokenize("as"), vec![Token::As]); assert_eq!(tokenize("let"), vec![Token::Let]); - assert_eq!(tokenize("data"), vec![Token::Data]); assert_eq!(tokenize("derive"), vec![Token::Ident("derive")]); - assert_eq!(tokenize("class"), vec![Token::Class]); - assert_eq!(tokenize("forall"), vec![Token::Forall]); - assert_eq!(tokenize("instance"), vec![Token::Instance]); + for keyword in [ + "enum", "trait", "impl", "from", "returns", "where", "mapping", "while", + ] { + assert_eq!(tokenize(keyword), vec![Token::Ident(keyword)]); + } assert_eq!(tokenize("if"), vec![Token::If]); assert_eq!(tokenize("else"), vec![Token::Else]); assert_eq!(tokenize("for"), vec![Token::For]); @@ -453,6 +454,8 @@ mod tests { #[test] fn test_multi_char_operators() { + // `:=` remains a token for inline Yul, even though Core declarations + // and assignments reject it. assert_eq!(tokenize(":="), vec![Token::ColonEq]); assert_eq!(tokenize("->"), vec![Token::Arrow]); assert_eq!(tokenize("=>"), vec![Token::FatArrow]); @@ -715,17 +718,23 @@ mod tests { ); assert_eq!( - tokenize("function foo(a, b) -> c"), + tokenize("function foo(a: word, b: word) returns (word)"), vec![ Token::Function, Token::Ident("foo"), Token::LParen, Token::Ident("a"), + Token::Colon, + Token::Ident("word"), Token::Comma, Token::Ident("b"), + Token::Colon, + Token::Ident("word"), + Token::RParen, + Token::Ident("returns"), + Token::LParen, + Token::Ident("word"), Token::RParen, - Token::Arrow, - Token::Ident("c"), ] ); } @@ -734,9 +743,9 @@ mod tests { fn test_contract_snippet() { let input = r#" contract Foo { - function bar() -> u256 { - let x := 0x1234; - return x + function bar() returns (u256) { + let x = 0x1234; + return x; } } "#; @@ -752,16 +761,19 @@ mod tests { Token::Ident("bar"), Token::LParen, Token::RParen, - Token::Arrow, + Token::Ident("returns"), + Token::LParen, Token::Ident("u256"), + Token::RParen, Token::LBrace, Token::Let, Token::Ident("x"), - Token::ColonEq, + Token::Eq, Token::HexLit("0x1234"), Token::Semi, Token::Return, Token::Ident("x"), + Token::Semi, Token::RBrace, Token::RBrace, ] diff --git a/crates/parser/src/lower/body.rs b/crates/parser/src/lower/body.rs index 781b2e7f..077feb89 100644 --- a/crates/parser/src/lower/body.rs +++ b/crates/parser/src/lower/body.rs @@ -20,17 +20,38 @@ use crate::{parse::parse_body_statements, types::*}; const MAX_EXPRESSION_NESTING: usize = 32; fn apply_implicit_return(stmts: &mut Vec>) { - let [stmt] = stmts.as_mut_slice() else { + let Some(stmt) = stmts.last_mut() else { return; }; let kind = std::mem::replace(&mut stmt.kind, ParsedStmtKind::Error); stmt.kind = match kind { - ParsedStmtKind::Expr(expr) => ParsedStmtKind::Return(Some(expr)), + ParsedStmtKind::Expr { + expr, + trailing_semi: false, + } => ParsedStmtKind::Return(Some(expr)), other => other, }; } +fn reject_unterminated_tail_expr(parsed: &mut ParseOutput>) { + let Some(ParsedStmt { + span, + kind: ParsedStmtKind::Expr { + trailing_semi: false, + .. + }, + }) = parsed.output.last() + else { + return; + }; + + parsed.errors.push(ParsedError::new( + *span, + "expression statement requires trailing `;`", + )); +} + fn lower_parsed_lit(lit: ParsedLitKind<'_>) -> function::LitKind { match lit { ParsedLitKind::Number(n) => function::LitKind::Number(n.to_owned()), @@ -150,9 +171,6 @@ impl<'db, 'a> LoweringCtx<'db, 'a> { ParsedExprKind::Field { base, field } => { self.lower_field_expr(anchor, base_start, *base, field, arenas) } - ParsedExprKind::TypeAnnot { expr, ty } => { - self.lower_type_annot_expr(anchor, base_start, *expr, ty, arenas) - } ParsedExprKind::UnaryOp { op, expr } => { self.lower_unary_expr(anchor, base_start, op, *expr, arenas) } @@ -242,19 +260,6 @@ impl<'db, 'a> LoweringCtx<'db, 'a> { function::ExprKind::Field { base, field } } - fn lower_type_annot_expr( - &mut self, - anchor: AnchorId<'db>, - base_start: usize, - expr: ParsedExpr<'_>, - ty: ParsedTy<'_>, - arenas: &mut BodyArenas<'db>, - ) -> function::ExprKind<'db> { - let expr = self.lower_expr(anchor, base_start, expr, arenas); - let ty = lower_type_ref(self.db, anchor, base_start, ty); - function::ExprKind::TypeAnnot { expr, ty } - } - fn lower_unary_expr( &mut self, anchor: AnchorId<'db>, @@ -327,7 +332,8 @@ impl<'db, 'a> LoweringCtx<'db, 'a> { ); let body_anchor = AnchorId::def(self.db, body_def); - let parsed_body = parse_body_statements(self.source, body_span); + let mut parsed_body = parse_body_statements(self.source, body_span); + reject_unterminated_tail_expr(&mut parsed_body); self.parse_errors.extend(parsed_body.errors); let mut lambda_arenas = BodyArenas::new(); @@ -414,7 +420,7 @@ impl<'db, 'a> LoweringCtx<'db, 'a> { ParsedStmtKind::Return(expr) => function::StmtKind::Return( expr.map(|expr| self.lower_expr(anchor, base_start, expr, arenas)), ), - ParsedStmtKind::Expr(expr) => { + ParsedStmtKind::Expr { expr, .. } => { function::StmtKind::Expr(self.lower_expr(anchor, base_start, expr, arenas)) } ParsedStmtKind::Assign { op, lhs, rhs } => function::StmtKind::Assign { @@ -527,6 +533,9 @@ impl<'db, 'a> LoweringCtx<'db, 'a> { implicit_return: bool, ) -> Vec>> { let mut parsed = parse_body_statements(self.source, body_span); + if !implicit_return { + reject_unterminated_tail_expr(&mut parsed); + } self.parse_errors.extend(parsed.errors); if implicit_return { @@ -563,7 +572,7 @@ fn drop_parsed_expr_iteratively(root: ParsedExpr<'_>) { pending.extend(args); } ParsedExprKind::Field { base, .. } => pending.push(*base), - ParsedExprKind::TypeAnnot { expr, .. } | ParsedExprKind::UnaryOp { expr, .. } => { + ParsedExprKind::UnaryOp { expr, .. } => { pending.push(*expr); } ParsedExprKind::If { diff --git a/crates/parser/src/lower/items.rs b/crates/parser/src/lower/items.rs index 5f882081..48df0764 100644 --- a/crates/parser/src/lower/items.rs +++ b/crates/parser/src/lower/items.rs @@ -272,21 +272,6 @@ pub(super) fn lower_type_ref<'db>( params_span, ret, } => { - // A comma-separated outer domain denotes source parameters, while - // another grouping keeps a tuple-valued unary domain: - // `(a, b) -> c` versus `((a, b)) -> c`. - // Keep the raw parser's unary domain node so the grouping remains - // observable until this lowering boundary. - let params = match params.len() { - 1 => match params.into_iter().next().expect("single arrow domain") { - ParsedTy { - kind: ParsedTyKind::Tuple { elems }, - .. - } if elems.len() != 1 => elems, - param => vec![param], - }, - _ => params, - }; let params = params .into_iter() .map(|param| lower_type_ref(db, anchor, base_start, param)) @@ -643,7 +628,7 @@ pub(super) fn lower_function<'db>( let body_anchor = AnchorId::def(ctx.db, body_def); let mut arenas = BodyArenas::new(); - let implicit_return = matches!(kind, item::FuncKind::Function | item::FuncKind::Fallback); + let implicit_return = matches!(kind, item::FuncKind::Function); let top_level_stmts = ctx.with_owner(body_def, |ctx| { ctx.lower_body_statements(body_anchor, body_span, &mut arenas, implicit_return) }); diff --git a/crates/parser/src/parse/common.rs b/crates/parser/src/parse/common.rs index 27839fdf..9fa77e3a 100644 --- a/crates/parser/src/parse/common.rs +++ b/crates/parser/src/parse/common.rs @@ -6,13 +6,7 @@ pub(super) fn ident_parser<'src, I>() -> impl Parser<'src, I, SpannedStr<'src>, where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { - select! { - Token::Ident(name) => name, - Token::True => "true", - Token::False => "false", - Token::Fallback => "fallback", - } - .validate(|name, e, emitter| { + select! { Token::Ident(name) => name }.validate(|name, e, emitter| { if name.contains('-') { emitter.emit(Rich::custom( e.span(), @@ -23,6 +17,24 @@ where }) } +/// Parses one of the built-in Boolean values while retaining the identifier- +/// shaped node expected by the current name-resolution and type-inference +/// representation. +/// +/// Keeping this separate from [`ident_parser`] prevents `true` and `false` +/// from being accepted in declaration, import, or type-name positions. +pub(super) fn boolean_value_parser<'src, I>() +-> impl Parser<'src, I, SpannedStr<'src>, ParserErr<'src>> +where + I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, +{ + select! { + Token::True => "true", + Token::False => "false", + } + .map_with(|name, e| (name, e.span())) +} + pub(super) fn pragma_ident_parser<'src, I>() -> impl Parser<'src, I, SpannedStr<'src>, ParserErr<'src>> where @@ -65,18 +77,31 @@ where select! { Token::Ident(name) if name == "comptime" => () }.map_with(|_, e| e.span()) } -pub(super) fn hiding_kw_parser<'src, I>() -> impl Parser<'src, I, (), ParserErr<'src>> -where - I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, -{ - select! { Token::Ident(name) if name == "hiding" => () } +macro_rules! contextual_keyword_parser { + ($name:ident, $keyword:literal) => { + pub(super) fn $name<'src, I>() -> impl Parser<'src, I, LexSpan, ParserErr<'src>> + where + I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, + { + select! { Token::Ident(name) if name == $keyword => () }.map_with(|_, e| e.span()) + } + }; } -pub(super) fn then_kw_parser<'src, I>() -> impl Parser<'src, I, (), ParserErr<'src>> +contextual_keyword_parser!(from_kw_parser, "from"); +contextual_keyword_parser!(returns_kw_parser, "returns"); +contextual_keyword_parser!(where_kw_parser, "where"); +contextual_keyword_parser!(enum_kw_parser, "enum"); +contextual_keyword_parser!(trait_kw_parser, "trait"); +contextual_keyword_parser!(impl_kw_parser, "impl"); +contextual_keyword_parser!(mapping_kw_parser, "mapping"); +contextual_keyword_parser!(while_kw_parser, "while"); + +pub(super) fn hiding_kw_parser<'src, I>() -> impl Parser<'src, I, (), ParserErr<'src>> where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { - select! { Token::Ident(name) if name == "then" => () }.labelled("then") + select! { Token::Ident(name) if name == "hiding" => () } } pub(super) fn top_level_item_start_token_parser<'src, I>() @@ -84,12 +109,15 @@ pub(super) fn top_level_item_start_token_parser<'src, I>() where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { - select! { - Token::Hash | Token::Import | Token::Export | Token::Pragma | Token::Type | Token::Data - | Token::Class | Token::Instance | Token::Contract | Token::Public - | Token::Payable | Token::Function | Token::Constructor | Token::Fallback - | Token::Forall | Token::Default => (), - } + choice(( + select! { + Token::Hash | Token::Import | Token::Export | Token::Pragma | Token::Type + | Token::Contract | Token::Function | Token::Default => (), + }, + enum_kw_parser().ignored(), + trait_kw_parser().ignored(), + impl_kw_parser().ignored(), + )) } pub(super) fn top_level_semicolon_parser<'src, I>( diff --git a/crates/parser/src/parse/expr_pat.rs b/crates/parser/src/parse/expr_pat.rs index b6871f95..2fad20d4 100644 --- a/crates/parser/src/parse/expr_pat.rs +++ b/crates/parser/src/parse/expr_pat.rs @@ -3,7 +3,7 @@ use hir::ast::function; use super::{ common::*, - items::{body_span_parser, param_parser}, + items::{body_span_parser, lambda_param_parser}, recovery::trace_recovery, types::type_parser, }; @@ -74,7 +74,7 @@ where let mut pat = Recursive::declare(); expr.define({ - let lambda_param = param_parser().boxed(); + let lambda_param = lambda_param_parser().boxed(); let lambda_params = lambda_param .separated_by(just(Token::Comma)) @@ -99,49 +99,12 @@ where }) .boxed(); - // Parse a right-nested `else if ...` chain as a flat list of heads. - // Recursing through the complete expression grammar once per `else` - // gives each level a very large Chumsky stack frame; folding the heads - // back into the same AST keeps ordinary else-if chains stack-bounded. - let if_head = just(Token::If) - .ignore_then(expr.clone()) - .then_ignore(then_kw_parser()) - .then(expr.clone()) - .then_ignore(just(Token::Else)) - .map_with(|(cond, then_expr), e| (e.span(), cond, then_expr)) - .boxed(); - let if_expr = if_head - .repeated() - .at_least(1) - .collect::>() - .then(expr.clone()) - .map(|(heads, tail)| { - heads.into_iter().rev().fold( - tail, - |else_expr, - (head_span, cond, then_expr): ( - LexSpan, - ParsedExpr<'src>, - ParsedExpr<'src>, - )| ParsedExpr { - span: LexSpan::from(head_span.start..else_expr.span.end), - kind: ParsedExprKind::If { - cond: Box::new(cond), - then_expr: Box::new(then_expr), - else_expr: Box::new(else_expr), - }, - }, - ) - }) - .boxed(); - let boundary = choice(( just(Token::Semi).ignored(), just(Token::Comma).ignored(), just(Token::RParen).ignored(), just(Token::RBracket).ignored(), just(Token::RBrace).ignored(), - then_kw_parser(), just(Token::Else).ignored(), just(Token::Question).ignored(), just(Token::Colon).ignored(), @@ -201,9 +164,13 @@ where span: e.span(), kind: ParsedExprKind::Lit(lit), }) + .or(boolean_value_parser().map(|ident| ParsedExpr { + span: ident.1, + kind: ParsedExprKind::Ident(ident), + })) .or(just(Token::Dot) .map_with(|_, e| e.span()) - .then(ident_parser()) + .then(ident_parser().or(boolean_value_parser())) .then( expr.clone() .separated_by(just(Token::Comma)) @@ -224,7 +191,6 @@ where .or(tuple_or_paren_expr) .or(array_expr) .or(lambda_expr) - .or(if_expr) .recover_with(via_parser(atom_recovery)) .boxed(); @@ -320,20 +286,7 @@ where parsed_bin_op_expr(lhs, op, rhs, e.span()) }); - let match_arm_separator = just(Token::Pipe) - .ignore_then( - pat.clone() - .separated_by(just(Token::Comma)) - .at_least(1) - .collect::>(), - ) - .then_ignore(just(Token::FatArrow)) - .ignored(); let bit_or_op = just(Token::Pipe) - // In a match body, `| pat =>` starts the next arm; without this - // guard the expression parser could consume the separator as a - // bitwise-or operator while recovering from the previous arm body. - .and_is(match_arm_separator.not()) .to(function::BinOp::BitOr) .map_with(|op, e| ParsedSpanned::new(op, e.span())); let bit_or = bit_xor @@ -391,43 +344,29 @@ where parsed_bin_op_expr(lhs, op, rhs, e.span()) }); - let ternary = recursive(|ternary| { - or.clone() - .then( - just(Token::Question) - .ignore_then(ternary.clone()) - .then_ignore(just(Token::Colon)) - .then(ternary) - .or_not(), - ) - .map_with(|(cond, arms), e| match arms { - Some((then_expr, else_expr)) => ParsedExpr { - span: e.span(), - kind: ParsedExprKind::If { - cond: Box::new(cond), - then_expr: Box::new(then_expr), - else_expr: Box::new(else_expr), - }, - }, - None => cond, - }) - }) - .boxed(); - - let type_annot = just(Token::Colon).ignore_then(type_parser()).or_not(); - ternary - .then(type_annot) - .map_with(|(expr, ty), e| match ty { - Some(ty) => ParsedExpr { - span: e.span(), - kind: ParsedExprKind::TypeAnnot { - expr: Box::new(expr), - ty, + // A conditional expression is right-associative. Parse the common + // `a ? b : c ? d : e` shape as a flat sequence and fold it from the + // right so a long chain does not recurse through Chumsky once per + // `else` arm. The then arm still uses the complete expression grammar, + // which preserves nested conditionals such as `a ? b ? c : d : e`. + let ternary_head = or + .clone() + .then_ignore(just(Token::Question)) + .then(expr.clone()) + .then_ignore(just(Token::Colon)); + ternary_head + .repeated() + .foldr(or, |(cond, then_expr), else_expr| { + let span = LexSpan::from(cond.span.start..else_expr.span.end); + ParsedExpr { + span, + kind: ParsedExprKind::If { + cond: Box::new(cond), + then_expr: Box::new(then_expr), + else_expr: Box::new(else_expr), }, - }, - None => expr, + } }) - .boxed() }); pat.define({ @@ -445,6 +384,16 @@ where }) .boxed(); + // Boolean values are represented as variable-shaped patterns in HIR; + // name resolution recognizes these two reserved spellings as the + // builtin nullary constructors rather than introducing bindings. + let bool_pat = boolean_value_parser() + .map(|name| ParsedPat { + span: name.1, + kind: ParsedPatKind::Var(name), + }) + .boxed(); + let tuple_or_paren_pat = pat .clone() .separated_by(just(Token::Comma)) @@ -471,7 +420,7 @@ where let dot_ctor = just(Token::Dot) .map_with(|_, e| e.span()) - .then(ident_parser()) + .then(ident_parser().or(boolean_value_parser())) .then(ctor_args.clone()) .map_with(|((dot, name), args), e| ParsedPat { span: e.span(), @@ -539,6 +488,7 @@ where wildcard .or(lit_pat) + .or(bool_pat) .or(tuple_or_paren_pat) .or(dot_ctor) .or(comptime_pat) diff --git a/crates/parser/src/parse/imports.rs b/crates/parser/src/parse/imports.rs index dc1b4847..0929e905 100644 --- a/crates/parser/src/parse/imports.rs +++ b/crates/parser/src/parse/imports.rs @@ -109,18 +109,12 @@ where alias, constructors: None, }); - let selected_or_wildcard = just(Token::Star).to(None).or(selected_item.map(Some)); - let named_selector = selected_or_wildcard + let named_selector = selected_item .separated_by(just(Token::Comma)) .at_least(1) + .allow_trailing() .collect::>() - .map(|entries| { - if entries.iter().any(Option::is_none) { - ParsedImportSelector::Wildcard - } else { - ParsedImportSelector::Names(entries.into_iter().flatten().collect()) - } - }); + .map(ParsedImportSelector::Names); let selector = named_selector .delimited_by(just(Token::LBrace), just(Token::RBrace)) .boxed(); @@ -128,21 +122,23 @@ where .ignore_then( import_name_parser() .separated_by(just(Token::Comma)) + .at_least(1) .allow_trailing() .collect::>() .delimited_by(just(Token::LBrace), just(Token::RBrace)), ) .or_not() - .map(Option::unwrap_or_default); + .map(Option::unwrap_or_default) + .boxed(); let selective = just(Token::Import) - .ignore_then(path.clone()) - .then_ignore(just(Token::Dot)) - .then(selector) - .then(hiding) + .ignore_then(selector) + .then_ignore(from_kw_parser()) + .then(path.clone()) + .then(hiding.clone()) .then_ignore(top_level_semicolon_parser("import declaration")) .map_with( - |(((external, path), selector), hiding), e| ParsedTopItem::Import { + |((selector, (external, path)), hiding), e| ParsedTopItem::Import { span: e.span(), leading_comments: Vec::new(), external, @@ -154,12 +150,14 @@ where ) .boxed(); - let with_alias = just(Token::Import) - .ignore_then(path.clone()) + let namespace_alias = just(Token::Import) + .ignore_then(just(Token::Star)) .then_ignore(just(Token::As)) - .then(ident_parser()) + .ignore_then(ident_parser()) + .then_ignore(from_kw_parser()) + .then(path.clone()) .then_ignore(top_level_semicolon_parser("import declaration")) - .map_with(|((external, path), alias), e| ParsedTopItem::Import { + .map_with(|(alias, (external, path)), e| ParsedTopItem::Import { span: e.span(), leading_comments: Vec::new(), external, @@ -170,6 +168,23 @@ where }) .boxed(); + let wildcard = just(Token::Import) + .ignore_then(just(Token::Star)) + .ignore_then(from_kw_parser()) + .ignore_then(path.clone()) + .then(hiding) + .then_ignore(top_level_semicolon_parser("import declaration")) + .map_with(|((external, path), hiding), e| ParsedTopItem::Import { + span: e.span(), + leading_comments: Vec::new(), + external, + path, + alias: None, + selector: Some(ParsedImportSelector::Wildcard), + hiding, + }) + .boxed(); + let plain = just(Token::Import) .ignore_then(path) .then_ignore(top_level_semicolon_parser("import declaration")) @@ -184,7 +199,7 @@ where }) .boxed(); - choice((selective, with_alias, plain)) + choice((namespace_alias, wildcard, selective, plain)) .labelled("import declaration") .as_context() .boxed() diff --git a/crates/parser/src/parse/items.rs b/crates/parser/src/parse/items.rs index 320a88fb..8b42569f 100644 --- a/crates/parser/src/parse/items.rs +++ b/crates/parser/src/parse/items.rs @@ -6,11 +6,11 @@ use super::{ expr_pat::parsed_expr_parser, imports::{export_parser, import_parser, pragma_parser}, recovery::trace_recovery, - types::{forall_clause_parser, pred_list_parser, pred_parser, type_parser}, + types::{pred_list_parser, type_parser}, }; use crate::{lexer::Token, types::*}; -pub(super) fn param_parser<'src, I>() -> impl Parser<'src, I, ParsedFuncParam<'src>, ParserErr<'src>> +fn param_parser<'src, I>() -> impl Parser<'src, I, ParsedFuncParam<'src>, ParserErr<'src>> where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { @@ -75,11 +75,69 @@ where }); choice((comptime_typed, comptime_untyped, typed, untyped)) + .validate(|param, _, emitter| { + if let ParsedFuncParam::Typed { ty, .. } = ¶m + && matches!(ty.kind, ParsedTyKind::Comptime { .. }) + { + emitter.emit(Rich::custom( + ty.span, + "`comptime` is not a parameter type; write `comptime name: T`", + )); + } + param + }) .recover_with(via_parser(recovery)) .labelled("function parameter") .as_context() } +/// Parses a parameter of a named function-like declaration. +/// +/// Named functions, trait methods, constructors, and fallbacks require an +/// explicit type for every parameter. Keeping the untyped shape as an error +/// node lets parsing recover at the following comma without exposing inferred +/// named parameters to later semantic phases. +fn named_param_parser<'src, I>() -> impl Parser<'src, I, ParsedFuncParam<'src>, ParserErr<'src>> +where + I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, +{ + param_parser().validate(|param, extra, emitter| match param { + ParsedFuncParam::Untyped { .. } => { + let span = extra.span(); + emitter.emit(Rich::custom( + span, + "named function parameter requires an explicit type", + )); + ParsedFuncParam::Error { span } + } + param => param, + }) +} + +/// Parses a lambda parameter. +/// +/// Ordinary lambda parameters may omit their type for inference. A `comptime` +/// parameter is still required to carry an explicit type. +pub(super) fn lambda_param_parser<'src, I>() +-> impl Parser<'src, I, ParsedFuncParam<'src>, ParserErr<'src>> +where + I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, +{ + param_parser().validate(|param, extra, emitter| match param { + ParsedFuncParam::Untyped { + comptime: Some(_), .. + } => { + let span = extra.span(); + emitter.emit(Rich::custom( + span, + "`comptime` parameter requires an explicit type", + )); + ParsedFuncParam::Error { span } + } + param => param, + }) +} + #[derive(Debug, Clone, Copy, Default)] struct ParsedFuncModifiers { public: Option, @@ -98,6 +156,73 @@ impl FunctionContext { } } +fn generic_param_list_parser<'src, I>() +-> impl Parser<'src, I, (Vec>, LexSpan), ParserErr<'src>> +where + I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, +{ + ident_parser() + .separated_by(just(Token::Comma)) + .at_least(1) + .allow_trailing() + .collect::>() + .delimited_by(just(Token::Less), just(Token::Greater)) + .map_with(|params, e| (params, e.span())) +} + +fn optional_generic_params_parser<'src, I>() +-> impl Parser<'src, I, (Vec>, Option), ParserErr<'src>> +where + I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, +{ + generic_param_list_parser() + .or_not() + .map(|params| match params { + Some((params, span)) => (params, Some(span)), + None => (Vec::new(), None), + }) +} + +fn return_type_parser<'src, I>() -> impl Parser<'src, I, ParsedTy<'src>, ParserErr<'src>> +where + I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, +{ + type_parser() + .separated_by(just(Token::Comma)) + .allow_trailing() + .collect::>() + .delimited_by(just(Token::LParen), just(Token::RParen)) + .map_with(|elems, e| match <[_; 1]>::try_from(elems) { + Ok([elem]) => elem, + Err(elems) => ParsedTy { + span: e.span(), + kind: ParsedTyKind::Tuple { elems }, + }, + }) +} + +fn where_clause_parser<'src, I>() -> impl Parser<'src, I, Vec>, ParserErr<'src>> +where + I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, +{ + where_kw_parser() + .ignore_then(pred_list_parser()) + .or_not() + .map(Option::unwrap_or_default) +} + +fn parsed_ident_type<'src>(ident: SpannedStr<'src>) -> ParsedTy<'src> { + ParsedTy { + span: ident.1, + kind: ParsedTyKind::Named { + qualifiers: Vec::new(), + name: ident, + args: Vec::new(), + args_span: None, + }, + } +} + fn contract_modifiers_parser<'src, I>( context: FunctionContext, ) -> impl Parser<'src, I, ParsedFuncModifiers, ParserErr<'src>> @@ -168,17 +293,9 @@ fn signature_parser<'src, I>( where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { - let forall = forall_clause_parser().boxed(); - - let preds = pred_list_parser() - .then_ignore(just(Token::FatArrow)) - .or_not() - .map(|preds| preds.unwrap_or_default()) - .boxed(); - let modifiers = contract_modifiers_parser(context).boxed(); - let params = param_parser() + let params = named_param_parser() .separated_by(just(Token::Comma)) .allow_trailing() .collect::>() @@ -186,26 +303,28 @@ where .map_with(|params, e| (params, e.span())) .boxed(); - let ret = just(Token::Arrow) - .ignore_then(type_parser()) + let ret = returns_kw_parser() + .ignore_then(return_type_parser()) .or_not() .boxed(); - forall - .then(preds) - .then(modifiers) - .then_ignore(just(Token::Function)) - .then(ident_parser()) + just(Token::Function) + .ignore_then(ident_parser()) + .then(optional_generic_params_parser()) .then(params) + .then(modifiers) .then(ret) + .then(where_clause_parser()) .map_with( - |(((((forall_info, mut preds), modifiers), name), (params, params_span)), ret), e| { - let (type_vars, mut forall_preds) = forall_info; - forall_preds.append(&mut preds); + |(((((name, (type_vars, _)), (params, params_span)), modifiers), ret), preds), e| { + let ret = Some(ret.unwrap_or_else(|| ParsedTy { + span: e.span(), + kind: ParsedTyKind::Tuple { elems: Vec::new() }, + })); ParsedFuncSig { span: e.span(), type_vars, - preds: forall_preds, + preds, public: modifiers.public, payable: modifiers.payable, name, @@ -274,7 +393,7 @@ where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { let modifiers = implicit_public_modifiers_parser(context, "constructor").boxed(); - let params = param_parser() + let params = named_param_parser() .separated_by(just(Token::Comma)) .allow_trailing() .collect::>() @@ -282,12 +401,13 @@ where .map_with(|params, e| (params, e.span())) .boxed(); - modifiers - .then(just(Token::Constructor).map_with(|_, e| e.span())) + just(Token::Constructor) + .map_with(|_, e| e.span()) .then(params) + .then(modifiers) .then(body_span_parser()) .map_with( - |(((modifiers, name_span), (params, params_span)), body_span), e| ParsedFunctionDef { + |(((name_span, (params, params_span)), modifiers), body_span), e| ParsedFunctionDef { span: e.span(), kind: FuncKind::Constructor, leading_comments: Vec::new(), @@ -310,31 +430,15 @@ where .boxed() } -fn parsed_ty_is_unit(ty: &ParsedTy<'_>) -> bool { - match &ty.kind { - ParsedTyKind::Tuple { elems } if elems.is_empty() => true, - ParsedTyKind::Tuple { elems } if elems.len() == 1 => parsed_ty_is_unit(&elems[0]), - _ => false, - } -} - fn fallback_def_parser<'src, I>( context: FunctionContext, ) -> impl Parser<'src, I, ParsedFunctionDef<'src>, ParserErr<'src>> where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { - let forall = forall_clause_parser().boxed(); - - let preds = pred_list_parser() - .then_ignore(just(Token::FatArrow)) - .or_not() - .map(|preds| preds.unwrap_or_default()) - .boxed(); - let modifiers = implicit_public_modifiers_parser(context, "fallback").boxed(); - let params = param_parser() + let params = named_param_parser() .separated_by(just(Token::Comma)) .allow_trailing() .collect::>() @@ -342,18 +446,11 @@ where .map_with(|params, e| (params, e.span())) .boxed(); - let ret = just(Token::Arrow) - .ignore_then(type_parser()) - .or_not() - .boxed(); - - forall - .then(preds) - .then(modifiers) - .then(just(Token::Fallback).map_with(|_, e| e.span())) + just(Token::Fallback) + .map_with(|_, e| e.span()) .then(params) .validate(|value, _, emitter| { - let ((((_, _), _), _), (params, params_span)) = &value; + let (_, (params, params_span)) = &value; if !params.is_empty() { emitter.emit(Rich::custom( *params_span, @@ -362,44 +459,25 @@ where } value }) - .then(ret) - .validate(|value, _, emitter| { - if let Some(ret_ty) = &value.1 - && !parsed_ty_is_unit(ret_ty) - { - emitter.emit(Rich::custom( - ret_ty.span, - "fallback function must return unit (`()`)", - )); - } - value - }) + .then(modifiers) .then(body_span_parser()) .map_with( - |( - (((((forall_info, mut preds), modifiers), name_span), (params, params_span)), ret), - body_span, - ), - e| { - let (type_vars, mut forall_preds) = forall_info; - forall_preds.append(&mut preds); - ParsedFunctionDef { + |(((name_span, (params, params_span)), modifiers), body_span), e| ParsedFunctionDef { + span: e.span(), + kind: FuncKind::Fallback, + leading_comments: Vec::new(), + sig: ParsedFuncSig { span: e.span(), - kind: FuncKind::Fallback, - leading_comments: Vec::new(), - sig: ParsedFuncSig { - span: e.span(), - type_vars, - preds: forall_preds, - public: modifiers.public, - payable: modifiers.payable, - name: ("fallback", name_span), - params, - params_span, - ret, - }, - body_span, - } + type_vars: Vec::new(), + preds: Vec::new(), + public: modifiers.public, + payable: modifiers.payable, + name: ("fallback", name_span), + params, + params_span, + ret: None, + }, + body_span, }, ) .labelled("fallback definition") @@ -492,7 +570,7 @@ where .map_with(|(name, fields), e| ParsedAdtCtor { span: e.span(), // Filled by `adt_payload_parser`, which owns the introducing - // `=`/`|` token. + // `{`/`,` token. introducer: None, leading_comments: Vec::new(), name, @@ -501,13 +579,6 @@ where .boxed() } -fn data_terminator_parser<'src, I>() -> impl Parser<'src, I, (), ParserErr<'src>> -where - I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, -{ - just(Token::Semi).ignored() -} - fn derive_target_parser<'src, I>() -> impl Parser<'src, I, ParsedDeriveTarget<'src>, ParserErr<'src>> where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, @@ -534,7 +605,7 @@ where if reserved { emitter.emit(Rich::custom( e.span(), - format!("reserved keyword `{name}` cannot name a derived class"), + format!("reserved keyword `{name}` cannot name a derived trait"), )); } if name.contains('-') { @@ -591,7 +662,7 @@ where if attr.targets.is_empty() { emitter.emit(Rich::custom( attr.span, - "derive attribute requires at least one class path", + "derive attribute requires at least one trait path", )); } attr @@ -618,7 +689,7 @@ where .validate(|attr, _, emitter| { emitter.emit(Rich::custom( attr.span, - "malformed derive attribute; expected `#[derive(Class, ...)]`", + "malformed derive attribute; expected `#[derive(Trait, ...)]`", )); attr }); @@ -670,16 +741,7 @@ fn adt_payload_parser<'src, I>() -> impl Parser< where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { - let ty_params = ident_parser() - .separated_by(just(Token::Comma)) - .allow_trailing() - .collect::>() - .delimited_by(just(Token::LParen), just(Token::RParen)) - .or_not() - .map(|params| params.unwrap_or_default()) - .boxed(); - - let following_ctor = just(Token::Pipe) + let following_ctor = just(Token::Comma) .map_with(|_, e| e.span()) .then(data_ctor_parser()) .map(|(introducer, mut ctor)| { @@ -688,32 +750,31 @@ where }); let ctor_list = data_ctor_parser() .then(following_ctor.repeated().collect::>()) + .then_ignore(just(Token::Comma).or_not()) .map(|(first, mut rest)| { let mut ctors = Vec::with_capacity(rest.len() + 1); ctors.push(first); ctors.append(&mut rest); ctors }); - let ctors = just(Token::Eq) + let ctors = just(Token::LBrace) .map_with(|_, e| e.span()) - .then(ctor_list) - .map(|(introducer, mut ctors)| { - ctors - .first_mut() - .expect("constructor list parser always returns one constructor") - .introducer = Some(introducer); + .then(ctor_list.or_not()) + .then_ignore(just(Token::RBrace)) + .map(|(introducer, ctors)| { + let mut ctors = ctors.unwrap_or_default(); + if let Some(first) = ctors.first_mut() { + first.introducer = Some(introducer); + } ctors }) - .or_not() - .map(|ctors| ctors.unwrap_or_default()) .boxed(); - just(Token::Data) + enum_kw_parser() .ignore_then(ident_parser()) - .then(ty_params) + .then(optional_generic_params_parser()) .then(ctors) - .then_ignore(data_terminator_parser()) - .map(|((name, ty_params), ctors)| (name, ty_params, ctors)) + .map(|((name, (ty_params, _)), ctors)| (name, ty_params, ctors)) } fn adt_parser<'src, I>() -> impl Parser<'src, I, ParsedTopItem<'src>, ParserErr<'src>> @@ -729,7 +790,7 @@ where ty_params, ctors, }) - .labelled("data declaration") + .labelled("enum declaration") .as_context() .boxed() } @@ -751,38 +812,41 @@ fn class_parser<'src, I>() -> impl Parser<'src, I, ParsedTopItem<'src>, ParserEr where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { - let forall = forall_clause_parser().boxed(); - - let super_preds = pred_list_parser() - .then_ignore(just(Token::FatArrow)) - .or_not() - .map(|preds| preds.unwrap_or_default()) - .boxed(); - let methods = method_sig_parser() .repeated() .collect::>() .delimited_by(just(Token::LBrace), just(Token::RBrace)) .boxed(); - forall - .then(super_preds) - .then_ignore(just(Token::Class)) - .then(pred_parser()) + trait_kw_parser() + .ignore_then(ident_parser()) + .then(generic_param_list_parser()) + .then(where_clause_parser()) .then(methods) - .map_with(|(((forall_info, mut super_preds), head), methods), e| { - let (type_vars, mut forall_preds) = forall_info; - forall_preds.append(&mut super_preds); - ParsedTopItem::Class { - span: e.span(), - leading_comments: Vec::new(), - type_vars, - super_preds: forall_preds, - head, - methods, - } - }) - .labelled("class declaration") + .map_with( + |(((name, (type_vars, args_span)), super_preds), methods), e| { + let mut head_types = type_vars.iter().copied().map(parsed_ident_type); + let subject = head_types + .next() + .expect("trait generic parameter parser is non-empty"); + let args = head_types.collect::>(); + let head = ParsedPred { + ty: subject, + class: name, + args, + args_span: Some(args_span), + }; + ParsedTopItem::Class { + span: e.span(), + leading_comments: Vec::new(), + type_vars, + super_preds, + head, + methods, + } + }, + ) + .labelled("trait declaration") .as_context() .boxed() } @@ -791,14 +855,6 @@ fn instance_parser<'src, I>() -> impl Parser<'src, I, ParsedTopItem<'src>, Parse where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { - let forall = forall_clause_parser().boxed(); - - let preds = pred_list_parser() - .then_ignore(just(Token::FatArrow)) - .or_not() - .map(|preds| preds.unwrap_or_default()) - .boxed(); - let default_kw = just(Token::Default) .map_with(|_, e| e.span()) .or_not() @@ -810,55 +866,45 @@ where .delimited_by(just(Token::LBrace), just(Token::RBrace)) .boxed(); - let pre_instance_preds = forall - .clone() - .then(preds.clone()) - .then(default_kw.clone()) - .then_ignore(just(Token::Instance)) - .then(pred_parser()) - .then(methods.clone()) - .map_with( - |((((forall_info, mut preds), default_kw), head), methods), e| { - let (type_vars, mut forall_preds) = forall_info; - forall_preds.append(&mut preds); - ParsedTopItem::Instance { - span: e.span(), - leading_comments: Vec::new(), - type_vars, - preds: forall_preds, - default_kw, - head, - methods, - } - }, + let head = ident_parser() + .then( + type_parser() + .separated_by(just(Token::Comma)) + .at_least(1) + .allow_trailing() + .collect::>() + .delimited_by(just(Token::Less), just(Token::Greater)) + .map_with(|args, e| (args, e.span())), ) + .map(|(class, (mut args, args_span))| { + let ty = args.remove(0); + ParsedPred { + ty, + class, + args, + args_span: Some(args_span), + } + }) .boxed(); - let post_instance_preds = forall - .then(default_kw) - .then_ignore(just(Token::Instance)) - .then(preds) - .then(pred_parser()) + default_kw + .then_ignore(impl_kw_parser()) + .then(optional_generic_params_parser()) + .then(head) + .then(where_clause_parser()) .then(methods) .map_with( - |((((forall_info, default_kw), mut preds), head), methods), e| { - let (type_vars, mut forall_preds) = forall_info; - forall_preds.append(&mut preds); - ParsedTopItem::Instance { - span: e.span(), - leading_comments: Vec::new(), - type_vars, - preds: forall_preds, - default_kw, - head, - methods, - } + |((((default_kw, (type_vars, _)), head), preds), methods), e| ParsedTopItem::Instance { + span: e.span(), + leading_comments: Vec::new(), + type_vars, + preds, + default_kw, + head, + methods, }, ) - .boxed(); - - choice((pre_instance_preds, post_instance_preds)) - .labelled("instance declaration") + .labelled("impl declaration") .as_context() .boxed() } @@ -928,15 +974,13 @@ where }) .boxed(); - let item_start = just(Token::Hash) - .or(just(Token::Public)) - .or(just(Token::Payable)) - .or(just(Token::Function)) - .or(just(Token::Constructor)) - .or(just(Token::Fallback)) - .or(just(Token::Type)) - .or(just(Token::Data)) - .or(just(Token::RBrace)); + let item_start = choice(( + select! { + Token::Hash | Token::Function | Token::Constructor | Token::Fallback + | Token::Type | Token::RBrace => (), + }, + enum_kw_parser().ignored(), + )); let recovery = any() .and_is(item_start.not()) .repeated() @@ -990,7 +1034,7 @@ where _ => { emitter.emit(Rich::custom( attr.span, - "derive attribute is only allowed on data declarations", + "derive attribute is only allowed on enum declarations", )); let span = match &mut member { ParsedContractMember::Field(field) => &mut field.span, @@ -1017,15 +1061,6 @@ fn contract_parser<'src, I>() -> impl Parser<'src, I, ParsedTopItem<'src>, Parse where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { - let ty_params = ident_parser() - .separated_by(just(Token::Comma)) - .allow_trailing() - .collect::>() - .delimited_by(just(Token::LParen), just(Token::RParen)) - .or_not() - .map(|params| params.unwrap_or_default()) - .boxed(); - let members = contract_member_parser() .repeated() .collect::>() @@ -1034,9 +1069,9 @@ where just(Token::Contract) .ignore_then(ident_parser()) - .then(ty_params) + .then(optional_generic_params_parser()) .then(body) - .map_with(|((name, ty_params), members), e| { + .map_with(|((name, (ty_params, _)), members), e| { let mut fields = Vec::new(); let mut items = Vec::new(); for member in members { @@ -1064,20 +1099,7 @@ pub(super) fn top_item_parser<'src, I>() where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { - let item_start = just(Token::Hash) - .or(just(Token::Import)) - .or(just(Token::Export)) - .or(just(Token::Pragma)) - .or(just(Token::Type)) - .or(just(Token::Data)) - .or(just(Token::Class)) - .or(just(Token::Instance)) - .or(just(Token::Contract)) - .or(just(Token::Public)) - .or(just(Token::Payable)) - .or(just(Token::Function)) - .or(just(Token::Forall)) - .or(just(Token::Default)); + let item_start = top_level_item_start_token_parser(); let recovery = any() .and_is(item_start.not()) .repeated() @@ -1121,7 +1143,7 @@ where _ => { emitter.emit(Rich::custom( attr.span, - "derive attribute is only allowed on data declarations", + "derive attribute is only allowed on enum declarations", )); let span = match &mut item { ParsedTopItem::Import { span, .. } diff --git a/crates/parser/src/parse/mod.rs b/crates/parser/src/parse/mod.rs index a2b654d3..a0196037 100644 --- a/crates/parser/src/parse/mod.rs +++ b/crates/parser/src/parse/mod.rs @@ -430,6 +430,9 @@ pub(crate) fn parse_body_statements<'src>( }) .collect::>(); nesting_errors.extend(suppress_body_cascades(parse_errors)); + if let Some(output) = output.as_deref() { + validate_expression_statement_terminators(output, true, &mut nesting_errors); + } ParseOutput { output: output.unwrap_or_default(), @@ -437,6 +440,44 @@ pub(crate) fn parse_body_statements<'src>( } } +fn validate_expression_statement_terminators( + stmts: &[ParsedStmt<'_>], + allow_final_unterminated: bool, + errors: &mut Vec, +) { + for (index, stmt) in stmts.iter().enumerate() { + let is_final = index + 1 == stmts.len(); + match &stmt.kind { + ParsedStmtKind::Expr { + trailing_semi: false, + .. + } if !(allow_final_unterminated && is_final) => errors.push(ParsedError::new( + stmt.span, + "expression statement requires trailing `;`; only a final named function body expression may omit it", + )), + ParsedStmtKind::Match { arms, .. } => { + for arm in arms { + validate_expression_statement_terminators(&arm.body, false, errors); + } + } + ParsedStmtKind::For { body, .. } | ParsedStmtKind::Block { body } => { + validate_expression_statement_terminators(body, false, errors); + } + ParsedStmtKind::If { + then_body, + else_body, + .. + } => { + validate_expression_statement_terminators(then_body, false, errors); + if let Some(else_body) = else_body { + validate_expression_statement_terminators(else_body, false, errors); + } + } + _ => {} + } + } +} + #[cfg(test)] mod tests { use chumsky::prelude::*; @@ -742,6 +783,7 @@ mod tests { "unexpected function definition: {stmt:#?}" ); + // syntax-migration: preserve-next-literal let stmt = parse_yul_stmt("function _(_) -> _ { _ := _ }"); assert!( matches!( @@ -937,7 +979,7 @@ mod tests { #[test] fn unicode_identifier_parses() { - let source = "function fλ(x: word) -> word { return x; }"; + let source = "function fλ(x: word) returns (word) { return x; }"; let parsed = parse_supported_items(source); assert!( parsed.errors.is_empty(), @@ -952,7 +994,11 @@ mod tests { #[test] fn parenthesized_single_pattern_parses_as_grouping() { - let source = "{ match p { | (y) => return y; | ((), (x, z)) => return x; } }"; + let source = "\ +{ match (p) { + case (y) { return y; } + case ((), (x, z)) { return x; } +} }"; let body = parse_body_statements(source, (0..source.len()).into()); assert!(body.errors.is_empty(), "body errors: {:?}", body.errors); @@ -974,10 +1020,10 @@ mod tests { #[test] fn qualified_constructor_patterns_parse() { let source = "\ -{ match mmx { -| Option.None => return x; -| Option.Some(Option.None) => return x; -| y => return y; +{ match (mmx) { +case Option.None { return x; } +case Option.Some(Option.None) { return x; } +case y { return y; } } }"; let body = parse_body_statements(source, (0..source.len()).into()); assert!(body.errors.is_empty(), "body errors: {:?}", body.errors); @@ -1020,7 +1066,7 @@ mod tests { #[test] fn import_with_alias_parses() { - let parsed = parse_supported_items("import math.bits as Bits;"); + let parsed = parse_supported_items("import * as Bits from math.bits;"); assert!(parsed.errors.is_empty(), "errors: {:?}", parsed.errors); match parsed.output.as_slice() { @@ -1049,7 +1095,7 @@ mod tests { #[test] fn import_with_selected_items_parses() { - let parsed = parse_supported_items("import math.words.{addWord, subWord};"); + let parsed = parse_supported_items("import {addWord, subWord} from math.words;"); assert!(parsed.errors.is_empty(), "errors: {:?}", parsed.errors); match parsed.output.as_slice() { @@ -1089,7 +1135,7 @@ mod tests { #[test] fn import_with_wildcard_and_hiding_parses() { - let parsed = parse_supported_items("import glob.{*} hiding {drop};"); + let parsed = parse_supported_items("import * from glob hiding {drop};"); assert!(parsed.errors.is_empty(), "errors: {:?}", parsed.errors); match parsed.output.as_slice() { @@ -1113,7 +1159,7 @@ mod tests { #[test] fn import_and_export_operator_names_parse() { - let parsed = parse_supported_items("import math.{pow, (^^)};\nexport { f, (^^) };"); + let parsed = parse_supported_items("import {pow, (^^)} from math;\nexport { f, (^^) };"); assert!(parsed.errors.is_empty(), "errors: {:?}", parsed.errors); assert!(matches!( @@ -1131,6 +1177,392 @@ mod tests { ); } + #[test] + fn canonical_function_headers_and_return_shapes_parse() { + let source = r#" +contract Box { + function one(value: U) public payable returns (Option) where U: Eq { + return Option.Some(value); + } + function pair() returns (word, bool) { return (0, true); } + function explicitUnit() returns () { return; } + function implicitUnit() { return; } +} +"#; + let parsed = parse_supported_items(source); + assert!(parsed.errors.is_empty(), "errors: {:#?}", parsed.errors); + + let [ + ParsedTopItem::Contract { + ty_params, items, .. + }, + ] = parsed.output.as_slice() + else { + panic!("unexpected parse output: {:#?}", parsed.output); + }; + assert!(matches!(ty_params.as_slice(), [("T", _)])); + + let [ + ParsedContractItem::Function(one), + ParsedContractItem::Function(pair), + ParsedContractItem::Function(explicit_unit), + ParsedContractItem::Function(implicit_unit), + ] = items.as_slice() + else { + panic!("unexpected contract items: {items:#?}"); + }; + + assert!(one.sig.public.is_some()); + assert!(one.sig.payable.is_some()); + assert!(matches!(one.sig.type_vars.as_slice(), [("U", _)])); + assert_eq!(one.sig.preds.len(), 1); + assert!(matches!( + &one.sig.ret, + Some(ParsedTy { + kind: ParsedTyKind::Named { name: ("Option", _), args, .. }, + .. + }) if args.len() == 1 + )); + assert!(matches!( + &pair.sig.ret, + Some(ParsedTy { + kind: ParsedTyKind::Tuple { elems }, + .. + }) if elems.len() == 2 + )); + assert!(matches!( + &explicit_unit.sig.ret, + Some(ParsedTy { + kind: ParsedTyKind::Tuple { elems }, + .. + }) if elems.is_empty() + )); + assert!(matches!( + &implicit_unit.sig.ret, + Some(ParsedTy { + kind: ParsedTyKind::Tuple { elems }, + .. + }) if elems.is_empty() + )); + } + + #[test] + fn canonical_composite_and_function_types_parse() { + let source = r#" +function useTypes( + callback: function(word) returns (bool), + fire: function(word), + table: mapping(address => memory>) +) returns (bool) { return true; } +"#; + let parsed = parse_supported_items(source); + assert!(parsed.errors.is_empty(), "errors: {:#?}", parsed.errors); + let [ParsedTopItem::Function { sig, .. }] = parsed.output.as_slice() else { + panic!("unexpected parse output: {:#?}", parsed.output); + }; + + let [ + ParsedFuncParam::Typed { ty: callback, .. }, + ParsedFuncParam::Typed { ty: fire, .. }, + ParsedFuncParam::Typed { ty: table, .. }, + ] = sig.params.as_slice() + else { + panic!("unexpected parameters: {:#?}", sig.params); + }; + assert!(matches!( + &callback.kind, + ParsedTyKind::Fn { params, ret, .. } + if params.len() == 1 + && matches!(ret.kind, ParsedTyKind::Named { name: ("bool", _), .. }) + )); + assert!(matches!( + &fire.kind, + ParsedTyKind::Fn { params, ret, .. } + if params.len() == 1 + && matches!(&ret.kind, ParsedTyKind::Tuple { elems } if elems.is_empty()) + )); + assert!(matches!( + &table.kind, + ParsedTyKind::Named { name: ("mapping", _), args, .. } + if args.len() == 2 + && matches!( + &args[1].kind, + ParsedTyKind::Named { name: ("memory", _), args, .. } + if args.len() == 1 + ) + )); + assert!(matches!( + &sig.ret, + Some(ParsedTy { + kind: ParsedTyKind::Named { + name: ("bool", _), + .. + }, + .. + }) + )); + } + + #[test] + fn generic_argument_and_where_lists_require_at_least_one_entry() { + for source in [ + "function value(x: Box) returns (T) where T: Eq { return x; }", + "function pair(x: Pair) where (T: Eq, U: Eq) {}", + ] { + let parsed = parse_supported_items(source); + assert!( + parsed.errors.is_empty(), + "canonical non-empty list failed to parse: {source}: {:#?}", + parsed.errors + ); + } + + // syntax-migration: preserve-literals-begin + for source in [ + "function emptyArgs(x: Box<>) {}", + "function emptyConstraintArgs(x: T) where T: Eq<> {}", + "function genericMapping(x: mapping) {}", + "function bareMapping(x: mapping) {}", + "function emptyWhere(x: T) where {}", + "function emptyParenWhere(x: T) where () {}", + ] { + let parsed = parse_supported_items(source); + assert!( + !parsed.errors.is_empty(), + "empty generic or constraint list unexpectedly parsed: {source}: {:#?}", + parsed.output + ); + } + // syntax-migration: preserve-literals-end + } + + #[test] + fn enum_trait_and_impl_surface_lowers_to_existing_nodes() { + let source = r#" +enum Option { None, Some(T), } +trait Eq { + function eq(left: T, right: T) returns (bool); +} +impl Eq> where T: Eq { + function eq(left: Option, right: Option) returns (bool) { return true; } +} +default impl ABIAttribs {} +"#; + let parsed = parse_supported_items(source); + assert!(parsed.errors.is_empty(), "errors: {:#?}", parsed.errors); + assert!(matches!( + parsed.output.as_slice(), + [ + ParsedTopItem::Adt { ty_params, ctors, .. }, + ParsedTopItem::Class { type_vars, methods, .. }, + ParsedTopItem::Instance { type_vars: impl_vars, preds, default_kw: None, .. }, + ParsedTopItem::Instance { default_kw: Some(_), .. }, + ] if ty_params.len() == 1 + && ctors.len() == 2 + && type_vars.len() == 1 + && methods.len() == 1 + && impl_vars.len() == 1 + && preds.len() == 1 + )); + } + + #[test] + fn case_default_match_and_while_lower_to_existing_statement_nodes() { + let source = r#"{ +while (keepGoing) { continue; } +match (left, right) { + case (Option.Some(a), Option.Some(b)) { return a; } + default { return 0; } +} +}"#; + let body = parse_body_statements(source, (0..source.len()).into()); + assert!(body.errors.is_empty(), "body errors: {:#?}", body.errors); + let [ + ParsedStmt { + kind: + ParsedStmtKind::For { + init, + post, + body: loop_body, + .. + }, + .. + }, + ParsedStmt { + kind: ParsedStmtKind::Match { scrutinees, arms }, + .. + }, + ] = body.output.as_slice() + else { + panic!("unexpected body: {:#?}", body.output); + }; + assert!(init.is_empty() && post.is_empty() && loop_body.len() == 1); + assert_eq!(scrutinees.len(), 2); + assert_eq!(arms.len(), 2); + assert_eq!(arms[0].pats.len(), 2); + assert!( + arms[1] + .pats + .iter() + .all(|pat| matches!(pat.kind, ParsedPatKind::Wildcard)) + ); + } + + #[test] + fn named_function_like_parameters_require_explicit_types() { + for source in [ + "function f(value) {}", + "function f(comptime value) {}", + "trait T { function f(value); }", + "impl T { function f(value) {} }", + "contract C { constructor(value) {} }", + ] { + let parsed = parse_supported_items(source); + assert!( + parsed.errors.iter().any(|error| { + error.message == "named function parameter requires an explicit type" + }), + "missing explicit-parameter-type error for `{source}`: {:#?}", + parsed.errors + ); + } + + let parsed = parse_supported_items( + "function f(value: word, comptime offset: word) returns (word) { return value; }", + ); + assert!(parsed.errors.is_empty(), "errors: {:#?}", parsed.errors); + + for source in [ + "function f(value: comptime) {}", + "function f(comptime value: comptime) {}", + ] { + let parsed = parse_supported_items(source); + assert!( + parsed.errors.iter().any(|error| error.message + == "`comptime` is not a parameter type; write `comptime name: T`"), + "missing canonical comptime-parameter-placement error for `{source}`: {:#?}", + parsed.errors + ); + } + } + + #[test] + fn lambda_parameters_allow_inference_but_comptime_still_requires_a_type() { + let source = "{ let inferred = lam (value) { return value; }; }"; + let parsed = parse_body_statements(source, (0..source.len()).into()); + assert!(parsed.errors.is_empty(), "errors: {:#?}", parsed.errors); + + let source = "{ let invalid = lam (comptime value) { return value; }; }"; + let parsed = parse_body_statements(source, (0..source.len()).into()); + assert!( + parsed + .errors + .iter() + .any(|error| { error.message == "`comptime` parameter requires an explicit type" }), + "missing comptime-parameter-type error: {:#?}", + parsed.errors + ); + + for source in [ + "{ let invalid = lam (value: comptime) { return value; }; }", + "{ let invalid = lam (comptime value: comptime) { return value; }; }", + ] { + let parsed = parse_body_statements(source, (0..source.len()).into()); + assert!( + parsed.errors.iter().any(|error| error.message + == "`comptime` is not a parameter type; write `comptime name: T`"), + "missing noncanonical comptime-parameter error for `{source}`: {:#?}", + parsed.errors + ); + } + } + + #[test] + fn if_statement_requires_a_parenthesized_condition() { + let source = "{ if (condition) { return 1; } else { return 0; } }"; + let parsed = parse_body_statements(source, (0..source.len()).into()); + assert!(parsed.errors.is_empty(), "errors: {:#?}", parsed.errors); + assert!(matches!( + parsed.output.as_slice(), + [ParsedStmt { + kind: ParsedStmtKind::If { .. }, + .. + }] + )); + + let source = "{ if condition { return 1; } }"; + let parsed = parse_body_statements(source, (0..source.len()).into()); + assert!( + !parsed.errors.is_empty(), + "unparenthesized legacy if statement unexpectedly parsed: {:#?}", + parsed.output + ); + } + + #[test] + fn only_a_root_tail_expression_may_omit_its_semicolon() { + let source = "{ first(); second() }"; + let parsed = parse_body_statements(source, (0..source.len()).into()); + assert!(parsed.errors.is_empty(), "errors: {:#?}", parsed.errors); + + for source in [ + "{ first() second(); }", + "{ if (condition) { branch() } }", + "{ { nested() } }", + "{ match (value) { case _ { arm() } } }", + ] { + let parsed = parse_body_statements(source, (0..source.len()).into()); + assert!( + parsed.errors.iter().any(|error| error + .message + .starts_with("expression statement requires trailing `;`")), + "unterminated non-tail expression unexpectedly parsed: {source}: {:#?}", + parsed.errors + ); + } + } + + #[test] + fn rejected_legacy_core_spellings_produce_parse_errors() { + // Every case below is intentionally written in a rejected legacy + // spelling. Keep this list as the explicit compatibility boundary. + // syntax-migration: preserve-literals-begin + for source in [ + "data Option(T) = None;", + "class Eq(T) {}", + "instance Eq: word {}", + "forall T. function id(x: T) -> T { return x; }", + "public function exposed() -> word { return 0; }", + "function arrowResult() -> word { return 0; }", + "function oldType(value: array(word)) {}", + "import old.module.{item};", + ] { + let parsed = parse_supported_items(source); + assert!( + !parsed.errors.is_empty(), + "legacy spelling unexpectedly parsed: {source}: {:#?}", + parsed.output + ); + } + + for source in [ + "{ return value: word; }", + "{ return value as word; }", + "{ return if true then 1 else 0; }", + "{ match value { | item => return item; } }", + "{ let value := 1; }", + "{ value := 1; }", + ] { + let parsed = parse_body_statements(source, (0..source.len()).into()); + assert!( + !parsed.errors.is_empty(), + "legacy body spelling unexpectedly parsed: {source}: {:#?}", + parsed.output + ); + } + // syntax-migration: preserve-literals-end + } + #[test] fn lexical_error_does_not_hide_independent_top_level_parse_error() { let parsed = parse_supported_items("§\nfunction ok() {}\nfunction broken( { }\n"); diff --git a/crates/parser/src/parse/recovery.rs b/crates/parser/src/parse/recovery.rs index e5172eed..81b1dfea 100644 --- a/crates/parser/src/parse/recovery.rs +++ b/crates/parser/src/parse/recovery.rs @@ -32,8 +32,7 @@ fn preview_span_source(source: &str, span: LexSpan, max_chars: usize) -> Option< } pub(super) fn top_level_recovery_message(source: &str, span: LexSpan) -> String { - let expected = - "`import`, `pragma`, `type`, `data`, `class`, `instance`, `contract`, or `function`"; + let expected = "`import`, `pragma`, `type`, `enum`, `trait`, `impl`, `contract`, or `function`"; match preview_span_source(source, span, 48) { Some(preview) => format!( "could not parse top-level item near `{preview}`; expected a declaration starting with {expected}" @@ -99,7 +98,7 @@ fn is_statement_start_token(token: &Token<'_>) -> bool { | Token::LBrace | Token::Break | Token::Continue - ) + ) || matches!(token, Token::Ident("while")) } pub(super) fn refine_body_parse_error<'src>( @@ -123,7 +122,7 @@ fn refine_let_parse_error<'src>( ) -> Option { let assignment_idx = tokens[let_idx + 1..] .iter() - .position(|(token, _)| matches!(token, Token::Eq | Token::ColonEq)) + .position(|(token, _)| matches!(token, Token::Eq)) .map(|idx| let_idx + 1 + idx)?; if let Some((Token::Semi, semi_span)) = tokens.get(assignment_idx + 1) { @@ -169,10 +168,10 @@ fn refine_match_parse_error<'src>( Some( ParsedError::new( LexSpan::from(lbrace_span.start..rbrace_span.end), - "match statement requires at least one arm", + "match requires at least one `case` or `default` arm", ) .with_label("empty match arm list") - .with_note("add a `| pattern =>` arm"), + .with_note("add a `case pattern { ... }` or `default { ... }` arm"), ) } diff --git a/crates/parser/src/parse/stmt.rs b/crates/parser/src/parse/stmt.rs index eaf76281..488d5888 100644 --- a/crates/parser/src/parse/stmt.rs +++ b/crates/parser/src/parse/stmt.rs @@ -45,6 +45,7 @@ where fn assign_stmt_kind<'src>( lhs: ParsedExpr<'src>, tail: Option>, + trailing_semi: bool, ) -> ParsedStmtKind<'src> { match tail { Some(ParsedAssignTail::Binary(op, rhs)) => { @@ -91,7 +92,10 @@ fn assign_stmt_kind<'src>( rhs, } } - None => ParsedStmtKind::Expr(lhs), + None => ParsedStmtKind::Expr { + expr: lhs, + trailing_semi, + }, } } @@ -116,12 +120,7 @@ where just(Token::Let) .ignore_then(ident_parser()) .then(just(Token::Colon).ignore_then(type_parser()).or_not()) - .then( - just(Token::Eq) - .or(just(Token::ColonEq)) - .ignore_then(parsed_expr_parser()) - .or_not(), - ) + .then(just(Token::Eq).ignore_then(parsed_expr_parser()).or_not()) .map_with(|((name, ty), init), e| ParsedStmt { span: e.span(), kind: ParsedStmtKind::Let { @@ -142,7 +141,10 @@ where .then(assign_tail_parser().or_not()) .map_with(|(lhs, tail), e| ParsedStmt { span: e.span(), - kind: assign_stmt_kind(lhs, tail), + // `for` header items are terminated by `,`, `;`, or `)` rather + // than by statement semicolons. They are never candidates for a + // function-body tail expression. + kind: assign_stmt_kind(lhs, tail, true), }) } @@ -152,31 +154,28 @@ where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { recursive(|stmt| { - let match_arm = just(Token::Pipe) - .ignore_then( - parsed_pat_parser() - .separated_by(just(Token::Comma)) - .at_least(1) - .collect::>(), - ) - .then_ignore(just(Token::FatArrow)) - .then(stmt.clone().repeated().collect::>()) - .map_with(|(pats, body), e| ParsedMatchArm { - span: e.span(), - pats, - body, - }) + let arm_body = stmt + .clone() + .repeated() + .collect::>() + .delimited_by(just(Token::LBrace), just(Token::RBrace)) + .boxed(); + + let case_arm = just(Token::Case) + .ignore_then(parsed_pat_parser()) + .then(arm_body.clone()) + .map_with(|(pat, body), e| (e.span(), pat, body)) + .boxed(); + + let default_arm = just(Token::Default) + .map_with(|_, e| e.span()) + .then(arm_body) .boxed(); let let_stmt = just(Token::Let) .ignore_then(ident_parser()) .then(just(Token::Colon).ignore_then(type_parser()).or_not()) - .then( - just(Token::Eq) - .or(just(Token::ColonEq)) - .ignore_then(parsed_expr_parser()) - .or_not(), - ) + .then(just(Token::Eq).ignore_then(parsed_expr_parser()).or_not()) .then_ignore(just(Token::Semi)) .map_with(|((name, ty), init), e| ParsedStmt { span: e.span(), @@ -203,20 +202,67 @@ where parsed_expr_parser() .separated_by(just(Token::Comma)) .at_least(1) - .collect::>(), + .allow_trailing() + .collect::>() + .delimited_by(just(Token::LParen), just(Token::RParen)), ) .then( - match_arm + case_arm .repeated() - .at_least(1) .collect::>() + .then(default_arm.or_not()) .delimited_by(just(Token::LBrace), just(Token::RBrace)), ) - .map_with(|(scrutinees, arms), e| ParsedStmt { - span: e.span(), - kind: ParsedStmtKind::Match { scrutinees, arms }, + .validate(|(scrutinees, (case_arms, default_arm)), e, emitter| { + if case_arms.is_empty() && default_arm.is_none() { + emitter.emit(Rich::custom( + e.span(), + "match requires at least one `case` or `default` arm", + )); + } + + let arity = scrutinees.len(); + let mut arms = + Vec::with_capacity(case_arms.len() + usize::from(default_arm.is_some())); + for (span, pat, body) in case_arms { + let pats = if arity > 1 { + match pat { + ParsedPat { + kind: ParsedPatKind::Tuple(pats), + .. + } => pats, + pat => vec![pat], + } + } else { + vec![pat] + }; + if pats.len() != arity { + emitter.emit(Rich::custom( + span, + format!( + "match has {arity} scrutinees but this case has {} patterns", + pats.len() + ), + )); + } + arms.push(ParsedMatchArm { span, pats, body }); + } + + if let Some((span, body)) = default_arm { + let pats = (0..arity) + .map(|_| ParsedPat { + span, + kind: ParsedPatKind::Wildcard, + }) + .collect(); + arms.push(ParsedMatchArm { span, pats, body }); + } + + ParsedStmt { + span: e.span(), + kind: ParsedStmtKind::Match { scrutinees, arms }, + } }) - .then_ignore(just(Token::Semi).or_not()) .boxed(); let for_item = parsed_for_let_parser() @@ -253,8 +299,31 @@ where }) .boxed(); + let while_stmt = while_kw_parser() + .ignore_then( + parsed_expr_parser().delimited_by(just(Token::LParen), just(Token::RParen)), + ) + .then( + stmt.clone() + .repeated() + .collect::>() + .delimited_by(just(Token::LBrace), just(Token::RBrace)), + ) + .map_with(|(cond, body), e| ParsedStmt { + span: e.span(), + kind: ParsedStmtKind::For { + init: Vec::new(), + cond, + post: Vec::new(), + body, + }, + }) + .boxed(); + let if_stmt = just(Token::If) - .ignore_then(parsed_expr_parser()) + .ignore_then( + parsed_expr_parser().delimited_by(just(Token::LParen), just(Token::RParen)), + ) .then( stmt.clone() .repeated() @@ -331,7 +400,7 @@ where } ParsedStmt { span: e.span(), - kind: assign_stmt_kind(lhs, tail), + kind: assign_stmt_kind(lhs, tail, semi.is_some()), } }) .boxed(); @@ -341,6 +410,7 @@ where return_stmt, match_stmt, for_stmt, + while_stmt, if_stmt, assembly_stmt, block_stmt, diff --git a/crates/parser/src/parse/types.rs b/crates/parser/src/parse/types.rs index 4478dfec..3f5b63c5 100644 --- a/crates/parser/src/parse/types.rs +++ b/crates/parser/src/parse/types.rs @@ -8,18 +8,19 @@ where I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, { recursive(|ty| { - let args = ty + let angle_args = ty .clone() .separated_by(just(Token::Comma)) + .at_least(1) .allow_trailing() .collect::>() - .delimited_by(just(Token::LParen), just(Token::RParen)) + .delimited_by(just(Token::Less), just(Token::Greater)) .map_with(|args, e| (args, e.span())) .or_not() .boxed(); let named_type = qualified_ident_parser() - .then(args) + .then(angle_args) .map_with(|(mut path, args), e| { let name = path.pop().expect("qualified path has at least one segment"); let (args, args_span) = args @@ -35,6 +36,38 @@ where }, } }) + .validate(|ty, _, emitter| { + if let ParsedTyKind::Named { + qualifiers, + name: ("mapping", _), + .. + } = &ty.kind + && qualifiers.is_empty() + { + emitter.emit(Rich::custom( + ty.span, + "the `mapping` type uses `mapping(Key => Value)`", + )); + } + ty + }) + .boxed(); + + let mapping_type = mapping_kw_parser() + .then_ignore(just(Token::LParen)) + .then(ty.clone()) + .then_ignore(just(Token::FatArrow)) + .then(ty.clone()) + .then_ignore(just(Token::RParen)) + .map_with(|((mapping, key), value), e| ParsedTy { + span: e.span(), + kind: ParsedTyKind::Named { + qualifiers: Vec::new(), + name: ("mapping", mapping), + args: vec![key, value], + args_span: Some(e.span()), + }, + }) .boxed(); let paren_types = ty @@ -47,7 +80,9 @@ where .boxed(); let comptime_type = comptime_kw_parser() + .then_ignore(just(Token::Less)) .then(ty.clone()) + .then_ignore(just(Token::Greater)) .map_with(|(kw, inner), e| ParsedTy { span: e.span(), kind: ParsedTyKind::Comptime { @@ -57,49 +92,74 @@ where }) .boxed(); - let tuple_type = paren_types - .map(|(elems, paren_span)| ParsedTy { - span: paren_span, - kind: ParsedTyKind::Tuple { elems }, + let function_params = ty + .clone() + .separated_by(just(Token::Comma)) + .allow_trailing() + .collect::>() + .delimited_by(just(Token::LParen), just(Token::RParen)) + .map_with(|params, e| (params, e.span())) + .boxed(); + let function_ret = ty + .clone() + .separated_by(just(Token::Comma)) + .allow_trailing() + .collect::>() + .delimited_by(just(Token::LParen), just(Token::RParen)) + .map_with(|elems, e| match <[_; 1]>::try_from(elems) { + Ok([elem]) => elem, + Err(elems) => ParsedTy { + span: e.span(), + kind: ParsedTyKind::Tuple { elems }, + }, }) .boxed(); - - let atom_type = recursive(|atom| { - let proxy_type = just(Token::At) - .map_with(|_, e| e.span()) - .then(atom) - .map_with(|(at, inner), e| ParsedTy { + let function_type = just(Token::Function) + .ignore_then(function_params) + .then(returns_kw_parser().ignore_then(function_ret).or_not()) + .map_with(|((params, params_span), ret), e| { + let ret = ret.unwrap_or_else(|| ParsedTy { span: e.span(), - kind: ParsedTyKind::Proxy { - at, - inner: Box::new(inner), - }, - }) - .boxed(); - - proxy_type.or(tuple_type).or(named_type) - }) - .boxed(); - - let atom_type = comptime_type.or(atom_type).boxed(); - - atom_type - .clone() - .then(just(Token::Arrow).ignore_then(ty.clone()).or_not()) - .map_with(|(domain, ret), e| match ret { - Some(ret) => ParsedTy { + kind: ParsedTyKind::Tuple { elems: Vec::new() }, + }); + ParsedTy { span: e.span(), - // Arrow types are right-associative over atom domains. - // A parenthesized tuple domain remains one unary domain, - // matching the Haskell reference parser. kind: ParsedTyKind::Fn { - params_span: domain.span, - params: vec![domain], + params, + params_span, ret: Box::new(ret), }, + } + }) + .boxed(); + + let tuple_type = paren_types + .map(|(elems, paren_span)| ParsedTy { + span: paren_span, + kind: ParsedTyKind::Tuple { elems }, + }) + .boxed(); + + let proxy_type = just(Token::At) + .map_with(|_, e| e.span()) + .then(ty.clone()) + .map_with(|(at, inner), e| ParsedTy { + span: e.span(), + kind: ParsedTyKind::Proxy { + at, + inner: Box::new(inner), }, - None => domain, }) + .boxed(); + + choice(( + function_type, + comptime_type, + mapping_type, + proxy_type, + tuple_type, + named_type, + )) }) .labelled("type") .as_context() @@ -118,9 +178,10 @@ where { let class_args = type_parser() .separated_by(just(Token::Comma)) + .at_least(1) .allow_trailing() .collect::>() - .delimited_by(just(Token::LParen), just(Token::RParen)) + .delimited_by(just(Token::Less), just(Token::Greater)) .map_with(|args, e| (args, e.span())) .or_not() .boxed(); @@ -152,6 +213,7 @@ where { let bare = pred_parser() .separated_by(just(Token::Comma)) + .at_least(1) .allow_trailing() .collect::>() .boxed(); @@ -159,100 +221,3 @@ where .delimited_by(just(Token::LParen), just(Token::RParen)) .or(bare) } - -#[derive(Debug, Clone)] -enum ParsedForallBinder<'src> { - Var(SpannedStr<'src>), - Bound { - var: SpannedStr<'src>, - pred: ParsedPred<'src>, - }, -} - -fn forall_binder_parser<'src, I>() -> impl Parser<'src, I, ParsedForallBinder<'src>, ParserErr<'src>> -where - I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, -{ - let class_args = type_parser() - .separated_by(just(Token::Comma)) - .allow_trailing() - .collect::>() - .delimited_by(just(Token::LParen), just(Token::RParen)) - .map_with(|args, e| (args, e.span())) - .or_not() - .boxed(); - - let bounded = ident_parser() - .then_ignore(just(Token::Colon)) - .then(ident_parser()) - .then(class_args) - .map(|((var, class), args)| { - let (args, args_span) = args - .map(|(args, span)| (args, Some(span))) - .unwrap_or_else(|| (Vec::new(), None)); - let ty = ParsedTy { - span: var.1, - kind: ParsedTyKind::Named { - qualifiers: Vec::new(), - name: var, - args: Vec::new(), - args_span: None, - }, - }; - let pred = ParsedPred { - ty, - class, - args, - args_span, - }; - ParsedForallBinder::Bound { var, pred } - }); - - let bare = ident_parser().map(ParsedForallBinder::Var); - - choice((bounded, bare)) -} - -pub(super) fn forall_clause_parser<'src, I>() --> impl Parser<'src, I, (Vec>, Vec>), ParserErr<'src>> -where - I: ValueInput<'src, Token = Token<'src>, Span = LexSpan>, -{ - let binder = forall_binder_parser().boxed(); - let binders = binder - .clone() - .then( - just(Token::Comma) - .or_not() - .ignore_then(binder) - .repeated() - .collect::>(), - ) - .map(|(first, mut rest)| { - let mut all = Vec::with_capacity(rest.len() + 1); - all.push(first); - all.append(&mut rest); - all - }); - - just(Token::Forall) - .ignore_then(binders) - .then_ignore(just(Token::Dot)) - .or_not() - .map(|binders| { - let mut type_vars = Vec::new(); - let mut preds = Vec::new(); - if let Some(binders) = binders { - for binder in binders { - match binder { - ParsedForallBinder::Var(var) => type_vars.push(var), - ParsedForallBinder::Bound { var, pred } => { - type_vars.push(var); - preds.push(pred); - } - } - } - } - (type_vars, preds) - }) -} diff --git a/crates/parser/src/parse/yul.rs b/crates/parser/src/parse/yul.rs index 682a75a5..4c885edf 100644 --- a/crates/parser/src/parse/yul.rs +++ b/crates/parser/src/parse/yul.rs @@ -12,6 +12,9 @@ where select! { Token::YulIdent(name) => name, Token::Underscore => "_", + // `fallback` is reserved by the Core surface, not by Yul. Keep + // the two identifier grammars independent inside assembly. + Token::Fallback => "fallback", } .map_with(|name, e| (name, e.span())), )) diff --git a/crates/parser/src/types.rs b/crates/parser/src/types.rs index baf6e3b1..24a352fc 100644 --- a/crates/parser/src/types.rs +++ b/crates/parser/src/types.rs @@ -93,21 +93,21 @@ pub(crate) struct ParseOutput { pub(crate) errors: Vec, } -/// One class named by a `derive` attribute. +/// One trait named by a `derive` attribute. #[derive(Debug, Clone)] pub(crate) struct ParsedDeriveTarget<'src> { - /// Span covering the complete possibly-qualified class path. + /// Span covering the complete possibly-qualified trait path. pub(crate) span: LexSpan, - /// Class path segments in source order. + /// Trait path segments in source order. pub(crate) path: Vec>, } -/// Parsed `#[derive(...)]` attribute attached to a data declaration. +/// Parsed `#[derive(...)]` attribute attached to an enum declaration. #[derive(Debug, Clone)] pub(crate) struct ParsedDeriveAttr<'src> { /// Span covering the complete attribute, from `#` through `]`. pub(crate) span: LexSpan, - /// Classes requested by the attribute, in source order. + /// Traits requested by the attribute, in source order. pub(crate) targets: Vec>, } @@ -164,13 +164,13 @@ pub(crate) enum ParsedTopItem<'src> { /// Aliased type. ty: ParsedTy<'src>, }, - /// Algebraic data type declaration. + /// Enum/algebraic data type declaration. Adt { /// Span covering the declaration. span: LexSpan, /// Consecutive comments directly preceding the declaration. leading_comments: Vec>, - /// Optional derive attribute preceding `data`. + /// Optional derive attribute preceding `enum`. derive_attr: Option>, /// Type name. name: SpannedStr<'src>, @@ -179,13 +179,13 @@ pub(crate) enum ParsedTopItem<'src> { /// Constructors. ctors: Vec>, }, - /// Class declaration. + /// Trait declaration, lowered to the existing class representation. Class { /// Span covering the declaration. span: LexSpan, /// Consecutive comments directly preceding the declaration. leading_comments: Vec>, - /// Type variables introduced by `forall`. + /// Type variables declared by the trait's generic parameter list. type_vars: Vec>, /// Superclass predicates. super_preds: Vec>, @@ -194,13 +194,13 @@ pub(crate) enum ParsedTopItem<'src> { /// Method signature declarations. methods: Vec>, }, - /// Instance declaration. + /// Impl declaration, lowered to the existing instance representation. Instance { /// Span covering the declaration. span: LexSpan, /// Consecutive comments directly preceding the declaration. leading_comments: Vec>, - /// Type variables introduced by `forall`. + /// Type variables declared by the impl's generic parameter list. type_vars: Vec>, /// Context predicates. preds: Vec>, @@ -431,7 +431,7 @@ pub(crate) enum ParsedFuncParam<'src> { pub(crate) struct ParsedFuncSig<'src> { /// Span covering the signature. pub(crate) span: LexSpan, - /// Type variables from `forall`. + /// Type variables from the angle-bracket generic parameter list. pub(crate) type_vars: Vec>, /// Qualifying predicates. pub(crate) preds: Vec>, @@ -445,7 +445,8 @@ pub(crate) struct ParsedFuncSig<'src> { pub(crate) params: Vec>, /// Span of the parameter list. pub(crate) params_span: LexSpan, - /// Optional return type. + /// Return type for an ordinary function, including explicit unit when + /// `returns` is omitted; absent only for constructor/fallback signatures. pub(crate) ret: Option>, } @@ -497,13 +498,13 @@ pub(crate) enum ParsedContractItem<'src> { /// Aliased type. ty: ParsedTy<'src>, }, - /// Contract-local ADT. + /// Contract-local enum/ADT. Adt { /// Span covering the declaration. span: LexSpan, /// Consecutive comments directly preceding the declaration. leading_comments: Vec>, - /// Optional derive attribute preceding `data`. + /// Optional derive attribute preceding `enum`. derive_attr: Option>, /// ADT name. name: SpannedStr<'src>, @@ -605,13 +606,6 @@ pub(crate) enum ParsedExprKind<'src> { /// Field name. field: SpannedStr<'src>, }, - /// Type annotation expression. - TypeAnnot { - /// Annotated expression. - expr: Box>, - /// Annotation type. - ty: ParsedTy<'src>, - }, /// Unary operator expression. UnaryOp { /// Operator and span. @@ -738,7 +732,12 @@ pub(crate) enum ParsedStmtKind<'src> { /// Return statement. Return(Option>), /// Expression statement. - Expr(ParsedExpr<'src>), + Expr { + /// Expression payload. + expr: ParsedExpr<'src>, + /// Whether the source expression was followed by `;`. + trailing_semi: bool, + }, /// Assignment. Assign { /// Assignment operator. diff --git a/crates/parser/tests/def_identity.rs b/crates/parser/tests/def_identity.rs index 7c4fccff..0c87da73 100644 --- a/crates/parser/tests/def_identity.rs +++ b/crates/parser/tests/def_identity.rs @@ -37,7 +37,7 @@ struct DefIdentity { } fn source_file(db: &TestDb, name: &str, src: &str) -> SourceFile { - let url = format!("memory:///{name}.solc").parse().expect("valid url"); + let url = format!("memory:///{name}.sol").parse().expect("valid url"); SourceFile::new(db, url, Some(src.to_owned())) } @@ -122,9 +122,9 @@ fn instances_of_same_class_on_different_heads_have_distinct_def_ids() { let file = source_file( &db, "instance-heads", - "class self:StorageType {}\n\n\ - instance word:StorageType {\n function rep(x:word) -> word { return x; }\n}\n\n\ - instance uint:StorageType {\n function rep(x:uint) -> uint { return x; }\n}\n", + "trait StorageType {}\n\n\ + impl StorageType {\n function rep(x:word) returns (word) { return x; }\n}\n\n\ + impl StorageType {\n function rep(x:uint) returns (uint) { return x; }\n}\n", ); let instances = defs_by_name(&db, file, DefKind::Instance, "StorageType"); @@ -145,9 +145,9 @@ fn instances_with_same_subject_and_different_class_args_have_distinct_def_ids() let file = source_file( &db, "instance-class-args", - "class self:Carrier(arg) {}\n\n\ - instance word:Carrier(uint) {}\n\n\ - instance word:Carrier(bool) {}\n", + "trait Carrier {}\n\n\ + impl Carrier {}\n\n\ + impl Carrier {}\n", ); let instances = defs_by_name(&db, file, DefKind::Instance, "Carrier"); @@ -214,11 +214,11 @@ fn import_selector_fingerprints_are_structural_and_order_independent() { let file = source_file( &db, "imports-selector-fingerprints", - "import A.{x as y, (^^)} hiding {z, w};\n\ - import A.{(^^), x as y} hiding {w, z};\n\ - import A.{x};\n\ - import A.{x as y};\n\ - import A.{*};\n", + "import {x as y, (^^)} from A hiding {z, w};\n\ + import {(^^), x as y} from A hiding {w, z};\n\ + import {x} from A;\n\ + import {x as y} from A;\n\ + import * from A;\n", ); let mut fingerprints = all_defs(&db, file) @@ -243,7 +243,7 @@ fn import_selector_fingerprints_are_structural_and_order_independent() { #[test] fn inserting_preceding_lambda_keeps_existing_lambda_body_identities_stable() { let mut db = TestDb::default(); - let before_src = "function f(z: word) -> word { + let before_src = "function f(z: word) returns (word) { let n = lam (x: word) { return x; }; let m = lam (y: word) { return y; }; return m(n(z)); @@ -254,7 +254,7 @@ fn inserting_preceding_lambda_keeps_existing_lambda_body_identities_stable() { assert_eq!(before.len(), 2); file.set_content(&mut db).to(Some( - "function f(z: word) -> word { + "function f(z: word) returns (word) { let ignored = lam (q: word) { return q + 1; }; let n = lam (x: word) { return x; }; let m = lam (y: word) { return y; }; @@ -280,7 +280,7 @@ fn inserting_preceding_lambda_keeps_existing_lambda_body_identities_stable() { #[test] fn lambda_body_edit_keeps_lambda_body_identity_stable() { let mut db = TestDb::default(); - let before_src = "function f(z: word) -> word { + let before_src = "function f(z: word) returns (word) { let n = lam (x: word) { return x + 1; }; return n(z); }"; @@ -290,7 +290,7 @@ fn lambda_body_edit_keeps_lambda_body_identity_stable() { assert_eq!(before.len(), 1); file.set_content(&mut db).to(Some( - "function f(z: word) -> word { + "function f(z: word) returns (word) { let n = lam (x: word) { return x + 2; }; return n(z); }" @@ -355,9 +355,9 @@ fn well_formed_program_defs_have_zero_disambiguators() { let file = source_file( &db, "zero-disambiguators", - "class self:StorageType {}\n\n\ - instance word:StorageType {\n function rep(x:word) -> word { return x; }\n}\n\n\ - contract Counter {\n function main() -> word { return 0; }\n}\n\n\ + "trait StorageType {}\n\n\ + impl StorageType {\n function rep(x:word) returns (word) { return x; }\n}\n\n\ + contract Counter {\n function main() returns (word) { return 0; }\n}\n\n\ function top() {}\n", ); diff --git a/crates/parser/tests/diagnostics.rs b/crates/parser/tests/diagnostics.rs index f07c7f6c..5c98a4b4 100644 --- a/crates/parser/tests/diagnostics.rs +++ b/crates/parser/tests/diagnostics.rs @@ -33,7 +33,7 @@ impl solcore_parser::Db for TestDb {} #[dir_test( dir: "$CARGO_MANIFEST_DIR/tests/fixtures/corpus/fail", - glob: "**/*.solc" + glob: "**/*.sol" )] fn parser_corpus_fail_diagnostics(fixture: Fixture<&str>) { run_fixture_assertion(fixture, assert_fail_fixture); @@ -55,7 +55,7 @@ fn assert_fail_fixture(path: &str, content: &str) { return; } - if path.ends_with("multiple_emitted_errors.solc") { + if path.ends_with("multiple_emitted_errors.sol") { assert!( diagnostics.len() > 1, "expected more than one diagnostic for `{}`", @@ -69,7 +69,7 @@ fn assert_fail_fixture(path: &str, content: &str) { #[dir_test( dir: "$CARGO_MANIFEST_DIR/tests/fixtures/ok", - glob: "**/*.solc" + glob: "**/*.sol" )] fn parser_ok_no_diagnostics(fixture: Fixture<&str>) { run_fixture_assertion(fixture, assert_ok_fixture); @@ -77,7 +77,7 @@ fn parser_ok_no_diagnostics(fixture: Fixture<&str>) { #[dir_test( dir: "$CARGO_MANIFEST_DIR/tests/fixtures/corpus/ok", - glob: "**/*.solc" + glob: "**/*.sol" )] fn parser_corpus_ok_no_diagnostics(fixture: Fixture<&str>) { run_fixture_assertion(fixture, assert_ok_fixture); @@ -122,7 +122,7 @@ fn fixture_source_file(db: &TestDb, path: &str, content: &str) -> SourceFile { let file_name = fixture_path .file_name() .and_then(|name| name.to_str()) - .unwrap_or("fixture.solc"); + .unwrap_or("fixture.sol"); let url = format!("memory:///{file_name}") .parse() .expect("valid fixture URL"); diff --git a/crates/parser/tests/fixtures/corpus/README.md b/crates/parser/tests/fixtures/corpus/README.md index 539b2efa..a82c0765 100644 --- a/crates/parser/tests/fixtures/corpus/README.md +++ b/crates/parser/tests/fixtures/corpus/README.md @@ -1,13 +1,14 @@ # Solcore 2f372bde frontend corpus -This corpus vendors every `.solc` source under `test/examples/` from +This corpus ports every source under `test/examples/` from [`argotorg/solcore@2f372bde`](https://github.com/argotorg/solcore/tree/2f372bde2801612814015a22319d0bc51486cbf0/test/examples). -The 499 example paths and their contents are byte-identical to that snapshot. +The 499 examples keep the snapshot's module layout and semantics while using +the canonical `.sol` syntax. Sources accepted by the reference frontend live under `ok/test/examples/`; reference failures and timeouts live under `fail/test/examples/`. -The standard-library sources in `ok/std/` are the matching 2f372bde snapshot. -They are also byte-identical to [`std/`](../../../../../std/); see +The standard-library sources in `ok/std/` are the syntax-migrated 2f372bde +snapshot. They are byte-identical to [`std/`](../../../../../std/); see [`std/README.md`](../../../../../std/README.md) for the synchronization policy. The `test/imports/` and `known-diagnostic-gaps/` trees are Rust-specific regressions and are not part of the reference example snapshot. @@ -26,10 +27,11 @@ sol-core --file <2f372bde>/test/examples/ \ --color never --unicode never --diagnostic-format short ``` -The snapshot contains 337 passes, 160 failures, and two timeouts. `code` is the +The original snapshot contains 337 passes, 160 failures, and two timeouts. +Ledger paths map to the migrated `.sol` files by module stem. `code` is the first structured `SCnnnn` diagnostic emitted for a failure; `-` means that no structured code applies. The two timeout rows are -`cases/tabled-cycle-fail.solc` and `cases/tabled-left-recursive-fail.solc`. +`cases/tabled-cycle-fail.sol` and `cases/tabled-left-recursive-fail.sol`. These verdicts describe the legacy frontend with specialization and generated dispatch disabled, not the full compiler or the tabled resolver. diff --git a/crates/parser/tests/fixtures/corpus/fail/test/diagnostics/parse-error.snap b/crates/parser/tests/fixtures/corpus/fail/test/diagnostics/parse-error.snap index 9da95013..331e3a22 100644 --- a/crates/parser/tests/fixtures/corpus/fail/test/diagnostics/parse-error.snap +++ b/crates/parser/tests/fixtures/corpus/fail/test/diagnostics/parse-error.snap @@ -1,10 +1,10 @@ --- source: crates/parser/tests/diagnostics.rs expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/diagnostics/parse-error.solc +input_file: crates/parser/tests/fixtures/corpus/fail/test/diagnostics/parse-error.sol --- error[SC0001]: parse error: unexpected end of input - --> /parse-error.solc:1:38 + --> /parse-error.sol:1:38 | 1 | function main( -> word { return 0; } | ^ unexpected token diff --git a/crates/parser/tests/fixtures/corpus/fail/test/diagnostics/parse-error.solc b/crates/parser/tests/fixtures/corpus/fail/test/diagnostics/parse-error.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/fail/test/diagnostics/parse-error.solc rename to crates/parser/tests/fixtures/corpus/fail/test/diagnostics/parse-error.sol diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/Convertible.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/Convertible.sol new file mode 100644 index 00000000..21dc4694 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/Convertible.sol @@ -0,0 +1,129 @@ +enum Pair { Pair(a, b) } +enum Proxy { Proxy } +enum Unit { Unit } + +trait Typedef { + function abs(x: r) returns (a) ; + function rep(x: a) returns (r) ; +} + + +enum uint16 { uint16(word) } + +impl Typedef { + function abs(r:word) returns (uint16) { return uint16(r);} + function rep(x: uint16) returns (word) { + match (x) { +case uint16(val) { +return val; +} +} + } +} + +enum uint8 { uint8(word) } + +impl Typedef { + function abs(r:word) returns (uint8) { return uint8(r);} + function rep(x: uint8) returns (word) { + match (x) { +case uint8(val) { +return val; +} +} + } +} + +enum uint256 { uint256(word) } + +impl Typedef { + function abs(r:word) returns (uint256) { return uint256(r);} + function rep(x: uint256) returns (word) { + match (x) { +case uint256(val) { +return val; +} +} + } +} + + +function foo(x: word) returns (uint16) { + let result : uint16 = Typedef.abs(x); + return result; +} + + +trait Convertible { + function convert(x: self) returns (r) ; +} + +impl Convertible>, uint16> { + function convert(p: Pair>) returns (uint16) { + match (p) { +case Pair(x, _) { +return Typedef.abs(Typedef.rep(x)); +} +} + } +} + + + +function uint8to16(x: uint8) returns (uint16) { + let proxy : Proxy = Proxy; + let result : uint16 = Convertible.convert(Pair(x,proxy)); + return result; +} + +/* +forall Pair(a,Proxy(b)):Convertible(b). function convert(x:a) -> b { + let proxy : Proxy(b) = Proxy; + let result : b = Convertible.convert(Pair(x,proxy)); + return result; +} +*/ + +function convert(x: a) returns (b) { + let proxy : Proxy = Proxy; + let result : b = Convertible.convert(Pair(x,proxy)); + return result; +} + +function bar(x: Unit) returns (word) { + let result: word = convert(x); + return result; +} + + +impl Convertible>, uint256> { + function convert(p: Pair>) returns (uint256) { + match (p) { +case Pair(x, _) { +return Typedef.abs(Typedef.rep(x)); +} +} + } +} + +impl Convertible>, uint256> { + function convert(p: Pair>) returns (uint256) { + match (p) { +case Pair(x, _) { +return Typedef.abs(Typedef.rep(x)); +} +} + } +} + + + +contract Bar { + +function main() public returns (word) { + let x = Unit; + let y : word = convert(x); + return y; +} + +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/Convertible.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/Convertible.solc deleted file mode 100644 index cccfa06c..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/Convertible.solc +++ /dev/null @@ -1,118 +0,0 @@ -data Pair(a,b) = Pair(a,b); -data Proxy(a) = Proxy; -data Unit = Unit; - -class a:Typedef(r) { - function abs(x:r) -> a; - function rep(x:a) -> r; -} - - -data uint16 = uint16(word); - -instance uint16:Typedef(word) { - function abs(r:word) { return uint16(r);} - function rep(x: uint16) -> word { - match x { - | uint16(val) => return val; - }; - } -} - -data uint8 = uint8(word); - -instance uint8:Typedef(word) { - function abs(r:word) { return uint8(r);} - function rep(x: uint8) -> word { - match x { - | uint8(val) => return val; - }; - } -} - -data uint256 = uint256(word); - -instance uint256:Typedef(word) { - function abs(r:word) { return uint256(r);} - function rep(x: uint256) -> word { - match x { - | uint256(val) => return val; - }; - } -} - - -function foo(x:word) -> uint16 { - let result : uint16 = Typedef.abs(x); - return result; -} - - -class self:Convertible(r) -{ - function convert(x:self) -> r; -} - -instance Pair(uint8,Proxy(uint16)):Convertible(uint16) { - function convert(p:Pair(uint8,Proxy(uint16))) -> uint16 { - match p { - | Pair(x, _) => return Typedef.abs(Typedef.rep(x)); - }; - } -} - - - -function uint8to16(x : uint8) -> uint16 { - let proxy : Proxy(uint16) = Proxy; - let result : uint16 = Convertible.convert(Pair(x,proxy)); - return result; -} - -/* -forall Pair(a,Proxy(b)):Convertible(b). function convert(x:a) -> b { - let proxy : Proxy(b) = Proxy; - let result : b = Convertible.convert(Pair(x,proxy)); - return result; -} -*/ - -forall a, b. function convert(x:a) -> b { - let proxy : Proxy(b) = Proxy; - let result : b = Convertible.convert(Pair(x,proxy)); - return result; -} - -function bar(x:Unit) -> word { - let result: word = convert(x); - return result; -} - - -instance Pair(uint8,Proxy(uint256)):Convertible(uint256) { - function convert(p:Pair(uint8,Proxy(uint256))) -> uint256 { - match p { - | Pair(x, _) => return Typedef.abs(Typedef.rep(x)); - }; - } -} - -instance Pair(uint16,Proxy(uint256)):Convertible(uint256) { - function convert(p:Pair(uint16,Proxy(uint256))) -> uint256 { - match p { - | Pair(x, _) => return Typedef.abs(Typedef.rep(x)); - }; - } -} - - - -contract Bar { - -public function main() -> word { - let x = Unit; - let y : word = convert(x); - return y; -} - -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/BadInstance.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/BadInstance.sol new file mode 100644 index 00000000..a8cfeec8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/BadInstance.sol @@ -0,0 +1,22 @@ +trait Enum { + function fromEnum(x: a) returns (word) ; + } + +enum Color { R, G, B } + +enum Bool { False, True } + +impl Enum { + function fromEnum(b: Bool) returns (word) { + match (b) { +case Color.R { +return 0; +} +case Color.G { +return 1; +} +} + } +} + + diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/BadInstance.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/BadInstance.solc deleted file mode 100644 index 0906c230..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/BadInstance.solc +++ /dev/null @@ -1,18 +0,0 @@ -class a:Enum { - function fromEnum(x:a) -> word; - } - -data Color = R | G | B; - -data Bool = False | True; - -instance Bool : Enum { - function fromEnum(b : Bool) -> word { - match b { - | Color.R => return 0; - | Color.G => return 1; - } - } -} - - diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/DupFun.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/DupFun.sol new file mode 100644 index 00000000..1159c19c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/DupFun.sol @@ -0,0 +1,11 @@ +function f(x: word) returns (word) { + return x; +} + +function f(x: word) returns (word) { + return 10; +} + +function g(x: word) returns (word) { + return f(x); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/DupFun.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/DupFun.solc deleted file mode 100644 index fbfc9dce..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/DupFun.solc +++ /dev/null @@ -1,11 +0,0 @@ -function f(x : word) -> word { - return x; -} - -function f(x : word) -> word { - return 10; -} - -function g(x : word) -> word { - return f(x); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Enum.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Enum.sol new file mode 100644 index 00000000..4548940f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Enum.sol @@ -0,0 +1,27 @@ +trait Enum { + function fromEnum(x: a) returns (word) ; +} + +enum Food { Curry, Beans, Other } + +impl Enum { + function fromEnum(x: Food) returns (word) { + match (x) { +case Food.Curry { +return 1; +} +case Food.Beans { +return 2; +} +case Food.Other { +return 3; +} +} + } +} + +contract Food { + function main() public returns (word) { + return Enum.fromEnum(Food.Beans); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Enum.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Enum.solc deleted file mode 100644 index 6b977e4a..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Enum.solc +++ /dev/null @@ -1,21 +0,0 @@ -class a: Enum { - function fromEnum(x : a) -> word; -} - -data Food = Curry | Beans | Other; - -instance Food : Enum { - function fromEnum(x : Food) -> word { - match x { - | Food.Curry => return 1; - | Food.Beans => return 2; - | Food.Other => return 3; - } - } -} - -contract Food { - public function main() -> word { - return Enum.fromEnum(Food.Beans); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Eq.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Eq.sol new file mode 100644 index 00000000..ec5118b4 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Eq.sol @@ -0,0 +1,22 @@ +enum Bool { True, False } + +trait Eq { + function eq(x: a, y: a) returns (Bool) ; +} + +trait Ord where a: Eq { + function lt(x: a, y: a) returns (Bool) ; +} + +impl Eq { + function eq (x: word, y: word) returns (Bool) { + match (primEqWord(x,y)) { +case 0 { +return Bool.False; +} +default { +return Bool.True ; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Eq.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Eq.solc deleted file mode 100644 index a36b462d..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Eq.solc +++ /dev/null @@ -1,20 +0,0 @@ -data Bool = True | False; - -class a : Eq { - function eq (x : a, y : a) -> Bool; -} - -forall a . a : Eq => class a : Ord { - function lt (x : a, y : a) -> Bool ; -} - -instance word : Eq { - function eq (x,y) { - match primEqWord(x,y) { - | 0 => - return Bool.False; - | _ => - return Bool.True ; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Filter.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Filter.sol new file mode 100644 index 00000000..aa8037ca --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Filter.sol @@ -0,0 +1,67 @@ +enum List { Nil, Cons(a, List) } +enum Bool { False, True } + +function and(x: Bool, y: Bool) returns (Bool) { + match (x, y) { +case (Bool.False, _) { +return Bool.False; +} +case (Bool.True, z) { +return z; +} +} +} + +trait Eq { + function eq(x: a, y: a) returns (Bool) ; +} + +impl Eq { + function eq(x: Word, y: Word) returns (Bool) { + match (primEqWord(x,y)) { +case 0 { +return Bool.False ; +} +default { +return Bool.True ; +} +} + } +} + + +function filter(f: function(Word) returns (Bool), xs: List) returns (List) { + match (xs) { +case List.Nil { +return List.Nil ; +} +case List.Cons(y,ys) { +match (f(y)) { +case Bool.False { +return filter(f,ys); +} +case Bool.True { +return List.Cons(y,filter(f,ys)); +} +} +} +} +} + +function list1() returns (List) { + return List.Cons(1, List.Cons(2, List.Cons(3, List.Nil))); +} + +function foo0(y: Word) returns (List) { + return filter((lam (x){ return eq(x,y); }), list1()); +} + +function foo1() returns (List) { + return filter((lam (x){ return eq(x,1); }), list1()); +} + +function foo2(p: function(Word) returns (Bool), q: function(Word) returns (Bool)) returns (List) { + return filter(lam (x) { return and(p(x), q(x)) ; } + , list1()); +} + diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Filter.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Filter.solc deleted file mode 100644 index fd0d0d59..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Filter.solc +++ /dev/null @@ -1,52 +0,0 @@ -data List(a) = Nil | Cons(a,List(a)); -data Bool = False | True; - -function and(x : Bool, y : Bool) -> Bool { - match x, y { - | Bool.False, _ => return Bool.False; - | Bool.True, z => return z; - } -} - -class a : Eq { - function eq (x : a, y : a) -> Bool ; -} - -instance Word : Eq { - function eq (x : Word, y : Word) -> Bool { - match primEqWord(x,y) { - | 0 => return Bool.False ; - | _ => return Bool.True ; - } - } -} - - -function filter (f : (Word) -> Bool, xs : List(Word)) -> List(Word) { - match xs { - | List.Nil => return List.Nil ; - | List.Cons(y,ys) => - match f(y) { - | Bool.False => return filter(f,ys); - | Bool.True => return List.Cons(y,filter(f,ys)); - } - } -} - -function list1 () -> List(Word) { - return List.Cons(1, List.Cons(2, List.Cons(3, List.Nil))); -} - -function foo0(y : Word) -> List(Word) { - return filter((lam (x){ return eq(x,y); }), list1()); -} - -function foo1() -> List(Word) { - return filter((lam (x){ return eq(x,1); }), list1()); -} - -function foo2(p : (Word) -> Bool, q : (Word) -> Bool) -> List(Word) { - return filter(lam (x) { return and(p(x), q(x)) ; } - , list1()); -} - diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/GetSet.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/GetSet.sol new file mode 100644 index 00000000..9ffbc80f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/GetSet.sol @@ -0,0 +1,11 @@ +contract GetSet { + value : Word ; + + function setValue(x: Word) public { + value = x ; + } + + function getValue() public returns (Word) { + return value ; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/GetSet.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/GetSet.solc deleted file mode 100644 index b8da1585..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/GetSet.solc +++ /dev/null @@ -1,11 +0,0 @@ -contract GetSet { - value : Word ; - - public function setValue (x) { - value = x ; - } - - public function getValue () { - return value ; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/GoodInstance.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/GoodInstance.sol new file mode 100644 index 00000000..6e2a03b7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/GoodInstance.sol @@ -0,0 +1,41 @@ +trait Enum { + function fromEnum(x: a) returns (Word) ; +} + + enum Color { R, G, B } + +impl Enum { + function fromEnum(c: Color) returns (Word) { + match (c) { +case Color.R { +return 1; +} +case Color.G { +return 2; +} +case Color.B { +return 3; +} +} + } +} + + +enum Bool { False, True } + +impl Enum { + function fromEnum(b: Bool) returns (Word) { + match (b) { +case Bool.False { +return 0; +} +case Bool.True { +return 1; +} +} + } +} + +contract GoodInstance { + function main() public returns (Word) { return fromEnum(Bool.True);} +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/GoodInstance.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/GoodInstance.solc deleted file mode 100644 index 14cf8a79..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/GoodInstance.solc +++ /dev/null @@ -1,31 +0,0 @@ -class a:Enum { - function fromEnum(x:a) -> Word; -} - - data Color = R | G | B; - -instance Color : Enum { - function fromEnum(c : Color) -> Word { - match c { - | Color.R => return 1; - | Color.G => return 2; - | Color.B => return 3; - } - } -} - - -data Bool = False | True; - -instance Bool : Enum { - function fromEnum(b : Bool) -> Word { - match b { - | Bool.False => return 0; - | Bool.True => return 1; - } - } -} - -contract GoodInstance { - public function main() -> Word { return fromEnum(Bool.True);} -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/IncompleteInstDef.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/IncompleteInstDef.sol new file mode 100644 index 00000000..8db8de8c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/IncompleteInstDef.sol @@ -0,0 +1,14 @@ +trait Foo { + function foo(x: a, y: b) returns (b) ; + function faa(y: a) returns (a) ; +} + +enum Bool { False, True } + +enum Maybe { Nothing, Just(a) } +// missing the definition of Foo.foo +impl Foo { + function faa(y: Bool) returns (Bool) { + return y ; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/IncompleteInstDef.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/IncompleteInstDef.solc deleted file mode 100644 index 4d86d53e..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/IncompleteInstDef.solc +++ /dev/null @@ -1,14 +0,0 @@ -forall a b . class a : Foo(b) { - function foo (x : a, y : b) -> b ; - function faa (y : a) -> a ; -} - -data Bool = False | True; - -data Maybe(a) = Nothing | Just(a); -// missing the definition of Foo.foo -instance Bool : Foo(Bool) { - function faa(y : Bool) -> Bool { - return y ; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Invokable.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Invokable.sol new file mode 100644 index 00000000..78e60dc7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Invokable.sol @@ -0,0 +1,16 @@ + +trait invokable { + function invoke(s: self, a: args) returns (ret) ; + } + + function id(x: a) returns (a) { + return x ; + } + + enum IdToken { IdToken } + +impl invokable, a, a> { + function invoke(token: IdToken, a: a) returns (a) { + return id(a); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Invokable.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Invokable.solc deleted file mode 100644 index 35e52735..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Invokable.solc +++ /dev/null @@ -1,16 +0,0 @@ - -class self : invokable(args, ret) { - function invoke (s:self, a:args) -> ret; - } - - forall a . function id(x : a) -> a { - return x ; - } - - data IdToken(a) = IdToken; - -instance IdToken(a) : invokable(a,a) { - function invoke(token: IdToken(a), a) -> a { - return id(a); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/KindTest.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/KindTest.sol new file mode 100644 index 00000000..69772cf5 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/KindTest.sol @@ -0,0 +1,5 @@ +enum M { M } +function foo(x: M) {} + +enum P { P } +function foo2(x: P) {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/KindTest.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/KindTest.solc deleted file mode 100644 index 9a4399f5..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/KindTest.solc +++ /dev/null @@ -1,5 +0,0 @@ -data M = M; -function foo(x: M(Word)) {} - -data P(a) = P; -function foo2(x: P) {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/PairMatch1.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/PairMatch1.sol new file mode 100644 index 00000000..155603d8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/PairMatch1.sol @@ -0,0 +1,6 @@ +enum Pair { Pair(a, b) } + +function foo(p: a) returns (word) { + let x: word = p; + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/PairMatch1.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/PairMatch1.solc deleted file mode 100644 index 3f2a4636..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/PairMatch1.solc +++ /dev/null @@ -1,6 +0,0 @@ -data Pair(a, b) = Pair(a, b); - -forall a . function foo(p: a) -> word { - let x: word = p; - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/PairMatch2.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/PairMatch2.sol new file mode 100644 index 00000000..7ddf8097 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/PairMatch2.sol @@ -0,0 +1,10 @@ + +function snd(p: (a, word)) returns (a) { + match (p) { +case (_, w) { +return w; +} +} +} + + diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/PairMatch2.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/PairMatch2.solc deleted file mode 100644 index 98395bb4..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/PairMatch2.solc +++ /dev/null @@ -1,8 +0,0 @@ - -forall a . function snd(p: (a, word)) -> a { - match p { - | (_, w) => return w; - } -} - - diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Ref.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Ref.sol new file mode 100644 index 00000000..0ca14d36 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Ref.sol @@ -0,0 +1,17 @@ +trait Ref { + function load(r: ref) returns (deref) ; + function store(r: ref, d: deref) returns (unit) ; +} + +enum Memory { new(a) } + +impl Ref, a> { + function load (r: Memory) returns (a) { + match (r) { +case Memory.new(x) { +return x; +} +} + } +} + diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Ref.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Ref.solc deleted file mode 100644 index afce6aa5..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/Ref.solc +++ /dev/null @@ -1,16 +0,0 @@ -class ref : Ref(deref) { - function load (r : ref) -> deref; - function store (r : ref, d : deref) -> unit; -} - -data Memory(a) = new(a); - -instance Memory(a) : Ref(a) { - function load (r) { - match r { - | Memory.new(x) => return x; - } - } -} - - diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/SillyReturn.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/SillyReturn.sol new file mode 100644 index 00000000..36fe79f3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/SillyReturn.sol @@ -0,0 +1,13 @@ +enum Nat { Zero, Succ(Nat) } +enum Bool { True, False } + +function even(n: word) returns (Bool) { + match (n) { +case Nat.Zero { +return 1; return Bool.True; +} +case Nat.Succ(m) { +return 0; return Bool.False; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/SillyReturn.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/SillyReturn.solc deleted file mode 100644 index 25dddd32..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/SillyReturn.solc +++ /dev/null @@ -1,9 +0,0 @@ -data Nat = Zero | Succ(Nat); -data Bool = True | False; - -function even (n) -> Bool { - match n { - | Nat.Zero => return 1; return Bool.True; - | Nat.Succ(m) => return 0; return Bool.False; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/SimpleInvoke.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/SimpleInvoke.sol new file mode 100644 index 00000000..55c4f74d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/SimpleInvoke.sol @@ -0,0 +1,18 @@ +function lambdaimpl1 (x: a) returns (a) { + return x; +} +enum LambdaTy0 { LambdaTy0 } +trait invokable { + function invoke(self: self, args: args) returns (ret) ; +} +impl invokable, a, a> { + function invoke(self: LambdaTy0, args: a) returns (a) { + return lambdaimpl1(args); + } +} +contract SimpleLambda { + function f() public returns (word) { + let n = LambdaTy0 ; + return invokable.invoke(n, 0); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/SimpleInvoke.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/SimpleInvoke.solc deleted file mode 100644 index 09f5ce97..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/SimpleInvoke.solc +++ /dev/null @@ -1,18 +0,0 @@ -function lambdaimpl1 (x) { - return x; -} -data LambdaTy0(a) = LambdaTy0; -class self : invokable (args, ret) { - function invoke (self : self, args : args) -> ret; -} -instance LambdaTy0(a) : invokable (a, a) { - forall a . function invoke (self : LambdaTy0(a), args : a) -> a { - return lambdaimpl1(args); - } -} -contract SimpleLambda { - public function f () { - let n = LambdaTy0 ; - return invokable.invoke(n, 0); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/StructMembers.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/StructMembers.snap index 56ead403..e49bccf3 100644 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/StructMembers.snap +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/StructMembers.snap @@ -1,15 +1,36 @@ --- source: crates/parser/tests/diagnostics.rs expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/StructMembers.solc ---- -error[SC0001]: parse error: unexpected `data` - --> /StructMembers.solc:7:1 - | -6 | data Uint256 = Uint256(Word) -7 | data Bool = True | False - | ^^^^ unexpected token -8 | data Bytes32 = Bytes32(Word) - | - = note: expecting `;`, or `|` - = note: while parsing data declaration +input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/StructMembers.sol +--- +error[SC0001]: parse error: unexpected `;` + --> /StructMembers.sol:77:40 + | +76 | let szb = memorySize(pb); +77 | assembly { sz := add(sz, szb) }; // TODO: bounds check? + | ^ unexpected token +78 | return sz; + | + = note: expecting end of input, or statement +--- + +error[SC0001]: parse error: unexpected `;` + --> /StructMembers.sol:91:37 + | +90 | let v; +91 | assembly { v := mload(off) }; + | ^ unexpected token +92 | return Uint256(v); + | + = note: expecting end of input, or statement +--- + +error[SC0001]: parse error: unexpected `;` + --> /StructMembers.sol:121:45 + | +120 | +121 | assembly { ptr := add(ptr, offset) }; + | ^ unexpected token +122 | + | + = note: expecting end of input, or statement diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/StructMembers.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/StructMembers.sol new file mode 100644 index 00000000..818e4941 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/StructMembers.sol @@ -0,0 +1,141 @@ +/// Other used stdlib classes and types: +trait Ref { + function load(x: self) returns (deref) ; +} + +enum Uint256 { Uint256(Word) } +enum Bool { True, False } +enum Bytes32 { Bytes32(Word) } +enum Unit { Unit } + +enum Proxy { Proxy } +enum Memory { Memory(Word) } + +/// Specific new stdlib classes and types: + +trait StructMember {} +enum StructMember { StructMember } + +// "dead" is only here to compensate for non-relaxed coverage condition and +// incorrectly implemented Paterson condition +enum MemberAccess { MemberAccess(ty) } + + +/// Usage Example / Proof of Concept: + +/* + struct S { + x:Uint256; + y:Bool; + z:Bytes32; + } +*/ + +enum S { S(Pair>) } + +enum Field_x { FieldX } // Selector type for "x" +enum Field_y { FieldY } // Selector type for "y" +enum Field_z { FieldZ } // Selector type for "z" + +// StructMember instances for field selectors: +impl StructMember, Unit, Uint256> {} +impl StructMember, Uint256, Bool> {} +impl StructMember, Pair, Bytes32> {} + +/* Further compiler-internal builtin instances for use on stack (at least the stackref versions cannot be expressed in-language, + * but none of these rely on any layout other than the compiler-builtin stack layout, so we can handle these purely internally + * as "compiler magic"): + */ +/* + instance MemberAccess(S, Field_x):Ref(Uint256); + instance MemberAccess(stackref(S), Field_x):Ref(stackref(Uint256)); + instance MemberAccess(S, Field_y):Ref(Bool); + instance MemberAccess(stackref(S), Field_y):Ref(stackref(Bool)); + instance MemberAccess(S, Field_z):Ref(Bytes32); + instance MemberAccess(stackref(S), Field_z):Ref(stackref(Bytes32)); +*/ + + +/// Size of a type in memory +trait MemorySize { + function memorySize(x: Proxy) returns (Word) ; +} + +/// Size of the struct member types in memory: +impl MemorySize { function memorySize(x: Proxy) returns (Word) { return 0; } } +impl MemorySize { function memorySize(x: Proxy) returns (Word) { return 32; } } +impl MemorySize { function memorySize(x: Proxy) returns (Word) { return 32; } } +impl MemorySize { function memorySize(x: Proxy) returns (Word) { return 32; } } + +/// Memory size of pairs +impl MemorySize> { + function memorySize(x: Proxy<(a, b)>) returns (Word) { + let pa:Proxy; + let pb:Proxy; + let sz = memorySize(pa); + let szb = memorySize(pb); + assembly { sz := add(sz, szb) }; // TODO: bounds check? + return sz; + } + +} + +/// Fragments of a generic memory implementation: +trait MemoryType { + function loadFromMemory(p: Proxy, off: Word) returns (self) ; +} + +impl MemoryType { + function loadFromMemory(p: Proxy, off: Word) returns (Uint256) { + let v; + assembly { v := mload(off) }; + return Uint256(v); + } +} + +impl Ref, a> { + function load(x: Memory) returns (a) { + let p:Proxy; + match (x) { +case Memory(off) { +return loadFromMemory(p, off); +} +} + } +} + +/// Crucial instance: member access to struct fields in memory: + +impl Ref, fieldType, Memory>, ty> { + function load(x: MemberAccess, fieldType, Memory>) returns (ty) { + let ptr:Word; + match (x) { +case MemberAccess(Memory(y)) { +ptr = y; +} +} + + let p:Proxy; + let offset = memorySize(p); + + assembly { ptr := add(ptr, offset) }; + + let tyPtr:Memory = Memory(ptr); + return load(tyPtr); + } +} + +function test() { + let x:Memory; + let memberAccess:MemberAccess, Field_x, Memory>; + memberAccess = MemberAccess(x); + let result = load(memberAccess); + /* + Eventually, I imagine ``let result = x.x;`` to merely desugar to + + let result = load(MemberAccess(x):MemberAccess(_, Field_x)); + + which is equivalent to the above. + */ +} + diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/StructMembers.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/StructMembers.solc deleted file mode 100644 index 89508d44..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/StructMembers.solc +++ /dev/null @@ -1,146 +0,0 @@ -/// Other used stdlib classes and types: -class self:Ref(deref) { - function load(x:self) -> deref; -} - -data Uint256 = Uint256(Word) -data Bool = True | False -data Bytes32 = Bytes32(Word) -data Unit = Unit - -data Proxy(t) = Proxy -data Memory(x) = Memory(Word) - -/// Specific new stdlib classes and types: - -class self:StructMember(preceding, memberTy) {} -data StructMember(structType, fieldType) = StructMember - -// "dead" is only here to compensate for non-relaxed coverage condition and -// incorrectly implemented Paterson condition -data MemberAccess(ty, field, dead) = MemberAccess(ty) - - -/// Usage Example / Proof of Concept: - -/* - struct S { - x:Uint256; - y:Bool; - z:Bytes32; - } -*/ - -data S = S(Pair(Uint256, Pair(Bool, Bytes32))) - -data Field_x = FieldX // Selector type for "x" -data Field_y = FieldY // Selector type for "y" -data Field_z = FieldZ // Selector type for "z" - -// StructMember instances for field selectors: -instance StructMember(S, Field_x):StructMember(Unit, Uint256) {} -instance StructMember(S, Field_y):StructMember(Uint256, Bool) {} -instance StructMember(S, Field_z):StructMember(Pair(Uint256, Bool), Bytes32) {} - -/* Further compiler-internal builtin instances for use on stack (at least the stackref versions cannot be expressed in-language, - * but none of these rely on any layout other than the compiler-builtin stack layout, so we can handle these purely internally - * as "compiler magic"): - */ -/* - instance MemberAccess(S, Field_x):Ref(Uint256); - instance MemberAccess(stackref(S), Field_x):Ref(stackref(Uint256)); - instance MemberAccess(S, Field_y):Ref(Bool); - instance MemberAccess(stackref(S), Field_y):Ref(stackref(Bool)); - instance MemberAccess(S, Field_z):Ref(Bytes32); - instance MemberAccess(stackref(S), Field_z):Ref(stackref(Bytes32)); -*/ - - -/// Size of a type in memory -class self:MemorySize { - function memorySize(x:Proxy(self)) -> Word; -} - -/// Size of the struct member types in memory: -instance Unit:MemorySize { function memorySize(x : Proxy(Unit)) -> Word { return 0; } } -instance Uint256:MemorySize { function memorySize(x : Proxy(Uint256)) -> Word { return 32; } } -instance Bool:MemorySize { function memorySize(x : Proxy(Bool)) -> Word { return 32; } } -instance Bytes32:MemorySize { function memorySize(x : Proxy(Bytes32)) -> Word { return 32; } } - -/// Memory size of pairs -instance Pair(a,b):MemorySize { - function memorySize(x : Proxy((a,b))) -> Word - { - let pa:Proxy(a); - let pb:Proxy(b); - let sz = memorySize(pa); - let szb = memorySize(pb); - assembly { sz := add(sz, szb) }; // TODO: bounds check? - return sz; - } - -} - -/// Fragments of a generic memory implementation: -class self:MemoryType { - function loadFromMemory(p:Proxy(self), off:Word) -> self; -} - -instance Uint256:MemoryType { - function loadFromMemory(p:Proxy(Uint256), off:Word) -> Uint256 { - let v; - assembly { v := mload(off) }; - return Uint256(v); - } -} - -instance (a:MemoryType) => Memory(a):Ref(a) { - function load(x : Memory(a)) -> a { - let p:Proxy(a); - match x { | Memory(off) => return loadFromMemory(p, off); }; - } -} - -/// Crucial instance: member access to struct fields in memory: - -instance ( - StructMember(structType, fieldType):StructMember(precedingTuple, ty), - precedingTuple:MemorySize, - Memory(ty):Ref(ty) -) => MemberAccess(Memory(structType), fieldType, - // Needs ridiculous amounts of constructor applications due to incorrect implementation of the Paterson Condition - // Needs to mention "ty" due to non-relaxed Coverage Condition - Memory(ty) -):Ref(ty) -{ - function load(x : MemberAccess(Memory(structType), fieldType, Memory(ty))) -> ty { - let ptr:Word; - match x { | MemberAccess(Memory(y)) => ptr = y; }; - - let p:Proxy(precedingTuple); - let offset = memorySize(p); - - assembly { ptr := add(ptr, offset) }; - - let tyPtr:Memory(ty) = Memory(ptr); - return load(tyPtr); - } -} - -function test() -> () -{ - let x:Memory(S); - let memberAccess:MemberAccess(Memory(S), Field_x, - Memory(Uint256) // will become unnecessary - ); - memberAccess = MemberAccess(x); - let result = load(memberAccess); - /* - Eventually, I imagine ``let result = x.x;`` to merely desugar to - - let result = load(MemberAccess(x):MemberAccess(_, Field_x)); - - which is equivalent to the above. - */ -} - diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/add-moritz.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/add-moritz.sol new file mode 100644 index 00000000..455ac54f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/add-moritz.sol @@ -0,0 +1,91 @@ +function add(x : word, y : word) returns (word) { + let res: word; + assembly { + res := add(x, y) + } + return res; +} + +trait Typedef { + function rep(x: self) returns (underlyingType) ; + function abs(x: underlyingType) returns (self) ; +} + +trait Add { + function add(x: a, y: a) returns (a) ; +} + +enum B { F, T } + + +impl Typedef { + function rep(x: B) returns (word) { + match (x) { +case B.F { +return 0; +} +case B.T { +return 1; +} +} + } + + function abs(x: word) returns (B) { + match (x) { +case 0 { +return B.F; +} +case 1 { +return B.T; +} +} + } +} + +impl Add { + function add(x: B, y: B) returns (B) { + match (x) { +case B.F { +match (y) { +case B.F { +return B.F; +} +case B.T { +return B.T; +} +} +} +case B.T { +match (y) { +case B.F { +return B.T; +} +case B.T { +return B.F; +} +} +} +} + } +} + +function fun(a: (B, B), b: (B, B)) returns (B, B) { // -> c + match (a, b) { +case ((a1, a2), (b1, b2)) { +return (Add.add(a1, b1), fun(a2, b2)); +} +} + +} + +contract Compose { + + function main() public returns (word) { + let res = fun ((B.T, B.T, B.F), (B.F, B.F, B.T)); + match (res) { +case (r1, r2, r3) { +return Typedef.rep(r1); +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/add-moritz.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/add-moritz.solc deleted file mode 100644 index d3654ec8..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/add-moritz.solc +++ /dev/null @@ -1,70 +0,0 @@ -function add(x : word, y : word) { - let res: word; - assembly { - res := add(x, y) - } - return res; -} - -class self:Typedef(underlyingType) { - function rep(x:self) -> underlyingType; - function abs(x:underlyingType) -> self; -} - -forall a.class a : Add { - function add(x:a, y:a) -> a; -} - -data B = F | T; - - -instance B : Typedef(word) { - function rep(x : B) -> word { - match x { - | B.F => return 0; - | B.T => return 1; - } - } - - function abs(x : word) -> B { - match x { - | 0 => return B.F; - | 1 => return B.T; - } - } -} - -instance B : Add { - function add(x : B, y : B) -> B { - match x { - | B.F => - match y { - | B.F => return B.F; - | B.T => return B.T; - } - - | B.T => - match y { - | B.F => return B.T; - | B.T => return B.F; - } - } - } -} - -function fun(a : (B, B), b : (B, B)) -> (B, B) { // -> c - match a, b { - | (a1, a2), (b1, b2) => return (Add.add(a1, b1), fun(a2, b2)); - } - -} - -contract Compose { - - public function main() -> word { - let res = fun ((B.T, B.T, B.F), (B.F, B.F, B.T)); - match res { - | (r1, r2, r3) => return Typedef.rep(r1); - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/array-elem-no-storagecopy.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/array-elem-no-storagecopy.sol new file mode 100644 index 00000000..d64d5421 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/array-elem-no-storagecopy.sol @@ -0,0 +1,19 @@ +// An element type with no `StorageCopy` instance cannot be the element of a +// storage array: `CanStore` for `storage(array(t))` -- which every field access +// goes through -- requires `t:StorageCopy`. Rejecting this at compile time is +// what keeps `a = b` from silently shallow-copying a type it cannot copy. +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +enum Odd { Odd(word) } + +contract NoCopy { + reserved : word; + xs : array; + + function main() returns (uint256) { + return Length.length(xs); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/array-elem-no-storagecopy.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/array-elem-no-storagecopy.solc deleted file mode 100644 index 11c7a15e..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/array-elem-no-storagecopy.solc +++ /dev/null @@ -1,19 +0,0 @@ -// An element type with no `StorageCopy` instance cannot be the element of a -// storage array: `CanStore` for `storage(array(t))` -- which every field access -// goes through -- requires `t:StorageCopy`. Rejecting this at compile time is -// what keeps `a = b` from silently shallow-copying a type it cannot copy. -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -data Odd = Odd(word); - -contract NoCopy { - reserved : word; - xs : array(Odd); - - function main() -> uint256 { - return Length.length(xs); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/array-push-no-canstore.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/array-push-no-canstore.sol new file mode 100644 index 00000000..fce7f1fb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/array-push-no-canstore.sol @@ -0,0 +1,20 @@ +// `push` stores through the element's storage reference, so the value must be +// something `storage(t)` can store. A type with no `CanStore` instance is +// rejected -- this is the constraint `storage(t):CanStore(v)` on ArrayPush, +// distinct from the `t:StorageCopy` one that whole-array assignment needs. +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +enum Odd { Odd(word) } + +contract PushNoStore { + reserved : word; + + function main() returns (uint256) { + let arr : storage> = storage(0x100); + ArrayPush.push(arr, Odd(1)); + return uint256(0); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/array-push-no-canstore.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/array-push-no-canstore.solc deleted file mode 100644 index 2d89a295..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/array-push-no-canstore.solc +++ /dev/null @@ -1,20 +0,0 @@ -// `push` stores through the element's storage reference, so the value must be -// something `storage(t)` can store. A type with no `CanStore` instance is -// rejected -- this is the constraint `storage(t):CanStore(v)` on ArrayPush, -// distinct from the `t:StorageCopy` one that whole-array assignment needs. -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -data Odd = Odd(word); - -contract PushNoStore { - reserved : word; - - function main() -> uint256 { - let arr : storage(array(Odd)) = storage(0x100); - ArrayPush.push(arr, Odd(1)); - return uint256(0); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/arraylit-bad-target.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/arraylit-bad-target.sol new file mode 100644 index 00000000..92d583a8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/arraylit-bad-target.sol @@ -0,0 +1,15 @@ +// An array literal may only be assigned to a storage *array* field: storeArrayLit +// does not unify with a plain word field. +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract ArrayLitBadTarget { + n : uint256; + + function main() returns (uint256) { + n = [1, 2, 3]; + return n; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/arraylit-bad-target.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/arraylit-bad-target.solc deleted file mode 100644 index 7af42326..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/arraylit-bad-target.solc +++ /dev/null @@ -1,15 +0,0 @@ -// An array literal may only be assigned to a storage *array* field: storeArrayLit -// does not unify with a plain word field. -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract ArrayLitBadTarget { - n : uint256; - - function main() -> uint256 { - n = [1, 2, 3]; - return n; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/arraylit-mixed-types.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/arraylit-mixed-types.sol new file mode 100644 index 00000000..efd851b1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/arraylit-mixed-types.sol @@ -0,0 +1,14 @@ +// All elements of an array literal must share one type: unifying uint256 with +// address must fail. +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract ArrayLitMixed { + function main() returns (uint256) { + let a : address = Typedef.abs(0x1234); + let m : memory> = [uint256(1), a]; + return m[uint256(0)]; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/arraylit-mixed-types.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/arraylit-mixed-types.solc deleted file mode 100644 index effe3a14..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/arraylit-mixed-types.solc +++ /dev/null @@ -1,14 +0,0 @@ -// All elements of an array literal must share one type: unifying uint256 with -// address must fail. -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract ArrayLitMixed { - function main() -> uint256 { - let a : address = Typedef.abs(0x1234); - let m : memory(DynArray(uint256)) = [uint256(1), a]; - return m[uint256(0)]; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-assign-no-return.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-assign-no-return.sol new file mode 100644 index 00000000..6ac3d2b7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-assign-no-return.sol @@ -0,0 +1,10 @@ +// mstore does not return a value, so it cannot be assigned. +contract Test { + function main() public { + let x : word; + assembly { + x := mstore(1, 1) + } + return x; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-assign-no-return.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-assign-no-return.solc deleted file mode 100644 index 2037d58a..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-assign-no-return.solc +++ /dev/null @@ -1,10 +0,0 @@ -// mstore does not return a value, so it cannot be assigned. -contract Test { - public function main() { - let x : word; - assembly { - x := mstore(1, 1) - } - return x; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-assign-non-word.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-assign-non-word.sol new file mode 100644 index 00000000..d77b3f7f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-assign-non-word.sol @@ -0,0 +1,11 @@ +// An assembly assignment writes a raw scalar word, so its LHS must have type +// 'word'. Assigning to a non-word local (here a 'bool', whose runtime layout +// is a tagged inl/inr pair) would corrupt that layout, so the type checker +// must reject this program. +contract AsmBool { + function main() public returns (word) { + let b : bool = false; + assembly { b := add(1, 1) } + if ( b ) { return 1; } else { return 0; } + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-assign-non-word.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-assign-non-word.solc deleted file mode 100644 index be96a1bb..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-assign-non-word.solc +++ /dev/null @@ -1,11 +0,0 @@ -// An assembly assignment writes a raw scalar word, so its LHS must have type -// 'word'. Assigning to a non-word local (here a 'bool', whose runtime layout -// is a tagged inl/inr pair) would corrupt that layout, so the type checker -// must reject this program. -contract AsmBool { - public function main() -> word { - let b : bool = false; - assembly { b := add(1, 1) } - if b { return 1; } else { return 0; } - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-let-no-return.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-let-no-return.sol new file mode 100644 index 00000000..61b625cc --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-let-no-return.sol @@ -0,0 +1,8 @@ +// mstore does not return a value, so it cannot initialize a `let`. +contract Test { + function main() public { + assembly { + let x := mstore(1, 1) + } + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-let-no-return.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-let-no-return.solc deleted file mode 100644 index 9a7b997f..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/asm-let-no-return.solc +++ /dev/null @@ -1,8 +0,0 @@ -// mstore does not return a value, so it cannot initialize a `let`. -contract Test { - public function main() { - assembly { - let x := mstore(1, 1) - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bound-minimal.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bound-minimal.sol new file mode 100644 index 00000000..9f201dee --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bound-minimal.sol @@ -0,0 +1,12 @@ +// Minimal test for bound variable condition +// This SHOULD FAIL - variable 'bad' in context but not in instance head + + +trait TestBound {} +trait TestHelper {} + +enum TestType { TestType } + +// Variable 'bad' appears in context but not in instance head +// Should fail bound variable check +impl TestBound> where bad: TestHelper {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bound-minimal.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bound-minimal.solc deleted file mode 100644 index 748b5c80..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bound-minimal.solc +++ /dev/null @@ -1,12 +0,0 @@ -// Minimal test for bound variable condition -// This SHOULD FAIL - variable 'bad' in context but not in instance head - - -forall a . class a:TestBound {} -forall a b . class a:TestHelper(b) {} - -data TestType(x) = TestType; - -// Variable 'bad' appears in context but not in instance head -// Should fail bound variable check -forall x . bad:TestHelper(x) => instance TestType(x):TestBound {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bound-only-test.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bound-only-test.sol new file mode 100644 index 00000000..94cd1505 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bound-only-test.sol @@ -0,0 +1,10 @@ +// Test only bound variable check, disable Patterson + +trait TestBound {} +trait TestHelper {} + +enum TestType { TestType } + +// Variable 'bad' appears in context but not in instance head +// Should fail bound variable check +impl TestBound> where bad: TestHelper {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bound-only-test.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bound-only-test.solc deleted file mode 100644 index 96695759..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bound-only-test.solc +++ /dev/null @@ -1,10 +0,0 @@ -// Test only bound variable check, disable Patterson - -forall a . class a:TestBound {} -forall a b . class a:TestHelper(b) {} - -data TestType(x) = TestType; - -// Variable 'bad' appears in context but not in instance head -// Should fail bound variable check -forall x . bad:TestHelper(x) => instance TestType(x):TestBound {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bug-spec-generic-let.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bug-spec-generic-let.sol new file mode 100644 index 00000000..dd37461d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bug-spec-generic-let.sol @@ -0,0 +1,62 @@ +// Bug: specStmt (Let i mty (Just e)) always called `atCurrentSubst i` AFTER +// `specExp`, causing `extSpSubst phi` (with original type-variable names) to +// corrupt subsequent let bindings. Concretely, `b_decoded : (uint256,uint256)` +// was mangled to `uint256` inside the ABIDecode instance for pairs. +// +// Root cause: when `ty'` is already concrete (freetv ty' == []), re-applying +// `atCurrentSubst` after `specExp` risks picking up unrelated bindings added +// by nested `specCall` invocations (e.g. {b -> uint256} from an inner decode). +// +// Fix: only re-apply when `freetv ty'` is non-empty (open type that needs +// resolution by the RHS, as in `let r : rep = Generic.from(x)`). +// +// Expected: compiles successfully. +// Actual (before fix): PANIC: Type mismatch expected uint256 actual (uint256,uint256) + +import * from std; +import * from std.dispatch; +import * from std.Generic; +pragma no-patterson-condition; +pragma no-coverage-condition; +pragma no-bounded-variable-condition; + +enum Pair { MkPair(uint256, uint256) } + +impl Generic { + function from(x: Pair) returns (uint256, uint256) { + match (x) { +case Pair.MkPair(a, b) { +return (a, b); +} +} + } + function to(x: (uint256, uint256)) returns (Pair) { + match (x) { +case (a, b) { +return Pair.MkPair(a, b); +} +} + } +} + +contract BugSpecGenericLet { + constructor() {} + + function roundtrip(a: uint256, b: uint256) returns (uint256) { + let p : Pair = Pair.MkPair(a, b); + let encoded : memory = abi_encode(p); + let decoded : (uint256, uint256) = abi_decode(encoded, @(uint256, uint256), @MemoryWordReader); + match (decoded) { +case (x, y) { +match (and(Eq.eq(x, a), Eq.eq(y, b))) { +case true { +return uint256(1); +} +case false { +return uint256(0); +} +} +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bug-spec-generic-let.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bug-spec-generic-let.solc deleted file mode 100644 index 9288943a..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/bug-spec-generic-let.solc +++ /dev/null @@ -1,49 +0,0 @@ -// Bug: specStmt (Let i mty (Just e)) always called `atCurrentSubst i` AFTER -// `specExp`, causing `extSpSubst phi` (with original type-variable names) to -// corrupt subsequent let bindings. Concretely, `b_decoded : (uint256,uint256)` -// was mangled to `uint256` inside the ABIDecode instance for pairs. -// -// Root cause: when `ty'` is already concrete (freetv ty' == []), re-applying -// `atCurrentSubst` after `specExp` risks picking up unrelated bindings added -// by nested `specCall` invocations (e.g. {b -> uint256} from an inner decode). -// -// Fix: only re-apply when `freetv ty'` is non-empty (open type that needs -// resolution by the RHS, as in `let r : rep = Generic.from(x)`). -// -// Expected: compiles successfully. -// Actual (before fix): PANIC: Type mismatch expected uint256 actual (uint256,uint256) - -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -pragma no-patterson-condition; -pragma no-coverage-condition; -pragma no-bounded-variable-condition; - -data Pair = MkPair(uint256, uint256); - -instance Pair : Generic((uint256, uint256)) { - function from(x : Pair) -> (uint256, uint256) { - match x { | Pair.MkPair(a, b) => return (a, b); } - } - function to(x : (uint256, uint256)) -> Pair { - match x { | (a, b) => return Pair.MkPair(a, b); } - } -} - -contract BugSpecGenericLet { - constructor() {} - - function roundtrip(a : uint256, b : uint256) -> uint256 { - let p : Pair = Pair.MkPair(a, b); - let encoded : memory(bytes) = abi_encode(p); - let decoded : (uint256, uint256) = abi_decode(encoded, @(uint256, uint256), @MemoryWordReader); - match decoded { - | (x, y) => - match and(Eq.eq(x, a), Eq.eq(y, b)) { - | true => return uint256(1); - | false => return uint256(0); - } - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/catenable-err.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/catenable-err.snap index cc4d06ec..2dd3e13d 100644 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/catenable-err.snap +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/catenable-err.snap @@ -1,15 +1,15 @@ --- source: crates/parser/tests/diagnostics.rs expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/catenable-err.solc +input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/catenable-err.sol --- -error[SC0001]: parse error: unexpected `}` - --> /catenable-err.solc:3:1 +error[SC0001]: parse error: unexpected `->` + --> /catenable-err.sol:2:21 | -1 | forall t.class t:Catenable { +1 | trait Catenable { 2 | function cat(x:t) -> memory(bytes) + | ^^ unexpected token 3 | } - | ^ unexpected token | - = note: expecting `->`, or `;` - = note: while parsing type + = note: expecting `;`, `payable`, or `public` + = note: while parsing function signature diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/catenable-err.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/catenable-err.sol new file mode 100644 index 00000000..0507fe38 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/catenable-err.sol @@ -0,0 +1,3 @@ +trait Catenable { + function cat(x:t) -> memory(bytes) +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/catenable-err.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/catenable-err.solc deleted file mode 100644 index 5bbf71e2..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/catenable-err.solc +++ /dev/null @@ -1,3 +0,0 @@ -forall t.class t:Catenable { - function cat(x:t) -> memory(bytes) -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/class-return-type-miss.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/class-return-type-miss.sol new file mode 100644 index 00000000..e52a1d4d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/class-return-type-miss.sol @@ -0,0 +1,9 @@ +enum bytes32 { bytes32(word) } + +trait Memory { + function encodeInto(v: t, target: word); +} + +impl Memory { + function encodeInto(v: bytes32, target: word) {} +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/class-return-type-miss.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/class-return-type-miss.solc deleted file mode 100644 index 01856331..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/class-return-type-miss.solc +++ /dev/null @@ -1,9 +0,0 @@ -data bytes32 = bytes32(word); - -forall t . class t:Memory { - function encodeInto(v: t, target: word); -} - -instance bytes32:Memory { - function encodeInto(v: bytes32, target: word) {} -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/class-type-name-collision.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/class-type-name-collision.sol new file mode 100644 index 00000000..b11ab376 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/class-type-name-collision.sol @@ -0,0 +1,5 @@ +enum Foo { MkFoo } + +trait Foo { + function foo(x: a) returns (word) ; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/class-type-name-collision.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/class-type-name-collision.solc deleted file mode 100644 index ee30b07b..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/class-type-name-collision.solc +++ /dev/null @@ -1,6 +0,0 @@ -data Foo = MkFoo; - -forall a. -class a:Foo { - function foo(x:a) -> word; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/comp.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/comp.sol new file mode 100644 index 00000000..82842abd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/comp.sol @@ -0,0 +1,3 @@ +function compose(f: function(b) returns (c), g: function(a) returns (b), x: a) { + return f(g(x)); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/comp.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/comp.solc deleted file mode 100644 index a49febd9..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/comp.solc +++ /dev/null @@ -1,3 +0,0 @@ -function compose (f,g,x) { - return f(g(x)); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/complexproxy.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/complexproxy.sol new file mode 100644 index 00000000..cbe2e96c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/complexproxy.sol @@ -0,0 +1,34 @@ +enum Proxy { Proxy } + +function add(x:word, y: word) returns (word) {return x;} + +trait BaseMemoryType { + function memorySize(x: Proxy) returns (word) ; +} + + +impl BaseMemoryType { + function memorySize(x: Proxy) returns (word) { + return 32; + } +} + +impl BaseMemoryType<(a, b)> where a: BaseMemoryType, b: BaseMemoryType { + + function memorySize(x: Proxy<(a, b)>) returns (word) { // not correct semantically, just for debugging + return add(BaseMemoryType.memorySize(@a), + // BaseMemoryType.memorySize(Proxy:Proxy(b)) + morefun(@b) + ); + } +} +// this should trigger a type error. +function morefun(p: Proxy) returns (word) { + return BaseMemoryType.memorySize(@t); +} + +contract TestMemoryType { + function main() public returns (word) { + return BaseMemoryType.memorySize(@(word, word)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/complexproxy.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/complexproxy.solc deleted file mode 100644 index 54ca326e..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/complexproxy.solc +++ /dev/null @@ -1,35 +0,0 @@ -data Proxy(a) = Proxy; - -function add(x:word, y: word) {return x;} - -class self:BaseMemoryType { - function memorySize(x:Proxy(self)) -> word; -} - - -instance word:BaseMemoryType { - function memorySize(x:Proxy(word)) -> word { - return 32; - } -} - -forall a b . a:BaseMemoryType, b:BaseMemoryType => - instance (a,b):BaseMemoryType { - - function memorySize(x) -> word { // not correct semantically, just for debugging - return add(BaseMemoryType.memorySize(Proxy:Proxy(a)), - // BaseMemoryType.memorySize(Proxy:Proxy(b)) - morefun(Proxy:Proxy(b)) - ); - } -} -// this should trigger a type error. -forall t. function morefun(p:Proxy(t)) -> word { - return BaseMemoryType.memorySize(Proxy:Proxy(t)); -} - -contract TestMemoryType { - public function main() -> word { - return BaseMemoryType.memorySize(Proxy:Proxy( (word,word) )); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/compose_desugared.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/compose_desugared.sol new file mode 100644 index 00000000..9513ebde --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/compose_desugared.sol @@ -0,0 +1,43 @@ +function compose(f: d, g: e) returns (t_closure1) where d: invokable, e: invokable { + return t_closure1(f,g); +} + +enum t_closure1 { t_closure1(d, e) } + +function lambda2(c: t_closure1, x: a) returns (c) where d: invokable, e: invokable { + match (c) { +case t_closure1(f, g) { +return invokable.invoke(f, invokable.invoke(g,x)); +} +} + } + +impl invokable, a, c> where d: invokable, e: invokable { + function invoke(self: t_closure1, args: a) returns (c) { + return lambda2(self, args); + } +} + +enum t_id3 { t_id3 } + +function id(x: a) returns (a) { + return x; +} + +impl invokable, a, a> { + function invoke(self: t_id3, args: a) returns (a) { + match (self) { +case t_id3 { +return id(args) ; +} +} + } +} + +contract Foo { + function main() public returns (word) { + let f = compose(t_id3, t_id3); + return invokable.invoke(f, 0); + } +} + diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/compose_desugared.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/compose_desugared.solc deleted file mode 100644 index 303c3b03..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/compose_desugared.solc +++ /dev/null @@ -1,45 +0,0 @@ -forall a b c d e . d : invokable(b,c) - , e : invokable(a,b) - => function compose(f : d, g : e) -> t_closure1(a,b,c,d,e) { - return t_closure1(f,g); -} - -data t_closure1(a,b,c,d,e) = t_closure1(d,e); - -forall a b c d e . d : invokable(b,c), e : invokable(a,b) => - function lambda2(c : t_closure1(a,b,c,d,e), x : a) -> c { - match c { - | t_closure1(f, g) => - return invokable.invoke(f, invokable.invoke(g,x)); - } - } - -forall a b c d e . d : invokable(b,c) - , e : invokable(a,b) - => instance t_closure1(a,b,c,d,e) : invokable(a,c) { - function invoke(self : t_closure1(a,b,c,d,e), args : a) -> c { - return lambda2(self, args); - } -} - -data t_id3(a) = t_id3 ; - -forall a . function id (x : a) -> a { - return x; -} - -forall a . instance t_id3(a) : invokable(a,a) { - function invoke(self : t_id3(a), args : a) -> a { - match self { - | t_id3 => return id(args) ; - } - } -} - -contract Foo { - public function main() -> word { - let f = compose(t_id3, t_id3); - return invokable.invoke(f, 0); - } -} - diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/const-array.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/const-array.sol new file mode 100644 index 00000000..fca4449a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/const-array.sol @@ -0,0 +1,118 @@ + +enum Zero {} +enum Succ {} + +trait TAdd {} +impl TAdd<(Zero, a), a> {} +impl TAdd<(Succ, a), Succ> where (b, a): TAdd {} + +trait Eq {} +impl Eq {} + +// this should work but doesnt: forall sizel sizer elem sizeout . (sizel, sizer):TAdd(sizeout) +function concat(lhs: memory>, rhs: memory>) returns (memory>) where pairSizelSizer: Eq<(sizel, sizer)>, pairSizelSizer: TAdd { + return memory(0) ; // :D +} + +enum Itself { ItselfRuntimeTag } + +enum array { array } +enum memory { memory(word) } + +trait IndexAccessible { + function set(self:self, ix:indexType, val:elementType); + function at(self: self, ix: indexType) returns (elementType) ; +} + +trait ToWord { + function toWord(self: Itself) returns (word) ; +} + +impl ToWord { + function toWord(zero: Itself) { return 0; } +} + +impl ToWord> where prev: ToWord { + function toWord(self: Itself>) { + let prevTag : Itself = Itself.ItselfRuntimeTag; + let returnVal : word = ToWord.toWord(prevTag); + assembly { + returnVal := add(1, returnVal) + } + return returnVal; + } +} + +trait MemoryType { + function load(ptr: word) returns (self) ; + function store(ptr:word, value:self); +} + +impl MemoryType { + function load(ptr: word) returns (word) { + let val : word; + assembly { val := mload(ptr) } + return val; + } + function store(ptr:word, value:word) { + assembly { mstore(ptr, value) } + } +} + +impl IndexAccessible>, word, elem> where size: ToWord, elem: MemoryType { + function at(self: memory>, index: word) returns (elem) { + let sizeTag : Itself = Itself.ItselfRuntimeTag; + let sizeValue = ToWord.toWord(sizeTag); + // this should work but doesn't + // assembly { + // if iszero(lt(index, sizeValue)) { + // revert(0, 0) + // } + //} + + match (self) { +case memory(offset) { +let x = offset; // can't use this inside the assembly block :-( + assembly { + index := add(x, mul(32, index)) + } + return MemoryType.load(index); +} +} + } + + function set(self: memory>, index: word, val: elem) { + let sizeTag : Itself = Itself.ItselfRuntimeTag; + let sizeValue = ToWord.toWord(sizeTag); + + //assembly { + // if iszero(lt(index, sizeValue)) { + // revert(0, 0) + // } + //} + + match (self) { +case memory(offset) { +let x = offset; // can't use this inside the assembly block :-( + assembly { + index := add(x, mul(32, index)) + } + MemoryType.store(index, val); +} +} + } +} + + + +contract Array { + + function main() public { + let arr : memory>>>, word>> = memory(42); // = (1,2,3,4,5,6,7,8,9,10); + IndexAccessible.set(arr, 4, 33); + + // this (correctly) typechecks but doesn't specialize + let res = concat(arr, arr); // this typechecks + return IndexAccessible.at(arr, 4); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/const-array.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/const-array.solc deleted file mode 100644 index 17a2fcec..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/const-array.solc +++ /dev/null @@ -1,113 +0,0 @@ - -data Zero; -data Succ(a); - -forall self res . class self:TAdd(res) {} -forall a . instance (Zero, a):TAdd(a) {} -forall a b c . (b, a):TAdd(c) => instance (Succ(b), a):TAdd(Succ(c)) {} - -forall lhs rhs . class lhs:Eq(rhs) {} -forall a . instance a:Eq(a) {} - -// this should work but doesnt: forall sizel sizer elem sizeout . (sizel, sizer):TAdd(sizeout) -forall sizel sizer elem sizeout pairSizelSizer . pairSizelSizer:Eq((sizel, sizer)), pairSizelSizer:TAdd(sizeout) => function concat(lhs:memory(array(sizel, elem)), rhs:memory(array(sizer, elem))) -> memory(array(sizeout, elem)) { - return memory(0) : memory(array(sizeout, elem)); // :D -} - -data Itself(a) = ItselfRuntimeTag; - -data array(size, elem) = array; -data memory(a) = memory(word); - -forall self indexType elementType . class self:IndexAccessible (indexType, elementType){ - function set(self:self, ix:indexType, val:elementType); - function at(self:self, ix:indexType) -> elementType; -} - -forall self . class self:ToWord{ - function toWord(self:Itself(self)) -> word; -} - -instance Zero : ToWord { - function toWord(zero) { return 0; } -} - -forall prev . prev:ToWord => instance Succ(prev) : ToWord { - function toWord(self: Itself(Succ(prev))) { - let returnVal : word = ToWord.toWord(Itself.ItselfRuntimeTag:Itself(prev)); - assembly { - returnVal := add(1, returnVal) - } - return returnVal; - } -} - -forall self . class self:MemoryType { - function load(ptr:word) -> self; - function store(ptr:word, value:self); -} - -instance word:MemoryType { - function load(ptr:word) -> word { - let val : word; - assembly { val := mload(ptr) } - return val; - } - function store(ptr:word, value:word) { - assembly { mstore(ptr, value) } - } -} - -forall size elem . size : ToWord, elem:MemoryType => instance memory(array(size, elem)) : IndexAccessible(word, elem) { - function at(self, index) -> elem { - let sizeValue = ToWord.toWord(Itself.ItselfRuntimeTag:Itself(size)); - // this should work but doesn't - // assembly { - // if iszero(lt(index, sizeValue)) { - // revert(0, 0) - // } - //} - - match self { - | memory(offset) => - let x = offset; // can't use this inside the assembly block :-( - assembly { - index := add(x, mul(32, index)) - } - return MemoryType.load(index); - } - } - - function set(self, index, val) { - let sizeValue = ToWord.toWord(Itself.ItselfRuntimeTag:Itself(size)); - - //assembly { - // if iszero(lt(index, sizeValue)) { - // revert(0, 0) - // } - //} - - match self { - | memory(offset) => - let x = offset; // can't use this inside the assembly block :-( - assembly { - index := add(x, mul(32, index)) - } - MemoryType.store(index, val); - } - } -} - - - -contract Array { - - public function main() { - let arr : memory(array(Succ(Succ(Succ(Succ(Zero)))), word)) = memory(42); // = (1,2,3,4,5,6,7,8,9,10); - IndexAccessible.set(arr, 4, 33); - - // this (correctly) typechecks but doesn't specialize - let res = concat(arr, arr); // this typechecks - return IndexAccessible.at(arr, 4); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/contract-local-type-escapes-fail.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/contract-local-type-escapes-fail.sol new file mode 100644 index 00000000..6a082272 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/contract-local-type-escapes-fail.sol @@ -0,0 +1,21 @@ +// A data type declared inside a contract is private to that contract: it may +// not be referenced from outside. Qualification (A.Secret) keeps the bare name +// `Secret` out of the top-level scope, so this must fail name resolution. +import * from std; + +contract A { + enum Secret { S } + + function useIt() public returns (word) { + match (Secret.S) { +case Secret.S { +return 1; +} +} + } +} + +// `Secret` is not in scope here — it belongs to contract A. +function leak(x: Secret) returns (word) { + return 0; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/contract-local-type-escapes-fail.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/contract-local-type-escapes-fail.solc deleted file mode 100644 index 9ed8f249..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/contract-local-type-escapes-fail.solc +++ /dev/null @@ -1,19 +0,0 @@ -// A data type declared inside a contract is private to that contract: it may -// not be referenced from outside. Qualification (A.Secret) keeps the bare name -// `Secret` out of the top-level scope, so this must fail name resolution. -import std.{*}; - -contract A { - data Secret = S; - - public function useIt() -> word { - match Secret.S { - | Secret.S => return 1; - } - } -} - -// `Secret` is not in scope here — it belongs to contract A. -function leak(x : Secret) -> word { - return 0; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-inst.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-inst.sol new file mode 100644 index 00000000..4ffebbde --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-inst.sol @@ -0,0 +1,18 @@ +trait Test { function f(x:self); } + +default impl Test { function f(x:self) {}} + +enum memory { memory(word) } +enum Proxy { Proxy } + +impl Test>> { function f(x:self) {}} + +function f(p: Proxy) { + let x:memory; + Test.f(x); +} + +function g() { + f(@memory>); // needs to choose default instance in Test.f + f(@memory); // needs to choose concrete instance +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-inst.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-inst.solc deleted file mode 100644 index 0cd1b9e6..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-inst.solc +++ /dev/null @@ -1,19 +0,0 @@ -class self:Test { function f(x:self); } - -default instance a:Test { function f(x:self) {}} - -data memory(a) = memory(word); -data Proxy(a) = Proxy; - -instance memory(memory(word)):Test { function f(x:self) {}} - -forall a. -function f(p:Proxy(a)) { - let x:memory(a); - Test.f(x); -} - -function g() { - f(Proxy:Proxy(memory(memory(word)))); // needs to choose default instance in Test.f - f(Proxy:Proxy(memory(word))); // needs to choose concrete instance -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-instance-missing.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-instance-missing.sol new file mode 100644 index 00000000..84fe0a3e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-instance-missing.sol @@ -0,0 +1,16 @@ +trait Test { function f(x:self); } + +enum memory { memory(word) } +enum Proxy { Proxy } + +impl Test>> { function f(x:self) {}} + +function f(p: Proxy) { + let x:memory; + Test.f(x); +} + +function g() { + f(@memory>); // needs to choose default instance in Test.f + f(@memory); // needs to choose concrete instance +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-instance-missing.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-instance-missing.solc deleted file mode 100644 index 59e710a2..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-instance-missing.solc +++ /dev/null @@ -1,17 +0,0 @@ -class self:Test { function f(x:self); } - -data memory(a) = memory(word); -data Proxy(a) = Proxy; - -instance memory(memory(word)):Test { function f(x:self) {}} - -forall a. -function f(p:Proxy(a)) { - let x:memory(a); - Test.f(x); -} - -function g() { - f(Proxy:Proxy(memory(memory(word)))); // needs to choose default instance in Test.f - f(Proxy:Proxy(memory(word))); // needs to choose concrete instance -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-instance-weak.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-instance-weak.sol new file mode 100644 index 00000000..a8c9b5a5 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-instance-weak.sol @@ -0,0 +1,24 @@ +trait Test { function f(x: self) returns (weak) ; } + +enum memory { memory(word) } +enum Proxy { Proxy } +enum Bool { True, False } +default impl Test { function f(x: a) returns (word) { return 42; }} + +impl Test>, Bool> { function f(x:self) returns (Bool) { return Bool.True; }} + +// If we choose the default instance to typecheck f, +// this will pass type-checking, since ``r`` is word. +// But: for a = memory(word), ``r`` will be ``bool`` and this is invalid! +function f(p: Proxy) { + let x:memory; + let r :word = Test.f(x); + assembly { + sstore(0, r) + } +} + +function g() { + f(@memory>); // valid, since default instance is used + f(@memory); // PROBLEM: now we have a bool cross the assembly barrier +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-instance-weak.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-instance-weak.solc deleted file mode 100644 index a9002afa..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/default-instance-weak.solc +++ /dev/null @@ -1,25 +0,0 @@ -class self:Test(weak) { function f(x:self) -> weak; } - -data memory(a) = memory(word); -data Proxy(a) = Proxy; -data Bool = True | False; -default instance a:Test(word) { function f(x:a) -> word { return 42; }} - -instance memory(memory(word)):Test(Bool) { function f(x:self) { return Bool.True; }} - -// If we choose the default instance to typecheck f, -// this will pass type-checking, since ``r`` is word. -// But: for a = memory(word), ``r`` will be ``bool`` and this is invalid! -forall a. -function f(p:Proxy(a)) { - let x:memory(a); - let r :word = Test.f(x); - assembly { - sstore(0, r) - } -} - -function g() { - f(Proxy:Proxy(memory(memory(word)))); // valid, since default instance is used - f(Proxy:Proxy(memory(word))); // PROBLEM: now we have a bool cross the assembly barrier -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/derive-unknown-class.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/derive-unknown-class.sol new file mode 100644 index 00000000..123936df --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/derive-unknown-class.sol @@ -0,0 +1,12 @@ +import * from std; +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +#[derive(NoSuchClass)] +enum Color { Red, Green, Blue } + +function useIt() returns (bool) { + return true; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/derive-unknown-class.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/derive-unknown-class.solc deleted file mode 100644 index 41ff931a..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/derive-unknown-class.solc +++ /dev/null @@ -1,12 +0,0 @@ -import std.{*}; -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -#[derive(NoSuchClass)] -data Color = Red | Green | Blue; - -function useIt() -> bool { - return true; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dispatch.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dispatch.sol new file mode 100644 index 00000000..d507db9f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dispatch.sol @@ -0,0 +1,299 @@ + +// --- Preliminaries --- + +enum Bool { True, False } +enum Proxy { Proxy } + +// --- Core Data Types --- + +// A contract contains a tuple of methods and a single fallback +// TODO: implement receive() +enum Contract { Contract(methods, fb) } + +// A method contains an implementation (fn) as well as it's name and type signature +enum Method { Method(name, args, rets, fn) } + +// Contains the implementation for the fallback (fn) as well as it's type signature +enum Fallback { Fallback(args, rets, fn) } + +// --- Method Selectors --- + +// For each method in a contract the compiler generates a unique type and +// produces a `Selector` instance for that type that returns the selector hash +trait Selector { + function hash(prx: Proxy) returns (word) ; +} + +// Method has a Selector if its name has a Selector +impl Selector> where name: Selector { + function hash(prx: Proxy>) returns (word) { + return Selector.hash(@name); + } +} + +// --- Method Execution --- + +// Describes how to execute a given method / fallback +trait ExecMethod { + function exec(x: ty, pstatus: Proxy) ; +} + +// If fn matches the provided args/ret types, then we can execute any method +impl ExecMethod, Proxy, fn>> where fn: invokable { + function exec(m: Method, pstatus: Proxy) { + match (m) { +case Method(nm,args,rets,fn) { +// check callvalue + MethodLevelCallvalueCheck.checkCallvalue(@Method, pstatus); + + // check we have enough calldata for the head of args + // abi decode args from calldata + // call fn with args + // abi encode rets to memory + // returndata copy encoded returns + // evm return + return (); +} +} + } +} + +// If fn matches the provided args/ret types, then we can execute any fallback +impl ExecMethod, Proxy, fn>> where fn: invokable { + function exec(fb: Fallback, pstatus: Proxy) { + match (fb) { +case Fallback(args, rets, fn) { +// check callvalue + MethodLevelCallvalueCheck.checkCallvalue(@Fallback, pstatus); + + // check we have enough calldata for the head of args + // abi decode args from calldata + // call fn with args + // abi encode rets to memory + // returndata copy encoded returns + // evm return + return (); +} +} + } +} + +// --- Method Dispatch --- + +// For a given tuple of methods this executes the method specified by the first four bytes of calldata +trait RunDispatch { + function go(methods: ty, pstatus: Proxy) ; +} + +// We can dispatch to a single executable method with a known selector +// TODO: do we need this instance? +impl RunDispatch where m: ExecMethod, m: Selector { + function go(method: m, pstatus: Proxy) { + match (selector_matches(@m)) { +case Bool.True { +ExecMethod.exec(method, pstatus); +} +case Bool.False { +return (); +} +} + } +} + +// We can dispatch to a tuple of executable methods with a known selector +impl RunDispatch<(n, m)> where n: ExecMethod, n: Selector, m: ExecMethod, m: Selector { + function go(methods: (n, m), pstatus: Proxy) { + match (methods) { +case (method_n, method_m) { +match (selector_matches(@n)) { +case Bool.True { +ExecMethod.exec(method_n); +} +case Bool.False { +match (selector_matches(@m)) { +case Bool.True { +ExecMethod.exec(method_m, pstatus); +} +case Bool.False { +return (); +} +} +} +} +} +} + } +} + +// Recursive instance +impl RunDispatch<(n, m)> where n: ExecMethod, n: Selector, m: RunDispatch { + function go(methods: (n, m), pstatus: Proxy) { + match (methods) { +case (method_n, rest) { +match (selector_matches(@n)) { +case Bool.True { +ExecMethod.exec(method_n, pstatus); +} +case Bool.False { +RunDispatch.go(rest, pstatus); +} +} +} +} + } +} + +// TODO: we only wanna do the calldataload once +// Given evidence of a name with a known selector, we can check if it matches the selector in the first four bytes of calldata +function selector_matches(prx: Proxy) returns (Bool) where name: Selector { + let hash = Selector.hash(prx); + let res : word; + assembly { + let sel := shr(224, calldataload(0)) + res := eq(sel, hash) + } + match (res) { +case 0 { +return Bool.False; +} +default { +return Bool.True; +} +} +} + +// --- Callvalue Checks --- + +// If every method on a contract is non payable, we lift the callvalue check to run before method dispatch +// NonPayable instances should be generated by the compiler as part of desugaring +trait NonPayable {} +trait AllNonPayable {} +impl AllNonPayable<(n, m)> where n: NonPayable, m: AllNonPayable {} + + +enum CallvalueChecked {} + +enum CallvalueUnchecked {} +trait MethodsMustCheckCalldata {} +impl MethodsMustCheckCalldata {} + +// If every method is non payable we run the callvalue check before method dispatch +trait TopLevelCallvalueCheck { + function checkCallvalue(prx: Proxy) returns (Proxy) ; +} + +default impl TopLevelCallvalueCheck { + function checkCallvalue(prx: Proxy) returns (Proxy) { return @CallvalueUnchecked; } +} + +impl TopLevelCallvalueCheck where methods: AllNonPayable { + function checkCallvalue(prx: Proxy) returns (Proxy) { + assembly { + if gt(callvalue(), 0) { + mstore(0,0x2) + revert(0,32) + } + } + return @CallvalueChecked; + } +} + +// If only some methods are non payable, then we run the check during method execution +trait MethodLevelCallvalueCheck { + function checkCallvalue(pty: Proxy, pstatus: Proxy) ; +} + +default impl MethodLevelCallvalueCheck { + function checkCallvalue(pty: Proxy, pstatus: Proxy) { } +} + +impl MethodLevelCallvalueCheck where method: NonPayable, status: MethodsMustCheckCalldata { + function checkCallvalue(pty: Proxy, pstatus: Proxy) { + assembly { + if gt(callvalue(), 0) { + mstore(0, 0x1) + revert(0, 32) + } + } + } +} + +// --- Contract Execution --- + +// Describes how to execute a given contract +trait RunContract { + function exec(v: c) ; +} + +// If we have a dispatch for the contracts methods, and we know how to execute it's fallback, then we can define an entrypoint +impl RunContract> where methods: RunDispatch, fb: ExecMethod { + function exec(c: Contract) { + match (c) { +case Contract(ms, fb) { +// set free memory pointer to the output of memoryguard + // https://docs.soliditylang.org/en/v0.8.30/yul.html#memoryguard + // TODO: we will need to consider immutables here at some point... + // assembly { mstore(0x40, memoryguard(128)) } + + // if all methods are non payable then check callvalue + let callvalueChecked = TopLevelCallvalueCheck.checkCallvalue(@(fb, methods)); + + // check that we have at least 4 bytes of calldata + let haveSelector : word; + assembly { + haveSelector := lt(3, calldatasize()) + } + + match (haveSelector) { +case 0 { +assembly { revert(0,0) } +} +default { +// dispatch to method based on selector + RunDispatch.go(ms, callvalueChecked); + // run fallback if no methods matched + ExecMethod.exec(fb); +} +} +} +} + } +} + +// --- Manually Desugared Example --- + +// compiler generated + +function revert_handler() { + assembly { revert(0,0) } +} + +enum C_Add2_Selector { C_Add2_Selector } + +impl Selector { + function hash(prx: Proxy) returns (word) { + // This would be keccak256("add2(uint256,uint256)") >> 224 + // Compiler computes this at compile time + return 0x29fcda33; // placeholder value + } +} + +// transform + +contract C { + function add2(x: word, y: word) public returns (word) { + let ret : word; + assembly { ret := add(x,y) } + return ret; + } + + function main() public returns (word) { + let c = Contract( + Method(C_Add2_Selector, @(word, word), @word, add2), + Fallback(@(),@(),revert_handler) + ); + + RunContract.exec(c); + return 0; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dispatch.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dispatch.solc deleted file mode 100644 index f33527b9..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dispatch.solc +++ /dev/null @@ -1,271 +0,0 @@ - -// --- Preliminaries --- - -data Bool = True | False; -data Proxy(a) = Proxy; - -// --- Core Data Types --- - -// A contract contains a tuple of methods and a single fallback -// TODO: implement receive() -data Contract(methods, fb) = Contract(methods,fb); - -// A method contains an implementation (fn) as well as it's name and type signature -data Method(name, args, rets, fn) = Method(name, args, rets, fn); - -// Contains the implementation for the fallback (fn) as well as it's type signature -data Fallback(args, rets, fn) = Fallback(args, rets, fn); - -// --- Method Selectors --- - -// For each method in a contract the compiler generates a unique type and -// produces a `Selector` instance for that type that returns the selector hash -forall nm . class nm:Selector { - function hash(prx: Proxy(nm)) -> word; -} - -// Method has a Selector if its name has a Selector -forall name args rets fn . name:Selector => instance Method(name,args,rets,fn):Selector { - function hash(prx: Proxy(Method(name,args,rets,fn))) -> word { - return Selector.hash(Proxy : Proxy(name)); - } -} - -// --- Method Execution --- - -// Describes how to execute a given method / fallback -forall ty callvalueCheckStatus . class ty:ExecMethod { - function exec(x: ty, pstatus : Proxy(callvalueCheckStatus)) -> (); -} - -// If fn matches the provided args/ret types, then we can execute any method -forall name args rets fn callvalueCheckStatus . fn:invokable(args,ret) => instance Method(name,Proxy(args),Proxy(rets),fn):ExecMethod { - function exec(m : Method(name,args,rets,fn), pstatus : Proxy(callvalueCheckStatus)) -> () { - match m { - | Method(nm,args,rets,fn) => - // check callvalue - MethodLevelCallvalueCheck.checkCallvalue(Proxy : Proxy(Method(name,args,rets,fn)), pstatus); - - // check we have enough calldata for the head of args - // abi decode args from calldata - // call fn with args - // abi encode rets to memory - // returndata copy encoded returns - // evm return - return (); - } - } -} - -// If fn matches the provided args/ret types, then we can execute any fallback -forall args rets fn callvalueCheckStatus . fn:invokable(args,ret) => instance Fallback(Proxy(args),Proxy(rets),fn):ExecMethod { - function exec(fb : Fallback(args,rets,fn), pstatus : Proxy (callvalueCheckStatus)) -> () { - match fb { - | Fallback(args, rets, fn) => - // check callvalue - MethodLevelCallvalueCheck.checkCallvalue(Proxy : Proxy(Fallback(args,rets,fn)), pstatus); - - // check we have enough calldata for the head of args - // abi decode args from calldata - // call fn with args - // abi encode rets to memory - // returndata copy encoded returns - // evm return - return (); - } - } -} - -// --- Method Dispatch --- - -// For a given tuple of methods this executes the method specified by the first four bytes of calldata -forall ty callvalueCheckStatus . class ty:RunDispatch { - function go(methods : ty, pstatus : Proxy(callvalueCheckStatus)) -> (); -} - -// We can dispatch to a single executable method with a known selector -// TODO: do we need this instance? -forall m callvalueCheckStatus . m:ExecMethod, m:Selector => instance m:RunDispatch { - function go(method : m, pstatus : Proxy(callvalueCheckStatus)) -> () { - match selector_matches(Proxy : Proxy(m)) { - | Bool.True => ExecMethod.exec(method, pstatus); - | Bool.False => return (); - } - } -} - -// We can dispatch to a tuple of executable methods with a known selector -forall n m callvalueCheckStatus . n:ExecMethod, n:Selector, m:ExecMethod, m:Selector => instance (n,m):RunDispatch { - function go(methods : (n,m), pstatus : Proxy(callvalueCheckStatus)) -> () { - match methods { - | (method_n, method_m) => - match selector_matches(Proxy : Proxy(n)) { - | Bool.True => ExecMethod.exec(method_n); - | Bool.False => match selector_matches(Proxy : Proxy(m)) { - | Bool.True => ExecMethod.exec(method_m, pstatus); - | Bool.False => return (); - } - } - } - } -} - -// Recursive instance -forall n m callvalueCheckStatus . n:ExecMethod, n:Selector, m:RunDispatch => instance (n,m):RunDispatch { - function go(methods : (n,m), pstatus : Proxy(callvalueCheckStatus)) -> () { - match methods { - | (method_n, rest) => - match selector_matches(Proxy : Proxy(n)) { - | Bool.True => ExecMethod.exec(method_n, pstatus); - | Bool.False => RunDispatch.go(rest, pstatus); - } - } - } -} - -// TODO: we only wanna do the calldataload once -// Given evidence of a name with a known selector, we can check if it matches the selector in the first four bytes of calldata -forall name . name:Selector => function selector_matches(prx : Proxy(name)) -> Bool { - let hash = Selector.hash(prx); - let res : word; - assembly { - let sel := shr(224, calldataload(0)) - res := eq(sel, hash) - } - match res { - | 0 => return Bool.False; - | _ => return Bool.True; - } -} - -// --- Callvalue Checks --- - -// If every method on a contract is non payable, we lift the callvalue check to run before method dispatch -// NonPayable instances should be generated by the compiler as part of desugaring -forall ty . class ty:NonPayable {} -forall ty . class ty:AllNonPayable {} -forall n m . n:NonPayable, m:AllNonPayable => instance (n,m):AllNonPayable {} - - -data CallvalueChecked; - -data CallvalueUnchecked; -forall ty . class ty:MethodsMustCheckCalldata {} -instance CallvalueUnchecked:MethodsMustCheckCalldata {} - -// If every method is non payable we run the callvalue check before method dispatch -forall ty ret . class ty:TopLevelCallvalueCheck(ret) { - function checkCallvalue(prx : Proxy(ty)) -> Proxy(ret); -} - -forall methods . default instance methods:TopLevelCallvalueCheck(CallvalueUnchecked) { - function checkCallvalue(prx : Proxy(methods)) -> Proxy(CallvalueUnchecked) { return Proxy : Proxy(CallvalueUnchecked); } -} - -forall methods . methods:AllNonPayable => instance methods:TopLevelCallvalueCheck(CallvalueChecked) { - function checkCallvalue(prx : Proxy(methods)) -> Proxy(CallvalueChecked) { - assembly { - if gt(callvalue(), 0) { - mstore(0,0x2) - revert(0,32) - } - } - return Proxy : Proxy(CallvalueChecked); - } -} - -// If only some methods are non payable, then we run the check during method execution -forall ty status . class ty:MethodLevelCallvalueCheck { - function checkCallvalue(pty : Proxy(ty), pstatus : Proxy(status)) -> (); -} - -forall method status . default instance method:MethodLevelCallvalueCheck { - function checkCallvalue(pty : Proxy(method), pstatus : Proxy(status)) -> () { } -} - -forall method status . method:NonPayable, status:MethodsMustCheckCalldata => instance method:MethodLevelCallvalueCheck { - function checkCallvalue(pty : Proxy(method), pstatus : Proxy(status)) -> (){ - assembly { - if gt(callvalue(), 0) { - mstore(0, 0x1) - revert(0, 32) - } - } - } -} - -// --- Contract Execution --- - -// Describes how to execute a given contract -forall c . class c:RunContract { - function exec(v : c) -> (); -} - -// If we have a dispatch for the contracts methods, and we know how to execute it's fallback, then we can define an entrypoint -forall methods fb . methods:RunDispatch, fb:ExecMethod => instance Contract(methods, fb):RunContract { - function exec(c : Contract(methods, fb)) -> () { - match c { - | Contract(ms, fb) => - // set free memory pointer to the output of memoryguard - // https://docs.soliditylang.org/en/v0.8.30/yul.html#memoryguard - // TODO: we will need to consider immutables here at some point... - // assembly { mstore(0x40, memoryguard(128)) } - - // if all methods are non payable then check callvalue - let callvalueChecked = TopLevelCallvalueCheck.checkCallvalue(Proxy : Proxy((fb, methods))); - - // check that we have at least 4 bytes of calldata - let haveSelector : word; - assembly { - haveSelector := lt(3, calldatasize()) - } - - match haveSelector { - | 0 => assembly { revert(0,0) } - | _ => - // dispatch to method based on selector - RunDispatch.go(ms, callvalueChecked); - // run fallback if no methods matched - ExecMethod.exec(fb); - } - } - } -} - -// --- Manually Desugared Example --- - -// compiler generated - -function revert_handler() -> () { - assembly { revert(0,0) } -} - -data C_Add2_Selector = C_Add2_Selector; - -instance C_Add2_Selector:Selector { - function hash(prx: Proxy(C_Add2_Selector)) -> word { - // This would be keccak256("add2(uint256,uint256)") >> 224 - // Compiler computes this at compile time - return 0x29fcda33; // placeholder value - } -} - -// transform - -contract C { - public function add2(x : word, y : word) -> word { - let ret : word; - assembly { ret := add(x,y) } - return ret; - } - - public function main() -> word { - let c = Contract( - Method(C_Add2_Selector, Proxy : Proxy((word,word)), Proxy : Proxy(word), add2), - Fallback(Proxy : Proxy(()),Proxy : Proxy(()),revert_handler) - ); - - RunContract.exec(c); - return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dot-expression-no-context-fail.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dot-expression-no-context-fail.sol new file mode 100644 index 00000000..6472dc4e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dot-expression-no-context-fail.sol @@ -0,0 +1,6 @@ +enum Option { None, Some(word) } + +function bad() returns (Option) { + let x = .Some(1); + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dot-expression-no-context-fail.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dot-expression-no-context-fail.solc deleted file mode 100644 index 485ed798..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dot-expression-no-context-fail.solc +++ /dev/null @@ -1,6 +0,0 @@ -data Option = None | Some(word); - -function bad() -> Option { - let x = .Some(1); - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dot-expression-unknown-fail.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dot-expression-unknown-fail.sol new file mode 100644 index 00000000..6a44271a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dot-expression-unknown-fail.sol @@ -0,0 +1,5 @@ +enum Option { None, Some(word) } + +function bad() returns (Option) { + return .Nope(1); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dot-expression-unknown-fail.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dot-expression-unknown-fail.solc deleted file mode 100644 index 11ab2af7..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/dot-expression-unknown-fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Option = None | Some(word); - -function bad() -> Option { - return .Nope(1); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/duplicated-contract-name.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/duplicated-contract-name.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/fail/test/examples/cases/duplicated-contract-name.solc rename to crates/parser/tests/fixtures/corpus/fail/test/examples/cases/duplicated-contract-name.sol diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/duplicated-type-name.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/duplicated-type-name.sol new file mode 100644 index 00000000..c5164fba --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/duplicated-type-name.sol @@ -0,0 +1,6 @@ +enum Foo { Bar } +enum Foo { Baz } + +function main() { + let x = Foo.Baz; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/duplicated-type-name.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/duplicated-type-name.solc deleted file mode 100644 index 18627795..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/duplicated-type-name.solc +++ /dev/null @@ -1,6 +0,0 @@ -data Foo = Bar; -data Foo = Baz; - -function main() { - let x = Foo.Baz; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-args.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-args.snap index 6e6585c5..86797ab7 100644 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-args.snap +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-args.snap @@ -1,13 +1,13 @@ --- source: crates/parser/tests/diagnostics.rs expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-args.solc +input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-args.sol --- error[SC0001]: fallback function must not declare input parameters - --> /fallback-with-args.solc:7:13 + --> /fallback-with-args.sol:7:13 | 6 | -7 | fallback(x: uint256) -> () { +7 | fallback(x: uint256) { | ^^^^^^^^^^^^ 8 | revert("fallback-was-called"); | diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-args.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-args.sol new file mode 100644 index 00000000..0cda10a8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-args.sol @@ -0,0 +1,10 @@ +import * from std; +import * from std.dispatch; + +contract BadFallback { + constructor() {} + + fallback(x: uint256) { + revert("fallback-was-called"); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-args.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-args.solc deleted file mode 100644 index 59387aed..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-args.solc +++ /dev/null @@ -1,10 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract BadFallback { - constructor() {} - - fallback(x: uint256) -> () { - revert("fallback-was-called"); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-return.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-return.snap index d9026f8e..c8bc231a 100644 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-return.snap +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-return.snap @@ -1,14 +1,15 @@ --- source: crates/parser/tests/diagnostics.rs expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-return.solc +input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-return.sol --- -error[SC0001]: fallback function must return unit (`()`) - --> /fallback-with-return.solc:7:19 +error[SC0001]: parse error: unexpected identifier `returns` + --> /fallback-with-return.sol:7:16 | 6 | -7 | fallback() -> uint256 { - | ^^^^^^^ +7 | fallback() returns (uint256) { + | ^^^^^^^ unexpected token 8 | return uint256(0); | + = note: expecting `payable`, `public`, or `{` = note: while parsing fallback definition diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-return.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-return.sol new file mode 100644 index 00000000..3fa6df89 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-return.sol @@ -0,0 +1,10 @@ +import * from std; +import * from std.dispatch; + +contract BadFallback { + constructor() {} + + fallback() returns (uint256) { + return uint256(0); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-return.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-return.solc deleted file mode 100644 index ca9e5223..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/fallback-with-return.solc +++ /dev/null @@ -1,10 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract BadFallback { - constructor() {} - - fallback() -> uint256 { - return uint256(0); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/field-access.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/field-access.sol new file mode 100644 index 00000000..1f61323e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/field-access.sol @@ -0,0 +1,18 @@ +import * from std; + +contract PoC { + field : word; + + function set_x(b: bool) public returns (bool) { + field = b; // BUG: `word` shouldn't be unified with `bool`. + return b; + } + + function init(foo: bool) public { + field = 2; + } + + function main() public { + + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/field-access.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/field-access.solc deleted file mode 100644 index b53e151f..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/field-access.solc +++ /dev/null @@ -1,18 +0,0 @@ -import std.{*}; - -contract PoC { - field : word; - - public function set_x(b: bool) -> bool { - field = b; // BUG: `word` shouldn't be unified with `bool`. - return b; - } - - public function init(foo: bool) -> () { - field = 2; - } - - public function main () -> () { - - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/for-let-post.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/for-let-post.sol new file mode 100644 index 00000000..a1f8ae93 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/for-let-post.sol @@ -0,0 +1,10 @@ +import {Num,Add,Sub,Eq,Ord,Bounded,Typedef,le} from std; + +contract C { + function main() public returns (word) { + let i : word = 0; + let s : word = 99; + for(i=0;i<=0;let j=1) { s = j; i = i + 1; } + return s; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/for-let-post.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/for-let-post.solc deleted file mode 100644 index a7f1b11d..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/for-let-post.solc +++ /dev/null @@ -1,10 +0,0 @@ -import std.{Num,Add,Sub,Eq,Ord,Bounded,Typedef,le}; - -contract C { - public function main() -> word { - let i : word = 0; - let s : word = 99; - for(i=0;i<=0;let j=1) { s = j; i = i + 1; } - return s; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-manual-no-pragma.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-manual-no-pragma.sol new file mode 100644 index 00000000..b1b27c71 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-manual-no-pragma.sol @@ -0,0 +1,22 @@ +// Error case: manual Generic instance without pragma no-generic-instance-for. +// The compiler must reject this with a conflict error. + +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +enum Foo { MkFoo(word) } + +impl Generic { + function from(x: Foo) returns (word) { + match (x) { +case Foo.MkFoo(v) { +return v; +} +} + } + function to(v: word) returns (Foo) { + return Foo.MkFoo(v); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-manual-no-pragma.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-manual-no-pragma.solc deleted file mode 100644 index 6551643c..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-manual-no-pragma.solc +++ /dev/null @@ -1,18 +0,0 @@ -// Error case: manual Generic instance without pragma no-generic-instance-for. -// The compiler must reject this with a conflict error. - -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -data Foo = MkFoo(word); - -instance Foo : Generic(word) { - function from(x : Foo) -> word { - match x { | Foo.MkFoo(v) => return v; } - } - function to(v : word) -> Foo { - return Foo.MkFoo(v); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-product-no-pragma.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-product-no-pragma.sol new file mode 100644 index 00000000..69b8580e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-product-no-pragma.sol @@ -0,0 +1,29 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; + +pragma no-patterson-condition; +pragma no-coverage-condition; +pragma no-bounded-variable-condition; + +enum Point { Point(uint256, uint256) } + +// Manual Generic instance without pragma no-generic-instance-for Point. +// The compiler must reject this with a conflict error. +impl Generic { + function from(p: Point) returns (uint256, uint256) { + match (p) { +case Point(x, y) { +return (x, y); +} +} + } + function to(t: (uint256, uint256)) returns (Point) { + match (t) { +case (x, y) { +return Point(x, y); +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-product-no-pragma.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-product-no-pragma.solc deleted file mode 100644 index bb2fb4db..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-product-no-pragma.solc +++ /dev/null @@ -1,21 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; - -pragma no-patterson-condition; -pragma no-coverage-condition; -pragma no-bounded-variable-condition; - -data Point = Point(uint256, uint256); - -// Manual Generic instance without pragma no-generic-instance-for Point. -// The compiler must reject this with a conflict error. -instance Point : Generic((uint256, uint256)) { - function from(p : Point) -> (uint256, uint256) { - match p { | Point(x, y) => return (x, y); } - } - function to(t : (uint256, uint256)) -> Point { - match t { | (x, y) => return Point(x, y); } - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-sum-no-pragma.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-sum-no-pragma.sol new file mode 100644 index 00000000..3d04cb34 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-sum-no-pragma.sol @@ -0,0 +1,35 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; + +pragma no-patterson-condition; +pragma no-coverage-condition; +pragma no-bounded-variable-condition; + +enum Option { None, Some(a) } + +// Manual Generic instance without pragma no-generic-instance-for Option. +// The compiler must reject this with a conflict error. +impl Generic, sum<(), uint256>> { + function from(x: Option) returns (sum<(), uint256>) { + match (x) { +case Option.None { +return inl(()); +} +case Option.Some(v) { +return inr(v); +} +} + } + function to(r: sum<(), uint256>) returns (Option) { + match (r) { +case inl(_) { +return Option.None; +} +case inr(v) { +return Option.Some(v); +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-sum-no-pragma.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-sum-no-pragma.solc deleted file mode 100644 index 49923afb..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/generic-sum-no-pragma.solc +++ /dev/null @@ -1,27 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; - -pragma no-patterson-condition; -pragma no-coverage-condition; -pragma no-bounded-variable-condition; - -data Option(a) = None | Some(a); - -// Manual Generic instance without pragma no-generic-instance-for Option. -// The compiler must reject this with a conflict error. -instance Option(uint256) : Generic(sum((), uint256)) { - function from(x : Option(uint256)) -> sum((), uint256) { - match x { - | Option.None => return inl(()); - | Option.Some(v) => return inr(v); - } - } - function to(r : sum((), uint256)) -> Option(uint256) { - match r { - | inl(_) => return Option.None; - | inr(v) => return Option.Some(v); - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/index-example.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/index-example.sol new file mode 100644 index 00000000..787433c2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/index-example.sol @@ -0,0 +1,71 @@ +enum storage { storage(word) } +enum storageRef { storageRef(word) } +enum Proxy { Proxy } + +enum mapping { mapping(word, Proxy, Proxy) } // storage by default +// data mapRef(a) = mapRef(word); //ref to a map elem + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +impl Assign, a> { + function assign(l:storageRef, y:a) { + } +} + +trait CStructField {} +enum StructField { StructField(structType) } + + +enum MemberAccessProxy { MemberAccessProxy(a, field) } + + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +// ------------------------------------------------------------------ +// Contract field access +// ------------------------------------------------------------------ + +impl LValueMemberAccess, storageRef> where StructField: CStructField { + function memberAccess(x: MemberAccessProxy) returns (storageRef) { + return storageRef(0x100); + } +} + +// ------------------------------------------------------------------ +// Indexed access +// ------------------------------------------------------------------ + +enum mapping { mapping(word) } +enum IndexAccessProxy { IndexAccessProxy(map, index) } +enum IndexAccessProxy2 { IndexAccessProxy2(map, index, Proxy) } + +impl LValueMemberAccess member)>, index, member>, storageRef> { + function memberAccess(x: IndexAccessProxy, index, member>) returns (storageRef) { + return storageRef(0); + } +} + +enum MintCtx { MintCtx } +enum balances_sel { balances_sel } +impl CStructField, mapping(word => word), ()> {} + + function mint(amount:word) { + let bal_prx = MemberAccessProxy(MintCtx, balances_sel); + let bal_ref = LValueMemberAccess.memberAccess(bal_prx); + + Assign.assign( + LValueMemberAccess.memberAccess( + IndexAccessProxy( + // bal_ref // this works, but inlining bal_ref leads to error + LValueMemberAccess.memberAccess(bal_prx) + , 0 + ) + ) + , amount + ) ; + + } diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/index-example.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/index-example.solc deleted file mode 100644 index db138d6c..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/index-example.solc +++ /dev/null @@ -1,74 +0,0 @@ -data storage(a) = storage(word); -data storageRef(a) = storageRef(word); -data Proxy(a) = Proxy; - -data mapping(member, index) = mapping(word, Proxy(member), Proxy(index)); // storage by default -// data mapRef(a) = mapRef(word); //ref to a map elem - -forall lhs rhs . class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -forall a . instance storageRef(a):Assign(a) { - function assign(l:storageRef(a), y:a) { - } -} - -forall self fieldType offsetType . class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - - -data MemberAccessProxy(a, field, offset) = MemberAccessProxy(a, field); - - -forall self memberRefType . class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -// ------------------------------------------------------------------ -// Contract field access -// ------------------------------------------------------------------ - -forall cxt fieldSelector fieldType offsetType - . StructField(cxt, fieldSelector):CStructField(fieldType, offsetType) - => instance MemberAccessProxy(cxt, fieldSelector, offsetType):LValueMemberAccess(storageRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(cxt, fieldSelector, offsetType)) -> storageRef(fieldType) { - return storageRef(0x100); - } -} - -// ------------------------------------------------------------------ -// Indexed access -// ------------------------------------------------------------------ - -data mapping(index, member) = mapping(word); -data IndexAccessProxy(map, index, member) = IndexAccessProxy(map, index); -data IndexAccessProxy2(map, index, member) = IndexAccessProxy2(map, index, Proxy(member)); - -forall map index member. - instance IndexAccessProxy(storageRef(mapping(index,member)), index, member):LValueMemberAccess(storageRef(member)) { - function memberAccess(x:IndexAccessProxy(storageRef(map), index, member)) -> storageRef(member) { - return storageRef(0); - } -} - -data MintCtx = MintCtx; -data balances_sel = balances_sel; -instance StructField(MintCtx, balances_sel):CStructField(mapping(word,word), ()) {} - - function mint(amount:word) { - let bal_prx = MemberAccessProxy(MintCtx, balances_sel); - let bal_ref = LValueMemberAccess.memberAccess(bal_prx); - - Assign.assign( - LValueMemberAccess.memberAccess( - IndexAccessProxy( - // bal_ref // this works, but inlining bal_ref leads to error - LValueMemberAccess.memberAccess(bal_prx) - , 0 - ) - ) - , amount - ) ; - - } diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-closure-error-invalid-member.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-closure-error-invalid-member.sol new file mode 100644 index 00000000..3e72850d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-closure-error-invalid-member.sol @@ -0,0 +1,11 @@ +trait CtFun { + function ct(x: t) returns (function(t) returns (t)) ; +} + +impl CtFun { + function ct(x: word) returns (function(word) returns (word)) { + return lam(y : bool) { + return x; + }; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-closure-error-invalid-member.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-closure-error-invalid-member.solc deleted file mode 100644 index ecd0fc2a..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-closure-error-invalid-member.solc +++ /dev/null @@ -1,11 +0,0 @@ -forall t . class t:CtFun { - function ct(x : t) -> ((t) -> t); -} - -instance word:CtFun { - function ct(x : word) -> ((word) -> word) { - return lam(y : bool) { - return x; - }; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-context-wrong-kind.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-context-wrong-kind.sol new file mode 100644 index 00000000..a8e6a0ed --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-context-wrong-kind.sol @@ -0,0 +1,5 @@ +trait Foo {} + +trait C {} + +impl C<(word, t)> where t: Foo {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-context-wrong-kind.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-context-wrong-kind.solc deleted file mode 100644 index 8487114f..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-context-wrong-kind.solc +++ /dev/null @@ -1,5 +0,0 @@ -forall a b . class a : Foo(b) {} - -forall a. class a:C {} - -forall t. t:Foo => instance (word,t):C {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-wrong-sig.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-wrong-sig.sol new file mode 100644 index 00000000..7623901a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-wrong-sig.sol @@ -0,0 +1,15 @@ +enum uint256 { uint256(word) } +enum Proxy { Proxy } +trait ABIAttribs { + function headSize(ty: Proxy) returns (word) ; + function isStatic(ty: Proxy) returns (bool) ; +} + +impl ABIAttribs<()> { + function headSize(ty: Proxy) returns (word) { return 0; } + function isStatic(ty: Proxy) returns (bool) { return true; } +} +impl ABIAttribs { + function headSize(ty: Proxy) returns (word) { return 32; } + function isStatic(ty: Proxy) returns (bool) { return true; } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-wrong-sig.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-wrong-sig.solc deleted file mode 100644 index ea5b8d7e..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/instance-wrong-sig.solc +++ /dev/null @@ -1,15 +0,0 @@ -data uint256 = uint256(word); -data Proxy(a) = Proxy; -forall self . class self:ABIAttribs { - function headSize(ty:Proxy(self)) -> word; - function isStatic(ty:Proxy(self)) -> bool; -} - -instance ():ABIAttribs { - function headSize(ty : Proxy(uint256)) -> word { return 0; } - function isStatic(ty : Proxy(uint256)) -> bool { return true; } -} -instance uint256:ABIAttribs { - function headSize(ty : Proxy(uint256)) -> word { return 32; } - function isStatic(ty : Proxy(uint256)) -> bool { return true; } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/joinErr.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/joinErr.sol new file mode 100644 index 00000000..d6729bf2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/joinErr.sol @@ -0,0 +1,33 @@ +contract Option { + enum Option { None, Some(a) } + enum Bool { False, True } + + function maybe(n: word, o: Option) public returns (word) { + match (o) { +case Option.None { +return n; +} +case Option.Some(x) { +return x; +} +} + } + + function join(mmx: Option>) public returns (Option) { + let result = Option.None; + match (mmx) { +case Option.Some(Option.Some(x)) { +result = Option.Some(x); +} +case Option.None { +result = Option.None; +} +} + return result; + } + + + function main() public returns (word) { + return maybe(0, join(Option.Some(Option.Some(Bool.False)))); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/joinErr.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/joinErr.solc deleted file mode 100644 index 6ae54697..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/joinErr.solc +++ /dev/null @@ -1,25 +0,0 @@ -contract Option { - data Option(a) = None | Some(a); - data Bool = False | True; - - public function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.None => return n; - | Option.Some(x) => return x; - } - } - - public function join(mmx : Option(Option(word))) -> Option(word) { - let result = Option.None; - match mmx { - | Option.Some(Option.Some(x)) => result = Option.Some(x); - | Option.None => result = Option.None; - } - return result; - } - - - public function main() -> word { - return maybe(0, join(Option.Some(Option.Some(Bool.False)))); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/listeq.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/listeq.sol new file mode 100644 index 00000000..6f2f5494 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/listeq.sol @@ -0,0 +1,10 @@ +enum List { Nil, Cons(a, List) } +enum Bool { False, True } + +trait Eq { + function eq(x: a, y: a) returns (Bool) ; +} + +function foo () { + return Eq.eq(List.Nil, List.Nil); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/listeq.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/listeq.solc deleted file mode 100644 index 21299f76..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/listeq.solc +++ /dev/null @@ -1,10 +0,0 @@ -data List(a) = Nil | Cons(a,List(a)); -data Bool = False | True; - -forall a . class a : Eq { - function eq (x : a, y : a) -> Bool ; -} - -function foo () { - return Eq.eq(List.Nil, List.Nil); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/mainproxy.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/mainproxy.sol new file mode 100644 index 00000000..b7a7b38b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/mainproxy.sol @@ -0,0 +1,22 @@ +enum Proxy { Proxy } + +trait BaseMemoryType { + function memorySize(x: Proxy) returns (word) ; +} + + +impl BaseMemoryType { + function memorySize(x: Proxy) returns (word) { + return 32; + } +} + + +function morefun(p: Proxy) returns (word) { return BaseMemoryType.memorySize(@t); +} + +contract TestMemoryType { + function main() public returns (word) { + return morefun(@word); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/mainproxy.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/mainproxy.solc deleted file mode 100644 index 1e3f5c87..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/mainproxy.solc +++ /dev/null @@ -1,22 +0,0 @@ -data Proxy(a) = Proxy; - -class self:BaseMemoryType { - function memorySize(x:Proxy(self)) -> word; -} - - -instance word:BaseMemoryType { - function memorySize(x:Proxy(self)) -> word { - return 32; - } -} - - -function morefun(p:Proxy(t)) -> word { return BaseMemoryType.memorySize(Proxy:Proxy(t)); -} - -contract TestMemoryType { - public function main() -> word { - return morefun(Proxy:Proxy(word)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/match-compiler-undef-asm.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/match-compiler-undef-asm.sol new file mode 100644 index 00000000..e9e7b2a4 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/match-compiler-undef-asm.sol @@ -0,0 +1,20 @@ +enum Foo { Foo(word) } + +function read(x: Foo) returns (word) { + let res : word; + match (x) { +case Foo(w) { +assembly { + res := w + } +} +} + return res; +} + +contract Bla { + + function main() public returns (word) { + return read(Foo(42)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/match-compiler-undef-asm.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/match-compiler-undef-asm.solc deleted file mode 100644 index 28b89fdc..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/match-compiler-undef-asm.solc +++ /dev/null @@ -1,19 +0,0 @@ -data Foo(a) = Foo(word); - -forall a . function read(x : Foo(a)) -> word { - let res : word; - match (x) { - | Foo(w) => - assembly { - res := w - } - } - return res; -} - -contract Bla { - - public function main () -> word { - return read(Foo(42)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/missing-instance.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/missing-instance.sol new file mode 100644 index 00000000..b41d8bb6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/missing-instance.sol @@ -0,0 +1,26 @@ +// Note: this class has no instances! +trait Typedef { + function rep(x: abs) returns (rep) ; + function abs(x: rep) returns (abs) ; +} + +trait MemoryType { + function load(ptr: word) returns (self) ; +} + +impl MemoryType { + function load(ptr: word) returns (word) { + return Typedef.abs(MemoryType.load(ptr) ); + // `abs` does not make sense here, but it triggers the bug: + // the typechecker should complain about missing instance here + } +} + +contract C { + function main() public returns (word) { + let ptr : word = 0; + // if we inline the let below into return then another bug occurs: main is typed as forall a. () -> a + // let w:word = MemoryType.load(0); + return MemoryType.load(0); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/missing-instance.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/missing-instance.solc deleted file mode 100644 index 6bdaf00a..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/missing-instance.solc +++ /dev/null @@ -1,26 +0,0 @@ -// Note: this class has no instances! -forall abs rep . class abs:Typedef(rep) { - function rep(x:abs) -> rep; - function abs(x:rep) -> abs; -} - -forall self . class self:MemoryType { - function load(ptr:word) -> self; -} - -instance word:MemoryType { - function load(ptr:word) -> word { - return Typedef.abs(MemoryType.load(ptr) : word); - // `abs` does not make sense here, but it triggers the bug: - // the typechecker should complain about missing instance here - } -} - -contract C { - public function main() -> word { - let ptr : word = 0; - // if we inline the let below into return then another bug occurs: main is typed as forall a. () -> a - // let w:word = MemoryType.load(0); - return MemoryType.load(0); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/nano-desugared.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/nano-desugared.sol new file mode 100644 index 00000000..d8761ebe --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/nano-desugared.sol @@ -0,0 +1,457 @@ +function addW (x : word, y : word) returns (word) { + let res : word ; + assembly { res := add(x, y) + } + return res; +} +function subW (x : word, y : word) returns (word) { + let res : word ; + assembly { res := sub(x, y) + } + return res; +} +function addU(x: uint, y: uint) returns (uint) { + let res : word ; + let xw : word = Num.toWord(x) ; + let yw : word = Num.toWord(y) ; + assembly { res := add(xw, yw) + } + return uint(res); +} +function hash1(x: word) returns (word) { + let result : word = 0 ; + assembly { mstore(0, x) + result := keccak256(0, 32) + } + return result; +} +function hash2(x: word, y: word) returns (word) { + let result : word = 0 ; + assembly { mstore(0, x) + mstore(32, y) + result := keccak256(0, 64) + } + return result; +} +enum Bool { False, True } +function not(b: Bool) returns (Bool) { + match (b) { +case Bool.False { +return Bool.True; +} +case Bool.True { +return Bool.False; +} +} +} +function or(x: Bool, y: Bool) returns (Bool) { + match (x) { +case Bool.False { +return y; +} +case Bool.True { +return Bool.True; +} +} +} +function fromBool (b: Bool) returns (word) { + match (b) { +case Bool.False { +return 0; +} +case Bool.True { +return 1; +} +} +} +function toBool (x : word) returns (Bool) { + match (x) { +case 0 { +return Bool.False; +} +default { +return Bool.True; +} +} +} +trait Num { + function toWord(x: a) returns (word) ; + function fromWord(x: word) returns (a) ; + function add(x: a, y: a) returns (a) ; + function sub(x: a, y: a) returns (a) ; + function eq(x: a, y: a) returns (Bool) ; + function gt(x: a, y: a) returns (Bool) ; +} +impl Num { + function toWord(x: word) returns (word) { + return x; + } + function fromWord(x: word) returns (word) { + return x; + } + function add(x: word, y: word) returns (word) { + return addW(x, y); + } + function sub(x: word, y: word) returns (word) { + return addW(x, y); + } + function eq(x: word, y: word) returns (Bool) { + let res : word ; + assembly { res := eq(x, y) + } + return toBool(res); + } + function gt(x: word, y: word) returns (Bool) { + let res : word ; + assembly { res := gt(x, y) + } + return toBool(res); + } +} +function ge(x: a, y: a) returns (Bool) where a: Num { + return or(Num.gt(x, y), Num.eq(x, y)); +} +enum uint { uint(word) } +impl Num { + function toWord(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + + function fromWord(x: word) returns (uint) { + return uint(x); + } + function add(x: uint, y: uint) returns (uint) { + return uint(addW(Num.toWord(x), Num.toWord(y))); + } + function sub(x: uint, y: uint) returns (uint) { + return uint(subW(Num.toWord(x), Num.toWord(y))); + } + function eq(x: uint, y: uint) returns (Bool) { + return Num.eq(Num.toWord(x), Num.toWord(y)); + } + function gt(x: uint, y: uint) returns (Bool) { + return Num.gt(Num.toWord(x), Num.toWord(y)); + } +} +trait Typedef { + function rep(x: abs) returns (rep) ; + function abs(x: rep) returns (abs) ; +} +impl Typedef { + function rep(x: word) returns (word) { + return x; + } + function abs(x: word) returns (word) { + return x; + } +} +impl Typedef { + function rep(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + function abs(x: word) returns (uint) { + return uint(x); + } +} +enum address { address(word) } +impl Typedef { + function rep(x: address) returns (word) { + match (x) { +case address(y) { +return y; +} +} + } + function abs(x: word) returns (address) { + return address(x); + } +} +enum storage { storage(word) } +enum ContractStorage { ContractStorage(cxt) } +enum storageRef { storageRef(word) } +enum Proxy { Proxy } +enum mapping { mapping(word, Proxy, Proxy) } +enum mapRef { mapRef(word) } +impl Typedef, word> { + function rep(x: storage) returns (word) { + match (x) { +case storage(y) { +return y; +} +} + } + function abs(x: word) returns (storage) { + return storage(x); + } +} +impl Typedef, word> { + function rep(x: storageRef) returns (word) { + match (x) { +case storageRef(y) { +return y; +} +} + } + function abs(x: word) returns (storageRef) { + return storageRef(x); + } +} +trait Assign { + function assign(l: lhs, r: rhs) ; +} +enum ref { ref(a) } +impl Assign, a> { + function assign(l: ref, r: a) { + return (); + } +} +trait StorageType { + function sload(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} +trait StorageSize { + function size(x: Proxy) returns (word) ; +} +function sload_(x: word) returns (word) { + let res : word ; + assembly { res := sload(x) + } + return res; +} +function sstore_ (a : word, v : word) { + assembly { sstore(a, v) + } +} +impl StorageType { + function sload(ptr: word) returns (word) { + let r : word ; + assembly { r := sload(ptr) + } + return r; + } + function store(ptr: word, value: word) { + assembly { sstore(ptr, value) + } + } +} +impl StorageType { + function sload(ptr: word) returns (uint) { + return Typedef.abs(sload_(ptr)) ; + } + function store(ptr: word, value: uint) { + return sstore_(ptr, Typedef.rep(value)); + } +} +impl StorageType
{ + function sload(ptr: word) returns (address) { + return Typedef.abs(sload_(ptr)) ; + } + function store(ptr: word, value: address) { + return sstore_(ptr, Typedef.rep(value)); + } +} +impl Assign, a> where a: StorageType { + function assign(l: storageRef, y: a) { + StorageType.store(Typedef.rep(l), y); + } +} +trait CStructField { +} +enum StructField { StructField(structType) } +enum MemberAccessProxy { MemberAccessProxy(a, field) } +function memberAccessD1(x: MemberAccessProxy) returns (a) { + match (x) { +case MemberAccessProxy(y, z) { +return y; +} +} +} +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} +trait RValueMemberAccess { + function memberAccess(x: self) returns (memberValueType) ; +} +impl LValueMemberAccess, fieldSelector, offsetType>, storageRef> where StructField: CStructField, offsetType: StorageSize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (storageRef) { + let ptr : word = Typedef.rep(memberAccessD1(x)) ; + let size : word = StorageSize.size(@offsetType) ; + assembly { ptr := add(ptr, size) + } + return storageRef(ptr); + } +} +impl StorageSize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} +impl StorageSize
{ + function size(x: Proxy
) returns (word) { + return 1; + } +} +impl StorageSize<(a, b)> where a: StorageSize, b: StorageSize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz : word = StorageSize.size(@a) ; + let b_sz : word = StorageSize.size(@b) ; + assembly { a_sz := add(a_sz, b_sz) + } + return a_sz; + } +} +impl LValueMemberAccess, fieldSelector, offsetType>, storageRef> where StructField, fieldSelector>: CStructField, offsetType: StorageSize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (storageRef) { + let ptr : word = 256 ; + let offsetSize : word = StorageSize.size(@offsetType) ; + assembly { ptr := add(ptr, offsetSize) + } + return storageRef(ptr); + } +} +impl RValueMemberAccess, fieldSelector, offsetType>, fieldType> where StructField, fieldSelector>: CStructField, fieldType: StorageType, offsetType: StorageSize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (fieldType) { + let ptr : word = 256 ; + let offsetSize : word = StorageSize.size(@offsetType) ; + return StorageType.sload(addW(ptr, offsetSize)) ; + } +} +enum mapping { mapping(word) } +impl Typedef member), word> { + function rep(x: mapping(index => member)) returns (word) { + match (x) { +case mapping(y) { +return y; +} +} + } + function abs(x: word) returns (mapping(index => member)) { + return mapping(x); + } +} +impl StorageSize member)> { + function size(x: Proxy member)>) returns (word) { + return 1; + } +} +enum IndexAccessProxy { IndexAccessProxy(map, index) } +impl LValueMemberAccess member)>, index, member>, storageRef> where index: Typedef { + function memberAccess(x: IndexAccessProxy member)>, index, member>) returns (storageRef) { + return storageRef(indexStorageSlot(x)); + } +} +impl RValueMemberAccess, member> where index: Typedef, member: StorageType, map: Typedef { + function memberAccess(x: IndexAccessProxy) returns (member) { + let slot : word = indexStorageSlot(x) ; + return StorageType.sload(slot); + } +} +function indexStorageSlot(x: IndexAccessProxy) returns (word) where map: Typedef, index: Typedef { + match (x) { +case IndexAccessProxy(map, i) { +let mapptr : word = Typedef.rep(map) ; + let rawidx : word = Typedef.rep(i) ; + let loc : word = hash2(mapptr, rawidx) ; + return loc; +} +} +} +function rval(x: a) returns (b) where a: RValueMemberAccess { + return RValueMemberAccess.memberAccess(x); +} +function caller() returns (address) { + let res : word ; + assembly { res := caller() + } + return address(res); +} +function require1fail () { + let res : word ; + assembly { mstore(0, 2320231852978620534530211544385868) + revert(0, 32) + } + return (); +} +function require1 (cond : Bool) { + match (cond) { +case Bool.False { +return require1fail(); +} +case Bool.True { +return (); +} +} +} +function nop() { + return (); +} +enum UintCxt { UintCxt } +enum reserved_sel { reserved_sel } +impl CStructField, reserved_sel>, word, ()> { +} +enum msg_sender_sel { msg_sender_sel } +impl CStructField, msg_sender_sel>, address, (word, ())> { +} +enum owner_sel { owner_sel } +impl CStructField, owner_sel>, address, (word, (address, ()))> { +} +enum decimals_sel { decimals_sel } +impl CStructField, decimals_sel>, uint, (word, (address, (address, ())))> { +} +enum totalSupply_sel { totalSupply_sel } +impl CStructField, totalSupply_sel>, uint, (word, (address, (address, (uint, ()))))> { +} +enum balances_sel { balances_sel } +impl CStructField, balances_sel>, mapping(address => uint), (word, (address, (address, (uint, (uint, ())))))> { +} +contract Uint { + function mint(amount: uint) public { + Assign.assign(LValueMemberAccess.memberAccess(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)))), Num.add(rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)))), amount)); + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), totalSupply_sel)), Num.add(rval(MemberAccessProxy(ContractStorage(UintCxt), totalSupply_sel)), amount)); + } + function transferFrom(src: address, dst: address, amt: uint) public returns (Bool) { + require1(ge(rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), src)), amt)); + withdraw(src, amt); + deposit(dst, amt); + return Bool.True; + } + function withdraw(src: address, amt: uint) public { + Assign.assign(LValueMemberAccess.memberAccess(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), src)), Num.sub(rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), src)), amt) ); + } + function deposit(dst: address, amt: uint) public { + Assign.assign(LValueMemberAccess.memberAccess(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), dst)), Num.add(rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), dst)), amt) ); + } + function init() public { + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)), address(81985529216486895)); + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), msg_sender_sel)), caller()); + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), decimals_sel)), Num.fromWord(18)); + } + function main() public returns (uint) { + init(); + mint(uint(1000)); + mint(uint(1000)); + let amt = uint(1) ; + let src : address = rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)) ; + transferFrom(rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), msg_sender_sel)), uint(42)); + require1(Bool.True) ; + return rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), msg_sender_sel)))); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/nano-desugared.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/nano-desugared.solc deleted file mode 100644 index 055dc9f8..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/nano-desugared.solc +++ /dev/null @@ -1,439 +0,0 @@ -function addW (x : word, y : word) { - let res : word ; - assembly { res := add(x, y) - } - return res; -} -function subW (x : word, y : word) { - let res : word ; - assembly { res := sub(x, y) - } - return res; -} -function addU (x : uint, y : uint) -> uint { - let res : word ; - let xw : word = Num.toWord(x) ; - let yw : word = Num.toWord(y) ; - assembly { res := add(xw, yw) - } - return uint(res); -} -function hash1 (x : word) -> word { - let result : word = 0 ; - assembly { mstore(0, x) - result := keccak256(0, 32) - } - return result; -} -function hash2 (x : word, y : word) -> word { - let result : word = 0 ; - assembly { mstore(0, x) - mstore(32, y) - result := keccak256(0, 64) - } - return result; -} -data Bool = False | True ; -function not (b : Bool) -> Bool { - match (b) { - | Bool.False => - return Bool.True; - | Bool.True => - return Bool.False; - } -} -function or (x : Bool, y : Bool) -> Bool { - match (x) { - | Bool.False => - return y; - | Bool.True => - return Bool.True; - } -} -function fromBool (b) { - match (b) { - | Bool.False => - return 0; - | Bool.True => - return 1; - } -} -function toBool (x : word) { - match (x) { - | 0 => - return Bool.False; - | _ => - return Bool.True; - } -} -forall a . class a : Num { - function toWord (x : a) -> word; - function fromWord (x : word) -> a; - function add (x : a, y : a) -> a; - function sub (x : a, y : a) -> a; - function eq (x : a, y : a) -> Bool; - function gt (x : a, y : a) -> Bool; -} -instance word : Num { - function toWord (x : word) -> word { - return x; - } - function fromWord (x : word) -> word { - return x; - } - function add (x : word, y : word) -> word { - return addW(x, y); - } - function sub (x : word, y : word) -> word { - return addW(x, y); - } - function eq (x : word, y : word) -> Bool { - let res : word ; - assembly { res := eq(x, y) - } - return toBool(res); - } - function gt (x : word, y : word) -> Bool { - let res : word ; - assembly { res := gt(x, y) - } - return toBool(res); - } -} -forall a . a : Num => function ge (x : a, y : a) -> Bool { - return or(Num.gt(x, y), Num.eq(x, y)); -} -data uint = uint(word) ; -instance uint : Num { - function toWord (x : uint) -> word { - match (x) { - | uint(y) => - return y; - } - } - - function fromWord (x : word) -> uint { - return uint(x); - } - function add (x : uint, y : uint) -> uint { - return uint(addW(Num.toWord(x), Num.toWord(y))); - } - function sub (x : uint, y : uint) -> uint { - return uint(subW(Num.toWord(x), Num.toWord(y))); - } - function eq (x : uint, y : uint) -> Bool { - return Num.eq(Num.toWord(x), Num.toWord(y)); - } - function gt (x : uint, y : uint) -> Bool { - return Num.gt(Num.toWord(x), Num.toWord(y)); - } -} -forall abs rep . class abs : Typedef (rep) { - function rep (x : abs) -> rep; - function abs (x : rep) -> abs; -} -instance word : Typedef (word) { - function rep (x : word) -> word { - return x; - } - function abs (x : word) -> word { - return x; - } -} -instance uint : Typedef (word) { - function rep (x : uint) -> word { - match (x) { - | uint(y) => - return y; - } - } - function abs (x : word) -> uint { - return uint(x); - } -} -data address = address(word) ; -instance address : Typedef (word) { - function rep (x : address) -> word { - match (x) { - | address(y) => - return y; - } - } - function abs (x : word) -> address { - return address(x); - } -} -data storage (a) = storage(word) ; -data ContractStorage (cxt) = ContractStorage(cxt) ; -data storageRef (a) = storageRef(word) ; -data Proxy (a) = Proxy ; -data mapping (member, index) = mapping(word, Proxy(member), Proxy(index)) ; -data mapRef (a) = mapRef(word) ; -forall a . instance storage(a) : Typedef (word) { - function rep (x : storage(a)) -> word { - match (x) { - | storage(y) => - return y; - } - } - function abs (x : word) -> storage(a) { - return storage(x); - } -} -forall a . instance storageRef(a) : Typedef (word) { - function rep (x : storageRef(a)) -> word { - match (x) { - | storageRef(y) => - return y; - } - } - function abs (x : word) -> storageRef(a) { - return storageRef(x); - } -} -forall lhs rhs . class lhs : Assign (rhs) { - function assign (l : lhs, r : rhs) -> (); -} -data ref (a) = ref(a) ; -forall a . instance ref(a) : Assign (a) { - function assign (l : ref(a), r : a) -> () { - return (); - } -} -forall self . class self : StorageType { - function sload (ptr : word) -> self; - function store (ptr : word, value : self) -> (); -} -forall self . class self : StorageSize { - function size (x : Proxy(self)) -> word; -} -function sload_ (x : word) -> word { - let res : word ; - assembly { res := sload(x) - } - return res; -} -function sstore_ (a : word, v : word) { - assembly { sstore(a, v) - } -} -instance word : StorageType { - function sload (ptr : word) -> word { - let r : word ; - assembly { r := sload(ptr) - } - return r; - } - function store (ptr : word, value : word) -> () { - assembly { sstore(ptr, value) - } - } -} -instance uint : StorageType { - function sload (ptr : word) -> uint { - return Typedef.abs(sload_(ptr)) : uint; - } - function store (ptr : word, value : uint) -> () { - return sstore_(ptr, Typedef.rep(value)); - } -} -instance address : StorageType { - function sload (ptr : word) -> address { - return Typedef.abs(sload_(ptr)) : address; - } - function store (ptr : word, value : address) -> () { - return sstore_(ptr, Typedef.rep(value)); - } -} -forall a . a : StorageType => instance storageRef(a) : Assign (a) { - function assign (l : storageRef(a), y : a) -> () { - StorageType.store(Typedef.rep(l), y); - } -} -forall self fieldType offsetType . class self :CStructField(fieldType, offsetType) { -} -data StructField (structType, fieldSelector) = StructField(structType) ; -data MemberAccessProxy (a, field, offset) = MemberAccessProxy(a, field) ; -forall a field offset . function memberAccessD1 (x : MemberAccessProxy(a, field, offset)) -> a { - match (x) { - | MemberAccessProxy(y, z) => - return y; - } -} -forall self memberRefType . class self : LValueMemberAccess (memberRefType) { - function memberAccess (x : self) -> memberRefType; -} -forall self memberValueType . class self : RValueMemberAccess (memberValueType) { - function memberAccess (x : self) -> memberValueType; -} -forall structType fieldSelector fieldType offsetType . StructField(structType, fieldSelector) :CStructField(fieldType, offsetType), offsetType : StorageSize => instance MemberAccessProxy(storage(structType), fieldSelector, offsetType) : LValueMemberAccess (storageRef(fieldType)) { - function memberAccess (x : MemberAccessProxy(storage(structType), fieldSelector, offsetType)) -> storageRef(fieldType) { - let ptr : word = Typedef.rep(memberAccessD1(x)) ; - let size : word = StorageSize.size(Proxy : Proxy(offsetType)) ; - assembly { ptr := add(ptr, size) - } - return storageRef(ptr); - } -} -instance () : StorageSize { - function size (x : Proxy(())) -> word { - return 0; - } -} -instance word : StorageSize { - function size (x : Proxy(word)) -> word { - return 1; - } -} -instance uint : StorageSize { - function size (x : Proxy(uint)) -> word { - return 1; - } -} -instance address : StorageSize { - function size (x : Proxy(address)) -> word { - return 1; - } -} -forall a b . a : StorageSize, b : StorageSize => instance (a, b) : StorageSize { - function size (x : Proxy((a, b))) -> word { - let a_sz : word = StorageSize.size(Proxy : Proxy(a)) ; - let b_sz : word = StorageSize.size(Proxy : Proxy(b)) ; - assembly { a_sz := add(a_sz, b_sz) - } - return a_sz; - } -} -forall cxt fieldSelector fieldType offsetType . StructField(ContractStorage(cxt), fieldSelector) :CStructField(fieldType, offsetType), offsetType : StorageSize => instance MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType) : LValueMemberAccess (storageRef(fieldType)) { - function memberAccess (x : MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType)) -> storageRef(fieldType) { - let ptr : word = 256 ; - let offsetSize : word = StorageSize.size(Proxy : Proxy(offsetType)) ; - assembly { ptr := add(ptr, offsetSize) - } - return storageRef(ptr); - } -} -forall cxt fieldSelector fieldType offsetType . StructField(ContractStorage(cxt), fieldSelector) :CStructField(fieldType, offsetType), fieldType : StorageType, offsetType : StorageSize => instance MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType) : RValueMemberAccess (fieldType) { - function memberAccess (x : MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType)) -> fieldType { - let ptr : word = 256 ; - let offsetSize : word = StorageSize.size(Proxy : Proxy(offsetType)) ; - return StorageType.sload(addW(ptr, offsetSize)) : fieldType; - } -} -data mapping (index, member) = mapping(word) ; -forall member index . instance mapping(index, member) : Typedef (word) { - function rep (x : mapping(index, member)) -> word { - match (x) { - | mapping(y) => - return y; - } - } - function abs (x : word) -> mapping(index, member) { - return mapping(x); - } -} -forall index member . instance mapping(index, member) : StorageSize { - function size (x : Proxy(mapping(index, member))) -> word { - return 1; - } -} -data IndexAccessProxy (map, index, member) = IndexAccessProxy(map, index) ; -forall index member . index : Typedef (word) => instance IndexAccessProxy(storageRef(mapping(index, member)), index, member) : LValueMemberAccess (storageRef(member)) { - function memberAccess (x : IndexAccessProxy(storageRef(mapping(index, member)), index, member)) -> storageRef(member) { - return storageRef(indexStorageSlot(x)); - } -} -forall map index member . index : Typedef (word), member : StorageType, map : Typedef (word) => instance IndexAccessProxy(map, index, member) : RValueMemberAccess (member) { - function memberAccess (x : IndexAccessProxy(map, index, member)) -> member { - let slot : word = indexStorageSlot(x) ; - return StorageType.sload(slot); - } -} -forall index map member . map : Typedef (word), index : Typedef (word) => function indexStorageSlot (x : IndexAccessProxy(map, index, member)) -> word { - match (x) { - | IndexAccessProxy(map, i) => - let mapptr : word = Typedef.rep(map) ; - let rawidx : word = Typedef.rep(i) ; - let loc : word = hash2(mapptr, rawidx) ; - return loc; - } -} -forall a b . a : RValueMemberAccess (b) => function rval (x : a) -> b { - return RValueMemberAccess.memberAccess(x); -} -function caller () -> address { - let res : word ; - assembly { res := caller() - } - return address(res); -} -function require1fail () { - let res : word ; - assembly { mstore(0, 2320231852978620534530211544385868) - revert(0, 32) - } - return (); -} -function require1 (cond : Bool) { - match (cond) { - | Bool.False => - return require1fail(); - | Bool.True => - return (); - } -} -function nop () -> () { - return (); -} -data UintCxt = UintCxt ; -data reserved_sel = reserved_sel ; -instance StructField(ContractStorage(UintCxt), reserved_sel) :CStructField(word, ()) { -} -data msg_sender_sel = msg_sender_sel ; -instance StructField(ContractStorage(UintCxt), msg_sender_sel) :CStructField(address, (word, ())) { -} -data owner_sel = owner_sel ; -instance StructField(ContractStorage(UintCxt), owner_sel) :CStructField(address, (word, (address, ()))) { -} -data decimals_sel = decimals_sel ; -instance StructField(ContractStorage(UintCxt), decimals_sel) :CStructField(uint, (word, (address, (address, ())))) { -} -data totalSupply_sel = totalSupply_sel ; -instance StructField(ContractStorage(UintCxt), totalSupply_sel) :CStructField(uint, (word, (address, (address, (uint, ()))))) { -} -data balances_sel = balances_sel ; -instance StructField(ContractStorage(UintCxt), balances_sel) :CStructField(mapping(address, uint), (word, (address, (address, (uint, (uint, ())))))) { -} -contract Uint { - public function mint (amount : uint) { - Assign.assign(LValueMemberAccess.memberAccess(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)))), Num.add(rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)))), amount)); - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), totalSupply_sel)), Num.add(rval(MemberAccessProxy(ContractStorage(UintCxt), totalSupply_sel)), amount)); - } - public function transferFrom (src : address, dst : address, amt : uint) -> Bool { - require1(ge(rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), src)), amt)); - withdraw(src, amt); - deposit(dst, amt); - return Bool.True; - } - public function withdraw (src : address, amt : uint) { - Assign.assign(LValueMemberAccess.memberAccess(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), src)), Num.sub(rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), src)), amt) : uint); - } - public function deposit (dst : address, amt : uint) { - Assign.assign(LValueMemberAccess.memberAccess(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), dst)), Num.add(rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), dst)), amt) : uint); - } - public function init () { - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)), address(81985529216486895)); - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), msg_sender_sel)), caller()); - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), decimals_sel)), Num.fromWord(18)); - } - public function main () -> uint { - init(); - mint(uint(1000)); - mint(uint(1000)); - let amt = uint(1) ; - let src : address = rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)) ; - transferFrom(rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), msg_sender_sel)), uint(42)); - require1(Bool.True) : (); - return rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), msg_sender_sel)))):uint; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/noconstr.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/noconstr.sol new file mode 100644 index 00000000..7f2634ce --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/noconstr.sol @@ -0,0 +1,17 @@ +trait Foo { + function foo(x: a) returns (word) ; +} + +// here the constraint a : Foo is +// defered to outer scope where the +// error should be detected. + +function bla(x: a) returns (word) { + return Foo.foo(x); +} + +contract Test { + function main() public returns (word) { + return bla(1); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/noconstr.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/noconstr.solc deleted file mode 100644 index 286d0ee6..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/noconstr.solc +++ /dev/null @@ -1,17 +0,0 @@ -class a : Foo { - function foo (x : a) -> word; -} - -// here the constraint a : Foo is -// defered to outer scope where the -// error should be detected. - -function bla (x : a) -> word { - return Foo.foo(x); -} - -contract Test { - public function main() { - return bla(1); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-detected.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-detected.sol new file mode 100644 index 00000000..78d4fbe0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-detected.sol @@ -0,0 +1,13 @@ +type W = word; + +trait IdTy { + function id(x: self) returns (self) ; +} + +impl IdTy { + function id(x: W) returns (W) { return x; } +} + +impl IdTy { + function id(x: word) returns (word) { return 0; } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-detected.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-detected.solc deleted file mode 100644 index fe64a653..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-detected.solc +++ /dev/null @@ -1,13 +0,0 @@ -type W = word; - -forall self . class self:IdTy { - function id(x:self) -> self; -} - -instance W:IdTy { - function id(x:W) -> W { return x; } -} - -instance word:IdTy { - function id(x:word) -> word { return 0; } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-missed-order.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-missed-order.sol new file mode 100644 index 00000000..c47074d6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-missed-order.sol @@ -0,0 +1,13 @@ +type W = word; + +trait IdTy { + function id(x: self) returns (self) ; +} + +impl IdTy { + function id(x: word) returns (word) { return 0; } +} + +impl IdTy { + function id(x: W) returns (W) { return x; } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-missed-order.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-missed-order.solc deleted file mode 100644 index faa30e06..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-missed-order.solc +++ /dev/null @@ -1,13 +0,0 @@ -type W = word; - -forall self . class self:IdTy { - function id(x:self) -> self; -} - -instance word:IdTy { - function id(x:word) -> word { return 0; } -} - -instance W:IdTy { - function id(x:W) -> W { return x; } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-missed-two-synonyms.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-missed-two-synonyms.sol new file mode 100644 index 00000000..023094be --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-missed-two-synonyms.sol @@ -0,0 +1,14 @@ +type W = word; +type V = word; + +trait IdTy { + function id(x: self) returns (self) ; +} + +impl IdTy { + function id(x: W) returns (W) { return x; } +} + +impl IdTy { + function id(x: V) returns (V) { return 0; } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-missed-two-synonyms.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-missed-two-synonyms.solc deleted file mode 100644 index 31cb10fa..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlap-synonym-missed-two-synonyms.solc +++ /dev/null @@ -1,14 +0,0 @@ -type W = word; -type V = word; - -forall self . class self:IdTy { - function id(x:self) -> self; -} - -instance W:IdTy { - function id(x:W) -> W { return x; } -} - -instance V:IdTy { - function id(x:V) -> V { return 0; } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlapping-heads.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlapping-heads.sol new file mode 100644 index 00000000..38382c67 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlapping-heads.sol @@ -0,0 +1,15 @@ +trait Foo { + function foo(x: a, y: word) returns (b) ; +} + +impl Foo<(), ()> { + function foo(x: (), y: word) { + return (); + } +} + +impl Foo { + function foo(x: a, y: word) { + return (); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlapping-heads.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlapping-heads.solc deleted file mode 100644 index 152ad54f..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/overlapping-heads.solc +++ /dev/null @@ -1,15 +0,0 @@ -forall a b . class a : Foo(b) { - function foo (x : a, y : word) -> b; -} - -instance () : Foo (()) { - function foo (x : (), y : word) -> () { - return (); - } -} - -forall a . instance a : Foo (()) { - function foo (x : a, y : word) -> () { - return (); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/patterson-bug.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/patterson-bug.sol new file mode 100644 index 00000000..d6f0eba0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/patterson-bug.sol @@ -0,0 +1,73 @@ +enum storage { storage(word) } +enum storageRef { storageRef(word) } +enum Proxy { Proxy } + +enum mapping { mapping(word, Proxy, Proxy) } // storage by default +// data mapRef(a) = mapRef(word); //ref to a map elem + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +impl Assign, a> { + function assign(l: storageRef, y: a) { + + } +} + +trait CStructField {} +enum StructField { StructField(structType) } + + +enum MemberAccessProxy { MemberAccessProxy(a, field) } + + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +// ------------------------------------------------------------------ +// Contract field access +// ------------------------------------------------------------------ + + +impl LValueMemberAccess, storageRef> where StructField: CStructField { + function memberAccess(x: MemberAccessProxy) returns (storageRef) { + return storageRef(0x100); + } +} + +// ------------------------------------------------------------------ +// Indexed access +// ------------------------------------------------------------------ + +enum mapping { mapping(word) } +enum IndexAccessProxy { IndexAccessProxy(map, index) } +enum IndexAccessProxy2 { IndexAccessProxy2(map, index, Proxy) } + +impl LValueMemberAccess member)>, index, member>, storageRef> { + function memberAccess(x: IndexAccessProxy, index, member>) returns (storageRef) { + return storageRef(0); + } +} + +enum MintCtx { MintCtx } +enum balances_sel { balances_sel } +impl CStructField, mapping(word => word), ()> {} + + function mint(amount:word) { + let bal_prx = MemberAccessProxy(MintCtx, balances_sel); + let bal_ref = LValueMemberAccess.memberAccess(bal_prx); + + Assign.assign( + LValueMemberAccess.memberAccess( + IndexAccessProxy( + // bal_ref // this works, but inlining bal_ref leads to error + LValueMemberAccess.memberAccess(bal_prx) + , 0 + ) + ) + , amount + ) ; + + } diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/patterson-bug.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/patterson-bug.solc deleted file mode 100644 index 4e636df6..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/patterson-bug.solc +++ /dev/null @@ -1,76 +0,0 @@ -data storage(a) = storage(word); -data storageRef(a) = storageRef(word); -data Proxy(a) = Proxy; - -data mapping(member, index) = mapping(word, Proxy(member), Proxy(index)); // storage by default -// data mapRef(a) = mapRef(word); //ref to a map elem - -forall lhs rhs . class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -forall a . instance storageRef(a):Assign(a) { - function assign(l:storageRef(a), y:a) -> () { - - } -} - -forall self fieldType offsetType . class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - - -data MemberAccessProxy(a, field, offset) = MemberAccessProxy(a, field); - - -forall self memberRefType . class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -// ------------------------------------------------------------------ -// Contract field access -// ------------------------------------------------------------------ - - -forall cxt fieldSelector fieldType offsetType - . StructField(cxt, fieldSelector):CStructField(fieldType, offsetType) - => instance MemberAccessProxy(cxt, fieldSelector, offsetType):LValueMemberAccess(storageRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(cxt, fieldSelector, offsetType)) -> storageRef(fieldType) { - return storageRef(0x100); - } -} - -// ------------------------------------------------------------------ -// Indexed access -// ------------------------------------------------------------------ - -data mapping(index, member) = mapping(word); -data IndexAccessProxy(map, index, member) = IndexAccessProxy(map, index); -data IndexAccessProxy2(map, index, member) = IndexAccessProxy2(map, index, Proxy(member)); - -forall map index member. - instance IndexAccessProxy(storageRef(mapping(index,member)), index, member):LValueMemberAccess(storageRef(member)) { - function memberAccess(x:IndexAccessProxy(storageRef(map), index, member)) -> storageRef(member) { - return storageRef(0); - } -} - -data MintCtx = MintCtx; -data balances_sel = balances_sel; -instance StructField(MintCtx, balances_sel):CStructField(mapping(word,word), ()) {} - - function mint(amount:word) { - let bal_prx = MemberAccessProxy(MintCtx, balances_sel); - let bal_ref = LValueMemberAccess.memberAccess(bal_prx); - - Assign.assign( - LValueMemberAccess.memberAccess( - IndexAccessProxy( - // bal_ref // this works, but inlining bal_ref leads to error - LValueMemberAccess.memberAccess(bal_prx) - , 0 - ) - ) - , amount - ) ; - - } diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/payable-toplevel-function.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/payable-toplevel-function.snap index 505c4182..cc018808 100644 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/payable-toplevel-function.snap +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/payable-toplevel-function.snap @@ -1,14 +1,14 @@ --- source: crates/parser/tests/diagnostics.rs expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/payable-toplevel-function.solc +input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/payable-toplevel-function.sol --- error[SC0001]: `payable` is only allowed on a function, constructor, or fallback inside a contract - --> /payable-toplevel-function.solc:3:1 + --> /payable-toplevel-function.sol:3:20 | 2 | // never on a top-level function. This must fail to parse. -3 | payable function deposit() -> uint256 { - | ^^^^^^^ +3 | function deposit() payable returns (uint256) { + | ^^^^^^^ 4 | return 0; | = note: while parsing function signature diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/payable-toplevel-function.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/payable-toplevel-function.sol new file mode 100644 index 00000000..c9fbc3ef --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/payable-toplevel-function.sol @@ -0,0 +1,5 @@ +// `payable` is only valid on a function/fallback inside a contract, +// never on a top-level function. This must fail to parse. +function deposit() payable returns (uint256) { + return 0; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/payable-toplevel-function.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/payable-toplevel-function.solc deleted file mode 100644 index 18f778fc..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/payable-toplevel-function.solc +++ /dev/null @@ -1,5 +0,0 @@ -// `payable` is only valid on a function/fallback inside a contract, -// never on a top-level function. This must fail to parse. -payable function deposit() -> uint256 { - return 0; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_fail_coverage.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_fail_coverage.sol new file mode 100644 index 00000000..24d78ee1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_fail_coverage.sol @@ -0,0 +1,10 @@ +// Negative test for pragma merging - should fail +import pragma_merge_base; + +trait TestFailClass {} + +enum FailType { FailType } + +// should fail because TestFailCoverage doesn't have no-coverage-condition +trait TestFailCoverage {} +impl TestFailCoverage, y> {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_fail_coverage.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_fail_coverage.solc deleted file mode 100644 index 2576051f..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_fail_coverage.solc +++ /dev/null @@ -1,10 +0,0 @@ -// Negative test for pragma merging - should fail -import pragma_merge_base; - -forall a . class a:TestFailClass {} - -data FailType(x) = FailType; - -// should fail because TestFailCoverage doesn't have no-coverage-condition -forall a b . class a:TestFailCoverage(b) {} -forall x y . instance FailType(x):TestFailCoverage(y) {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_fail_patterson.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_fail_patterson.sol new file mode 100644 index 00000000..0187b84a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_fail_patterson.sol @@ -0,0 +1,11 @@ +// This file should FAIL compilation to demonstrate that checks are working when the imported file contains violations + +import pragma_merge_base; + + +// --- Patterson Violation --- + +trait TestFailClass {} + +// Should fail because TestFailClass doesn't have no-patterson-condition +impl TestFailClass where U: TestClassP1, U: TestClassP2, U: TestClassP3 {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_fail_patterson.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_fail_patterson.solc deleted file mode 100644 index 89637290..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_fail_patterson.solc +++ /dev/null @@ -1,11 +0,0 @@ -// This file should FAIL compilation to demonstrate that checks are working when the imported file contains violations - -import pragma_merge_base; - - -// --- Patterson Violation --- - -forall a . class a:TestFailClass {} - -// Should fail because TestFailClass doesn't have no-patterson-condition -forall U . U:TestClassP1, U:TestClassP2, U:TestClassP3 => instance U:TestFailClass {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_import.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_import.sol new file mode 100644 index 00000000..44e7f726 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_import.sol @@ -0,0 +1,22 @@ +// Test import file for pragma merging functionality +// This file imports pragma_merge_base and adds its own violations with pragmas +// Testing that pragmas from both files are properly merged + +import pragma_merge_base; + +// Add more pragmas - these should merge with imported ones + +trait TestClassC3 {} +trait TestClassB4 {} + +// fails coverage & patterson (pragma set here) +impl TestClassC3 where (i, j): TestClassP1 {} + +// fails coverage & patterson (pragma set in base) +impl TestClassP3 where (i, j): TestClassP1 {} + +// fails bound var & patterson (pragma set here) +impl TestClassB4> where c: TestClassB1 {} + +// fails bound var & patterson (pragma set in base) +impl TestClassB3> where c: TestClassB1 {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_import.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_import.solc deleted file mode 100644 index 418fb766..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_import.solc +++ /dev/null @@ -1,22 +0,0 @@ -// Test import file for pragma merging functionality -// This file imports pragma_merge_base and adds its own violations with pragmas -// Testing that pragmas from both files are properly merged - -import pragma_merge_base; - -// Add more pragmas - these should merge with imported ones - -forall a b . class a:TestClassC3(b) {} -forall a . class a:TestClassB4 {} - -// fails coverage & patterson (pragma set here) -forall i j . (i,j):TestClassP1 => instance i:TestClassC3(j) {} - -// fails coverage & patterson (pragma set in base) -forall i j . (i,j):TestClassP1 => instance i:TestClassP3(j) {} - -// fails bound var & patterson (pragma set here) -forall a c . c:TestClassB1(a) => instance TestType1(a):TestClassB4 {} - -// fails bound var & patterson (pragma set in base) -forall a c . c:TestClassB1(a) => instance TestType1(a):TestClassB3 {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_verify.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_verify.sol new file mode 100644 index 00000000..05d1787a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_verify.sol @@ -0,0 +1,13 @@ +// Verification file for pragma merging +// This file imports pragma_merge_base but has no pragmas of its own +// Tests that pragmas from imported files are properly inherited + +import pragma_merge_base; + +enum VerifyType { VerifyType } + +// Would fail without imported pragma no-patterson-condition TestClassP3 +impl TestClassP3 where (a, word): TestClassP3 {} + +// Would fail without imported pragma no-coverage-condition TestClassC1 +impl TestClassC1, q> {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_verify.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_verify.solc deleted file mode 100644 index 123f9b51..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/pragma_merge_verify.solc +++ /dev/null @@ -1,13 +0,0 @@ -// Verification file for pragma merging -// This file imports pragma_merge_base but has no pragmas of its own -// Tests that pragmas from imported files are properly inherited - -import pragma_merge_base; - -data VerifyType(x) = VerifyType; - -// Would fail without imported pragma no-patterson-condition TestClassP3 -forall a . (a,word):TestClassP3(a) => instance a:TestClassP3(word) {} - -// Would fail without imported pragma no-coverage-condition TestClassC1 -forall p q . instance VerifyType(p):TestClassC1(q) {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/proxy1.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/proxy1.sol new file mode 100644 index 00000000..bfcce334 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/proxy1.sol @@ -0,0 +1,9 @@ +enum Proxy { Proxy } + +trait C { + function fun(p: Proxy) returns (word) ; +} + +function morefun(p: Proxy) returns (word) { + return C.fun(@t); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/proxy1.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/proxy1.solc deleted file mode 100644 index 34da29be..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/proxy1.solc +++ /dev/null @@ -1,9 +0,0 @@ -data Proxy(a) = Proxy; - -forall a. class a:C { - function fun(p:Proxy(a)) -> word; -} - -forall t. function morefun(p:Proxy(t)) -> word { - return C.fun(Proxy:Proxy(t)); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-constructor.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-constructor.snap index 8ccc711b..9140c4d2 100644 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-constructor.snap +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-constructor.snap @@ -1,14 +1,14 @@ --- source: crates/parser/tests/diagnostics.rs expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-constructor.solc +input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-constructor.sol --- error[SC0001]: constructor is implicitly public; remove the 'public' keyword - --> /public-constructor.solc:5:5 + --> /public-constructor.sol:5:19 | 4 | contract PublicConstructor { -5 | public constructor() {} - | ^^^^^^ +5 | constructor() public {} + | ^^^^^^ 6 | | = note: while parsing constructor definition diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-constructor.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-constructor.sol new file mode 100644 index 00000000..73b60c1e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-constructor.sol @@ -0,0 +1,10 @@ +import * from std; +import * from std.dispatch; + +contract PublicConstructor { + constructor() public {} + + function answer() public returns (uint256) { + return uint256(42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-constructor.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-constructor.solc deleted file mode 100644 index 99728d16..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-constructor.solc +++ /dev/null @@ -1,10 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract PublicConstructor { - public constructor() {} - - public function answer() -> uint256 { - return uint256(42); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-fallback.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-fallback.snap index 1a08cc64..694dfc94 100644 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-fallback.snap +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-fallback.snap @@ -1,14 +1,14 @@ --- source: crates/parser/tests/diagnostics.rs expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-fallback.solc +input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-fallback.sol --- error[SC0001]: fallback is implicitly public; remove the 'public' keyword - --> /public-fallback.solc:7:5 + --> /public-fallback.sol:7:16 | 6 | -7 | public fallback() -> () { - | ^^^^^^ +7 | fallback() public { + | ^^^^^^ 8 | revert("fallback-was-called"); | = note: while parsing fallback definition diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-fallback.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-fallback.sol new file mode 100644 index 00000000..020d3fa8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-fallback.sol @@ -0,0 +1,10 @@ +import * from std; +import * from std.dispatch; + +contract PublicFallback { + constructor() {} + + fallback() public { + revert("fallback-was-called"); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-fallback.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-fallback.solc deleted file mode 100644 index a4a37821..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-fallback.solc +++ /dev/null @@ -1,10 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract PublicFallback { - constructor() {} - - public fallback() -> () { - revert("fallback-was-called"); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-top-level-function.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-top-level-function.snap index 5bf70516..3c4fd6f0 100644 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-top-level-function.snap +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-top-level-function.snap @@ -1,14 +1,14 @@ --- source: crates/parser/tests/diagnostics.rs expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-top-level-function.solc +input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-top-level-function.sol --- error[SC0001]: 'public' is only allowed on functions declared inside a contract - --> /public-top-level-function.solc:6:1 + --> /public-top-level-function.sol:6:19 | 5 | // top-level function (outside any `contract { … }` body) must be rejected. -6 | public function answer() -> uint256 { - | ^^^^^^ +6 | function answer() public returns (uint256) { + | ^^^^^^ 7 | return uint256(42); | = note: while parsing function signature diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-top-level-function.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-top-level-function.sol new file mode 100644 index 00000000..f65df4b9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-top-level-function.sol @@ -0,0 +1,8 @@ +import * from std; +import * from std.dispatch; + +// `public` is a contract-function visibility modifier. Applying it to a +// top-level function (outside any `contract { … }` body) must be rejected. +function answer() public returns (uint256) { + return uint256(42); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-top-level-function.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-top-level-function.solc deleted file mode 100644 index 4e553ad1..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/public-top-level-function.solc +++ /dev/null @@ -1,8 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// `public` is a contract-function visibility modifier. Applying it to a -// top-level function (outside any `contract { … }` body) must be rejected. -public function answer() -> uint256 { - return uint256(42); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference-encoding.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference-encoding.sol new file mode 100644 index 00000000..3bafd3fc --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference-encoding.sol @@ -0,0 +1,234 @@ + +/////// Construction +trait Typedef { + function rep(x: abs) returns (rep) ; + function abs(x: rep) returns (abs) ; +} + + +enum uint { uint(word) } + +impl Typedef { + function rep(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + function abs(x: word) returns (uint) { + return uint(x); + } +} + +enum memory { memory(word) } +enum memoryRef { memoryRef(word) } +enum Proxy { Proxy } + +impl Typedef, word> { + function rep(x: memory) returns (word) { + match (x) { +case memory(y) { +return y; +} +} + } + function abs(x: word) returns (memory) { + return memory(x); + } +} +impl Typedef, word> { + function rep(x: memoryRef) returns (word) { + match (x) { +case memoryRef(y) { +return y; +} +} + } + function abs(x: word) returns (memoryRef) { + return memoryRef(x); + } +} + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +enum ref { ref(a) } + +impl Assign, a> { + function assign(l: ref, r: a) { + // builtin "stack store" + return (); + } +} + +trait MemoryType { + function load(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +trait MemorySize { + function size(x: Proxy) returns (word) ; +} + +impl MemoryType { + function load(ptr: word) returns (word) { + let r:word; + assembly { + r := mload(ptr) + } + return r; + } + function store(ptr: word, value: word) { + assembly { + mstore(ptr, value) + } + } +} + +impl MemoryType { + function load(ptr: word) returns (uint) { + return Typedef.abs(MemoryType.load(ptr)); + } + function store(ptr: word, value: uint) { + return MemoryType.store(ptr, Typedef.rep(value)); + } +} + +impl Assign, a> where a: MemoryType { + function assign(l:memoryRef, y:a) { + MemoryType.store(Typedef.rep(l), y); + } +} + + + +enum MemberAccessProxy { MemberAccessProxy(a, field) } + +function memberAccessD1(x: MemberAccessProxy) returns (a) { + match (x) { +case MemberAccessProxy(y,z) { +return y; +} +} +} + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +trait RValueMemberAccess { + function memberAccess(x: self) returns (memberValueType) ; +} + +// This is *a lot* of pragmas... +trait CStructField {} +enum StructField { StructField(structType) } + +impl LValueMemberAccess, fieldSelector>, memoryRef> where StructField: CStructField, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector>) returns (memoryRef) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return memoryRef(Typedef.abs(ptr)); + } +} + +impl MemorySize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + +impl MemorySize<(a, b)> where a: MemorySize, b: MemorySize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz:word = MemorySize.size(@a); + let b_sz:word = MemorySize.size(@b); + assembly { + a_sz := add(a_sz, b_sz) + } + return a_sz; + } +} + +impl RValueMemberAccess, fieldSelector>, fieldType> where StructField: CStructField, fieldType: MemoryType, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector>) returns (fieldType) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + // BUG: Something wrong here? Complains about ptr not being word... + /*assembly { + ptr := add(ptr, size) + }*/ + return MemoryType.load(Typedef.abs(ptr)); + } +} + +////// Testing + +// struct S { x:word; y:uint; z:word; } +enum S { S(word, uint, word) } +enum x_sel { x_sel } +enum y_sel { y_sel } +enum z_sel { z_sel } + +impl CStructField, word, ()> {} +impl CStructField, uint, word> {} +// BUG: This next one should really be the following, but that breaks weirdly: +// (I get a patterson condition violation on an invoke instance for g) +// instance StructField(S, z_sel):CStructField(word, (word,uint)) {} +// So instead I use: +impl CStructField, word, word> {} + + +function f() { + let x:memory; + let y:memory; + // x = y + Assign.assign(ref(x), y); + /* + * Idea in the above: to avoid overlapping instances, + * we can desugar a simple identifier referring to a local variable on the lhs of an assignment to ref(x), + * to be able to choose a disjoint assign instance. + * Of course this needs special treatment during code generation, + * on the other hand, stack assignments generally do... + * Actually, even simpler might be just *not* to desugar assignments at all, if the lhs is just an identifier referring to a local variable and just directly take care of it when translating to core. + */ +} + +function g() { + let s:memory = Typedef.abs(0x80); + let y:word = 42; + let z:uint = uint(42); + // s.x = y + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel)), y); + // s.y = 21 + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, y_sel)), z); + // s.z = y; + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, z_sel)), y); + // y = s.x + Assign.assign(ref(y), RValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel))); + // s.z = s.x + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, z_sel)), RValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel))); +} +contract C { + function main() public { + f(); + g(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference-encoding.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference-encoding.solc deleted file mode 100644 index 93ae8736..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference-encoding.solc +++ /dev/null @@ -1,227 +0,0 @@ - -/////// Construction -class abs:Typedef(rep) { - function rep(x:abs) -> rep; - function abs(x:rep) -> abs; -} - - -data uint = uint(word); - -instance uint:Typedef(word) { - function rep(x:uint) -> word { - match x { - | uint(y) => return y; - } - } - function abs(x:word) -> uint { - return uint(x); - } -} - -data memory(a) = memory(word); -data memoryRef(a) = memoryRef(word); -data Proxy(a) = Proxy; - -instance memory(a):Typedef(word) { - function rep(x:memory(a)) -> word { - match x { - | memory(y) => return y; - } - } - function abs(x:word) -> memory(a) { - return memory(x); - } -} -instance memoryRef(a):Typedef(word) { - function rep(x:memoryRef(a)) -> word { - match x { - | memoryRef(y) => return y; - } - } - function abs(x:word) -> memoryRef(a) { - return memoryRef(x); - } -} - -class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -data ref(a) = ref(a); - -instance ref(a):Assign(a) { - function assign(l:ref(a), r:a) -> () { - // builtin "stack store" - return (); - } -} - -class self:MemoryType { - function load(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -class self:MemorySize { - function size(x:Proxy(self)) -> word; -} - -instance word:MemoryType { - function load(ptr:word) -> word { - let r:word; - assembly { - r := mload(ptr) - } - return r; - } - function store(ptr:word, value:word) -> () { - assembly { - mstore(ptr, value) - } - } -} - -instance uint:MemoryType { - function load(ptr:word) -> uint { - return Typedef.abs(MemoryType.load(ptr)); - } - function store(ptr:word, value:uint) -> () { - return MemoryType.store(ptr, Typedef.rep(value)); - } -} - -forall a . a : MemoryType => instance memoryRef(a):Assign(a) { - function assign(l:memoryRef(a), y:a) { - MemoryType.store(Typedef.rep(l), y); - } -} - - - -data MemberAccessProxy(a, field) = MemberAccessProxy(a, field); - -forall a field . -function memberAccessD1(x:MemberAccessProxy(a, field)) -> a { - match x { - | MemberAccessProxy(y,z) => return y; - } -} - -class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -class self:RValueMemberAccess(memberValueType) { - function memberAccess(x:self) -> memberValueType; -} - -// This is *a lot* of pragmas... -class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - -forall structType fieldSelector fieldType offsetType . StructField(structType, fieldSelector):CStructField(fieldType, offsetType), offsetType:MemorySize => instance MemberAccessProxy(memory(structType), fieldSelector):LValueMemberAccess(memoryRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector)) -> memoryRef(fieldType) { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return memoryRef(Typedef.abs(ptr)); - } -} - -instance ():MemorySize { - function size(x:Proxy(())) -> word { - return 0; - } -} - -instance word:MemorySize { - function size(x:Proxy(word)) -> word { - return 32; - } -} - - -instance uint:MemorySize { - function size(x:Proxy(uint)) -> word { - return 32; - } -} - -forall a b. a:MemorySize, b:MemorySize => instance (a,b):MemorySize { - function size(x:Proxy((a,b))) -> word { - let a_sz:word = MemorySize.size(Proxy:Proxy(a)); - let b_sz:word = MemorySize.size(Proxy:Proxy(b)); - assembly { - a_sz := add(a_sz, b_sz) - } - return a_sz; - } -} - -forall structType fieldSelector fieldType offsetType . StructField(structType, fieldSelector):CStructField(fieldType, offsetType), fieldType:MemoryType, offsetType:MemorySize => instance MemberAccessProxy(memory(structType), fieldSelector):RValueMemberAccess(fieldType) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector)) -> fieldType { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - // BUG: Something wrong here? Complains about ptr not being word... - /*assembly { - ptr := add(ptr, size) - }*/ - return MemoryType.load(Typedef.abs(ptr)):fieldType; - } -} - -////// Testing - -// struct S { x:word; y:uint; z:word; } -data S = S(word, uint, word); -data x_sel = x_sel; -data y_sel = y_sel; -data z_sel = z_sel; - -instance StructField(S, x_sel):CStructField(word, ()) {} -instance StructField(S, y_sel):CStructField(uint, word) {} -// BUG: This next one should really be the following, but that breaks weirdly: -// (I get a patterson condition violation on an invoke instance for g) -// instance StructField(S, z_sel):CStructField(word, (word,uint)) {} -// So instead I use: -instance StructField(S, z_sel):CStructField(word, word) {} - - -function f() { - let x:memory(word); - let y:memory(word); - // x = y - Assign.assign(ref(x), y); - /* - * Idea in the above: to avoid overlapping instances, - * we can desugar a simple identifier referring to a local variable on the lhs of an assignment to ref(x), - * to be able to choose a disjoint assign instance. - * Of course this needs special treatment during code generation, - * on the other hand, stack assignments generally do... - * Actually, even simpler might be just *not* to desugar assignments at all, if the lhs is just an identifier referring to a local variable and just directly take care of it when translating to core. - */ -} - -function g() { - let s:memory(S) = Typedef.abs(0x80); - let y:word = 42; - let z:uint = uint(42); - // s.x = y - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel)), y); - // s.y = 21 - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, y_sel)), z); - // s.z = y; - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, z_sel)), y); - // y = s.x - Assign.assign(ref(y), RValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel))); - // s.z = s.x - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, z_sel)), RValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel))); -} -contract C { - public function main() { - f(); - g(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference-test.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference-test.sol new file mode 100644 index 00000000..9481706a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference-test.sol @@ -0,0 +1,57 @@ +enum memory { memory(word) } + +trait Typedef { + function abs(v: rep) returns (abs) ; + function rep(v: abs) returns (rep) ; +} + +impl Typedef, word> { + function abs(ptr: word) returns (memory) { + return memory(ptr); + } + function rep(v: memory) returns (word) { + match (v) { +case memory(ptr) { +return ptr; +} +} + } +} + +trait Test { + function test(x: self) returns (word) ; +} + +impl Test { + function test(x: word) returns (word) { + return x; + } +} + +enum test { test(memory) } + +impl Typedef, memory> { + function rep(x: test) returns (memory) { + match (x) { +case test(m) { +return m; +} +} + } + function abs(m: memory) returns (test) { + return test(m); + } +} + +impl Test> where test: Typedef, rep: Test { + function test(x: test) returns (word) { + return Test.test(Typedef.rep(x)); + } + } + +contract C { + function main() public { + let x:test = test(memory(42)); + let ptr:word = Test.test(x); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference-test.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference-test.solc deleted file mode 100644 index a7922668..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference-test.solc +++ /dev/null @@ -1,54 +0,0 @@ -data memory(a) = memory(word); - -class abs:Typedef(rep) { - function abs(v:rep) -> abs; - function rep(v:abs) -> rep; -} - -instance memory(a):Typedef(word) { - function abs(ptr:word) -> memory(a) { - return memory(ptr); - } - function rep(v:memory(a)) -> word { - match v { - | memory(ptr) => return ptr; - } - } -} - -class self:Test { - function test(x:self) -> word; -} - -instance word:Test { - function test(x:word) -> word { - return x; - } -} - -data test(a) = test(memory(a)); - -instance test(a):Typedef(memory(a)) { - function rep(x:test(a)) -> memory(a) { - match x { - | test(m) => return m; - } - } - function abs(m:memory(a)) -> test(a) { - return test(m); - } -} - -forall abs rep . test(abs):Typedef(rep), rep:Test => - instance test(abs):Test { - function test(x:test(abs)) -> word { - return Test.test(Typedef.rep(x)); - } - } - -contract C { - public function main() { - let x:test(word) = test(memory(42)); - let ptr:word = Test.test(x); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference.sol new file mode 100644 index 00000000..26969ba4 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference.sol @@ -0,0 +1,31 @@ +trait Ref { + function load(r: ref) returns (deref) ; + function store(r: ref, v: deref) returns (unit) ; +} + +enum stack { stack(a) } + +impl Ref, a> { +} + +enum MemberAccess { MemberAccess(ty) } + +enum PairFst { PairFst } +enum PairSnd { PairSnd } + +enum XRef { XRef(st, field) } +impl Ref, a> where r: Ref {} +impl Ref, b> where r: Ref {} + +contract AssignNested { + function main() public returns (word) { + let x : stack<(word, (word, word))>; + let z : stack<(word, (word, word))>; + + // either of the next lines is fine on their own, but not together + Ref.store( XRef(z,PairFst), 21); + Ref.store( XRef(XRef(x, PairSnd), PairFst), 20 ); + + return 77; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference.solc deleted file mode 100644 index 23c8e63f..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/reference.solc +++ /dev/null @@ -1,31 +0,0 @@ -class ref : Ref(deref) { - function load (r:ref) -> deref; - function store(r:ref, v:deref) -> unit; -} - -data stack(a) = stack(a); - -instance stack(a) : Ref(a) { -} - -data MemberAccess(ty, field) = MemberAccess(ty); - -data PairFst = PairFst; -data PairSnd = PairSnd; - -data XRef(st, field, fieldType) = XRef(st, field); -forall r : Ref (a,b) . instance XRef(r, PairFst, a) : Ref(a) {} -forall r : Ref (a,b) . instance XRef(r, PairSnd, b) : Ref(b) {} - -contract AssignNested { - public function main() { - let x : stack( (word, (word, word)) ); - let z : stack( (word, (word, word)) ); - - // either of the next lines is fine on their own, but not together - Ref.store( XRef(z,PairFst), 21); - Ref.store( XRef(XRef(x, PairSnd), PairFst), 20 ); - - return 77; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/references-daniel.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/references-daniel.sol new file mode 100644 index 00000000..daa3d81f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/references-daniel.sol @@ -0,0 +1,244 @@ +/////// Construction +trait Typedef { + function rep(x: abs) returns (rep) ; + function abs(x: rep) returns (abs) ; +} + +enum xunit { xunit } + +enum uint { uint(word) } + +impl Typedef { + function rep(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + function abs(x: word) returns (uint) { + return uint(x); + } +} + +enum memory { memory(word) } +enum memoryRef { memoryRef(word) } +enum Proxy { Proxy } + +impl Typedef, word> { + function rep(x: memory) returns (word) { + match (x) { +case memory(y) { +return y; +} +} + } + function abs(x: word) returns (memory) { + return memory(x); + } +} +impl Typedef, word> { + function rep(x: memoryRef) returns (word) { + match (x) { +case memoryRef(y) { +return y; +} +} + } + function abs(x: word) returns (memoryRef) { + return memoryRef(x); + } +} + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +enum ref { ref(a) } + +impl Assign, a> { + function assign(l: ref, r: a) { + // builtin "stack store" + return (); + } +} + +trait MemoryType { + function load(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +trait MemorySize { + function size(x: Proxy) returns (word) ; +} + +impl MemoryType { + function load(ptr: word) returns (word) { + let r:word; + assembly { + r := mload(ptr) + } + return r; + } + function store(ptr: word, value: word) { + assembly { + mstore(ptr, value) + } + } +} + +impl MemoryType { + function load(ptr: word) returns (uint) { + return Typedef.abs(MemoryType.load(ptr)); + } + function store(ptr: word, value: uint) { + return MemoryType.store(ptr, Typedef.rep(value)); + } +} + +impl Assign, a> where a: MemoryType { + function assign(l:memoryRef, y:a) { + MemoryType.store(Typedef.rep(l), y); + } +} + + + +enum MemberAccessProxy { MemberAccessProxy(a, Proxy) } + +function memberAccessPtr(x: MemberAccessProxy, field>) returns (word) { + match (x) { +case MemberAccessProxy(y,z) { +match (y) { +case memory(ptr) { +return ptr; +} +} +} +} +} + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +trait RValueMemberAccess { + function memberAccess(x: self) returns (memberValueType) ; +} + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 0; + } +} + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + +enum zero { zero } +enum suc { suc(a) } + +impl Typedef, zero>, word> {} +impl LValueMemberAccess, zero>, memoryRef> { + function memberAccess(mptr: MemberAccessProxy, zero>, f: Proxy) returns (memoryRef) { + let ptr:word = Typedef.rep(mptr); + return memoryRef(ptr); + } +} + +impl LValueMemberAccess, suc>, c> where MemberAccessProxy, n>: LValueMemberAccess, a: MemorySize { + function memberAccess(map: MemberAccessProxy, suc>, f: Proxy>) returns (c) { + let ptr:word = memberAccessPtr(map); + let sz:word = MemorySize.size(@a); + assembly { ptr := add(ptr, sz) } + let newPtr:memory = memory(ptr); + return LValueMemberAccess.memberAccess(MemberAccessProxy(newPtr, @n)); + } +} + +impl LValueMemberAccess, zero>, word> {} +impl LValueMemberAccess, suc>, uint> {} +impl LValueMemberAccess, suc>>, word> {} +impl Assign {} +impl Assign {} + +////// Testing + +// struct S { x:word; y:uint; z:word; } +enum S { S(word, uint, word) } +enum x_sel { x_sel } +enum y_sel { y_sel } +enum z_sel { z_sel } + +impl Typedef { + function abs(x: (word, uint, word)) returns (S) { + match (x) { +case (a, b, c) { +return S(a, b, c); +} +} + } + function rep(x: S) returns (word, uint, word) { + match (x) { +case S(a, b, c) { +return (a, b, c); +} +} + } +} + + +// The idea here would be to generate these particularly on the definition of a struct with fields. +impl LValueMemberAccess, x_sel>, word> where S: Typedef, MemberAccessProxy, zero>: LValueMemberAccess { + function memberAccess(map: MemberAccessProxy, x_sel>, f: Proxy) returns (word) { + return (LValueMemberAccess.memberAccess(MemberAccessProxy(memory(memberAccessPtr(map)), @zero)) ); + } +} + +impl LValueMemberAccess, y_sel>, uint> where S: Typedef, MemberAccessProxy, suc>: LValueMemberAccess { + function memberAccess(map: MemberAccessProxy, y_sel>, f: Proxy) returns (uint) { + return LValueMemberAccess.memberAccess(MemberAccessProxy(memory(memberAccessPtr(map)), @suc)); + } +} + +impl LValueMemberAccess, z_sel>, word> where S: Typedef, MemberAccessProxy, suc>>: LValueMemberAccess { + function memberAccess(map: MemberAccessProxy, z_sel>, f: Proxy) returns (word) { + return LValueMemberAccess.memberAccess(MemberAccessProxy(memory(memberAccessPtr(map)), @suc>)); + } +} + +function f() { + let x:memory; + let y:memory; + x = y; +} + +function g() { + let s:memory = Typedef.abs(0x80); + let x:word = 42; + let y:uint = Typedef.abs(21); + let z:word = 7; + // s.x = x; + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, @x_sel)), x); + // s.y = y; + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, @y_sel)), y); + // s.z = z; + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, @z_sel)), z); +} + +contract C { + function main() public { + f(); + g(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/references-daniel.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/references-daniel.solc deleted file mode 100644 index 4260971e..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/references-daniel.solc +++ /dev/null @@ -1,236 +0,0 @@ -/////// Construction -class abs:Typedef(rep) { - function rep(x:abs) -> rep; - function abs(x:rep) -> abs; -} - -data xunit = xunit; - -data uint = uint(word); - -instance uint:Typedef(word) { - function rep(x:uint) -> word { - match x { - | uint(y) => return y; - } - } - function abs(x:word) -> uint { - return uint(x); - } -} - -data memory(a) = memory(word); -data memoryRef(a) = memoryRef(word); -data Proxy(a) = Proxy; - -instance memory(a):Typedef(word) { - function rep(x:memory(a)) -> word { - match x { - | memory(y) => return y; - } - } - function abs(x:word) -> memory(a) { - return memory(x); - } -} -instance memoryRef(a):Typedef(word) { - function rep(x:memoryRef(a)) -> word { - match x { - | memoryRef(y) => return y; - } - } - function abs(x:word) -> memoryRef(a) { - return memoryRef(x); - } -} - -class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -data ref(a) = ref(a); - -instance ref(a):Assign(a) { - function assign(l:ref(a), r:a) -> () { - // builtin "stack store" - return (); - } -} - -class self:MemoryType { - function load(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -class self:MemorySize { - function size(x:Proxy(self)) -> word; -} - -instance word:MemoryType { - function load(ptr:word) -> word { - let r:word; - assembly { - r := mload(ptr) - } - return r; - } - function store(ptr:word, value:word) -> () { - assembly { - mstore(ptr, value) - } - } -} - -instance uint:MemoryType { - function load(ptr:word) -> uint { - return Typedef.abs(MemoryType.load(ptr)); - } - function store(ptr:word, value:uint) -> () { - return MemoryType.store(ptr, Typedef.rep(value)); - } -} - -forall a . a:MemoryType => -instance memoryRef(a):Assign(a) { - function assign(l:memoryRef(a), y:a) { - MemoryType.store(Typedef.rep(l), y); - } -} - - - -data MemberAccessProxy(a, field) = MemberAccessProxy(a, Proxy(field)); - -forall a field . -function memberAccessPtr(x:MemberAccessProxy(memory(a), field)) -> word { - match x { - | MemberAccessProxy(y,z) => match y { - | memory(ptr) => return ptr; - } - } -} - -class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -class self:RValueMemberAccess(memberValueType) { - function memberAccess(x:self) -> memberValueType; -} - -instance xunit:MemorySize { - function size(x:Proxy(xunit)) -> word { - return 0; - } -} - -instance word:MemorySize { - function size(x:Proxy(word)) -> word { - return 32; - } -} - - -instance uint:MemorySize { - function size(x:Proxy(uint)) -> word { - return 32; - } -} - -data zero = zero; -data suc(a) = suc(a); - -forall a b . instance MemberAccessProxy(memory((a, b)), zero) : Typedef (word) {} -forall a b . instance MemberAccessProxy(memory((a,b)), zero):LValueMemberAccess(memoryRef(a)) { - function memberAccess(mptr:MemberAccessProxy(memory((a,b)), zero), f:Proxy(zero)) -> memoryRef(a) { - let ptr:word = Typedef.rep(mptr); - return memoryRef(ptr); - } -} - -forall a b c n. MemberAccessProxy(memory(b), n):LValueMemberAccess(c), a:MemorySize => -instance MemberAccessProxy(memory((a,b)), suc(n)):LValueMemberAccess(c) { - function memberAccess(map:MemberAccessProxy(memory((a,b)), suc(n)), f:Proxy(suc(n))) -> c { - let ptr:word = memberAccessPtr(map); - let sz:word = MemorySize.size(Proxy:Proxy(a)); - assembly { ptr := add(ptr, sz) } - let newPtr:memory(b) = memory(ptr); - return LValueMemberAccess.memberAccess(MemberAccessProxy(newPtr, Proxy:Proxy(n))); - } -} - -instance MemberAccessProxy(memory(a), zero) : LValueMemberAccess (word) {} -instance MemberAccessProxy(memory(a), suc(zero)) : LValueMemberAccess (uint) {} -instance MemberAccessProxy(memory(a), suc(suc(zero))) : LValueMemberAccess (word) {} -instance word:Assign(word){} -instance uint:Assign(uint){} - -////// Testing - -// struct S { x:word; y:uint; z:word; } -data S = S(word, uint, word); -data x_sel = x_sel; -data y_sel = y_sel; -data z_sel = z_sel; - -instance S:Typedef((word, uint, word)) { - function abs(x:(word, uint, word)) -> S { - match x { - | (a, b, c) => return S(a, b, c); - } - } - function rep(x:S) -> (word, uint, word) { - match x { - | S(a, b, c) => return (a, b, c); - } - } -} - - -// The idea here would be to generate these particularly on the definition of a struct with fields. -forall c rep . S:Typedef(rep), MemberAccessProxy(memory(rep), zero):LValueMemberAccess(word) => -instance MemberAccessProxy(memory(S), x_sel):LValueMemberAccess(word) { - function memberAccess(map:MemberAccessProxy(memory(S), x_sel), f:Proxy(x_sel)) -> word { - return (LValueMemberAccess.memberAccess(MemberAccessProxy(memory(memberAccessPtr(map)):memory(rep), Proxy:Proxy(zero))) : word); - } -} - -forall c rep . S:Typedef(rep), MemberAccessProxy(memory(rep), suc(zero)):LValueMemberAccess(uint) => -instance MemberAccessProxy(memory(S), y_sel):LValueMemberAccess(uint) { - function memberAccess(map:MemberAccessProxy(memory(S), y_sel), f:Proxy(y_sel)) -> uint { - return LValueMemberAccess.memberAccess(MemberAccessProxy(memory(memberAccessPtr(map)):memory(rep), Proxy:Proxy(suc(zero)))); - } -} - -forall c rep . S:Typedef(rep), MemberAccessProxy(memory(rep), suc(suc(zero))):LValueMemberAccess(word) => -instance MemberAccessProxy(memory(S), z_sel):LValueMemberAccess(word) { - function memberAccess(map:MemberAccessProxy(memory(S), z_sel), f:Proxy(z_sel)) -> word { - return LValueMemberAccess.memberAccess(MemberAccessProxy(memory(memberAccessPtr(map)):memory(rep), Proxy:Proxy(suc(suc(zero))))); - } -} - -function f() { - let x:memory(word); - let y:memory(word); - x = y; -} - -function g() { - let s:memory(S) = Typedef.abs(0x80); - let x:word = 42; - let y:uint = Typedef.abs(21); - let z:word = 7; - // s.x = x; - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, Proxy:Proxy(x_sel))), x); - // s.y = y; - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, Proxy:Proxy(y_sel))), y); - // s.z = z; - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, Proxy:Proxy(z_sel))), z); -} - -contract C { - public function main() { - f(); - g(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-contract-method.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-contract-method.sol new file mode 100644 index 00000000..fe6e0760 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-contract-method.sol @@ -0,0 +1,10 @@ +// Error: contract method missing return type annotation +contract Doubler { + function double(x: word) public { + return x; + } + + function main() public returns (word) { + return double(21); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-contract-method.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-contract-method.solc deleted file mode 100644 index 55bd2005..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-contract-method.solc +++ /dev/null @@ -1,10 +0,0 @@ -// Error: contract method missing return type annotation -contract Doubler { - public function double(x : word) { - return x; - } - - public function main() -> word { - return double(21); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-both.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-both.snap new file mode 100644 index 00000000..e6f79c4d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-both.snap @@ -0,0 +1,14 @@ +--- +source: crates/parser/tests/diagnostics.rs +expression: value +input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-both.sol +--- +error[SC0001]: named function parameter requires an explicit type + --> /require-annotation-missing-both.sol:2:13 + | +1 | // Error: top-level free function with no annotations at all +2 | function id(x) { + | ^ +3 | return x; + | + = note: while parsing function signature diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-both.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-both.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-both.solc rename to crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-both.sol diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-param.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-param.snap new file mode 100644 index 00000000..cde6c2bc --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-param.snap @@ -0,0 +1,14 @@ +--- +source: crates/parser/tests/diagnostics.rs +expression: value +input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-param.sol +--- +error[SC0001]: named function parameter requires an explicit type + --> /require-annotation-missing-param.sol:2:14 + | +1 | // Error: top-level free function with an unannotated parameter +2 | function add(x, y: word) returns (word) { + | ^ +3 | let res : word; + | + = note: while parsing function signature diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-param.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-param.sol new file mode 100644 index 00000000..c410bf70 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-param.sol @@ -0,0 +1,6 @@ +// Error: top-level free function with an unannotated parameter +function add(x, y: word) returns (word) { + let res : word; + assembly { res := add(x, y) } + return res; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-param.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-param.solc deleted file mode 100644 index 5d498984..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-param.solc +++ /dev/null @@ -1,6 +0,0 @@ -// Error: top-level free function with an unannotated parameter -function add(x, y : word) -> word { - let res : word; - assembly { res := add(x, y) } - return res; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-return.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-return.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-return.solc rename to crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-missing-return.sol diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-mutual.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-mutual.sol new file mode 100644 index 00000000..f8e5afe1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-mutual.sol @@ -0,0 +1,8 @@ +// Error: mutually recursive free functions without annotations +function foo(x : word) { + return bar(x); +} + +function bar(x: word) returns (word) { + return foo(x); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-mutual.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-mutual.solc deleted file mode 100644 index dc7fd31e..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/require-annotation-mutual.solc +++ /dev/null @@ -1,8 +0,0 @@ -// Error: mutually recursive free functions without annotations -function foo(x : word) { - return bar(x); -} - -function bar(x : word) -> word { - return foo(x); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-arity.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-arity.sol new file mode 100644 index 00000000..fe0be4df --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-arity.sol @@ -0,0 +1,11 @@ +// INCORRECT: the signature promises a one-argument function (word) -> word, +// but the returned lambda takes two arguments. +function makeF(x: word) returns (function(word) returns (word)) { + return lam (y : word, z : word) -> word { + let res : word; + assembly { + res := add(y, z) + } + return res; + }; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-arity.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-arity.solc deleted file mode 100644 index 4eaf6ae1..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-arity.solc +++ /dev/null @@ -1,11 +0,0 @@ -// INCORRECT: the signature promises a one-argument function (word) -> word, -// but the returned lambda takes two arguments. -function makeF(x : word) -> ((word) -> word) { - return lam (y : word, z : word) -> word { - let res : word; - assembly { - res := add(y, z) - } - return res; - }; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-param.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-param.sol new file mode 100644 index 00000000..7113ddf6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-param.sol @@ -0,0 +1,8 @@ +// INCORRECT: the returned lambda's parameter is `bool`, but the signature +// promises (word) -> word. Closure conversion would erase the arrow type; +// the single-pass checker must still reject this. +function makeAdder(x: word) returns (function(word) returns (word)) { + return lam (y : bool) -> word { + return x; + }; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-param.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-param.solc deleted file mode 100644 index b93c35b0..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-param.solc +++ /dev/null @@ -1,8 +0,0 @@ -// INCORRECT: the returned lambda's parameter is `bool`, but the signature -// promises (word) -> word. Closure conversion would erase the arrow type; -// the single-pass checker must still reject this. -function makeAdder(x : word) -> ((word) -> word) { - return lam (y : bool) -> word { - return x; - }; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-return.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-return.sol new file mode 100644 index 00000000..f80ad2b5 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-return.sol @@ -0,0 +1,7 @@ +// INCORRECT: the returned lambda's body has type bool, but the signature +// promises the result is word. +function makeConst(x: word) returns (function(word) returns (word)) { + return lam (y : word) -> bool { + return true; + }; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-return.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-return.solc deleted file mode 100644 index a6cb6efa..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-return.solc +++ /dev/null @@ -1,7 +0,0 @@ -// INCORRECT: the returned lambda's body has type bool, but the signature -// promises the result is word. -function makeConst(x : word) -> ((word) -> word) { - return lam (y : word) -> bool { - return true; - }; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-sig.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-sig.sol new file mode 100644 index 00000000..413edc24 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-sig.sol @@ -0,0 +1,7 @@ +// INCORRECT: signature says the result consumes a bool ((bool) -> word), +// but the returned lambda consumes a word. +function makeF(x: word) returns (function(bool) returns (word)) { + return lam (y : word) -> word { + return x; + }; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-sig.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-sig.solc deleted file mode 100644 index 21021b25..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-bad-sig.solc +++ /dev/null @@ -1,7 +0,0 @@ -// INCORRECT: signature says the result consumes a bool ((bool) -> word), -// but the returned lambda consumes a word. -function makeF(x : word) -> ((bool) -> word) { - return lam (y : word) -> word { - return x; - }; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-not-fun.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-not-fun.sol new file mode 100644 index 00000000..8f407cbb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-not-fun.sol @@ -0,0 +1,5 @@ +// INCORRECT: the signature promises a function (word) -> word, but the body +// returns a plain word instead of a function. +function makeF(x: word) returns (function(word) returns (word)) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-not-fun.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-not-fun.solc deleted file mode 100644 index 686b2236..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/return-fun-not-fun.solc +++ /dev/null @@ -1,5 +0,0 @@ -// INCORRECT: the signature promises a function (word) -> word, but the body -// returns a plain word instead of a function. -function makeF(x : word) -> ((word) -> word) { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/signature.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/signature.sol new file mode 100644 index 00000000..baf36972 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/signature.sol @@ -0,0 +1,8 @@ +trait Typedef { + function rep(x: self) returns (underlyingType) ; +} + + +function tripleFun(x: t) returns (word) where t: Typedef { + return Typedef.rep(x); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/signature.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/signature.solc deleted file mode 100644 index 1be7243b..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/signature.solc +++ /dev/null @@ -1,8 +0,0 @@ -class self:Typedef(underlyingType) { - function rep(x:self) -> underlyingType; -} - - -forall t:Typedef(word) . function tripleFun(x:t) { - return Typedef.rep(x); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/simpleIfExpr.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/simpleIfExpr.sol new file mode 100644 index 00000000..65d96131 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/simpleIfExpr.sol @@ -0,0 +1,3 @@ +contract SimpleIfStmt { + function main() public { return ( (true) ? 1 : 0); } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/simpleIfExpr.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/simpleIfExpr.solc deleted file mode 100644 index a6c812a8..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/simpleIfExpr.solc +++ /dev/null @@ -1,3 +0,0 @@ -contract SimpleIfStmt { - public function main() { return (if (true) then 1 else 0); } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/simpleIfStmt.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/simpleIfStmt.sol new file mode 100644 index 00000000..c81311f0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/simpleIfStmt.sol @@ -0,0 +1,3 @@ +contract SimpleIfStmt { + function main() public { if (true) {return 1;} else {return 0;} } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/simpleIfStmt.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/simpleIfStmt.solc deleted file mode 100644 index 80e672f2..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/simpleIfStmt.solc +++ /dev/null @@ -1,3 +0,0 @@ -contract SimpleIfStmt { - public function main() { if (true) {return 1;} else {return 0;} } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/skolem-let.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/skolem-let.sol new file mode 100644 index 00000000..975ef8fd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/skolem-let.sol @@ -0,0 +1,13 @@ + +function fromWord(x: word) returns (a) { + let result : a; + assembly { result := x } + return result; + } + +contract Unsafe { + function main() public returns (word) { + fromWord(7); + return 42; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/skolem-let.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/skolem-let.solc deleted file mode 100644 index 2f8ea1c6..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/skolem-let.solc +++ /dev/null @@ -1,13 +0,0 @@ - -forall a. function fromWord(x: word) -> a { - let result : a; - assembly { result := x } - return result; - } - -contract Unsafe { - public function main() { - fromWord(7):(); - return 42; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/storage-adt-mapping-field-fail.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/storage-adt-mapping-field-fail.sol new file mode 100644 index 00000000..15856de8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/storage-adt-mapping-field-fail.sol @@ -0,0 +1,21 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.StorageGeneric; + +// A mapping cannot be a field of a data type. std only provides +// `storage(mapping(k,v)) : CanStore(storage(mapping(k,v)))` — the slot handle +// loads back as a handle, never as a mapping value — so the structural CanStore +// decomposition of `Wrapper` asks for `storage(mapping(uint256,uint256)) : +// CanStore(mapping(uint256,uint256))`, which does not exist. +// +// (Even if it did, that instance's store/load are `unimplemented()`: copying a +// mapping is not a meaningful storage operation.) + +enum Wrapper { Wrapper(mapping(uint256 => uint256)) } + +contract C { + w : Wrapper; + + constructor() {} +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/storage-adt-mapping-field-fail.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/storage-adt-mapping-field-fail.solc deleted file mode 100644 index 15f842a6..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/storage-adt-mapping-field-fail.solc +++ /dev/null @@ -1,21 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -// A mapping cannot be a field of a data type. std only provides -// `storage(mapping(k,v)) : CanStore(storage(mapping(k,v)))` — the slot handle -// loads back as a handle, never as a mapping value — so the structural CanStore -// decomposition of `Wrapper` asks for `storage(mapping(uint256,uint256)) : -// CanStore(mapping(uint256,uint256))`, which does not exist. -// -// (Even if it did, that instance's store/load are `unimplemented()`: copying a -// mapping is not a meaningful storage operation.) - -data Wrapper = Wrapper(mapping(uint256, uint256)); - -contract C { - w : Wrapper; - - constructor() {} -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/string-const.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/string-const.sol new file mode 100644 index 00000000..58e78346 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/string-const.sol @@ -0,0 +1,5 @@ +contract Answer { + function main() public { + return "42"; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/string-const.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/string-const.solc deleted file mode 100644 index 735a6d6f..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/string-const.solc +++ /dev/null @@ -1,5 +0,0 @@ -contract Answer { - public function main() { - return "42"; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subject-index.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subject-index.sol new file mode 100644 index 00000000..70b4f2d8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subject-index.sol @@ -0,0 +1,74 @@ +enum storage { storage(word) } +enum storageRef { storageRef(word) } +enum Proxy { Proxy } + +enum mapping { mapping(word, Proxy, Proxy) } + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +impl Assign, a> { + function assign(l:storageRef, y:a) { + } +} + +trait CStructField {} +enum StructField { StructField(structType) } + + +enum MemberAccessProxy { MemberAccessProxy(a, field) } + + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +// ------------------------------------------------------------------ +// Contract field access +// ------------------------------------------------------------------ + +impl LValueMemberAccess, storageRef> where StructField: CStructField { + function memberAccess(x: MemberAccessProxy) returns (storageRef) { + return storageRef(0x100); + } +} + +// ------------------------------------------------------------------ +// Indexed access +// ------------------------------------------------------------------ + +enum mapping { mapping(word) } +enum IndexAccessProxy { IndexAccessProxy(map, index) } + +impl LValueMemberAccess, index, member>, storageRef> { + function memberAccess(x: IndexAccessProxy, index, member>) returns (storageRef) { + return storageRef(0); + } +} + +enum MintCtx { MintCtx } +enum balances_sel { balances_sel } +impl CStructField, mapping(word => word), ()> {} + + function mint(amount:word) { + let bal_prx = MemberAccessProxy(MintCtx, balances_sel); + let bal_ref = LValueMemberAccess.memberAccess(bal_prx); + + Assign.assign( + LValueMemberAccess.memberAccess( + IndexAccessProxy( + // bal_ref // this works, but inlining bal_ref leads to error + LValueMemberAccess.memberAccess(bal_prx) + , 0 + ) + ) + , amount + ) ; + + } +contract Map { + function main() public { + mint(1000); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subject-index.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subject-index.solc deleted file mode 100644 index 667f65e5..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subject-index.solc +++ /dev/null @@ -1,77 +0,0 @@ -data storage(a) = storage(word); -data storageRef(a) = storageRef(word); -data Proxy(a) = Proxy; - -data mapping(member, index) = mapping(word, Proxy(member), Proxy(index)); - -forall lhs rhs . class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -forall a . instance storageRef(a):Assign(a) { - function assign(l:storageRef(a), y:a) { - } -} - -forall self fieldType offsetType . class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - - -data MemberAccessProxy(a, field, offset) = MemberAccessProxy(a, field); - - -forall self memberRefType . class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -// ------------------------------------------------------------------ -// Contract field access -// ------------------------------------------------------------------ - -forall cxt fieldSelector fieldType offsetType - . StructField(cxt, fieldSelector):CStructField(fieldType, offsetType) - => instance MemberAccessProxy(cxt, fieldSelector, offsetType):LValueMemberAccess(storageRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(cxt, fieldSelector, offsetType)) -> storageRef(fieldType) { - return storageRef(0x100); - } -} - -// ------------------------------------------------------------------ -// Indexed access -// ------------------------------------------------------------------ - -data mapping(index, member) = mapping(word); -data IndexAccessProxy(map, index, member) = IndexAccessProxy(map, index); - -forall map index member. - instance IndexAccessProxy(storageRef(map), index, member):LValueMemberAccess(storageRef(member)) { - function memberAccess(x:IndexAccessProxy(storageRef(map), index, member)) -> storageRef(member) { - return storageRef(0); - } -} - -data MintCtx = MintCtx; -data balances_sel = balances_sel; -instance StructField(MintCtx, balances_sel):CStructField(mapping(word,word), ()) {} - - function mint(amount:word) { - let bal_prx = MemberAccessProxy(MintCtx, balances_sel); - let bal_ref = LValueMemberAccess.memberAccess(bal_prx); - - Assign.assign( - LValueMemberAccess.memberAccess( - IndexAccessProxy( - // bal_ref // this works, but inlining bal_ref leads to error - LValueMemberAccess.memberAccess(bal_prx) - , 0 - ) - ) - , amount - ) ; - - } -contract Map { - public function main () { - mint(1000); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subject-reduction.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subject-reduction.sol new file mode 100644 index 00000000..4ef1c00b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subject-reduction.sol @@ -0,0 +1,77 @@ +enum storage { storage(word) } +enum storageRef { storageRef(word) } +enum Proxy { Proxy } + +enum mapping { mapping(word, Proxy, Proxy) } + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +impl Assign, a> { + function assign(l:storageRef, y:a) { + } +} + +trait CStructField {} +enum StructField { StructField(structType) } + + +enum MemberAccessProxy { MemberAccessProxy(a, field) } + + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +// ------------------------------------------------------------------ +// Contract field access +// ------------------------------------------------------------------ + +impl LValueMemberAccess, storageRef> where StructField: CStructField { + function memberAccess(x: MemberAccessProxy) returns (storageRef) { + return storageRef(0x100); + } +} + +// ------------------------------------------------------------------ +// Indexed access +// ------------------------------------------------------------------ + +enum mapping { mapping(word) } +enum IndexAccessProxy { IndexAccessProxy(map, index) } + +impl LValueMemberAccess, index, member>, storageRef> { + function memberAccess(x: IndexAccessProxy, index, member>) returns (storageRef) { + return storageRef(0); + } +} + + +enum MintCtx { MintCtx } +enum balances_sel { balances_sel } +impl CStructField, mapping(word => word), ()> {} + + function mint(amount:word) { + let bal_prx = MemberAccessProxy(MintCtx, balances_sel); + let bal_ref = LValueMemberAccess.memberAccess(bal_prx); + + Assign.assign( + LValueMemberAccess.memberAccess( + IndexAccessProxy( + // bal_ref // this works, but inlining bal_ref leads to error + LValueMemberAccess.memberAccess(bal_prx) + , 0 + ) + ) + , amount + ) ; + + } +/* +contract Map { + function main () { + mint(1000); + } +} +*/ diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subject-reduction.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subject-reduction.solc deleted file mode 100644 index f1d32e96..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subject-reduction.solc +++ /dev/null @@ -1,80 +0,0 @@ -data storage(a) = storage(word); -data storageRef(a) = storageRef(word); -data Proxy(a) = Proxy; - -data mapping(member, index) = mapping(word, Proxy(member), Proxy(index)); - -forall lhs rhs . class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -forall a . instance storageRef(a):Assign(a) { - function assign(l:storageRef(a), y:a) { - } -} - -forall self fieldType offsetType . class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - - -data MemberAccessProxy(a, field, offset) = MemberAccessProxy(a, field); - - -forall self memberRefType . class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -// ------------------------------------------------------------------ -// Contract field access -// ------------------------------------------------------------------ - -forall cxt fieldSelector fieldType offsetType - . StructField(cxt, fieldSelector):CStructField(fieldType, offsetType) - => instance MemberAccessProxy(cxt, fieldSelector, offsetType):LValueMemberAccess(storageRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(cxt, fieldSelector, offsetType)) -> storageRef(fieldType) { - return storageRef(0x100); - } -} - -// ------------------------------------------------------------------ -// Indexed access -// ------------------------------------------------------------------ - -data mapping(index, member) = mapping(word); -data IndexAccessProxy(map, index, member) = IndexAccessProxy(map, index); - -forall map index member. - instance IndexAccessProxy(storageRef(map), index, member):LValueMemberAccess(storageRef(member)) { - function memberAccess(x:IndexAccessProxy(storageRef(map), index, member)) -> storageRef(member) { - return storageRef(0); - } -} - - -data MintCtx = MintCtx; -data balances_sel = balances_sel; -instance StructField(MintCtx, balances_sel):CStructField(mapping(word,word), ()) {} - - function mint(amount:word) { - let bal_prx = MemberAccessProxy(MintCtx, balances_sel); - let bal_ref = LValueMemberAccess.memberAccess(bal_prx); - - Assign.assign( - LValueMemberAccess.memberAccess( - IndexAccessProxy( - // bal_ref // this works, but inlining bal_ref leads to error - LValueMemberAccess.memberAccess(bal_prx) - , 0 - ) - ) - , amount - ) ; - - } -/* -contract Map { - function main () { - mint(1000); - } -} -*/ diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subsumption-constraint.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subsumption-constraint.sol new file mode 100644 index 00000000..42135de5 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subsumption-constraint.sol @@ -0,0 +1,22 @@ +// This code should FAIL, but PASSES! +enum Bool { True, False } + +trait MyCls { + function f(x: a, y: a) returns (Bool) ; +} + +function the_bug(x: a, y: a) returns (Bool) { + return MyCls.f(x, y); +} + +contract Foo { + function x() public { + let b1 = Bool.True; + let b2 = Bool.False; + the_bug(b1, b2); + } + + function main() public { + x(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subsumption-constraint.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subsumption-constraint.solc deleted file mode 100644 index bf0cd6b5..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subsumption-constraint.solc +++ /dev/null @@ -1,22 +0,0 @@ -// This code should FAIL, but PASSES! -data Bool = True | False; - -forall a . class a : MyCls { - function f(x : a, y : a) -> Bool; -} - -forall a . function the_bug(x : a, y : a) -> Bool { - return MyCls.f(x, y); -} - -contract Foo { - public function x() { - let b1 = Bool.True; - let b2 = Bool.False; - the_bug(b1, b2); - } - - public function main() { - x(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subsumption-test.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subsumption-test.sol new file mode 100644 index 00000000..cb1057f7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subsumption-test.sol @@ -0,0 +1,7 @@ +function id(x: word) returns (word) { + return x; +} + +function fakeid(x: word) returns (a) { + return x ; +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subsumption-test.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subsumption-test.solc deleted file mode 100644 index 014b0a36..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/subsumption-test.solc +++ /dev/null @@ -1,7 +0,0 @@ -function id (x) -> word { - return x; -} - -forall a . function fakeid(x : word) -> a { - return x ; -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/super-class-cycle-fail.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/super-class-cycle-fail.sol new file mode 100644 index 00000000..cde3b317 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/super-class-cycle-fail.sol @@ -0,0 +1,15 @@ +trait A where a: B {} +trait B where a: A {} +trait C {} + +function needsC(x: a) where a: C { + return (); +} + +function cannotGetC(x: a) where a: A { + return needsC(x); +} + +function main() { + return (); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/super-class-cycle-fail.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/super-class-cycle-fail.solc deleted file mode 100644 index c6567a6b..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/super-class-cycle-fail.solc +++ /dev/null @@ -1,15 +0,0 @@ -forall a . a:B => class a:A {} -forall a . a:A => class a:B {} -forall a . class a:C {} - -forall a . a:C => function needsC(x:a) -> () { - return (); -} - -forall a . a:A => function cannotGetC(x:a) -> () { - return needsC(x); -} - -function main() -> () { - return (); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/super-class-recursive-arg.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/super-class-recursive-arg.sol new file mode 100644 index 00000000..ef3d82cf --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/super-class-recursive-arg.sol @@ -0,0 +1,17 @@ +pragma no-patterson-condition A; + +enum Wrap { Wrap(a) } + +trait A where Wrap: A {} + +function needsWrappedA(x: a) where Wrap: A { + return (); +} + +function shouldUseSuperclass(x: a) where a: A { + return needsWrappedA(x); +} + +function main() { + return (); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/super-class-recursive-arg.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/super-class-recursive-arg.solc deleted file mode 100644 index c6b0c2d9..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/super-class-recursive-arg.solc +++ /dev/null @@ -1,17 +0,0 @@ -pragma no-patterson-condition A; - -data Wrap(a) = Wrap(a); - -forall a . Wrap(a):A => class a:A {} - -forall a . Wrap(a):A => function needsWrappedA(x:a) -> () { - return (); -} - -forall a . a:A => function shouldUseSuperclass(x:a) -> () { - return needsWrappedA(x); -} - -function main() -> () { - return (); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-arity-mismatch.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-arity-mismatch.sol new file mode 100644 index 00000000..93780c46 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-arity-mismatch.sol @@ -0,0 +1,5 @@ +type F(a) = pair; + +function main() returns (F) { + return pair(42, 0); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-arity-mismatch.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-arity-mismatch.solc deleted file mode 100644 index 0486adc2..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-arity-mismatch.solc +++ /dev/null @@ -1,5 +0,0 @@ -type F(a) = pair(a, word); - -function main() -> F(word, word) { - return pair(42, 0); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-long-cycle.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-long-cycle.sol new file mode 100644 index 00000000..46d626c6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-long-cycle.sol @@ -0,0 +1,8 @@ +// Longer recursive cycle should be rejected +type A = B; +type B = C; +type C = A; + +function main() returns (word) { + return 0; +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-long-cycle.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-long-cycle.solc deleted file mode 100644 index d06783dc..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-long-cycle.solc +++ /dev/null @@ -1,8 +0,0 @@ -// Longer recursive cycle should be rejected -type A = B; -type B = C; -type C = A; - -function main() -> word { - return 0; -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-recursive.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-recursive.sol new file mode 100644 index 00000000..3fcc1e8b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-recursive.sol @@ -0,0 +1,8 @@ +type A = B; +type B = A; + +contract RecursiveTest { + function main() public returns (word) { + return 0; + } +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-recursive.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-recursive.solc deleted file mode 100644 index 3e34ef4c..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-recursive.solc +++ /dev/null @@ -1,8 +0,0 @@ -type A = B; -type B = A; - -contract RecursiveTest { - public function main() -> word { - return 0; - } -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-self-recursive.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-self-recursive.sol new file mode 100644 index 00000000..d79ec946 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-self-recursive.sol @@ -0,0 +1,6 @@ +// Self-recursive synonym should be rejected +type A = A; + +function main() returns (word) { + return 0; +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-self-recursive.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-self-recursive.solc deleted file mode 100644 index 9ecb567d..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/synonym-self-recursive.solc +++ /dev/null @@ -1,6 +0,0 @@ -// Self-recursive synonym should be rejected -type A = A; - -function main() -> word { - return 0; -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-answer-reuse.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-answer-reuse.sol new file mode 100644 index 00000000..13f3124a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-answer-reuse.sol @@ -0,0 +1,16 @@ +pragma no-patterson-condition Derived; + +trait Seed {} +trait Derived {} + +impl Seed {} + +impl Derived where a: Seed {} + +function needsDerivedTwice(x: a) where a: Derived, a: Derived { + return (); +} + +function main() { + return needsDerivedTwice(0); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-answer-reuse.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-answer-reuse.solc deleted file mode 100644 index d815c67e..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-answer-reuse.solc +++ /dev/null @@ -1,16 +0,0 @@ -pragma no-patterson-condition Derived; - -forall a . class a:Seed {} -forall a . class a:Derived {} - -instance word:Seed {} - -forall a . a:Seed => instance a:Derived {} - -forall a . a:Derived, a:Derived => function needsDerivedTwice(x:a) -> () { - return (); -} - -function main() -> () { - return needsDerivedTwice(0); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-cycle-fail.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-cycle-fail.sol new file mode 100644 index 00000000..a0813eb7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-cycle-fail.sol @@ -0,0 +1,16 @@ +pragma no-patterson-condition A; +pragma no-patterson-condition B; + +trait A {} +trait B {} + +impl A where a: B {} +impl B where a: A {} + +function needsA(x: a) where a: A { + return (); +} + +function main() { + return needsA(0); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-cycle-fail.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-cycle-fail.solc deleted file mode 100644 index 3402f733..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-cycle-fail.solc +++ /dev/null @@ -1,16 +0,0 @@ -pragma no-patterson-condition A; -pragma no-patterson-condition B; - -forall a . class a:A {} -forall a . class a:B {} - -forall a . a:B => instance a:A {} -forall a . a:A => instance a:B {} - -forall a . a:A => function needsA(x:a) -> () { - return (); -} - -function main() -> () { - return needsA(0); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-left-recursive-fail.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-left-recursive-fail.sol new file mode 100644 index 00000000..ae8b3736 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-left-recursive-fail.sol @@ -0,0 +1,13 @@ +pragma no-patterson-condition Loop; + +trait Loop {} + +impl Loop where a: Loop {} + +function needsLoop(x: a) where a: Loop { + return (); +} + +function main() { + return needsLoop(0); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-left-recursive-fail.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-left-recursive-fail.solc deleted file mode 100644 index 1784286e..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-left-recursive-fail.solc +++ /dev/null @@ -1,13 +0,0 @@ -pragma no-patterson-condition Loop; - -forall a . class a:Loop {} - -forall a . a:Loop => instance a:Loop {} - -forall a . a:Loop => function needsLoop(x:a) -> () { - return (); -} - -function main() -> () { - return needsLoop(0); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-mutual-chain.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-mutual-chain.sol new file mode 100644 index 00000000..709fd904 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-mutual-chain.sol @@ -0,0 +1,18 @@ +enum WrapA { WrapA(a) } +enum WrapB { WrapB(a) } + +trait A {} +trait B {} + +impl A {} + +impl B> where a: A {} +impl A> where a: B {} + +function needsA(x: a) where a: A { + return (); +} + +function main() { + return needsA(WrapA(WrapB(0))); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-mutual-chain.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-mutual-chain.solc deleted file mode 100644 index d195a58a..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/tabled-mutual-chain.solc +++ /dev/null @@ -1,18 +0,0 @@ -data WrapA(a) = WrapA(a); -data WrapB(a) = WrapB(a); - -forall a . class a:A {} -forall a . class a:B {} - -instance word:A {} - -forall a . a:A => instance WrapB(a):B {} -forall a . a:B => instance WrapA(a):A {} - -forall a . a:A => function needsA(x:a) -> () { - return (); -} - -function main() -> () { - return needsA(WrapA(WrapB(0))); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-constructor.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-constructor.snap index facde07c..5589e99c 100644 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-constructor.snap +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-constructor.snap @@ -1,10 +1,10 @@ --- source: crates/parser/tests/diagnostics.rs expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-constructor.solc +input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-constructor.sol --- -error[SC0001]: could not parse top-level item near `constructor() {}`; expected a declaration starting with `import`, `pragma`, `type`, `data`, `class`, `instance`, `contract`, or `function` - --> /toplevel-constructor.solc:3:1 +error[SC0001]: could not parse top-level item near `constructor() {}`; expected a declaration starting with `import`, `pragma`, `type`, `enum`, `trait`, `impl`, `contract`, or `function` + --> /toplevel-constructor.sol:3:1 | 1 | // A `constructor` may only be declared inside a contract. 2 | // At the top level this must fail to parse. diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-constructor.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-constructor.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-constructor.solc rename to crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-constructor.sol diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-fallback.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-fallback.snap index 08088eb0..cdfeed62 100644 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-fallback.snap +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-fallback.snap @@ -1,12 +1,12 @@ --- source: crates/parser/tests/diagnostics.rs expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-fallback.solc +input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-fallback.sol --- -error[SC0001]: could not parse top-level item near `fallback() -> () {}`; expected a declaration starting with `import`, `pragma`, `type`, `data`, `class`, `instance`, `contract`, or `function` - --> /toplevel-fallback.solc:3:1 +error[SC0001]: could not parse top-level item near `fallback() {}`; expected a declaration starting with `import`, `pragma`, `type`, `enum`, `trait`, `impl`, `contract`, or `function` + --> /toplevel-fallback.sol:3:1 | 1 | // A `fallback` may only be declared inside a contract. 2 | // At the top level this must fail to parse. -3 | fallback() -> () {} - | ^^^^^^^^^^^^^^^^^^^ +3 | fallback() {} + | ^^^^^^^^^^^^^ diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-fallback.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-fallback.sol new file mode 100644 index 00000000..f5bc0367 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-fallback.sol @@ -0,0 +1,3 @@ +// A `fallback` may only be declared inside a contract. +// At the top level this must fail to parse. +fallback() {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-fallback.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-fallback.solc deleted file mode 100644 index 850ecf86..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/toplevel-fallback.solc +++ /dev/null @@ -1,3 +0,0 @@ -// A `fallback` may only be declared inside a contract. -// At the top level this must fail to parse. -fallback() -> () {} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/unbound-instance-var.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/unbound-instance-var.sol new file mode 100644 index 00000000..86a8b1bc --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/unbound-instance-var.sol @@ -0,0 +1,15 @@ +trait C { + function size(x: self) returns (word) ; +} + +impl C<()> { + function size(x: ()) returns (word) { + return 0; + } +} + +impl C { + function size(x: uint) returns (word) { + return 1; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/unbound-instance-var.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/unbound-instance-var.solc deleted file mode 100644 index 7b2e1a8b..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/unbound-instance-var.solc +++ /dev/null @@ -1,16 +0,0 @@ -forall self. -class self:C { - function size(x:self) -> word; -} - -instance ():C { - function size(x:()) -> word { - return 0; - } -} - -instance uint:C { - function size(x:uint) -> word { - return 1; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/unconstrained-instance.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/unconstrained-instance.sol new file mode 100644 index 00000000..9a6955b0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/unconstrained-instance.sol @@ -0,0 +1,26 @@ +enum memory { memory(word) } + +trait ValueTy { + function rep(x: t) returns (word) ; +} + +impl ValueTy> { + function rep(x: memory) returns (word) { + match (x) { +case memory(w) { +return w; +} +} + } +} + +trait Ref { + function store(loc: ref, value: deref) ; +} + +impl Ref, t> { + function store(loc: memory, value: t) { + // We don't have a `ValueTy` bound on `t` anywhere, so this should raise a type error... + let vw = ValueTy.rep(value); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/unconstrained-instance.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/unconstrained-instance.solc deleted file mode 100644 index 6e838cc5..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/unconstrained-instance.solc +++ /dev/null @@ -1,24 +0,0 @@ -data memory(t) = memory(word); - -class t:ValueTy { - function rep(x:t) -> word; -} - -instance memory(t) : ValueTy { - function rep(x: memory(t)) -> word { - match x { - | memory(w) => return w; - }; - } -} - -class ref:Ref(deref) { - function store(loc: ref, value: deref) -> (); -} - -instance memory(t) : Ref(t) { - function store(loc: memory(t), value: t) -> () { - // We don't have a `ValueTy` bound on `t` anywhere, so this should raise a type error... - let vw = ValueTy.rep(value); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/user-op-lambda.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/user-op-lambda.snap index 08755e85..a4826c37 100644 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/user-op-lambda.snap +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/user-op-lambda.snap @@ -1,10 +1,10 @@ --- source: crates/parser/tests/diagnostics.rs expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/user-op-lambda.solc +input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/cases/user-op-lambda.sol --- -error[SC0001]: could not parse top-level item near `infixl 70 (^^) => pow;`; expected a declaration starting with `import`, `pragma`, `type`, `data`, `class`, `instance`, `contract`, or `function` - --> /user-op-lambda.solc:6:1 +error[SC0001]: could not parse top-level item near `infixl 70 (^^) => pow;`; expected a declaration starting with `import`, `pragma`, `type`, `enum`, `trait`, `impl`, `contract`, or `function` + --> /user-op-lambda.sol:6:1 | 5 | 6 | infixl 70 (^^) => pow; @@ -13,12 +13,12 @@ error[SC0001]: could not parse top-level item near `infixl 70 (^^) => pow;`; exp | --- -error[SC0001]: parse error: unexpected `^` - --> /user-op-lambda.solc:17:47 +error[SC0001]: parse error: unexpected `;` + --> /user-op-lambda.sol:17:50 | 16 | // operator (^^) used inside a lambda body 17 | let f = lam(x : word) -> word { return x ^^ 3; }; - | ^ unexpected token + | ^ unexpected token 18 | return f(2); | - = note: expecting `!`, `(`, `.`, `@`, `[`, `if`, `lam`, or `~` + = note: expecting `&&`, `&`, `(`, `.`, `?`, `[`, `^`, `|`, or `||` diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/user-op-lambda.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/user-op-lambda.sol new file mode 100644 index 00000000..02b88be6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/user-op-lambda.sol @@ -0,0 +1,20 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +infixl 70 (^^) => pow; + +function pow(b: word, e: word) returns (word) { + let r : word; + assembly { r := exp(b, e) } + return r; +} + +contract UserOpLambda { + function main() returns (word) { + // operator (^^) used inside a lambda body + let f = lam(x : word) -> word { return x ^^ 3; }; + return f(2); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/user-op-lambda.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/user-op-lambda.solc deleted file mode 100644 index ec2d8cd5..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/user-op-lambda.solc +++ /dev/null @@ -1,20 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -infixl 70 (^^) => pow; - -function pow(b : word, e : word) -> word { - let r : word; - assembly { r := exp(b, e) } - return r; -} - -contract UserOpLambda { - function main() -> word { - // operator (^^) used inside a lambda body - let f = lam(x : word) -> word { return x ^^ 3; }; - return f(2); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/vartyped.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/vartyped.sol new file mode 100644 index 00000000..68249b3e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/vartyped.sol @@ -0,0 +1,4 @@ +function foo () { + let f : function(word) returns (word) = lam (x) { return x ; } ; + return f(1); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/vartyped.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/vartyped.solc deleted file mode 100644 index 3b89a402..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/vartyped.solc +++ /dev/null @@ -1,4 +0,0 @@ -function foo () { - let f : (word) -> word = lam (x) { return x ; } ; - return f(1); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/weird-error-foo.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/weird-error-foo.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/fail/test/examples/cases/weird-error-foo.solc rename to crates/parser/tests/fixtures/corpus/fail/test/examples/cases/weird-error-foo.sol diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/weirdfoo.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/weirdfoo.sol new file mode 100644 index 00000000..c077a262 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/weirdfoo.sol @@ -0,0 +1,5 @@ +enum W { W(a) } +trait Foo {function foo(); } +impl Foo<(word, W)> { + function foo() {} +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/weirdfoo.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/weirdfoo.solc deleted file mode 100644 index a94677e9..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/weirdfoo.solc +++ /dev/null @@ -1,5 +0,0 @@ -data W(a) = W(a); -class a: Foo {function foo(); } -instance ((word, a) : Foo) => (word, W(a)) : Foo { - function foo() {} -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/xref.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/xref.sol new file mode 100644 index 00000000..0cc13002 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/xref.sol @@ -0,0 +1,144 @@ +function add_(x:word, y:word) { // _add is not a legal identifier :( + let res: word; + assembly { + res := add(x, y) + } + return res; + } + +function mload_(x: word) returns (word) { + let res: word; + assembly { + res := mload(x) + } + return res; + } + +function mstore_(a:word, v:word) { + assembly { mstore(a,v) } +} + +trait Ref { function load(x: r) returns (d) ; function store(x: r, v: d) ;} + +trait Typedef { + function rep(x: self) returns (underlyingType) ; // abbr: x.rep = Typedef.rep(x) + function abs(x: underlyingType) returns (self) ; // abbr: x.abs +} +enum Proxy { Proxy } + +enum M { M(word) } + +impl Typedef, word> { + function rep(m: M) returns (word) { match (m) { +case M(w) { +return w; +} +}} + function abs(w: word) returns (M) { return M(w); } +} + +trait MemoryType { + function memorySize(p: Proxy) returns (word) ; + /* inline function sizeof(Self) -> word { // an abbreviation to avoid writing Proxy; wasteful unless inlined + return memorySize(Proxy:Proxy(self)); + } */ + function memoryStep(offset: word, self: Self) returns (word) ; + function mload(r: word) returns (Self) ; + function mstore(r: word, v: Self) ; +} + +function sizeof(self: Self) returns (word) where Self: MemoryType { + return MemoryType.memorySize(@Self); +} + +trait MemoryRef { function addr(r: a) returns (word) ; } +impl MemoryRef, a> { function addr(r: M) returns (word) {return Typedef.rep(r);} } + +function xaddr(r: M) returns (word) { return MemoryRef.addr(r); } +function asMemRefTo(r: M, p: Proxy) returns (M) { return Typedef.abs(xaddr(r)); } + +function stepStore(aa: word, va: a) returns (word) where a: MemoryType { + MemoryType.mstore(aa, va); + return add_(aa, MemoryType.memorySize(@a)); +} + +impl Ref where Self: MemoryType, r: MemoryRef { + function load(r: M) returns (Self) { return MemoryType.mload(xaddr(r)); } + function store(r: M, v: Self) { MemoryType.mstore(xaddr(r), v); } +} + +impl MemoryType { + function memorySize(p: Proxy) returns (word) { return 32; } + function memoryStep(a: word, self: word) returns (word) { return add_(a,32); } + function mload(a: word) returns (word) { return mload_(a); } + function mstore(a: word, v: word) { mstore_(a, v); } +} + +impl MemoryType<(a, b)> where a: MemoryType, b: MemoryType { + function memorySize(p: Proxy<(a, b)>) returns (word) { + return add_(MemoryType.memorySize(@a), MemoryType.memorySize(@a) ); + } + + function mload(aa: word) returns (a, b) { + let va = MemoryType.mload(aa); + let ab = add_(aa, sizeof(va)); + let vb = MemoryType.mload(ab); + return (va,vb); + } + + function mstore(aa: word, v: (a, b)) { + match (v) { +case pair(va, vb) { +mstore2(aa, va, vb); +} +} // match-compiler cannot compile mopre than 1 stmt in a branch :( + } +} + +function mstore2(aa: word, va: a, vb: b) where a: MemoryType, b: MemoryType { //needed because of bug in match-compiler + let ab = stepStore(aa, va); + MemoryType.mstore(ab, vb); +} + +enum XRef { XRef(st, field) } +enum PairFst { PairFst } +enum PairSnd { PairSnd } + + +impl MemoryRef, a> where r: MemoryRef<(a, b)>, a: MemoryType, b: MemoryType { + function addr(xr: XRef) returns (word) { + match (xr) { +case XRef(r, _) { +return MemoryRef.addr(r); +} +} + } +} + +impl MemoryRef, b> where r: MemoryRef<(a, b)>, a: MemoryType, b: MemoryType { + function addr(xr: XRef) returns (word) { + match (xr) { +case XRef(r, _) { +return add_(MemoryRef.addr(r), MemoryType.memorySize(@b)); +} +} + } +} + +contract Ref219 { + function main() public { + let mp:M<(word, word, word)> = M(96); // no alloc yet + let p = (1,16,25); + Ref.store(mp, p); + + let ra = XRef(mp, PairFst); + let a = Ref.load(ra); + let r2 = XRef(mp, PairSnd); + let rb = XRef(r2, PairFst); + let a = Ref.load(ra); + let b = Ref.load(rb); + let rc = XRef(r2, PairSnd); + let c = Ref.load(rc); + return add_(a, add_(b, c)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/xref.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/xref.solc deleted file mode 100644 index d3cc27d2..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/xref.solc +++ /dev/null @@ -1,130 +0,0 @@ -function add_(x:word, y:word) { // _add is not a legal identifier :( - let res: word; - assembly { - res := add(x, y) - } - return res; - } - -function mload_(x:word) -> word { - let res: word; - assembly { - res := mload(x) - } - return res; - } - -function mstore_(a:word, v:word) { - assembly { mstore(a,v) } -} - -forall r d . class r:Ref(d) { function load(x:r) -> d; function store(x:r, v:d) -> ();} - -forall self underlyingType . class self:Typedef(underlyingType) { - function rep(x:self) -> underlyingType; // abbr: x.rep = Typedef.rep(x) - function abs(x:underlyingType) -> self; // abbr: x.abs -} -data Proxy(a) = Proxy; - -data M(a) = M(word); - -forall a . instance M(a) : Typedef(word) { - function rep(m : M(a)) -> word { match m { | M(w) => return w; }} - function abs(w : word) -> M(a) { return M(w); } -} - -forall Self . class Self:MemoryType { - function memorySize(p:Proxy(Self)) -> word; - /* inline function sizeof(Self) -> word { // an abbreviation to avoid writing Proxy; wasteful unless inlined - return memorySize(Proxy:Proxy(self)); - } */ - function memoryStep(word, self:Self) -> word; - function mload(r:word) -> Self; - function mstore(r:word, v:Self) -> (); -} - -forall Self . Self:MemoryType => function sizeof(self:Self) -> word { - return MemoryType.memorySize(Proxy:Proxy(Self)); -} - -forall a d . class a:MemoryRef(d) { function addr(r:a) -> word; } -forall a . instance M(a):MemoryRef(a) { function addr(r:M(a)) -> word {return Typedef.rep(r);} } - -forall a . function xaddr(r:M(a)) -> word { return MemoryRef.addr(r); } -forall a b . function asMemRefTo(r:M(a), p:Proxy(b)) -> M(b) { return Typedef.abs(xaddr(r)); } - -forall a . a:MemoryType => function stepStore(aa: word, va: a) -> word { - MemoryType.mstore(aa, va); - return add_(aa, MemoryType.memorySize(Proxy:Proxy(a))); -} - -forall Self r . Self:MemoryType, r:MemoryRef(Self) => instance r : Ref(Self) { - function load(r:M(Self)) -> Self { return MemoryType.mload(xaddr(r)); } - function store(r:M(Self), v:Self) -> () { MemoryType.mstore(xaddr(r), v); } -} - -instance word:MemoryType { - function memorySize(p:Proxy(word)) -> word { return 32; } - function memoryStep(a:word, self:word) -> word { return add_(a,32); } - function mload(a: word) -> word { return mload_(a); } - function mstore(a: word, v:word) -> () { mstore_(a, v); } -} - -forall a b . a:MemoryType, b:MemoryType => instance (a,b) : MemoryType { - function memorySize(p:Proxy((a,b))) -> word { - return add_(MemoryType.memorySize(Proxy:Proxy(a)), MemoryType.memorySize(Proxy:Proxy(a)) ); - } - - function mload(aa:word) -> (a,b) { - let va = MemoryType.mload(aa); - let ab = add_(aa, sizeof(va)); - let vb = MemoryType.mload(ab); - return (va,vb); - } - - function mstore(aa:word, v: (a,b)) -> () { - match v { | pair(va, vb) => mstore2(aa, va, vb); } // match-compiler cannot compile mopre than 1 stmt in a branch :( - } -} - -forall a b . a: MemoryType, b: MemoryType => function mstore2(aa:word, va:a, vb: b) { //needed because of bug in match-compiler - let ab = stepStore(aa, va); - MemoryType.mstore(ab, vb); -} - -data XRef(st, field, fieldType) = XRef(st, field); -data PairFst = PairFst; -data PairSnd = PairSnd; - - -forall a b r . r:MemoryRef ( (a,b)), a:MemoryType, b:MemoryType => instance XRef(r, PairFst, a) : MemoryRef(a) { - function addr(xr : XRef(r, PairFst, a)) -> word { - match xr { | XRef(r, _) => return MemoryRef.addr(r); } - } -} - -forall a b r . r:MemoryRef ((a,b)), a:MemoryType, b:MemoryType => instance XRef(r, PairSnd, b) : MemoryRef(b) { - function addr(xr : XRef (r, PairSnd, b)) -> word { - match xr { - | XRef(r, _) => return add_(MemoryRef.addr(r), MemoryType.memorySize(Proxy : Proxy(b))); - } - } -} - -contract Ref219 { - public function main() { - let mp:M((word, word, word)) = M(96); // no alloc yet - let p = (1,16,25); - Ref.store(mp, p); - - let ra = XRef(mp, PairFst); - let a = Ref.load(ra); - let r2 = XRef(mp, PairSnd); - let rb = XRef(r2, PairFst); - let a = Ref.load(ra); - let b = Ref.load(rb); - let rc = XRef(r2, PairSnd); - let c = Ref.load(rc); - return add_(a, add_(b, c)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/yul-multi-return-arity-fail.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/yul-multi-return-arity-fail.sol new file mode 100644 index 00000000..1d6a5f43 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/yul-multi-return-arity-fail.sol @@ -0,0 +1,18 @@ +// The arity check must still reject a genuine mismatch: 'pair' returns 2 +// values but 3 names are being assigned, so this Yul is invalid and the type +// checker must report the arity error. +contract YulMultiRetBad { + function main() public returns (word) { + let x : word; + let y : word; + let z : word; + assembly { + function pair() -> a, b { + a := 1 + b := 2 + } + x, y, z := pair() + } + return x; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/yul-multi-return-arity-fail.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/yul-multi-return-arity-fail.solc deleted file mode 100644 index de58b945..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/cases/yul-multi-return-arity-fail.solc +++ /dev/null @@ -1,18 +0,0 @@ -// The arity check must still reject a genuine mismatch: 'pair' returns 2 -// values but 3 names are being assigned, so this Yul is invalid and the type -// checker must report the arity error. -contract YulMultiRetBad { - public function main() -> word { - let x : word; - let y : word; - let z : word; - assembly { - function pair() -> a, b { - a := 1 - b := 2 - } - x, y, z := pair() - } - return x; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/OneOne.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/OneOne.sol new file mode 100644 index 00000000..5ba804da --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/OneOne.sol @@ -0,0 +1,14 @@ +function addWord(l: word, r: word) returns (word) { + let rw : word; + assembly { + rw := add(l,r); + } + return rw; +} + +function zero () returns (word) { 0 } +function one() returns (word) { addWord(1, zero()) } + +contract OneOne { + function main() returns (word) { addWord(one(), one()) } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/OneOne.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/OneOne.solc deleted file mode 100644 index 0c74f7ba..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/OneOne.solc +++ /dev/null @@ -1,14 +0,0 @@ -function addWord(l: word, r: word) -> word { - let rw : word; - assembly { - rw := add(l,r); - } - return rw; -} - -function zero () { 0 } -function one() { addWord(1, zero()) } - -contract OneOne { - function main() -> word { addWord(one(), one()) } -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_poly_runtime.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_poly_runtime.sol new file mode 100644 index 00000000..66341e77 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_poly_runtime.sol @@ -0,0 +1,27 @@ +/* Negative: comptime violation in a polymorphic (generic) function. + Before specialisation the concrete type of 'z' is unknown, so this + cannot be resolved by inlining. The SAIL-level check catches the + violation: 'z' is a non-comptime parameter and cannot satisfy the + comptime contract of 'unwrap'. +*/ +import std; + +trait Wrap { + function unwrap(comptime x: t) returns (comptime) ; +} + +impl Wrap { + function unwrap(comptime x: word) returns (comptime) { + return x; + } +} + +function process(z: t) returns (word) where t: Wrap { + return Wrap.unwrap(z); +} + +contract ComptimeParamPolyRuntime { + function main() returns (word) { + return process(42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_poly_runtime.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_poly_runtime.solc deleted file mode 100644 index e67a24c1..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_poly_runtime.solc +++ /dev/null @@ -1,27 +0,0 @@ -/* Negative: comptime violation in a polymorphic (generic) function. - Before specialisation the concrete type of 'z' is unknown, so this - cannot be resolved by inlining. The SAIL-level check catches the - violation: 'z' is a non-comptime parameter and cannot satisfy the - comptime contract of 'unwrap'. -*/ -import std; - -forall t. class t : Wrap { - function unwrap(comptime x : t) -> comptime word; -} - -instance word : Wrap { - function unwrap(comptime x : word) -> comptime word { - return x; - } -} - -forall t. t:Wrap => function process(z : t) -> word { - return Wrap.unwrap(z); -} - -contract ComptimeParamPolyRuntime { - function main() -> word { - return process(42); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_runtime.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_runtime.sol new file mode 100644 index 00000000..d9dd45f3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_runtime.sol @@ -0,0 +1,19 @@ +/* Negative: non-comptime function parameter passed to a comptime parameter. + Caught by the SAIL-level check: 'process' CAN be called with an argument + not known at compile time, which would violate the comptime requirement + of 'double'. The SAIL check rejects this on the parameter type alone, + before looking at specific call sites. +*/ +import std; + +contract ComptimeParamRuntime { + function double(comptime x: word) returns (comptime) { + return x + x; + } + function process(value: word) returns (word) { + return double(value); + } + function main() returns (word) { + return process(21); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_runtime.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_runtime.solc deleted file mode 100644 index 496cb2a7..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/ct_param_runtime.solc +++ /dev/null @@ -1,19 +0,0 @@ -/* Negative: non-comptime function parameter passed to a comptime parameter. - Caught by the SAIL-level check: 'process' CAN be called with an argument - not known at compile time, which would violate the comptime requirement - of 'double'. The SAIL check rejects this on the parameter type alone, - before looking at specific call sites. -*/ -import std; - -contract ComptimeParamRuntime { - function double(comptime x : word) -> comptime word { - return x + x; - } - function process(value : word) -> word { - return double(value); - } - function main() -> word { - return process(21); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt.sol new file mode 100644 index 00000000..77a338b9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt.sol @@ -0,0 +1,79 @@ +/* Handling numeric literals + +Eventually we may want to have a comptime integer type (unlimited precision) +and literals desugar to `fromInteger(lit)` + +Here we use a bit less ambitious approach: literals of type word and `fromWord` method +*/ + +import std; + +type uint = uint256; // misleads instance solver + +trait Int { + function fromWord(x: word) returns (comptime) ; // meaning result is comptime whenever arg is + + function toWord(x: i) returns (comptime) ; +} + + +impl Int { + function fromWord(x: word) returns (comptime) { x } + function toWord(x: word) returns (comptime) { x } +} + +impl Int { + function fromWord(x: word) returns (comptime) { uint256(x) } + function toWord(x: uint) returns (comptime) { Typedef.rep(x) } +} + + +// specialised for numbers +function fromInt(x: a) returns (b) where a: Int, b: Int { Int.fromWord(Int.toWord(x)) } +function staticInt(comptime x: a) returns (comptime) where a: Int, b: Int { Int.fromWord(Int.toWord(x)) } + +// limited usability +function dynamic_cast(x: a) returns (b) where a: Typedef, b: Typedef { Typedef.abs(Typedef.rep(x)) } +function static_cast(comptime x: a) returns (comptime) where a: Typedef, b: Typedef { Typedef.abs(Typedef.rep(x)) } + +// wider usability +function dynamic_cast_via(p: @r, x: a) returns (b) where a: Typedef, b: Typedef { Typedef.abs(Typedef.rep(x)) } + +function static_cast_via(comptime p: @r, comptime x: a) returns (comptime) where a: Typedef, b: Typedef { Typedef.abs(Typedef.rep(x)) } +// maybe: `comptime function static_cast_via` as equivalent notation + +function notcomptime(x: word) returns (word) { + let res : word; + assembly { + res := mload(0) + } + return res; +} + +function id(x: a) returns (comptime) { x } +function id_uint(x: uint) returns (comptime) { x } +contract FromWord { + constructor() {} + function f1(x: word) returns (comptime) { x } + function f2(x: uint) returns (comptime) { x } + function g() returns (uint) { + let y1 : comptime = static_cast( // cast on top level of comptime let + f1( + static_cast(42) //cast a literal - could be fromWord/staticInt + )); + + let y2 : comptime = staticInt( id_uint(staticInt(42)) ); // cast at literal, cast at let + + let z = notcomptime(Typedef.rep(y1)); // no cast - not comptime + let t = dynamic_cast(y1); // just testing + return t; + } + + function h() returns (comptime) { + let y2 : comptime = staticInt( ( staticInt(42) )); // error w/o type annotation + return y2; + } + function main() returns (uint) { + return g(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt.solc deleted file mode 100644 index f1525446..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt.solc +++ /dev/null @@ -1,82 +0,0 @@ -/* Handling numeric literals - -Eventually we may want to have a comptime integer type (unlimited precision) -and literals desugar to `fromInteger(lit)` - -Here we use a bit less ambitious approach: literals of type word and `fromWord` method -*/ - -import std; - -type uint = uint256; // misleads instance solver - -forall i. -class i : Int { - function fromWord(x:word) -> comptime i; // meaning result is comptime whenever arg is - - function toWord(x:i) -> comptime word; -} - - -instance word : Int { - function fromWord(x:word) -> comptime word { x } - function toWord(x:word) -> comptime word { x } -} - -instance uint : Int { - function fromWord(x:word) -> comptime uint { uint256(x) } - function toWord(x:uint) -> comptime word { Typedef.rep(x) } -} - - -// specialised for numbers -forall a b. a:Int, b:Int => function fromInt(x:a) -> b { Int.fromWord(Int.toWord(x)) } -forall a b. a:Int, b:Int => function staticInt(comptime x:a) -> comptime b { Int.fromWord(Int.toWord(x)) } - -// limited usability -forall a b r. a:Typedef(r), b:Typedef(r) => function dynamic_cast(x:a) -> b { Typedef.abs(Typedef.rep(x):r) } -forall a b r. a:Typedef(r), b:Typedef(r) => function static_cast(comptime x:a) -> comptime b { Typedef.abs(Typedef.rep(x):r) } - -// wider usability -forall a b r. a:Typedef(r), b:Typedef(r) => -function dynamic_cast_via(p:@r, x:a) -> b { Typedef.abs(Typedef.rep(x):r) } - -forall a b r. a:Typedef(r), b:Typedef(r) => -function static_cast_via(comptime p:@r, comptime x:a) -> comptime b { Typedef.abs(Typedef.rep(x):r) } -// maybe: `comptime function static_cast_via` as equivalent notation - -function notcomptime(x:word) -> word { - let res : word; - assembly { - res := mload(0) - } - return res; -} - -forall a. function id(x:a) -> comptime a { x } -function id_uint(x:uint) -> comptime uint { x } -contract FromWord { - constructor() {} - function f1(x : word) -> comptime word { x } - function f2(x : uint) -> comptime uint { x } - function g() -> uint { - let y1 : comptime uint256 = static_cast( // cast on top level of comptime let - f1( - static_cast(42) //cast a literal - could be fromWord/staticInt - )); - - let y2 : comptime uint256 = staticInt( id_uint(staticInt(42)) ); // cast at literal, cast at let - - let z = notcomptime(Typedef.rep(y1)); // no cast - not comptime - let t = dynamic_cast(y1); // just testing - return t; - } - - function h() -> comptime uint256 { - let y2 : comptime uint256 = staticInt( ( staticInt(42) ):uint256); // error w/o type annotation - return y2; - } - function main() { - return g(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt2.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt2.sol new file mode 100644 index 00000000..b5aa5e9a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt2.sol @@ -0,0 +1,46 @@ +// import std.{Num,Add,Sub,Eq,Ord,Bounded,Typedef,le}; +import std; + +trait Int { + function fromWord(x: word) returns (comptime) ; // meaning result is comptime whenever arg is + + function toWord(x: i) returns (comptime) ; +} + +impl Int { + function fromWord(x: word) returns (uint256) { Typedef.abs(x) } + function toWord(y: uint256) returns (word) { Typedef.rep(y) } +} + +impl Mul { + function mul(x: uint256, y: uint256) returns (uint256) { + Int.fromWord(Mul.mul(Int.toWord(x), Int.toWord(y))) + } +} +impl Int { + function fromWord(x: word) returns (word) { x } + function toWord(y: word) returns (word) { y } +} + +function bitAnd(x: word, y: word) returns (comptime) { + let res : word; + assembly { + res := and(x,y) + } + return res; +} +function fromLit(x: word) returns (a) where a: Num { Num.fromWord(x) } + +contract FromInt { + function main() returns (uint256) { + let a : uint256 = fromLit(1); + let b : comptime = fromLit((2 + 2)); // CTE + let c : uint256 = fromLit(3) + fromLit(3); // RTE + // let d : comptime word = fromLit(bitAnd(0xff,keccakLit("foo"+"bar"))); // CTE + let d : comptime = fromLit(bitAnd(0xff,keccakLit("foo"+"bar"))); // CTE + + let k = fromLit(40); + return k+2; + // return b*b + fromLit(4)*a*c + fromLit(d); + } +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt2.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt2.solc deleted file mode 100644 index c2714fab..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt2.solc +++ /dev/null @@ -1,48 +0,0 @@ -// import std.{Num,Add,Sub,Eq,Ord,Bounded,Typedef,le}; -import std; - -forall i. -class i : Int { - function fromWord(x:word) -> comptime i; // meaning result is comptime whenever arg is - - function toWord(x:i) -> comptime word; -} - -instance uint256 : Int { - function fromWord(x:word) -> uint256 { Typedef.abs(x) } - function toWord(y:uint256) -> word { Typedef.rep(y) } -} - -instance uint256 : Mul { - function mul(x: uint256, y: uint256) -> uint256 { - Int.fromWord(Mul.mul(Int.toWord(x), Int.toWord(y))) - } -} -instance word : Int { - function fromWord(x:word) -> word { x } - function toWord(y:word) -> word { y } -} - -function bitAnd(x:word, y:word) -> comptime word { - let res : word; - assembly { - res := and(x,y) - } - return res; -} -forall a. a: Num => -function fromLit(x:word) -> a { Num.fromWord(x) } - -contract FromInt { - function main() -> uint256 { - let a : uint256 = fromLit(1); - let b : comptime uint256 = fromLit((2 + 2)); // CTE - let c : uint256 = fromLit(3) + fromLit(3); // RTE - // let d : comptime word = fromLit(bitAnd(0xff,keccakLit("foo"+"bar"))); // CTE - let d : comptime word = fromLit(bitAnd(0xff,keccakLit("foo"+"bar"))); // CTE - - let k = fromLit(40); - return k+2; - // return b*b + fromLit(4)*a*c + fromLit(d); - } -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt3.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt3.sol new file mode 100644 index 00000000..b4342875 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt3.sol @@ -0,0 +1,39 @@ +// import std.{Num,Add,Sub,Eq,Ord,Bounded,Typedef,le}; +import std; + +trait Int { + function fromWord(x: word) returns (comptime) ; // meaning result is comptime whenever arg is + + function toWord(x: i) returns (comptime) ; +} + +impl Int { + function fromWord(x: word) returns (uint256) { Typedef.abs(x) } + function toWord(y: uint256) returns (word) { Typedef.rep(y) } +} + +impl Mul { + function mul(x: uint256, y: uint256) returns (uint256) { + Int.fromWord(Mul.mul(Int.toWord(x), Int.toWord(y))) + } +} +impl Int { + function fromWord(x: word) returns (word) { x } + function toWord(y: word) returns (word) { y } +} + +function bitAnd(x: word, y: word) returns (comptime) { + let res : word; + assembly { + res := and(x,y) + } + return res; +} +function fromLit(x: word) returns (a) where a: Num { Num.fromWord(x) } + +contract FromInt { + function main() returns (uint256) { + let k = fromLit(40); + return k+2; + } +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt3.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt3.solc deleted file mode 100644 index 88d4cacf..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromInt3.solc +++ /dev/null @@ -1,41 +0,0 @@ -// import std.{Num,Add,Sub,Eq,Ord,Bounded,Typedef,le}; -import std; - -forall i. -class i : Int { - function fromWord(x:word) -> comptime i; // meaning result is comptime whenever arg is - - function toWord(x:i) -> comptime word; -} - -instance uint256 : Int { - function fromWord(x:word) -> uint256 { Typedef.abs(x) } - function toWord(y:uint256) -> word { Typedef.rep(y) } -} - -instance uint256 : Mul { - function mul(x: uint256, y: uint256) -> uint256 { - Int.fromWord(Mul.mul(Int.toWord(x), Int.toWord(y))) - } -} -instance word : Int { - function fromWord(x:word) -> word { x } - function toWord(y:word) -> word { y } -} - -function bitAnd(x:word, y:word) -> comptime word { - let res : word; - assembly { - res := and(x,y) - } - return res; -} -forall a. a: Num => -function fromLit(x:word) -> a { Num.fromWord(x) } - -contract FromInt { - function main() -> uint256 { - let k = fromLit(40); - return k+2; - } -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromLit.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromLit.sol new file mode 100644 index 00000000..fe5f2d21 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromLit.sol @@ -0,0 +1,35 @@ +import std; + +trait FromLit { + function fromLit(l: b) returns (a) ; +} + +function fromLit(l: b) returns (a) where a: FromLit { FromLit.fromLit(l) } + +impl FromLit { + function fromLit(l: word) returns (word) { l } +} + +impl FromLit { + function fromLit(l: word) returns (uint256) { uint256(l) } +} + +/* +// this does not define instance uint256:fromLit(uint256) +forall a. +default instance a:FromLit(a) { + function fromLit(l:a) -> a { l } +} +*/ +impl Mul { + function mul(a: uint256, b: uint256) returns (uint256) { uint256(Mul.mul(Typedef.rep(a),Typedef.rep(b))) } +} + +function main() returns (uint256) { + let a : uint256 = fromLit(1); + let b : comptime = fromLit(2 + 2); // CTE + let c : uint256 = fromLit(3) + fromLit(3); // RTE + let d : comptime = fromLit(keccakLit("foo"+"bar")); // CTE + + return b*b - fromLit(4)*a*c + fromLit(d); // RTE in RTC +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromLit.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromLit.solc deleted file mode 100644 index 52b6d15a..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/fromLit.solc +++ /dev/null @@ -1,36 +0,0 @@ -import std; - -forall a b. class a:FromLit(b) { - function fromLit(l:b) -> a; -} - -forall a b. a:FromLit(b) => -function fromLit(l:b) -> a { FromLit.fromLit(l) } - -instance word:FromLit(word) { - function fromLit(l:word) -> word { l } -} - -instance uint256:FromLit(word) { - function fromLit(l:word) -> uint256 { uint256(l) } -} - -/* -// this does not define instance uint256:fromLit(uint256) -forall a. -default instance a:FromLit(a) { - function fromLit(l:a) -> a { l } -} -*/ -instance uint256:Mul { - function mul(a:uint256, b:uint256) -> uint256 { uint256(Mul.mul(Typedef.rep(a),Typedef.rep(b))) } -} - -function main() -> uint256 { - let a : uint256 = fromLit(1); - let b : comptime uint256 = fromLit(2 + 2); // CTE - let c : uint256 = fromLit(3) + fromLit(3); // RTE - let d : comptime word = fromLit(keccakLit("foo"+"bar")); // CTE - - return b*b - fromLit(4)*a*c + fromLit(d); // RTE in RTC -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/string-mem-runtime-fail.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/string-mem-runtime-fail.sol new file mode 100644 index 00000000..7db1a9e9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/string-mem-runtime-fail.sol @@ -0,0 +1,13 @@ +// A `string` is comptime-only: it has no runtime representation. Returning a +// string-typed value where memory(string) is expected, without an explicit +// Str.fromString conversion, must be rejected by the type checker. + +import std; +import * from std; + +contract StringMemRuntimeFail { + function f() public returns (memory) { + let s : string = "x"; + return s; + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/string-mem-runtime-fail.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/string-mem-runtime-fail.solc deleted file mode 100644 index 0f0ab9ca..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/comptime/string-mem-runtime-fail.solc +++ /dev/null @@ -1,13 +0,0 @@ -// A `string` is comptime-only: it has no runtime representation. Returning a -// string-typed value where memory(string) is expected, without an explicit -// Str.fromString conversion, must be rejected by the type checker. - -import std; -import std.{*}; - -contract StringMemRuntimeFail { - public function f() -> memory(string) { - let s : string = "x"; - return s; - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/dispatch/fib.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/dispatch/fib.sol new file mode 100644 index 00000000..a768e126 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/dispatch/fib.sol @@ -0,0 +1,12 @@ +import * from std.dispatch; + +function fib(n: word) returns (word) { + if(n < 2) { return n; } else {return fib(n-1) + fib(n-2); } +} + +contract Fib { + constructor() {} + function test() public returns (uint256) { + return uint256(fib(10)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/dispatch/fib.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/dispatch/fib.solc deleted file mode 100644 index 3c01cd4b..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/dispatch/fib.solc +++ /dev/null @@ -1,12 +0,0 @@ -import std.dispatch.{*}; - -function fib(n : word) -> word { - if(n < 2) { return n; } else {return fib(n-1) + fib(n-2); } -} - -contract Fib { - constructor() {} - public function test() -> uint256 { - return uint256(fib(10)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/021nid.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/021nid.sol new file mode 100644 index 00000000..99aa703e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/021nid.sol @@ -0,0 +1,15 @@ +contract Id1 { +function id(x: a) public { + return x ; + } + + function nid() public { + return id; + } + +function const(x: a, y: b) public { return x; } + + function main() public { + return nid(42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/021nid.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/021nid.solc deleted file mode 100644 index a8deffa3..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/021nid.solc +++ /dev/null @@ -1,15 +0,0 @@ -contract Id1 { - public function id(x) { - return x ; - } - - public function nid() { - return id; - } - - public function const(x, y) { return x; } - - public function main() { - return nid(42); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/022nid-invoke.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/022nid-invoke.snap deleted file mode 100644 index 35214316..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/022nid-invoke.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/parser/tests/diagnostics.rs -expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/022nid-invoke.solc ---- -error[SC0001]: parse error: unexpected `instance` - --> /022nid-invoke.solc:12:1 - | -11 | -12 | instance IdToken(a) : Invokable(a,a) { - | ^^^^^^^^ unexpected token -13 | function invoke(token: IdToken(a), arg:a) -> a { - | - = note: expecting `(`, `;`, or `|` - = note: while parsing data declaration diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/022nid-invoke.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/022nid-invoke.sol new file mode 100644 index 00000000..84c3eb53 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/022nid-invoke.sol @@ -0,0 +1,37 @@ + +trait Invokable { + function invoke(s: self, a: args) returns (ret) ; + } + + function id(x: a) returns (a) { + return x ; + } + + enum IdToken { IdToken } + +impl Invokable, a, a> { + function invoke(token: IdToken, arg: a) returns (a) { + return id(arg); + } +} + +contract InvokeId { + function id(x: a) public returns (a) { + return x ; + } + + /* + function nid() { + return id; + } + */ + + function nidimpl() public returns (IdToken) { + return IdToken; + } + + function main() public returns (word) { + // Instead of: `return nid(42)` + return invoke(nidimpl(), 42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/022nid-invoke.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/022nid-invoke.solc deleted file mode 100644 index 81346bfe..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/022nid-invoke.solc +++ /dev/null @@ -1,37 +0,0 @@ - -class self : Invokable(args, ret) { - function invoke (s:self, a:args) -> ret; - } - - function id(x) { - return x ; - } - - data IdToken(a) = IdToken - -instance IdToken(a) : Invokable(a,a) { - function invoke(token: IdToken(a), arg:a) -> a { - return id(arg); - } -} - -contract InvokeId { - public function id(x) { - return x ; - } - - /* - function nid() { - return id; - } - */ - - public function nidimpl() { - return IdToken; - } - - public function main() { - // Instead of: `return nid(42)` - return invoke(nidimpl(), 42); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/024lamid.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/024lamid.sol new file mode 100644 index 00000000..dc3586d5 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/024lamid.sol @@ -0,0 +1,11 @@ +contract Id1 { +function id(x: a) public { + return x ; + } + + + function main() public { + let nid = lam(x) {return x;}; + return nid(42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/024lamid.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/024lamid.solc deleted file mode 100644 index f4e794d5..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/024lamid.solc +++ /dev/null @@ -1,11 +0,0 @@ -contract Id1 { - public function id(x) { - return x ; - } - - - public function main() { - let nid = lam(x) {return x;}; - return nid(42); - } -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/025lamid-invoke.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/025lamid-invoke.snap deleted file mode 100644 index d16ac1a3..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/025lamid-invoke.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/parser/tests/diagnostics.rs -expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/025lamid-invoke.solc ---- -error[SC0001]: parse error: unexpected `instance` - --> /025lamid-invoke.solc:18:1 - | -17 | -18 | instance Lam0Token(a) : Invokable(a,a) { - | ^^^^^^^^ unexpected token -19 | function invoke(token: Lam0Token(a), arg:a) -> a { - | - = note: expecting `(`, `;`, or `|` - = note: while parsing data declaration diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/025lamid-invoke.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/025lamid-invoke.sol new file mode 100644 index 00000000..3c47533c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/025lamid-invoke.sol @@ -0,0 +1,30 @@ +/* Manual translation of: +contract Id1 { + function main() { + let nid = lam(x) {return x;}; + return nid(42); + } +} +*/ + +trait Invokable { + function invoke(s: self, a: args) returns (ret) ; +} + +function lam0impl(x: c) returns (c) { return x; } + +enum Lam0Token { Lam0Token } + +impl Invokable, a, a> { + function invoke(token: Lam0Token, arg: a) returns (a) { + return lam0impl(arg); + } +} + + +contract InvokeLam { +function main() public returns (word) { + let nid = Lam0Token; + return invoke(nid, 42); +} +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/025lamid-invoke.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/025lamid-invoke.solc deleted file mode 100644 index 0697ad10..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/025lamid-invoke.solc +++ /dev/null @@ -1,30 +0,0 @@ -/* Manual translation of: -contract Id1 { - function main() { - let nid = lam(x) {return x;}; - return nid(42); - } -} -*/ - -class self : Invokable(args, ret) { - function invoke (s:self, a:args) -> ret; -} - -function lam0impl(x: c) -> c { return x; } - -data Lam0Token(a) = Lam0Token - -instance Lam0Token(a) : Invokable(a,a) { - function invoke(token: Lam0Token(a), arg:a) -> a { - return lam0impl(arg); - } -} - - -contract InvokeLam { -public function main() { - let nid = Lam0Token; - return invoke(nid, 42); -} -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/026capture.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/026capture.snap deleted file mode 100644 index 17ad9ef2..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/026capture.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/parser/tests/diagnostics.rs -expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/026capture.solc ---- -error[SC0001]: parse error: unexpected `instance` - --> /026capture.solc:31:1 - | -30 | -31 | instance Lam1Closure(a) : Invokable(a,Word) { - | ^^^^^^^^ unexpected token -32 | function invoke(clos: Lam1Closure(a), arg:a) -> Word { - | - = note: expecting `;`, or `|` - = note: while parsing data declaration diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/026capture.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/026capture.sol new file mode 100644 index 00000000..b588f3be --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/026capture.sol @@ -0,0 +1,49 @@ +/* Manual translation of: +contract Id1 { + function main() { + let y = 42; + let nid = lam(x) {return addW(x,y);}; + return nid(17); + } +} +*/ + +function addW(x: Word, y: Word) returns (Word) { + let res : Word; + assembly { + res := add(x, y) + } + return res; +} + +trait Invokable { + function invoke(s: self, a: args) returns (ret) ; +} + +// env might be a tuple, here it is a single Word +function lam1impl(env: Word, x: c) returns (c) { + let y = env; + return addW(x,y); +} + +enum Lam1Closure { Lam1Closure(Word) } + +impl Invokable, a, Word> { + function invoke(clos: Lam1Closure, arg: a) returns (Word) { + match (clos) { +case Lam1Closure(env) { +return lam1impl(env, arg); +} +} + } +} + + +contract InvokeCapLam { +function main() public returns (Word) { + let y = 42; + let clos = Lam1Closure(y); + + return invoke(clos, 17); +} +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/026capture.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/026capture.solc deleted file mode 100644 index 4da24815..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/026capture.solc +++ /dev/null @@ -1,47 +0,0 @@ -/* Manual translation of: -contract Id1 { - function main() { - let y = 42; - let nid = lam(x) {return addW(x,y);}; - return nid(17); - } -} -*/ - -function addW(x: Word, y:Word) -> Word { - let res : Word; - assembly { - res := add(x, y) - } - return res; -} - -class self : Invokable(args, ret) { - function invoke (s:self, a:args) -> ret; -} - -// env might be a tuple, here it is a single Word -function lam1impl(env: Word, x: c) -> c { - let y = env; - return addW(x,y); -} - -data Lam1Closure(a) = Lam1Closure(Word) - -instance Lam1Closure(a) : Invokable(a,Word) { - function invoke(clos: Lam1Closure(a), arg:a) -> Word { - match clos { - | Lam1Closure(env) => return lam1impl(env, arg); - }; - } -} - - -contract InvokeCapLam { -public function main() { - let y = 42; - let clos = Lam1Closure(y); - - return invoke(clos, 17); -} -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/027retfun.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/027retfun.snap deleted file mode 100644 index 188695b9..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/027retfun.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/parser/tests/diagnostics.rs -expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/027retfun.solc ---- -error[SC0001]: parse error: unexpected `instance` - --> /027retfun.solc:24:1 - | -23 | -24 | instance Lam1Closure(a) : Invokable(a,Word) { - | ^^^^^^^^ unexpected token -25 | function invoke(clos: Lam1Closure(a), arg:a) -> Word { - | - = note: expecting `;`, or `|` - = note: while parsing data declaration diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/027retfun.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/027retfun.sol new file mode 100644 index 00000000..08eec9fe --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/027retfun.sol @@ -0,0 +1,46 @@ +/* Manual translation of: +contract Id1 { + + function foo() { + let y = 42; + let nid = lam(x) {return y;}; + return nid; + } + function main() { + return nid(17); + } +} +*/ + +trait Invokable { + function invoke(s: self, a: args) returns (ret) ; +} + +// env might be a tuple, here it is a single Word +function lam1impl(env: Word, x: c) returns (c) { return env; } + +enum Lam1Closure { Lam1Closure(Word) } + +impl Invokable, a, Word> { + function invoke(clos: Lam1Closure, arg: a) returns (Word) { + match (clos) { +case Lam1Closure(env) { +return lam1impl(env, arg); +} +} + } +} + + +contract InvokeCapLam { +function foo() public returns (Lam1Closure) { + let y = 42; + let clos = Lam1Closure(y); + return clos; +} + +function main() public returns (Word) { + + return invoke(foo(), 17); +} +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/027retfun.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/027retfun.solc deleted file mode 100644 index 7bdefb80..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/027retfun.solc +++ /dev/null @@ -1,44 +0,0 @@ -/* Manual translation of: -contract Id1 { - - function foo() { - let y = 42; - let nid = lam(x) {return y;}; - return nid; - } - function main() { - return nid(17); - } -} -*/ - -class self : Invokable(args, ret) { - function invoke (s:self, a:args) -> ret; -} - -// env might be a tuple, here it is a single Word -function lam1impl(env: Word, x: c) -> c { return env; } - -data Lam1Closure(a) = Lam1Closure(Word) - -instance Lam1Closure(a) : Invokable(a,Word) { - function invoke(clos: Lam1Closure(a), arg:a) -> Word { - match clos { - | Lam1Closure(env) => return lam1impl(env, arg); - }; - } -} - - -contract InvokeCapLam { -public function foo() { - let y = 42; - let clos = Lam1Closure(y); - return clos; -} - -public function main() { - - return invoke(foo(), 17); -} -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/028modifier.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/028modifier.snap deleted file mode 100644 index a2f2f154..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/028modifier.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/parser/tests/diagnostics.rs -expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/028modifier.solc ---- -error[SC0001]: parse error: unexpected `instance` - --> /028modifier.solc:42:1 - | -41 | -42 | instance FooToken:Invokable(Word, Word) { - | ^^^^^^^^ unexpected token -43 | function invoke(self:FooToken, arg: Word) -> Word { - | - = note: expecting `(`, `;`, or `|` - = note: while parsing data declaration diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/028modifier.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/028modifier.sol new file mode 100644 index 00000000..d07927b2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/028modifier.sol @@ -0,0 +1,89 @@ +function add1(x: Word) returns (Word) { + return addW(x,1); +} + +function addW(x: Word, y: Word) returns (Word) { + let res : Word; + assembly { + res := add(x, y) + } + return res; +} + +trait Invokable { + function invoke(s: self, a: args) returns (ret) ; +} + + +/* Manual translation of: +contract Id1 { + // modifier calls its argument and adds one to result + function add1mod(f) { + return lam(a) { return add1(f(a)); } + } + + function foo(x) { + return addW(x,2); + } + + function main() { + let bar = add1mod(foo); + return bar(39); + } +} +*/ + +function foo(x: Word) returns (Word) { + return addW(x, 2); +} + +enum FooToken { FooToken } + +impl Invokable { + function invoke(self: FooToken, arg: Word) returns (Word) { + return foo(arg); + } +} + +// lambda in add1mod captures a function +// so env contains the closure + +function lam1impl(env: f, a: Word) returns (Word) where f: Invokable { + let f = env; + return add1(invoke(f, a)); +} + +// we want: +// data Lam1Closure = f:Invokable(Word,Word) => Lam1Closure(f) + +enum Lam1Closure { Lam1Closure(f) } + +/* +function extractEnv(clos: Lam1Closure(f)) -> f { + match clos { + | Lam1Closure(env) => return env; + }; +} +*/ +impl Invokable, Word, Word> where f: Invokable { + function invoke(clos: Lam1Closure, arg: Word) returns (Word) { + match (clos) { +case Lam1Closure(env) { +return lam1impl(env, arg); +} +} + } +} + +function add1mod(f: f) returns (Lam1Closure) where f: Invokable { + return Lam1Closure(f); +} + +contract Modifier { + + +function main() public returns (Word) { + let barClos = add1mod(FooToken); + return invoke(barClos, 39); +} +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/028modifier.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/028modifier.solc deleted file mode 100644 index 264f49dd..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/028modifier.solc +++ /dev/null @@ -1,87 +0,0 @@ -function add1(x) { - return addW(x,1); -} - -function addW(x: Word, y:Word) -> Word { - let res : Word; - assembly { - res := add(x, y) - } - return res; -} - -class self : Invokable(args, ret) { - function invoke (s:self, a:args) -> ret; -} - - -/* Manual translation of: -contract Id1 { - // modifier calls its argument and adds one to result - function add1mod(f) { - return lam(a) { return add1(f(a)); } - } - - function foo(x) { - return addW(x,2); - } - - function main() { - let bar = add1mod(foo); - return bar(39); - } -} -*/ - -function foo(x:Word) -> Word { - return addW(x, 2); -} - -data FooToken = FooToken - -instance FooToken:Invokable(Word, Word) { - function invoke(self:FooToken, arg: Word) -> Word { - return foo(arg); - } -} - -// lambda in add1mod captures a function -// so env contains the closure - -forall f.(f: Invokable(Word,Word)) => function lam1impl (env : f, a:Word) { - let f = env; - return add1(invoke(f, a)); -} - -// we want: -// data Lam1Closure = f:Invokable(Word,Word) => Lam1Closure(f) - -data Lam1Closure(f) = Lam1Closure(f) - -/* -function extractEnv(clos: Lam1Closure(f)) -> f { - match clos { - | Lam1Closure(env) => return env; - }; -} -*/ -instance (f:Invokable(Word,Word)) => Lam1Closure(f) : Invokable(Word,Word) { - function invoke(clos, arg:Word) -> Word { - match clos { - | Lam1Closure(env) => return lam1impl(env, arg); - }; - } -} - -function add1mod(f) { - return Lam1Closure(f); -} - -contract Modifier { - - -public function main() { - let barClos = add1mod(FooToken); - return invoke(barClos, 39); -} -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/031enum.snap b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/031enum.snap deleted file mode 100644 index 973845d8..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/031enum.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/parser/tests/diagnostics.rs -expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/031enum.solc ---- -error[SC0001]: parse error: unexpected `instance` - --> /031enum.solc:15:1 - | -14 | -15 | instance Color : Enum { - | ^^^^^^^^ unexpected token -16 | function fromEnum(c) { - | - = note: expecting `(`, `;`, or `|` - = note: while parsing data declaration diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/031enum.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/031enum.sol new file mode 100644 index 00000000..a24fbfc3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/031enum.sol @@ -0,0 +1,69 @@ +function addW(x: Word, y: Word) returns (Word) { + let res : Word; + assembly { + res := add(x, y) + } + return res; +} + +trait Enum { + function fromEnum(x: a) returns (Word) ; + } + + enum Color { R, G, B } + +impl Enum { + function fromEnum(c: Color) returns (Word) { + match (c) { +case R { +return 1; +} +case Color.G { +return 2; +} +case Color.B { +return 3; +} +} + } +} + +enum Bool { False, True } + +impl Enum { + function fromEnum(b: Bool) returns (Word) { + match (b) { +case False { +return 0; +} +case Bool.True { +return 1; +} +} + } +} +enum FromEnumToken { FromEnumToken } + +trait Invokable { + function invoke(s: self, a: args) returns (ret) ; +} + +impl Invokable, a, Word> where a: Enum { + function invoke(fet: FromEnumToken, arg: a) returns (Word) { + return fromEnum(arg); + } +} +contract RGB { + function main() public returns (Word) { + /* + let x = fromEnum(Color.B); + let y = fromEnum(Bool.True); + */ + + let fetC = FromEnumToken; + let fetB = FromEnumToken; + let x = invoke(fetC, Color.B); + let y = invoke(fetB,Bool.True); + return addW(x,y); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/031enum.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/031enum.solc deleted file mode 100644 index b31d30cd..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/invokable/031enum.solc +++ /dev/null @@ -1,59 +0,0 @@ -function addW(x: Word, y:Word) -> Word { - let res : Word; - assembly { - res := add(x, y) - } - return res; -} - -class a:Enum { - function fromEnum(x:a) -> Word; - } - - data Color = R | G | B - -instance Color : Enum { - function fromEnum(c) { - match c { - | R => return 1; - | Color.G => return 2; - | Color.B => return 3; - }; - } -} - -data Bool = False | True - -instance Bool : Enum { - function fromEnum(b) { - match b { - | False => return 0; - | Bool.True => return 1; - }; - } -} -data FromEnumToken(a) = FromEnumToken - -class self : Invokable(args, ret) { - function invoke (s:self, a:args) -> ret; -} - -instance (a:Enum) => FromEnumToken(a) : Invokable(a,Word) { - function invoke(fet : FromEnumToken(a), arg) -> Word { - return fromEnum(arg); - } -} -contract RGB { - public function main() { - /* - let x = fromEnum(Color.B); - let y = fromEnum(Bool.True); - */ - - let fetC = FromEnumToken; - let fetB = FromEnumToken; - let x = invoke(fetC, Color.B); - let y = invoke(fetB,Bool.True); - return addW(x,y); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/pragmas/bound.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/pragmas/bound.sol new file mode 100644 index 00000000..e76f23eb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/pragmas/bound.sol @@ -0,0 +1,15 @@ + +trait D { function f(x:a); } +trait F {} + +enum Memory { Memory(word) } + +impl F, Memory>>> {} +impl D>>> { + function f(x:Memory>>) {} +} + +function g(y: b) { + let x : Memory>>>; + f(x); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/pragmas/bound.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/pragmas/bound.solc deleted file mode 100644 index 546c850d..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/pragmas/bound.solc +++ /dev/null @@ -1,15 +0,0 @@ - -forall a . class a:D { function f(x:a); } -forall a b . class a:F(b) {} - -data Memory(a) = Memory(word); - -forall a . instance Memory(a):F(Memory(Memory(Memory(a)))) {} -forall a c . instance (c:D,a:F(c)) => Memory(Memory(Memory(a))):D { - function f(x:Memory(Memory(Memory(a)))) {} -} - -forall b . function g(y:b) { - let x : Memory(Memory(Memory(Memory(b)))); - f(x); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/010answer.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/010answer.sol new file mode 100644 index 00000000..5681aed3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/010answer.sol @@ -0,0 +1,5 @@ +contract Answer { + function main() public { + return 42; + } +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/010answer.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/010answer.solc deleted file mode 100644 index 5699ce86..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/010answer.solc +++ /dev/null @@ -1,5 +0,0 @@ -contract Answer { - public function main() { - return 42; - } -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/011id.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/011id.sol new file mode 100644 index 00000000..2bd40e99 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/011id.sol @@ -0,0 +1,14 @@ +contract Id1 { + + enum Bool { False, True } + +function id(x: a) public { + return x ; + } + +function const(x: a, y: b) public { return x; } + + function main() public { + return const(id(42), Bool.False); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/011id.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/011id.solc deleted file mode 100644 index 2e79a47e..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/011id.solc +++ /dev/null @@ -1,14 +0,0 @@ -contract Id1 { - - data Bool = False | True; - - public function id(x) { - return x ; - } - - public function const(x, y) { return x; } - - public function main() { - return const(id(42), Bool.False); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/012nid.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/012nid.sol new file mode 100644 index 00000000..687fdf13 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/012nid.sol @@ -0,0 +1,15 @@ +contract Id1 { +function id(x: a) public { + return x ; + } + + function nid() public { + return id; + } + +function const(x: a, y: b) public { return x; } + + function main() public { + return const(nid(42), id(1)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/012nid.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/012nid.solc deleted file mode 100644 index a27a6565..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/012nid.solc +++ /dev/null @@ -1,15 +0,0 @@ -contract Id1 { - public function id(x) { - return x ; - } - - public function nid() { - return id; - } - - public function const(x, y) { return x; } - - public function main() { - return const(nid(42), id(1)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/013comp.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/013comp.sol new file mode 100644 index 00000000..d99f49ac --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/013comp.sol @@ -0,0 +1,16 @@ +contract Compose { +function compose(f: function(b) returns (c), g: function(a) returns (b)) public { + return lam (x) { + return f(g(x)); + } ; + } + +function id(x: a) public { return x; } + + function idid() public { return compose(id,id); } + + function main() public { + let f = compose(id,id); + return f(42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/013comp.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/013comp.solc deleted file mode 100644 index a6900271..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/013comp.solc +++ /dev/null @@ -1,16 +0,0 @@ -contract Compose { - public function compose(f,g) { - return lam (x) { - return f(g(x)); - } ; - } - - public function id(x) { return x; } - - public function idid() { return compose(id,id); } - - public function main() { - let f = compose(id,id); - return f(42); - } -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/027sstore.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/027sstore.sol new file mode 100644 index 00000000..e4b8d5f4 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/027sstore.sol @@ -0,0 +1,10 @@ +contract Sstore { + function main() public { + let res : word; + assembly { + sstore(0, 42) + res := sload(0) + } + return res; + } +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/027sstore.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/027sstore.solc deleted file mode 100644 index cfd5619a..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/027sstore.solc +++ /dev/null @@ -1,10 +0,0 @@ -contract Sstore { - public function main() { - let res : word; - assembly { - sstore(0, 42) - res := sload(0) - } - return res; - } -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/051expreturn.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/051expreturn.sol new file mode 100644 index 00000000..7a974592 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/051expreturn.sol @@ -0,0 +1,63 @@ +enum Bool { False, True } +enum W { W(Word) } +enum U { U } + +// empty class needed since forall expects a nonempty context +trait Top {} +impl Top {} + +/* For experiments, special handling when emitting code */ +// this does not work, typechecker forces a ~ b +// forall a, b.(a:Top, b:Top) => function ereturn(x:a) -> b { let res: b; return res; } +// we might have +// forall a.(a:Top) => function ereturn(x:a) -> a +// or + +function ereturn(x: a) returns (Unit) { let res: Unit; return res; } +// and then cast it to any type using unsafeCast + +/* simulate match expression + x = match { | Bool.False => return 77; | Bool.True => W(22) } +*/ +function elimBool1(b: Bool) returns (Word) { + let x : W; + x = W(1); + match (b) { +// this works + // | Bool.False => x = unsafeCast(ereturn(77)); + // but this does not - unknown intermediate type + // | Bool.False => x = unsafeCast(unsafeCast(ereturn(77))); + // what about "return(return 77)"? + // this works +case Bool.False { +x = unsafeCast(ereturn(ereturn(77))); + // but this does not + // | Bool.False => x = unsafeCast(ereturn(unsafeCast(ereturn(77)))); +} +case Bool.True { +x = W(22); +} +} + + match (x) { +case W(y) { +return y; +} +} + +} + +// "semicolon" +function semi(x: a) returns (U) { return U;} + +function unsafeCast(x: a) returns (b) { + let res: b; return res; +} + + +contract ExpReturn { + function main() public returns (Word) { + return elimBool1(Bool.False); + // return elimBool1(Bool.False); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/051expreturn.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/051expreturn.solc deleted file mode 100644 index 9bbbd056..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/051expreturn.solc +++ /dev/null @@ -1,57 +0,0 @@ -data Bool = False | True; -data W = W(Word); -data U = U; - -// empty class needed since forall expects a nonempty context -class a :Top {} -instance a:Top {} - -/* For experiments, special handling when emitting code */ -// this does not work, typechecker forces a ~ b -// forall a, b.(a:Top, b:Top) => function ereturn(x:a) -> b { let res: b; return res; } -// we might have -// forall a.(a:Top) => function ereturn(x:a) -> a -// or - -forall a . function ereturn(x:a) -> Unit { let res: Unit; return res; } -// and then cast it to any type using unsafeCast - -/* simulate match expression - x = match { | Bool.False => return 77; | Bool.True => W(22) } -*/ -function elimBool1(b:Bool) -> Word { - let x : W; - x = W(1); - match b { - // this works - // | Bool.False => x = unsafeCast(ereturn(77)); - // but this does not - unknown intermediate type - // | Bool.False => x = unsafeCast(unsafeCast(ereturn(77))); - // what about "return(return 77)"? - // this works - | Bool.False => x = unsafeCast(ereturn(ereturn(77))); - // but this does not - // | Bool.False => x = unsafeCast(ereturn(unsafeCast(ereturn(77)))); - | Bool.True => x = W(22); - } - - match x { - | W(y) => return y; - } - -} - -// "semicolon" -forall a. function semi(x:a) -> U { return U;} - -forall a b. function unsafeCast(x:a) -> b { - let res: b; return res; -} - - -contract ExpReturn { - public function main() -> Word { - return elimBool1(Bool.False); - // return elimBool1(Bool.False); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/051negBool.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/051negBool.sol new file mode 100644 index 00000000..dd8236c2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/051negBool.sol @@ -0,0 +1,37 @@ + +trait Neg { + function neg(x: a) returns (a) ; +} + +enum B { F, T } + + +impl Neg { + function neg (x : B) returns (B) { + match (x) { +case B.F { +return B.T; +} +case B.T { +return B.F; +} +} + } +} + + +contract NegBool { + + function fromB(b: B) public returns (word) { + match (b) { +case B.F { +return 0; +} +case B.T { +return 1; +} +} + } + + function main() public returns (word) { return fromB(Neg.neg(B.F)); } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/051negBool.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/051negBool.solc deleted file mode 100644 index f034aa1b..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/051negBool.solc +++ /dev/null @@ -1,29 +0,0 @@ - -class a : Neg { - function neg(x:a) -> a; -} - -data B = F | T; - - -instance B : Neg { - function neg (x : B) { - match x { - | B.F => return B.T; - | B.T => return B.F; - } - } -} - - -contract NegBool { - - public function fromB(b) { - match b { - | B.F => return 0; - | B.T => return 1; - } - } - - public function main() { return fromB(Neg.neg(B.F)); } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/052negPair.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/052negPair.sol new file mode 100644 index 00000000..37bcb652 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/052negPair.sol @@ -0,0 +1,70 @@ + +trait Neg { + function neg(x: a) returns (a) ; +} + +enum B { F, T } +enum Pair { Pair(a, b) } + +impl Neg { + function neg (x : B) returns (B) { + match (x) { +case B.F { +return B.T; +} +case B.T { +return B.F; +} +} + } +} + +function fst (p: Pair) returns (a) { + match (p) { +case Pair(x,y) { +return x; +} +} +} + +function snd(p: Pair) returns (b) { + match (p) { +case Pair(x,y) { +return y; +} +} +} + + +impl Neg> where a: Neg, b: Neg { + function neg(p: Pair) returns (Pair) { + return Pair(Neg.neg (fst(p)), Neg.neg(snd (p))); + } +} + +contract NegPair { + + function bnot(x: B) public returns (B) { + match (x) { +case B.T { +return B.F; +} +case B.F { +return B.T; +} +} +} + + function fromB(b: B) public returns (word) { + match (b) { +case B.F { +return 0; +} +case B.T { +return 1; +} +} +} + + function main() public returns (word) { return fromB(fst(Neg.neg(Pair(B.F,B.T)))); } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/052negPair.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/052negPair.solc deleted file mode 100644 index f578d8e9..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/052negPair.solc +++ /dev/null @@ -1,63 +0,0 @@ - -class a : Neg { - function neg(x:a) -> a; -} - -data B = F | T; -data Pair(a,b) = Pair(a,b); - -instance B : Neg { - function neg (x : B) { - match x { - | B.F => return B.T; - | B.T => return B.F; - } - } -} - -function fst (p) { - match p { - | Pair(x,y) => return x; - } -} - -function snd(p) { - match p { - | Pair(x,y) => return y; - } -} - - -instance (a:Neg,b:Neg) => Pair(a,b):Neg { - function neg(p) { - return Pair(Neg.neg (fst(p)), Neg.neg(snd (p))); - } -} - -/* -instance (a:Neg,b:Neg) => Pair(a,b):Neg { - function neg(p) { - match p { - | Pair(a,b) => return Pair(neg(a), neg(b)); - } - } -} -*/ -contract NegPair { - - public function bnot(x) { - match x { - | B.T => return B.F; - | B.F => return B.T; - } -} - - public function fromB(b) { - match b { - | B.F => return 0; - | B.T => return 1; - } -} - - public function main() { return fromB(fst(Neg.neg(Pair(B.F,B.T)))); } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/052return.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/052return.sol new file mode 100644 index 00000000..f5cc08c4 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/052return.sol @@ -0,0 +1,62 @@ +enum Bool { False, True } +enum W { W(word) } +enum U { U } + + +/* For experiments, special handling when emitting code */ +// this does not work, typechecker forces a ~ b +// function ereturn(x:a) -> b { let res: b; return res; } +// we might have +// function ereturn(x:a) -> a +// or + +function ereturn(x: a) returns (unit) { let res: unit; return res; } +// and then cast it to any type using unsafeCast + +/* simulate match expression + x = match { | Bool.False => return 77; | Bool.True => W(22) } +*/ +function elimBool1(b: Bool) returns (word) { + let x : W; + x = W(1); + match (b) { +// this works +case Bool.False { +x = unsafeCast(ereturn(77)); + // but this does not - unknown intermediate type + // | Bool.False => x = unsafeCast(unsafeCast(ereturn(77))); + // what about "return(return 77)"? + // this does not work + // | Bool.False => x = ereturn(ereturn(77)); + // this works + // | Bool.False => x = unsafeCast(ereturn(ereturn(77))); + // this does not work (monomorphisation fails): + // | Bool.False => x = unsafeCast(ereturn(unsafeCast(ereturn(77)))); +} +case Bool.True { +x = W(22); +} +} + + match (x) { +case W(y) { +return y; +} +} + +} + +// "semicolon" +function semi(x: a) returns (U) { return U;} + +function unsafeCast(x: a) returns (b) { +let res: b; return res; +} + + +contract ExpReturn { + function main() public returns (word) { + return elimBool1(Bool.False); + // return elimBool1(Bool.True); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/052return.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/052return.solc deleted file mode 100644 index e62afc9b..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/052return.solc +++ /dev/null @@ -1,57 +0,0 @@ -data Bool = False | True; -data W = W(word); -data U = U; - - -/* For experiments, special handling when emitting code */ -// this does not work, typechecker forces a ~ b -// function ereturn(x:a) -> b { let res: b; return res; } -// we might have -// function ereturn(x:a) -> a -// or - -function ereturn(x:a) -> unit { let res: unit; return res; } -// and then cast it to any type using unsafeCast - -/* simulate match expression - x = match { | Bool.False => return 77; | Bool.True => W(22) } -*/ -function elimBool1(b:Bool) -> word { - let x : W; - x = W(1); - match b { - // this works - | Bool.False => x = unsafeCast(ereturn(77)); - // but this does not - unknown intermediate type - // | Bool.False => x = unsafeCast(unsafeCast(ereturn(77))); - // what about "return(return 77)"? - // this does not work - // | Bool.False => x = ereturn(ereturn(77)); - // this works - // | Bool.False => x = unsafeCast(ereturn(ereturn(77))); - // this does not work (monomorphisation fails): - // | Bool.False => x = unsafeCast(ereturn(unsafeCast(ereturn(77)))); - - | Bool.True => x = W(22); - } - - match x { - | W(y) => return y; - } - -} - -// "semicolon" -function semi(x:a) -> U { return U;} - -function unsafeCast(x:a) -> b { -let res: b; return res; -} - - -contract ExpReturn { - public function main() -> word { - return elimBool1(Bool.False); - // return elimBool1(Bool.True); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/053return.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/053return.sol new file mode 100644 index 00000000..abab6d7e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/053return.sol @@ -0,0 +1,41 @@ +enum Bool { False, True } +enum W { W(word) } + + +/* For experiments, special handling when emitting code */ +function ereturn(x: a) returns (b) { let res: b; return res; } + +/* simulate match expression + x = match { | Bool.False => return 77; | Bool.True => W(22) } +*/ +function elimBool1(b: Bool) returns (word) { + let x : W; + x = W(1); + match (b) { +// this works +case Bool.False { +x = ereturn(77); + // what about "return(return 77)"? + // this does not work (monomorphisation fails) + // | Bool.False => x = ereturn(ereturn(77)); +} +case Bool.True { +x = W(22); +} +} + + match (x) { +case W(y) { +return y; +} +} + +} + + +contract ExpReturn { + function main() public returns (word) { + return elimBool1(Bool.False); + // return elimBool1(Bool.True); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/053return.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/053return.solc deleted file mode 100644 index 0639c116..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/053return.solc +++ /dev/null @@ -1,36 +0,0 @@ -data Bool = False | True; -data W = W(word); - - -/* For experiments, special handling when emitting code */ -function ereturn(x:a) -> b { let res: b; return res; } - -/* simulate match expression - x = match { | Bool.False => return 77; | Bool.True => W(22) } -*/ -function elimBool1(b:Bool) -> word { - let x : W; - x = W(1); - match b { - // this works - | Bool.False => x = ereturn(77); - // what about "return(return 77)"? - // this does not work (monomorphisation fails) - // | Bool.False => x = ereturn(ereturn(77)); - - | Bool.True => x = W(22); - } - - match x { - | W(y) => return y; - } - -} - - -contract ExpReturn { - public function main() -> word { - return elimBool1(Bool.False); - // return elimBool1(Bool.True); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/101struct1Field.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/101struct1Field.sol new file mode 100644 index 00000000..4d230dd9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/101struct1Field.sol @@ -0,0 +1,253 @@ + +/////// Construction +trait Typedef { + function rep(x: abs) returns (rep) ; + function abs(x: rep) returns (abs) ; +} + + +enum uint { uint(word) } + +impl Typedef { + function rep(x: word) returns (word) { return x; } + function abs(x: word) returns (word) { return x;} +} + +impl Typedef { + function rep(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + function abs(x: word) returns (uint) { + return uint(x); + } +} + +enum memory { memory(word) } +enum memoryRef { memoryRef(word) } +enum Proxy { Proxy } + +impl Typedef, word> { + function rep(x: memory) returns (word) { + match (x) { +case memory(y) { +return y; +} +} + } + function abs(x: word) returns (memory) { + return memory(x); + } +} +impl Typedef, word> { + function rep(x: memoryRef) returns (word) { + match (x) { +case memoryRef(y) { +return y; +} +} + } + function abs(x: word) returns (memoryRef) { + return memoryRef(x); + } +} + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +enum ref { ref(a) } + +impl Assign, a> { + function assign(l: ref, r: a) { + // builtin "stack store" + return (); + } +} + +trait MemoryType { + function load(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +trait MemorySize { + function size(x: Proxy) returns (word) ; +} + + +function mload_(x: word) returns (word) { + let res: word; + assembly { + res := mload(x) + } + return res; + } + +function mstore_(a:word, v:word) { + assembly { mstore(a,v) } +} + +impl MemoryType { + function load(ptr: word) returns (word) { + let r:word; + assembly { + r := mload(ptr) + } + return r; + } + function store(ptr: word, value: word) { + assembly { + mstore(ptr, value) + } + } +} + +impl MemoryType { + function load(ptr: word) returns (uint) { + return Typedef.abs(mload_(ptr)); // type annotation needed due to a typechecker bug + } + function store(ptr: word, value: uint) { + return mstore_(ptr, Typedef.rep(value)); + } +} + +impl Assign, a> where a: MemoryType { + function assign(l:memoryRef, y:a) { + MemoryType.store(Typedef.rep(l), y); + } +} + + + +enum MemberAccessProxy { MemberAccessProxy(a, field, Proxy) } + +function memberAccessD1(x: MemberAccessProxy) returns (a) { + match (x) { +case MemberAccessProxy(y,z,p) { +return y; +} +} +} + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +trait RValueMemberAccess { + function memberAccess(x: self) returns (memberValueType) ; +} + +// This is *a lot* of pragmas... +// pragma no-coverage-condition CStructField, LValueMemberAccess, RValueMemberAccess; +// pragma no-patterson-condition LValueMemberAccess, RValueMemberAccess; +// pragma no-bounded-variable-condition LValueMemberAccess, RValueMemberAccess; +trait CStructField {} +enum StructField { StructField(structType) } + +impl LValueMemberAccess, fieldSelector, offsetType>, memoryRef> where StructField: CStructField, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (memoryRef) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return memoryRef(ptr); + } +} + +impl MemorySize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + +impl MemorySize<(a, b)> where a: MemorySize, b: MemorySize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz:word = MemorySize.size(@a); + let b_sz:word = MemorySize.size(@b); + assembly { + a_sz := add(a_sz, b_sz) + } + return a_sz; + } +} + +impl RValueMemberAccess, fieldSelector, offsetType>, fieldType> where StructField: CStructField, fieldType: MemoryType, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (fieldType) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return MemoryType.load(ptr); + } +} + +////// Testing + +// struct S { fld1:word; } +enum S { S(word) } +enum fld1_sel { fld1_sel } +// data y_sel = y_sel; +// data z_sel = z_sel; + +impl CStructField, word, ()> {} +// instance StructField(S, y_sel):CStructField(uint, word) {} +// BUG: This next one should really be the following, but that breaks weirdly: +// (I get a patterson condition violation on an invoke instance for g) +impl CStructField, word, (word, uint)> {} +// So instead I use: +// instance StructField(S, z_sel):CStructField(word, word) {} + + +function f() { + let x:memory; + let y:memory; + // x = y + Assign.assign(ref(x), y); + /* + * Idea in the above: to avoid overlapping instances, + * we can desugar a simple identifier referring to a local variable on the lhs of an assignment to ref(x), + * to be able to choose a disjoint assign instance. + * Of course this needs special treatment during code generation, + * on the other hand, stack assignments generally do... + * Actually, even simpler might be just *not* to desugar assignments at all, if the lhs is just an identifier referring to a local variable and just directly take care of it when translating to core. + */ +} + +function g() returns (word) { + let s:memory = Typedef.abs(0x80); + + let offset0 : Proxy<()> = Proxy; + // s.fld1 = y + let fld1_lval : memoryRef + = LValueMemberAccess.memberAccess(MemberAccessProxy(s, fld1_sel, offset0)); + Assign.assign(fld1_lval, y); + // return s.fld1 + let r : word = 17; + r = RValueMemberAccess.memberAccess(MemberAccessProxy(s, fld1_sel, offset0) ); + return r; +} + +contract C { + function main() public returns (word) { + f(); + return g(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/101struct1Field.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/101struct1Field.solc deleted file mode 100644 index 35840a39..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/101struct1Field.solc +++ /dev/null @@ -1,254 +0,0 @@ - -/////// Construction -class abs:Typedef(rep) { - function rep(x:abs) -> rep; - function abs(x:rep) -> abs; -} - - -data uint = uint(word); - -instance word:Typedef(word) { - function rep(x:word) -> word { return x; } - function abs(x:word) -> word { return x;} -} - -instance uint:Typedef(word) { - function rep(x:uint) -> word { - match x { - | uint(y) => return y; - } - } - function abs(x:word) -> uint { - return uint(x); - } -} - -data memory(a) = memory(word); -data memoryRef(a) = memoryRef(word); -data Proxy(a) = Proxy; - -instance memory(a):Typedef(word) { - function rep(x:memory(a)) -> word { - match x { - | memory(y) => return y; - } - } - function abs(x:word) -> memory(a) { - return memory(x); - } -} -instance memoryRef(a):Typedef(word) { - function rep(x:memoryRef(a)) -> word { - match x { - | memoryRef(y) => return y; - } - } - function abs(x:word) -> memoryRef(a) { - return memoryRef(x); - } -} - -class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -data ref(a) = ref(a); - -instance ref(a):Assign(a) { - function assign(l:ref(a), r:a) -> () { - // builtin "stack store" - return (); - } -} - -class self:MemoryType { - function load(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -class self:MemorySize { - function size(x:Proxy(self)) -> word; -} - - -function mload_(x:word) -> word { - let res: word; - assembly { - res := mload(x) - } - return res; - } - -function mstore_(a:word, v:word) { - assembly { mstore(a,v) } -} - -instance word:MemoryType { - function load(ptr:word) -> word { - let r:word; - assembly { - r := mload(ptr) - } - return r; - } - function store(ptr:word, value:word) -> () { - assembly { - mstore(ptr, value) - } - } -} - -instance uint:MemoryType { - function load(ptr:word) -> uint { - return Typedef.abs(mload_(ptr)):uint; // type annotation needed due to a typechecker bug - } - function store(ptr:word, value:uint) -> () { - return mstore_(ptr, Typedef.rep(value)); - } -} - -forall a . a : MemoryType => instance memoryRef(a):Assign(a) { - function assign(l:memoryRef(a), y:a) { - MemoryType.store(Typedef.rep(l), y); - } -} - - - -data MemberAccessProxy(a, field, offset) = MemberAccessProxy(a, field, Proxy(offset)); - -forall a field offset . -function memberAccessD1(x:MemberAccessProxy(a, field, offset)) -> a { - match x { - | MemberAccessProxy(y,z,p) => return y; - } -} - -class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -forall self memberValueType . -class self:RValueMemberAccess(memberValueType) { - function memberAccess(x:self) -> memberValueType; -} - -// This is *a lot* of pragmas... -// pragma no-coverage-condition CStructField, LValueMemberAccess, RValueMemberAccess; -// pragma no-patterson-condition LValueMemberAccess, RValueMemberAccess; -// pragma no-bounded-variable-condition LValueMemberAccess, RValueMemberAccess; -class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - -forall structType fieldSelector fieldType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , offsetType:MemorySize - => instance MemberAccessProxy(memory(structType), fieldSelector, offsetType):LValueMemberAccess(memoryRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector, offsetType)) -> memoryRef(fieldType) { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return memoryRef(ptr); - } -} - -instance ():MemorySize { - function size(x:Proxy(())) -> word { - return 0; - } -} - -instance word:MemorySize { - function size(x:Proxy(word)) -> word { - return 32; - } -} - - -instance uint:MemorySize { - function size(x:Proxy(uint)) -> word { - return 32; - } -} - -forall a b . a:MemorySize, b:MemorySize => instance (a,b):MemorySize { - function size(x:Proxy((a,b))) -> word { - let a_sz:word = MemorySize.size(Proxy:Proxy(a)); - let b_sz:word = MemorySize.size(Proxy:Proxy(b)); - assembly { - a_sz := add(a_sz, b_sz) - } - return a_sz; - } -} - -forall structType fieldSelector fieldType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , fieldType:MemoryType - , offsetType:MemorySize - => instance MemberAccessProxy(memory(structType), fieldSelector, offsetType):RValueMemberAccess(fieldType) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector, offsetType)) -> fieldType { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return MemoryType.load(ptr); - } -} - -////// Testing - -// struct S { fld1:word; } -data S = S(word); -data fld1_sel = fld1_sel; -// data y_sel = y_sel; -// data z_sel = z_sel; - -instance StructField(S, x_sel):CStructField(word, ()) {} -// instance StructField(S, y_sel):CStructField(uint, word) {} -// BUG: This next one should really be the following, but that breaks weirdly: -// (I get a patterson condition violation on an invoke instance for g) -instance StructField(S, z_sel):CStructField(word, (word,uint)) {} -// So instead I use: -// instance StructField(S, z_sel):CStructField(word, word) {} - - -function f() { - let x:memory(word); - let y:memory(word); - // x = y - Assign.assign(ref(x), y); - /* - * Idea in the above: to avoid overlapping instances, - * we can desugar a simple identifier referring to a local variable on the lhs of an assignment to ref(x), - * to be able to choose a disjoint assign instance. - * Of course this needs special treatment during code generation, - * on the other hand, stack assignments generally do... - * Actually, even simpler might be just *not* to desugar assignments at all, if the lhs is just an identifier referring to a local variable and just directly take care of it when translating to core. - */ -} - -function g() -> word { - let s:memory(S) = Typedef.abs(0x80); - - let offset0 : Proxy( () ) = Proxy; - // s.fld1 = y - let fld1_lval : memoryRef(word) - = LValueMemberAccess.memberAccess(MemberAccessProxy(s, fld1_sel, offset0)); - Assign.assign(fld1_lval, y); - // return s.fld1 - let r : word = 17; - r = RValueMemberAccess.memberAccess(MemberAccessProxy(s, fld1_sel, offset0) ); - return r; -} - -contract C { - public function main() { - f(); - return g(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/102uintField.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/102uintField.sol new file mode 100644 index 00000000..0bb4659f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/102uintField.sol @@ -0,0 +1,260 @@ + +/////// Construction +trait Typedef { + function rep(x: abs) returns (rep) ; + function abs(x: rep) returns (abs) ; +} + + +enum uint { uint(word) } + +// this does not work :( +/* +forall a +. default instance a:Typedef(a) { + function rep(x:a) -> word { return a; } + function abs(x:a) -> word { return a;} +} +*/ + +impl Typedef { + function rep(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + function abs(x: word) returns (uint) { + return uint(x); + } +} + +enum memory { memory(word) } +enum memoryRef { memoryRef(word) } +enum Proxy { Proxy } + +impl Typedef, word> { + function rep(x: memory) returns (word) { + match (x) { +case memory(y) { +return y; +} +} + } + function abs(x: word) returns (memory) { + return memory(x); + } +} +impl Typedef, word> { + function rep(x: memoryRef) returns (word) { + match (x) { +case memoryRef(y) { +return y; +} +} + } + function abs(x: word) returns (memoryRef) { + return memoryRef(x); + } +} + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +enum ref { ref(a) } + +impl Assign, a> { + function assign(l: ref, r: a) { + // builtin "stack store" + return (); + } +} + +trait MemoryType { + function load(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +trait MemorySize { + function size(x: Proxy) returns (word) ; +} + + +function mload_(x: word) returns (word) { + let res: word; + assembly { + res := mload(x) + } + return res; + } + +function mstore_(a:word, v:word) { + assembly { mstore(a,v) } +} + +impl MemoryType { + function load(ptr: word) returns (word) { + let r:word; + assembly { + r := mload(ptr) + } + return r; + } + function store(ptr: word, value: word) { + assembly { + mstore(ptr, value) + } + } +} + +impl MemoryType { + function load(ptr: word) returns (uint) { + return Typedef.abs(mload_(ptr)); // type annotation needed due to a typechecker bug + } + function store(ptr: word, value: uint) { + return mstore_(ptr, Typedef.rep(value)); + } +} + +impl Assign, a> where a: MemoryType { + function assign(l:memoryRef, y:a) { + MemoryType.store(Typedef.rep(l), y); + } +} + + + +enum MemberAccessProxy { MemberAccessProxy(a, field, Proxy) } + +function memberAccessD1(x: MemberAccessProxy) returns (a) { + match (x) { +case MemberAccessProxy(y,z,p) { +return y; +} +} +} + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +trait RValueMemberAccess { + function memberAccess(x: self) returns (memberValueType) ; +} + +// This is *a lot* of pragmas... +// pragma no-coverage-condition CStructField, LValueMemberAccess, RValueMemberAccess; +// pragma no-patterson-condition LValueMemberAccess, RValueMemberAccess; +// pragma no-bounded-variable-condition LValueMemberAccess, RValueMemberAccess; +trait CStructField {} +enum StructField { StructField(structType) } + +impl LValueMemberAccess, fieldSelector, offsetType>, memoryRef> where StructField: CStructField, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (memoryRef) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return memoryRef(ptr); + } +} + +impl MemorySize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + +impl MemorySize<(a, b)> where a: MemorySize, b: MemorySize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz:word = MemorySize.size(@a); + let b_sz:word = MemorySize.size(@b); + assembly { + a_sz := add(a_sz, b_sz) + } + return a_sz; + } +} + +impl RValueMemberAccess, fieldSelector, offsetType>, fieldType> where StructField: CStructField, fieldType: MemoryType, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (fieldType) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return MemoryType.load(ptr); + } +} + +////// Testing + +// struct S { fld1:uint; } +enum S { S(uint) } +enum fld1_sel { fld1_sel } +// data y_sel = y_sel; +// data z_sel = z_sel; + +impl CStructField, uint, ()> {} +// instance StructField(S, y_sel):CStructField(uint, uint) {} +// BUG: This next one should really be the following, but that breaks weirdly: +// (I get a patterson condition violation on an invoke instance for g) +// instance StructField(S, z_sel):CStructField(word, (word,uint)) {} +// So instead I use: +// instance StructField(S, z_sel):CStructField(word, word) {} + + +function f() { + let x:memory; + let y:memory; + // x = y + Assign.assign(ref(x), y); + /* + * Idea in the above: to avoid overlapping instances, + * we can desugar a simple identifier referring to a local variable on the lhs of an assignment to ref(x), + * to be able to choose a disjoint assign instance. + * Of course this needs special treatment during code generation, + * on the other hand, stack assignments generally do... + * Actually, even simpler might be just *not* to desugar assignments at all, if the lhs is just an identifier referring to a local variable and just directly take care of it when translating to core. + */ +} + +function g() returns (word) { + let s:memory = Typedef.abs(0x80); + // let y:word = 42; + let z:uint = uint(42); + + let offset0 : Proxy<()> = Proxy; + // s.fld1 = z + let fld1_lval : memoryRef + = LValueMemberAccess.memberAccess(MemberAccessProxy(s, fld1_sel, offset0)); + Assign.assign(fld1_lval, z); + // return s.fld1 + let r : uint = uint(17); + r = RValueMemberAccess.memberAccess(MemberAccessProxy(s, fld1_sel, offset0) ); + let r2 : word = Typedef.rep(r ); + return r2; +} + +contract C { + function main() public returns (word) { + f(); + return g(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/102uintField.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/102uintField.solc deleted file mode 100644 index 629c0762..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/102uintField.solc +++ /dev/null @@ -1,261 +0,0 @@ - -/////// Construction -class abs:Typedef(rep) { - function rep(x:abs) -> rep; - function abs(x:rep) -> abs; -} - - -data uint = uint(word); - -// this does not work :( -/* -forall a -. default instance a:Typedef(a) { - function rep(x:a) -> word { return a; } - function abs(x:a) -> word { return a;} -} -*/ - -instance uint:Typedef(word) { - function rep(x:uint) -> word { - match x { - | uint(y) => return y; - } - } - function abs(x:word) -> uint { - return uint(x); - } -} - -data memory(a) = memory(word); -data memoryRef(a) = memoryRef(word); -data Proxy(a) = Proxy; - -instance memory(a):Typedef(word) { - function rep(x:memory(a)) -> word { - match x { - | memory(y) => return y; - } - } - function abs(x:word) -> memory(a) { - return memory(x); - } -} -instance memoryRef(a):Typedef(word) { - function rep(x:memoryRef(a)) -> word { - match x { - | memoryRef(y) => return y; - } - } - function abs(x:word) -> memoryRef(a) { - return memoryRef(x); - } -} - -class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -data ref(a) = ref(a); - -instance ref(a):Assign(a) { - function assign(l:ref(a), r:a) -> () { - // builtin "stack store" - return (); - } -} - -class self:MemoryType { - function load(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -class self:MemorySize { - function size(x:Proxy(self)) -> word; -} - - -function mload_(x:word) -> word { - let res: word; - assembly { - res := mload(x) - } - return res; - } - -function mstore_(a:word, v:word) { - assembly { mstore(a,v) } -} - -instance word:MemoryType { - function load(ptr:word) -> word { - let r:word; - assembly { - r := mload(ptr) - } - return r; - } - function store(ptr:word, value:word) -> () { - assembly { - mstore(ptr, value) - } - } -} - -instance uint:MemoryType { - function load(ptr:word) -> uint { - return Typedef.abs(mload_(ptr)):uint; // type annotation needed due to a typechecker bug - } - function store(ptr:word, value:uint) -> () { - return mstore_(ptr, Typedef.rep(value)); - } -} - -forall a . a : MemoryType => instance memoryRef(a):Assign(a) { - function assign(l:memoryRef(a), y:a) { - MemoryType.store(Typedef.rep(l), y); - } -} - - - -data MemberAccessProxy(a, field, offset) = MemberAccessProxy(a, field, Proxy(offset)); - -forall a field offset . -function memberAccessD1(x:MemberAccessProxy(a, field, offset)) -> a { - match x { - | MemberAccessProxy(y,z,p) => return y; - } -} - -class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -forall self memberValueType . -class self:RValueMemberAccess(memberValueType) { - function memberAccess(x:self) -> memberValueType; -} - -// This is *a lot* of pragmas... -// pragma no-coverage-condition CStructField, LValueMemberAccess, RValueMemberAccess; -// pragma no-patterson-condition LValueMemberAccess, RValueMemberAccess; -// pragma no-bounded-variable-condition LValueMemberAccess, RValueMemberAccess; -class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - -forall structType fieldSelector fieldType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , offsetType:MemorySize - => instance MemberAccessProxy(memory(structType), fieldSelector, offsetType):LValueMemberAccess(memoryRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector, offsetType)) -> memoryRef(fieldType) { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return memoryRef(ptr); - } -} - -instance ():MemorySize { - function size(x:Proxy(())) -> word { - return 0; - } -} - -instance word:MemorySize { - function size(x:Proxy(word)) -> word { - return 32; - } -} - - -instance uint:MemorySize { - function size(x:Proxy(uint)) -> word { - return 32; - } -} - -forall a b . a:MemorySize, b:MemorySize => instance (a,b):MemorySize { - function size(x:Proxy((a,b))) -> word { - let a_sz:word = MemorySize.size(Proxy:Proxy(a)); - let b_sz:word = MemorySize.size(Proxy:Proxy(b)); - assembly { - a_sz := add(a_sz, b_sz) - } - return a_sz; - } -} - -forall structType fieldSelector fieldType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , fieldType:MemoryType - , offsetType:MemorySize - => instance MemberAccessProxy(memory(structType), fieldSelector, offsetType):RValueMemberAccess(fieldType) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector, offsetType)) -> fieldType { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return MemoryType.load(ptr):fieldType; - } -} - -////// Testing - -// struct S { fld1:uint; } -data S = S(uint); -data fld1_sel = fld1_sel; -// data y_sel = y_sel; -// data z_sel = z_sel; - -instance StructField(S, fld1_sel):CStructField(uint, ()) {} -// instance StructField(S, y_sel):CStructField(uint, uint) {} -// BUG: This next one should really be the following, but that breaks weirdly: -// (I get a patterson condition violation on an invoke instance for g) -// instance StructField(S, z_sel):CStructField(word, (word,uint)) {} -// So instead I use: -// instance StructField(S, z_sel):CStructField(word, word) {} - - -function f() { - let x:memory(word); - let y:memory(word); - // x = y - Assign.assign(ref(x), y); - /* - * Idea in the above: to avoid overlapping instances, - * we can desugar a simple identifier referring to a local variable on the lhs of an assignment to ref(x), - * to be able to choose a disjoint assign instance. - * Of course this needs special treatment during code generation, - * on the other hand, stack assignments generally do... - * Actually, even simpler might be just *not* to desugar assignments at all, if the lhs is just an identifier referring to a local variable and just directly take care of it when translating to core. - */ -} - -function g() -> word { - let s:memory(S) = Typedef.abs(0x80); - // let y:word = 42; - let z:uint = uint(42); - - let offset0 : Proxy( () ) = Proxy; - // s.fld1 = z - let fld1_lval : memoryRef(uint) - = LValueMemberAccess.memberAccess(MemberAccessProxy(s, fld1_sel, offset0)); - Assign.assign(fld1_lval, z); - // return s.fld1 - let r : uint = uint(17); - r = RValueMemberAccess.memberAccess(MemberAccessProxy(s, fld1_sel, offset0) ); - let r2 : word = Typedef.rep(r : uint); - return r2; -} - -contract C { - public function main() { - f(); - return g(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/103struct3Fields.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/103struct3Fields.sol new file mode 100644 index 00000000..df3f5d4d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/103struct3Fields.sol @@ -0,0 +1,283 @@ +// v4: Simplified Member AccessProxy (no Proxy(offset)) +// variables holding field MAPs + +function add(x : word, y : word) returns (word) { + let res: word; + assembly { + res := add(x, y) + } + return res; +} + +/////// Construction +trait Typedef { + function rep(x: abs) returns (rep) ; + function abs(x: rep) returns (abs) ; +} + + +enum uint { uint(word) } + +// this does not work :( +/* +forall a +. default instance a:Typedef(a) { + function rep(x:a) -> word { return a; } + function abs(x:a) -> word { return a;} +} +*/ + +impl Typedef { + function rep(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + function abs(x: word) returns (uint) { + return uint(x); + } +} + +enum memory { memory(word) } +enum memoryRef { memoryRef(word) } +enum Proxy { Proxy } + +impl Typedef, word> { + function rep(x: memory) returns (word) { + match (x) { +case memory(y) { +return y; +} +} + } + function abs(x: word) returns (memory) { + return memory(x); + } +} +impl Typedef, word> { + function rep(x: memoryRef) returns (word) { + match (x) { +case memoryRef(y) { +return y; +} +} + } + function abs(x: word) returns (memoryRef) { + return memoryRef(x); + } +} + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +enum ref { ref(a) } + +impl Assign, a> { + function assign(l: ref, r: a) { + // builtin "stack store" + return (); + } +} + +trait MemoryType { + function load(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +trait MemorySize { + function size(x: Proxy) returns (word) ; +} + + +function mload_(x: word) returns (word) { + let res: word; + assembly { + res := mload(x) + } + return res; + } + +function mstore_(a:word, v:word) { + assembly { mstore(a,v) } +} + +impl MemoryType { + function load(ptr: word) returns (word) { + let r:word; + assembly { + r := mload(ptr) + } + return r; + } + function store(ptr: word, value: word) { + assembly { + mstore(ptr, value) + } + } +} + +impl MemoryType { + function load(ptr: word) returns (uint) { + return Typedef.abs(mload_(ptr)); + } + function store(ptr: word, value: uint) { + return mstore_(ptr, Typedef.rep(value)); + } +} + +impl Assign, a> where a: MemoryType { + function assign(l:memoryRef, y:a) { + MemoryType.store(Typedef.rep(l), y); + } +} + + + +enum MemberAccessProxy { MemberAccessProxy(a, field) } + +function memberAccessD1(x: MemberAccessProxy) returns (a) { + match (x) { +case MemberAccessProxy(y,z) { +return y; +} +} +} + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +trait RValueMemberAccess { + function memberAccess(x: self) returns (memberValueType) ; +} + +trait CStructField {} +enum StructField { StructField(structType) } + +impl LValueMemberAccess, fieldSelector, offsetType>, memoryRef> where StructField: CStructField, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (memoryRef) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return memoryRef(ptr); + } +} + +impl MemorySize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + + +/* +// fails Patterson cond +forall a b . a:Typedef(b), b:MemorySize +=> instance a:MemorySize { + function size(x:Proxy(a)) -> word { + return MemorySize.size(Proxy(b)); + } +} +*/ + +impl MemorySize<(a, b)> where a: MemorySize, b: MemorySize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz:word = MemorySize.size(@a); + let b_sz:word = MemorySize.size(@b); + assembly { + a_sz := add(a_sz, b_sz) + } + return a_sz; + } +} + +impl RValueMemberAccess, fieldSelector, offsetType>, fieldType> where StructField: CStructField, fieldType: MemoryType, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (fieldType) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return MemoryType.load(ptr); + } +} + +////// Testing + +// struct S { fld1:uint; fld2:word; fld3:word } +enum S { S } // (uint, word, word); +enum fld1_sel { fld1_sel } +enum fld2_sel { fld2_sel } +enum fld3_sel { fld3_sel } + +// form: +// instance StructField(S, f_sel):CStructField(ftype, preceding)) {} +impl CStructField, uint, ()> {} +impl CStructField, word, uint> {} +impl CStructField, word, (uint, word)> {} + + +function g() returns (word) { + let s:memory = Typedef.abs(0x80); + let fld1_map : MemberAccessProxy, fld1_sel, ()> = MemberAccessProxy(s, fld1_sel); + let fld2_map : MemberAccessProxy, fld2_sel, uint> = MemberAccessProxy(s, fld2_sel); + let fld3_map = MemberAccessProxy(s, fld3_sel) + ; + // let y:word = 13; + let z:uint = uint(13); + + // s.fld1 = z + + let fld1_lval : memoryRef + = LValueMemberAccess.memberAccess(fld1_map ); + Assign.assign(fld1_lval, z); + + // s.fld2 = 14 + let fld2_lval // : memoryRef(word) + = LValueMemberAccess.memberAccess(fld2_map); + Assign.assign(fld2_lval, 14); + + // s.fld3 = 15 + let fld3_lval // : memoryRef(word) + = LValueMemberAccess.memberAccess(fld3_map); + Assign.assign(fld3_lval, 15); + + // let f1 = S.fld1 + let f1 : uint; + f1 = RValueMemberAccess.memberAccess(fld1_map); + + let f2 : word; + f2 = RValueMemberAccess.memberAccess(fld2_map); + + let f3 : word; + f3 = RValueMemberAccess.memberAccess(fld3_map); + + let f12 = add(Typedef.rep(f1) , f2); + let f123 = add(f12, f3); + + return f123; + +} + +contract C { + function main() public returns (word) { + return g(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/103struct3Fields.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/103struct3Fields.solc deleted file mode 100644 index 87bf761b..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/103struct3Fields.solc +++ /dev/null @@ -1,284 +0,0 @@ -// v4: Simplified Member AccessProxy (no Proxy(offset)) -// variables holding field MAPs - -function add(x : word, y : word) { - let res: word; - assembly { - res := add(x, y) - } - return res; -} - -/////// Construction -class abs:Typedef(rep) { - function rep(x:abs) -> rep; - function abs(x:rep) -> abs; -} - - -data uint = uint(word); - -// this does not work :( -/* -forall a -. default instance a:Typedef(a) { - function rep(x:a) -> word { return a; } - function abs(x:a) -> word { return a;} -} -*/ - -instance uint:Typedef(word) { - function rep(x:uint) -> word { - match x { - | uint(y) => return y; - } - } - function abs(x:word) -> uint { - return uint(x); - } -} - -data memory(a) = memory(word); -data memoryRef(a) = memoryRef(word); -data Proxy(a) = Proxy; - -instance memory(a):Typedef(word) { - function rep(x:memory(a)) -> word { - match x { - | memory(y) => return y; - } - } - function abs(x:word) -> memory(a) { - return memory(x); - } -} -instance memoryRef(a):Typedef(word) { - function rep(x:memoryRef(a)) -> word { - match x { - | memoryRef(y) => return y; - } - } - function abs(x:word) -> memoryRef(a) { - return memoryRef(x); - } -} - -class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -data ref(a) = ref(a); - -instance ref(a):Assign(a) { - function assign(l:ref(a), r:a) -> () { - // builtin "stack store" - return (); - } -} - -class self:MemoryType { - function load(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -class self:MemorySize { - function size(x:Proxy(self)) -> word; -} - - -function mload_(x:word) -> word { - let res: word; - assembly { - res := mload(x) - } - return res; - } - -function mstore_(a:word, v:word) { - assembly { mstore(a,v) } -} - -instance word:MemoryType { - function load(ptr:word) -> word { - let r:word; - assembly { - r := mload(ptr) - } - return r; - } - function store(ptr:word, value:word) -> () { - assembly { - mstore(ptr, value) - } - } -} - -instance uint:MemoryType { - function load(ptr:word) -> uint { - return Typedef.abs(mload_(ptr)):uint; - } - function store(ptr:word, value:uint) -> () { - return mstore_(ptr, Typedef.rep(value)); - } -} - -forall a . a : MemoryType => instance memoryRef(a):Assign(a) { - function assign(l:memoryRef(a), y:a) { - MemoryType.store(Typedef.rep(l), y); - } -} - - - -data MemberAccessProxy(a, field, offset) = MemberAccessProxy(a, field); - -forall a field offset . -function memberAccessD1(x:MemberAccessProxy(a, field, offset)) -> a { - match x { - | MemberAccessProxy(y,z) => return y; - } -} - -class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -forall self memberValueType . -class self:RValueMemberAccess(memberValueType) { - function memberAccess(x:self) -> memberValueType; -} - -class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - -forall structType fieldSelector fieldType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , offsetType:MemorySize - => instance MemberAccessProxy(memory(structType), fieldSelector, offsetType):LValueMemberAccess(memoryRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector, offsetType)) -> memoryRef(fieldType) { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return memoryRef(ptr); - } -} - -instance ():MemorySize { - function size(x:Proxy(())) -> word { - return 0; - } -} - -instance word:MemorySize { - function size(x:Proxy(word)) -> word { - return 32; - } -} - -instance uint:MemorySize { - function size(x:Proxy(uint)) -> word { - return 32; - } -} - - -/* -// fails Patterson cond -forall a b . a:Typedef(b), b:MemorySize -=> instance a:MemorySize { - function size(x:Proxy(a)) -> word { - return MemorySize.size(Proxy(b)); - } -} -*/ - -forall a b . a:MemorySize, b:MemorySize => instance (a,b):MemorySize { - function size(x:Proxy((a,b))) -> word { - let a_sz:word = MemorySize.size(Proxy:Proxy(a)); - let b_sz:word = MemorySize.size(Proxy:Proxy(b)); - assembly { - a_sz := add(a_sz, b_sz) - } - return a_sz; - } -} - -forall structType fieldSelector fieldType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , fieldType:MemoryType - , offsetType:MemorySize - => instance MemberAccessProxy(memory(structType), fieldSelector, offsetType):RValueMemberAccess(fieldType) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector, offsetType)) -> fieldType { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return MemoryType.load(ptr):fieldType; - } -} - -////// Testing - -// struct S { fld1:uint; fld2:word; fld3:word } -data S = S; // (uint, word, word); -data fld1_sel = fld1_sel; -data fld2_sel = fld2_sel; -data fld3_sel = fld3_sel; - -// form: -// instance StructField(S, f_sel):CStructField(ftype, preceding)) {} -instance StructField(S, fld1_sel):CStructField(uint, ()) {} -instance StructField(S, fld2_sel):CStructField(word, uint) {} -instance StructField(S, fld3_sel):CStructField(word, (uint, word)) {} - - -function g() -> word { - let s:memory(S) = Typedef.abs(0x80); - let fld1_map : MemberAccessProxy(memory(S), fld1_sel, ()) = MemberAccessProxy(s, fld1_sel); - let fld2_map : MemberAccessProxy(memory(S), fld2_sel, uint) = MemberAccessProxy(s, fld2_sel); - let fld3_map = MemberAccessProxy(s, fld3_sel) - : MemberAccessProxy(memory(S), fld3_sel, (uint,word)); - // let y:word = 13; - let z:uint = uint(13); - - // s.fld1 = z - - let fld1_lval : memoryRef(uint) - = LValueMemberAccess.memberAccess(fld1_map ); - Assign.assign(fld1_lval, z); - - // s.fld2 = 14 - let fld2_lval // : memoryRef(word) - = LValueMemberAccess.memberAccess(fld2_map); - Assign.assign(fld2_lval, 14); - - // s.fld3 = 15 - let fld3_lval // : memoryRef(word) - = LValueMemberAccess.memberAccess(fld3_map); - Assign.assign(fld3_lval, 15); - - // let f1 = S.fld1 - let f1 : uint; - f1 = RValueMemberAccess.memberAccess(fld1_map); - - let f2 : word; - f2 = RValueMemberAccess.memberAccess(fld2_map); - - let f3 : word; - f3 = RValueMemberAccess.memberAccess(fld3_map); - - let f12 = add(Typedef.rep(f1) : word, f2); - let f123 = add(f12, f3); - - return f123; - -} - -contract C { - public function main() { - return g(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/105nestedStruct.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/105nestedStruct.sol new file mode 100644 index 00000000..b228d7c9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/105nestedStruct.sol @@ -0,0 +1,341 @@ +// v5: nested struct +// variables holding field MAPs + +function add(x : word, y : word) returns (word) { + let res: word; + assembly { + res := add(x, y) + } + return res; +} + +/////// Construction +trait Typedef { + function rep(x: abs) returns (rep) ; + function abs(x: rep) returns (abs) ; +} + + +enum uint { uint(word) } + +// this does not work :( +/* +forall a +. default instance a:Typedef(a) { + function rep(x:a) -> word { return a; } + function abs(x:a) -> word { return a;} +} +*/ + +impl Typedef { + function rep(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + function abs(x: word) returns (uint) { + return uint(x); + } +} + +enum memory { memory(word) } +enum memoryRef { memoryRef(word) } +enum Proxy { Proxy } + +impl Typedef, word> { + function rep(x: memory) returns (word) { + match (x) { +case memory(y) { +return y; +} +} + } + function abs(x: word) returns (memory) { + return memory(x); + } +} +impl Typedef, word> { + function rep(x: memoryRef) returns (word) { + match (x) { +case memoryRef(y) { +return y; +} +} + } + function abs(x: word) returns (memoryRef) { + return memoryRef(x); + } +} + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +enum ref { ref(a) } + +impl Assign, a> { + function assign(l: ref, r: a) { + // builtin "stack store" + return (); + } +} + +trait MemoryType { + function load(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +trait MemorySize { + function size(x: Proxy) returns (word) ; +} + + +function mload_(x: word) returns (word) { + let res: word; + assembly { + res := mload(x) + } + return res; + } + +function mstore_(a:word, v:word) { + assembly { mstore(a,v) } +} + +impl MemoryType { + function load(ptr: word) returns (word) { + let r:word; + assembly { + r := mload(ptr) + } + return r; + } + function store(ptr: word, value: word) { + assembly { + mstore(ptr, value) + } + } +} + +impl MemoryType { + function load(ptr: word) returns (uint) { + return Typedef.abs(mload_(ptr)); + } + function store(ptr: word, value: uint) { + return mstore_(ptr, Typedef.rep(value)); + } +} + +impl MemoryType> { + function load(ptr: word) returns (memory) { + return Typedef.abs(mload_(ptr)); + } + function store(ptr: word, value: memory) { + return mstore_(ptr, Typedef.rep(value)); + } +} + +impl Assign, a> where a: MemoryType { + function assign(l:memoryRef, y:a) { + MemoryType.store(Typedef.rep(l), y); + } +} + + + +enum MemberAccessProxy { MemberAccessProxy(a, field) } + +function memberAccessD1(x: MemberAccessProxy) returns (a) { + match (x) { +case MemberAccessProxy(y,z) { +return y; +} +} +} + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +trait RValueMemberAccess { + function memberAccess(x: self) returns (memberValueType) ; +} + +trait CStructField {} +enum StructField { StructField(structType) } + +impl LValueMemberAccess, fieldSelector, offsetType>, memoryRef> where StructField: CStructField, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (memoryRef) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return memoryRef(ptr); + } +} + +impl MemorySize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + +impl MemorySize> { + function size(x: Proxy>) returns (word) { + return 32; + } +} + + +/* +// fails Patterson cond +forall a b . a:Typedef(b), b:MemorySize +=> instance a:MemorySize { + function size(x:Proxy(a)) -> word { + return MemorySize.size(Proxy(b)); + } +} +*/ + +impl MemorySize<(a, b)> where a: MemorySize, b: MemorySize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz:word = MemorySize.size(@a); + let b_sz:word = MemorySize.size(@b); + assembly { + a_sz := add(a_sz, b_sz) + } + return a_sz; + } +} + +impl RValueMemberAccess, fieldSelector, offsetType>, fieldType> where StructField: CStructField, fieldType: MemoryType, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (fieldType) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return MemoryType.load(ptr); + } +} + +////// Testing + +// struct S { fld1:uint; fld2:word; fld3:word } +enum S { S } // (uint, word, word); + +// struct W { flds : memory(W) } +enum W { W } + +enum fld1_sel { fld1_sel } +enum fld2_sel { fld2_sel } +enum fld3_sel { fld3_sel } + +enum flds_sel { flds_sel } + +// form: +// instance StructField(S, f_sel):CStructField(ftype, preceding)) {} +impl CStructField, uint, ()> {} +impl CStructField, word, uint> {} +impl CStructField, word, (uint, word)> {} + +impl CStructField, memory, ()> {} + +function makeS() returns (memory) { + let s:memory = Typedef.abs(0x80); + let fld1_map : MemberAccessProxy, fld1_sel, ()> = MemberAccessProxy(s, fld1_sel); + let fld2_map : MemberAccessProxy, fld2_sel, uint> = MemberAccessProxy(s, fld2_sel); + let fld3_map = MemberAccessProxy(s, fld3_sel) + ; + // let y:word = 13; + let z:uint = uint(13); + + // s.fld1 = z + + let fld1_lval : memoryRef + = LValueMemberAccess.memberAccess(fld1_map ); + Assign.assign(fld1_lval, z); + + // s.fld2 = 14 + let fld2_lval // : memoryRef(word) + = LValueMemberAccess.memberAccess(fld2_map); + Assign.assign(fld2_lval, 14); + + // s.fld3 = 15 + let fld3_lval // : memoryRef(word) + = LValueMemberAccess.memberAccess(fld3_map); + Assign.assign(fld3_lval, 15); + return s; +} + +function readS(s: memory) returns (word) { + let s:memory = Typedef.abs(0x80); + let fld1_map : MemberAccessProxy, fld1_sel, ()> = MemberAccessProxy(s, fld1_sel); + let fld2_map : MemberAccessProxy, fld2_sel, uint> = MemberAccessProxy(s, fld2_sel); + let fld3_map = MemberAccessProxy(s, fld3_sel) + ; + + // let f1 = s.fld1 + let f1 : uint; + f1 = RValueMemberAccess.memberAccess(fld1_map); + + // let f2 = s.fld2 + let f2 : word; + f2 = RValueMemberAccess.memberAccess(fld2_map); + + let f3 : word; + f3 = RValueMemberAccess.memberAccess(fld3_map); + + let f12 = add(Typedef.rep(f1) , f2); + let f123 = add(f12, f3); + + return f123; +} + +function rwS() returns (word) { + let s:memory = makeS(); + return readS(s); + +} + + +function makeW(s: memory) returns (memory) { + let w:memory = Typedef.abs(0xe0); + let flds_map : MemberAccessProxy, flds_sel, ()> = MemberAccessProxy(w, flds_sel); + + // w.flds = s + let flds_lval : memoryRef> + = LValueMemberAccess.memberAccess(flds_map ); + Assign.assign(flds_lval, s); + + return w; +} + +function readW(w: memory) returns (memory) { + let flds_map : MemberAccessProxy, flds_sel, ()> = MemberAccessProxy(w, flds_sel); + return RValueMemberAccess.memberAccess(flds_map); +} + +contract C { + function main() public returns (word) { + let s:memory = makeS(); + let w:memory = makeW(s); + let s2:memory = readW(w); + return readS(s2); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/105nestedStruct.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/105nestedStruct.solc deleted file mode 100644 index 6a6cc7df..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/105nestedStruct.solc +++ /dev/null @@ -1,343 +0,0 @@ -// v5: nested struct -// variables holding field MAPs - -function add(x : word, y : word) { - let res: word; - assembly { - res := add(x, y) - } - return res; -} - -/////// Construction -class abs:Typedef(rep) { - function rep(x:abs) -> rep; - function abs(x:rep) -> abs; -} - - -data uint = uint(word); - -// this does not work :( -/* -forall a -. default instance a:Typedef(a) { - function rep(x:a) -> word { return a; } - function abs(x:a) -> word { return a;} -} -*/ - -instance uint:Typedef(word) { - function rep(x:uint) -> word { - match x { - | uint(y) => return y; - } - } - function abs(x:word) -> uint { - return uint(x); - } -} - -data memory(a) = memory(word); -data memoryRef(a) = memoryRef(word); -data Proxy(a) = Proxy; - -instance memory(a):Typedef(word) { - function rep(x:memory(a)) -> word { - match x { - | memory(y) => return y; - } - } - function abs(x:word) -> memory(a) { - return memory(x); - } -} -instance memoryRef(a):Typedef(word) { - function rep(x:memoryRef(a)) -> word { - match x { - | memoryRef(y) => return y; - } - } - function abs(x:word) -> memoryRef(a) { - return memoryRef(x); - } -} - -class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -data ref(a) = ref(a); - -instance ref(a):Assign(a) { - function assign(l:ref(a), r:a) -> () { - // builtin "stack store" - return (); - } -} - -class self:MemoryType { - function load(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -class self:MemorySize { - function size(x:Proxy(self)) -> word; -} - - -function mload_(x:word) -> word { - let res: word; - assembly { - res := mload(x) - } - return res; - } - -function mstore_(a:word, v:word) { - assembly { mstore(a,v) } -} - -instance word:MemoryType { - function load(ptr:word) -> word { - let r:word; - assembly { - r := mload(ptr) - } - return r; - } - function store(ptr:word, value:word) -> () { - assembly { - mstore(ptr, value) - } - } -} - -instance uint:MemoryType { - function load(ptr:word) -> uint { - return Typedef.abs(mload_(ptr)); - } - function store(ptr:word, value:uint) -> () { - return mstore_(ptr, Typedef.rep(value)); - } -} - -forall a . instance memory(a):MemoryType { - function load(ptr:word) -> memory(a) { - return Typedef.abs(mload_(ptr)); - } - function store(ptr:word, value:memory(a)) -> () { - return mstore_(ptr, Typedef.rep(value)); - } -} - -forall a . a : MemoryType => instance memoryRef(a):Assign(a) { - function assign(l:memoryRef(a), y:a) { - MemoryType.store(Typedef.rep(l), y); - } -} - - - -data MemberAccessProxy(a, field, offset) = MemberAccessProxy(a, field); - -forall a field offset . -function memberAccessD1(x:MemberAccessProxy(a, field, offset)) -> a { - match x { - | MemberAccessProxy(y,z) => return y; - } -} - -class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -forall self memberValueType . -class self:RValueMemberAccess(memberValueType) { - function memberAccess(x:self) -> memberValueType; -} - -class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - -forall structType fieldSelector fieldType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , offsetType:MemorySize - => instance MemberAccessProxy(memory(structType), fieldSelector, offsetType):LValueMemberAccess(memoryRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector, offsetType)) -> memoryRef(fieldType) { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return memoryRef(ptr); - } -} - -instance ():MemorySize { - function size(x:Proxy(())) -> word { - return 0; - } -} - -instance word:MemorySize { - function size(x:Proxy(word)) -> word { - return 32; - } -} - -instance uint:MemorySize { - function size(x:Proxy(uint)) -> word { - return 32; - } -} - -forall a -. instance memory(a):MemorySize { - function size(x:Proxy(memory(a))) -> word { - return 32; - } -} - - -/* -// fails Patterson cond -forall a b . a:Typedef(b), b:MemorySize -=> instance a:MemorySize { - function size(x:Proxy(a)) -> word { - return MemorySize.size(Proxy(b)); - } -} -*/ - -forall a b . a:MemorySize, b:MemorySize => instance (a,b):MemorySize { - function size(x:Proxy((a,b))) -> word { - let a_sz:word = MemorySize.size(Proxy:Proxy(a)); - let b_sz:word = MemorySize.size(Proxy:Proxy(b)); - assembly { - a_sz := add(a_sz, b_sz) - } - return a_sz; - } -} - -forall structType fieldSelector fieldType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , fieldType:MemoryType - , offsetType:MemorySize - => instance MemberAccessProxy(memory(structType), fieldSelector, offsetType):RValueMemberAccess(fieldType) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector, offsetType)) -> fieldType { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return MemoryType.load(ptr):fieldType; - } -} - -////// Testing - -// struct S { fld1:uint; fld2:word; fld3:word } -data S = S; // (uint, word, word); - -// struct W { flds : memory(W) } -data W = W; - -data fld1_sel = fld1_sel; -data fld2_sel = fld2_sel; -data fld3_sel = fld3_sel; - -data flds_sel = flds_sel; - -// form: -// instance StructField(S, f_sel):CStructField(ftype, preceding)) {} -instance StructField(S, fld1_sel):CStructField(uint, ()) {} -instance StructField(S, fld2_sel):CStructField(word, uint) {} -instance StructField(S, fld3_sel):CStructField(word, (uint, word)) {} - -instance StructField(W, flds_sel):CStructField(memory(S), ()) {} - -function makeS() -> memory(S) { - let s:memory(S) = Typedef.abs(0x80); - let fld1_map : MemberAccessProxy(memory(S), fld1_sel, ()) = MemberAccessProxy(s, fld1_sel); - let fld2_map : MemberAccessProxy(memory(S), fld2_sel, uint) = MemberAccessProxy(s, fld2_sel); - let fld3_map = MemberAccessProxy(s, fld3_sel) - : MemberAccessProxy(memory(S), fld3_sel, (uint,word)); - // let y:word = 13; - let z:uint = uint(13); - - // s.fld1 = z - - let fld1_lval : memoryRef(uint) - = LValueMemberAccess.memberAccess(fld1_map ); - Assign.assign(fld1_lval, z); - - // s.fld2 = 14 - let fld2_lval // : memoryRef(word) - = LValueMemberAccess.memberAccess(fld2_map); - Assign.assign(fld2_lval, 14); - - // s.fld3 = 15 - let fld3_lval // : memoryRef(word) - = LValueMemberAccess.memberAccess(fld3_map); - Assign.assign(fld3_lval, 15); - return s; -} - -function readS(s:memory(S)) -> word { - let s:memory(S) = Typedef.abs(0x80); - let fld1_map : MemberAccessProxy(memory(S), fld1_sel, ()) = MemberAccessProxy(s, fld1_sel); - let fld2_map : MemberAccessProxy(memory(S), fld2_sel, uint) = MemberAccessProxy(s, fld2_sel); - let fld3_map = MemberAccessProxy(s, fld3_sel) - : MemberAccessProxy(memory(S), fld3_sel, (uint,word)); - - // let f1 = s.fld1 - let f1 : uint; - f1 = RValueMemberAccess.memberAccess(fld1_map); - - // let f2 = s.fld2 - let f2 : word; - f2 = RValueMemberAccess.memberAccess(fld2_map); - - let f3 : word; - f3 = RValueMemberAccess.memberAccess(fld3_map); - - let f12 = add(Typedef.rep(f1) : word, f2); - let f123 = add(f12, f3); - - return f123; -} - -function rwS() -> word { - let s:memory(S) = makeS(); - return readS(s); - -} - - -function makeW(s:memory(S)) -> memory(W) { - let w:memory(W) = Typedef.abs(0xe0); - let flds_map : MemberAccessProxy(memory(W), flds_sel, ()) = MemberAccessProxy(w, flds_sel); - - // w.flds = s - let flds_lval : memoryRef(memory(S)) - = LValueMemberAccess.memberAccess(flds_map ); - Assign.assign(flds_lval, s); - - return w; -} - -function readW(w:memory(W)) -> memory(S) { - let flds_map : MemberAccessProxy(memory(W), flds_sel, ()) = MemberAccessProxy(w, flds_sel); - return RValueMemberAccess.memberAccess(flds_map); -} - -contract C { - public function main() { - let s:memory(S) = makeS(); - let w:memory(W) = makeW(s); - let s2:memory(S) = readW(w); - return readS(s2); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/111storageStruct.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/111storageStruct.sol new file mode 100644 index 00000000..92759dc9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/111storageStruct.sol @@ -0,0 +1,287 @@ +// v4: Simplified Member AccessProxy (no Proxy(offset)) +// variables holding field MAPs + +function add(x : word, y : word) returns (word) { + let res: word; + assembly { + res := add(x, y) + } + return res; +} + +/////// Construction +trait Typedef { + function rep(x: abs) returns (rep) ; + function abs(x: rep) returns (abs) ; +} + + +enum uint { uint(word) } + +// this does not work :( +/* +forall a +. default instance a:Typedef(a) { + function rep(x:a) -> word { return a; } + function abs(x:a) -> word { return a;} +} +*/ + +impl Typedef { + function rep(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + function abs(x: word) returns (uint) { + return uint(x); + } +} + +enum storage { storage(word) } +enum storageRef { storageRef(word) } +enum Proxy { Proxy } + +impl Typedef, word> { + function rep(x: storage) returns (word) { + match (x) { +case storage(y) { +return y; +} +} + } + function abs(x: word) returns (storage) { + return storage(x); + } +} +impl Typedef, word> { + function rep(x: storageRef) returns (word) { + match (x) { +case storageRef(y) { +return y; +} +} + } + function abs(x: word) returns (storageRef) { + return storageRef(x); + } +} + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +/* +data ref(a) = ref(a); + +instance ref(a):Assign(a) { + function assign(l:ref(a), r:a) -> () { + // builtin "stack store" + return (); + } +} +*/ + +trait StorageType { + function sload(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +trait StorageSize { + function size(x: Proxy) returns (word) ; +} + + +function sload_(x: word) returns (word) { + let res: word; + assembly { + res := sload(x) + } + return res; + } + +function sstore_(a:word, v:word) { + assembly { sstore(a,v) } +} + +impl StorageType { + function sload(ptr: word) returns (word) { + let r:word; + assembly { + r := sload(ptr) + } + return r; + } + function store(ptr: word, value: word) { + assembly { + sstore(ptr, value) + } + } +} + +impl StorageType { + function sload(ptr: word) returns (uint) { + return Typedef.abs(sload_(ptr)); // type annotation needed due to a typechecker bug + } + function store(ptr: word, value: uint) { + return sstore_(ptr, Typedef.rep(value)); + } +} + +impl Assign, a> where a: StorageType { + function assign(l:storageRef, y:a) { + StorageType.store(Typedef.rep(l), y); + } +} + + + +enum MemberAccessProxy { MemberAccessProxy(a, field) } + +function memberAccessD1(x: MemberAccessProxy) returns (a) { + match (x) { +case MemberAccessProxy(y,z) { +return y; +} +} +} + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +trait RValueMemberAccess { + function memberAccess(x: self) returns (memberValueType) ; +} + +trait CStructField {} +enum StructField { StructField(structType) } + +impl LValueMemberAccess, fieldSelector, offsetType>, storageRef> where StructField: CStructField, offsetType: StorageSize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (storageRef) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = StorageSize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return storageRef(ptr); + } +} + +impl StorageSize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} + +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + + +/* +// fails Patterson cond +forall a b . a:Typedef(b), b:StorageSize +=> instance a:StorageSize { + function size(x:Proxy(a)) -> word { + return StorageSize.size(Proxy(b)); + } +} +*/ + +impl StorageSize<(a, b)> where a: StorageSize, b: StorageSize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz:word = StorageSize.size(@a); + let b_sz:word = StorageSize.size(@b); + assembly { + a_sz := add(a_sz, b_sz) + } + return a_sz; + } +} + +impl RValueMemberAccess, fieldSelector, offsetType>, fieldType> where StructField: CStructField, fieldType: StorageType, offsetType: StorageSize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (fieldType) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = StorageSize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return StorageType.sload(ptr); + } +} + +// helpers + +////// Testing + +// struct S { fld1:uint; fld2:word; fld3:word } +enum S { S } // (uint, word, word); +enum fld1_sel { fld1_sel } +enum fld2_sel { fld2_sel } +enum fld3_sel { fld3_sel } + +// form: +// instance StructField(S, f_sel):CStructField(ftype, preceding)) {} +impl CStructField, uint, ()> {} +impl CStructField, word, uint> {} +impl CStructField, word, (uint, word)> {} + + +function g() returns (word) { + let s:storage = Typedef.abs(0x80); + let fld1_map : MemberAccessProxy, fld1_sel, ()> = MemberAccessProxy(s, fld1_sel); + let fld2_map : MemberAccessProxy, fld2_sel, uint> = MemberAccessProxy(s, fld2_sel); + let fld3_map = MemberAccessProxy(s, fld3_sel) + ; + // let y:word = 13; + let z:uint = uint(13); + + // s.fld1 = z + + let fld1_lval : storageRef + = LValueMemberAccess.memberAccess(fld1_map ); + Assign.assign(fld1_lval, z); + + // s.fld2 = 14 + let fld2_lval // : storageRef(word) + = LValueMemberAccess.memberAccess(fld2_map); + Assign.assign(fld2_lval, 14); + + // s.fld3 = 15 + let fld3_lval // : storageRef(word) + = LValueMemberAccess.memberAccess(fld3_map); + Assign.assign(fld3_lval, 15); + + // let f1 = S.fld1 + let f1 : uint; + f1 = RValueMemberAccess.memberAccess(fld1_map); + + let f2 : word; + f2 = RValueMemberAccess.memberAccess(fld2_map); + + let f3 : word; + f3 = RValueMemberAccess.memberAccess(fld3_map); + + let f12 = add(Typedef.rep(f1) , f2); + let f123 = add(f12, f3); + + return f123; + +} + +contract C { + function main() public returns (word) { + return g(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/111storageStruct.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/111storageStruct.solc deleted file mode 100644 index a65ae96c..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/111storageStruct.solc +++ /dev/null @@ -1,288 +0,0 @@ -// v4: Simplified Member AccessProxy (no Proxy(offset)) -// variables holding field MAPs - -function add(x : word, y : word) { - let res: word; - assembly { - res := add(x, y) - } - return res; -} - -/////// Construction -class abs:Typedef(rep) { - function rep(x:abs) -> rep; - function abs(x:rep) -> abs; -} - - -data uint = uint(word); - -// this does not work :( -/* -forall a -. default instance a:Typedef(a) { - function rep(x:a) -> word { return a; } - function abs(x:a) -> word { return a;} -} -*/ - -instance uint:Typedef(word) { - function rep(x:uint) -> word { - match x { - | uint(y) => return y; - } - } - function abs(x:word) -> uint { - return uint(x); - } -} - -data storage(a) = storage(word); -data storageRef(a) = storageRef(word); -data Proxy(a) = Proxy; - -instance storage(a):Typedef(word) { - function rep(x:storage(a)) -> word { - match x { - | storage(y) => return y; - } - } - function abs(x:word) -> storage(a) { - return storage(x); - } -} -instance storageRef(a):Typedef(word) { - function rep(x:storageRef(a)) -> word { - match x { - | storageRef(y) => return y; - } - } - function abs(x:word) -> storageRef(a) { - return storageRef(x); - } -} - -class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -/* -data ref(a) = ref(a); - -instance ref(a):Assign(a) { - function assign(l:ref(a), r:a) -> () { - // builtin "stack store" - return (); - } -} -*/ - -class self:StorageType { - function sload(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -class self:StorageSize { - function size(x:Proxy(self)) -> word; -} - - -function sload_(x:word) -> word { - let res: word; - assembly { - res := sload(x) - } - return res; - } - -function sstore_(a:word, v:word) { - assembly { sstore(a,v) } -} - -instance word:StorageType { - function sload(ptr:word) -> word { - let r:word; - assembly { - r := sload(ptr) - } - return r; - } - function store(ptr:word, value:word) -> () { - assembly { - sstore(ptr, value) - } - } -} - -instance uint:StorageType { - function sload(ptr:word) -> uint { - return Typedef.abs(sload_(ptr)):uint; // type annotation needed due to a typechecker bug - } - function store(ptr:word, value:uint) -> () { - return sstore_(ptr, Typedef.rep(value)); - } -} - -forall a . a : StorageType => instance storageRef(a):Assign(a) { - function assign(l:storageRef(a), y:a) { - StorageType.store(Typedef.rep(l), y); - } -} - - - -data MemberAccessProxy(a, field, offset) = MemberAccessProxy(a, field); - -forall a field offset . -function memberAccessD1(x:MemberAccessProxy(a, field, offset)) -> a { - match x { - | MemberAccessProxy(y,z) => return y; - } -} - -class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -forall self memberValueType . -class self:RValueMemberAccess(memberValueType) { - function memberAccess(x:self) -> memberValueType; -} - -class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - -forall structType fieldSelector fieldType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , offsetType:StorageSize - => instance MemberAccessProxy(storage(structType), fieldSelector, offsetType):LValueMemberAccess(storageRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(storage(structType), fieldSelector, offsetType)) -> storageRef(fieldType) { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = StorageSize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return storageRef(ptr); - } -} - -instance ():StorageSize { - function size(x:Proxy(())) -> word { - return 0; - } -} - -instance word:StorageSize { - function size(x:Proxy(word)) -> word { - return 1; - } -} - -instance uint:StorageSize { - function size(x:Proxy(uint)) -> word { - return 1; - } -} - - -/* -// fails Patterson cond -forall a b . a:Typedef(b), b:StorageSize -=> instance a:StorageSize { - function size(x:Proxy(a)) -> word { - return StorageSize.size(Proxy(b)); - } -} -*/ - -forall a b . a:StorageSize, b:StorageSize => instance (a,b):StorageSize { - function size(x:Proxy((a,b))) -> word { - let a_sz:word = StorageSize.size(Proxy:Proxy(a)); - let b_sz:word = StorageSize.size(Proxy:Proxy(b)); - assembly { - a_sz := add(a_sz, b_sz) - } - return a_sz; - } -} - -forall structType fieldSelector fieldType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , fieldType:StorageType - , offsetType:StorageSize - => instance MemberAccessProxy(storage(structType), fieldSelector, offsetType):RValueMemberAccess(fieldType) { - function memberAccess(x:MemberAccessProxy(storage(structType), fieldSelector, offsetType)) -> fieldType { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = StorageSize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return StorageType.sload(ptr):fieldType; - } -} - -// helpers - -////// Testing - -// struct S { fld1:uint; fld2:word; fld3:word } -data S = S; // (uint, word, word); -data fld1_sel = fld1_sel; -data fld2_sel = fld2_sel; -data fld3_sel = fld3_sel; - -// form: -// instance StructField(S, f_sel):CStructField(ftype, preceding)) {} -instance StructField(S, fld1_sel):CStructField(uint, ()) {} -instance StructField(S, fld2_sel):CStructField(word, uint) {} -instance StructField(S, fld3_sel):CStructField(word, (uint, word)) {} - - -function g() -> word { - let s:storage(S) = Typedef.abs(0x80); - let fld1_map : MemberAccessProxy(storage(S), fld1_sel, ()) = MemberAccessProxy(s, fld1_sel); - let fld2_map : MemberAccessProxy(storage(S), fld2_sel, uint) = MemberAccessProxy(s, fld2_sel); - let fld3_map = MemberAccessProxy(s, fld3_sel) - : MemberAccessProxy(storage(S), fld3_sel, (uint,word)); - // let y:word = 13; - let z:uint = uint(13); - - // s.fld1 = z - - let fld1_lval : storageRef(uint) - = LValueMemberAccess.memberAccess(fld1_map ); - Assign.assign(fld1_lval, z); - - // s.fld2 = 14 - let fld2_lval // : storageRef(word) - = LValueMemberAccess.memberAccess(fld2_map); - Assign.assign(fld2_lval, 14); - - // s.fld3 = 15 - let fld3_lval // : storageRef(word) - = LValueMemberAccess.memberAccess(fld3_map); - Assign.assign(fld3_lval, 15); - - // let f1 = S.fld1 - let f1 : uint; - f1 = RValueMemberAccess.memberAccess(fld1_map); - - let f2 : word; - f2 = RValueMemberAccess.memberAccess(fld2_map); - - let f3 : word; - f3 = RValueMemberAccess.memberAccess(fld3_map); - - let f12 = add(Typedef.rep(f1) : word, f2); - let f123 = add(f12, f3); - - return f123; - -} - -contract C { - public function main() { - return g(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/112ContractStorage.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/112ContractStorage.sol new file mode 100644 index 00000000..02e1edf6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/112ContractStorage.sol @@ -0,0 +1,43 @@ +import StorageLib; + +/* +// Translating contract: +contract Counter { + counter : word; + + function main() -> word { + counter = add(counter, 1); + return counter; + } +} +*/ + + + +// form: +// instance StructField(S, f_sel):CStructField(ftype, preceding)) {} +enum CounterCxt { CounterCxt } +enum counter_sel { counter_sel } +impl CStructField, counter_sel>, word, ()> {} + +contract Counter { + // struct CounterCxt { counter:word } + + function main() public returns (word) { + let cxt : ContractStorage = ContractStorage(CounterCxt); + let counter_map : MemberAccessProxy, counter_sel, ()> + = MemberAccessProxy(cxt, counter_sel); + + // let c1 = this.counter + let c1 : word; + c1 = RValueMemberAccess.memberAccess(counter_map); + + // this.counter = c1 + 7 + let counter_lval // : storageRef(word) + = LValueMemberAccess.memberAccess(counter_map); + + Assign.assign(counter_lval, add(c1, 7)); + + return RValueMemberAccess.memberAccess(counter_map); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/112ContractStorage.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/112ContractStorage.solc deleted file mode 100644 index f672661a..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/112ContractStorage.solc +++ /dev/null @@ -1,43 +0,0 @@ -import StorageLib; - -/* -// Translating contract: -contract Counter { - counter : word; - - function main() -> word { - counter = add(counter, 1); - return counter; - } -} -*/ - - - -// form: -// instance StructField(S, f_sel):CStructField(ftype, preceding)) {} -data CounterCxt = CounterCxt; -data counter_sel = counter_sel; -instance StructField(ContractStorage(CounterCxt), counter_sel):CStructField(word, ()) {} - -contract Counter { - // struct CounterCxt { counter:word } - - public function main() -> word { - let cxt : ContractStorage(CounterCxt) = ContractStorage(CounterCxt); - let counter_map : MemberAccessProxy(ContractStorage(CounterCxt), counter_sel, ()) - = MemberAccessProxy(cxt, counter_sel); - - // let c1 = this.counter - let c1 : word; - c1 = RValueMemberAccess.memberAccess(counter_map); - - // this.counter = c1 + 7 - let counter_lval // : storageRef(word) - = LValueMemberAccess.memberAccess(counter_map); - - Assign.assign(counter_lval, add(c1, 7)); - - return RValueMemberAccess.memberAccess(counter_map); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/113counter.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/113counter.sol new file mode 100644 index 00000000..985806e7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/113counter.sol @@ -0,0 +1,23 @@ +import StorageLib; + +/* +contract Counter { + counter : word; + + function main() -> word { + counter = add(counter, 1); + return counter; + } +} +*/ + +enum counter_sel { counter_sel } +impl CStructField, counter_sel>, word, ()> {} + +contract Counter { + function main() public returns (word) { + let counter_map /*: MemberAccessProxy(ContractStorage(()), counter_sel, ()) */ = MemberAccessProxy(ContractStorage(()), counter_sel); + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(()), counter_sel)), add(rval(counter_map), 1)); + return rval(counter_map); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/113counter.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/113counter.solc deleted file mode 100644 index 7fd85e4b..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/113counter.solc +++ /dev/null @@ -1,23 +0,0 @@ -import StorageLib; - -/* -contract Counter { - counter : word; - - function main() -> word { - counter = add(counter, 1); - return counter; - } -} -*/ - -data counter_sel = counter_sel; -instance StructField(ContractStorage(()), counter_sel):CStructField(word, ()) {} - -contract Counter { - public function main () -> word { - let counter_map /*: MemberAccessProxy(ContractStorage(()), counter_sel, ()) */ = MemberAccessProxy(ContractStorage(()), counter_sel); - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(()), counter_sel)), add(rval(counter_map), 1)); - return rval(counter_map); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/131constructor.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/131constructor.sol new file mode 100644 index 00000000..ab26a3ff --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/131constructor.sol @@ -0,0 +1,27 @@ +// test constructor + +contract Counter { + + function setCounter(v: word) public { + assembly { + sstore(0x00, v) + } + } + + function getCounter() public returns (word) { + let res; + assembly { + res := sload(0x00) + } + return res; + } + + + constructor() { + setCounter(42); + } + + function main() public returns (word) { + return getCounter(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/131constructor.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/131constructor.solc deleted file mode 100644 index 4e0381b9..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/131constructor.solc +++ /dev/null @@ -1,27 +0,0 @@ -// test constructor - -contract Counter { - - public function setCounter(v: word) { - assembly { - sstore(0x00, v) - } - } - - public function getCounter() -> word { - let res; - assembly { - res := sload(0x00) - } - return res; - } - - - constructor() { - setCounter(42); - } - - public function main() -> word { - return getCounter(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/135cons3.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/135cons3.sol new file mode 100644 index 00000000..ba775985 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/135cons3.sol @@ -0,0 +1,98 @@ +// test constructor with multiple args +import * from std; +// import prelude; + + +function log1(v: t, topic: word) where t: Typedef { + let w : word = Typedef.rep(v); + assembly { + mstore(0,w) + log1(0,32,topic) + } +} + +contract Counter { + + // setCounter & getCounter are intentionally low-level to avoid clutter + function setCounter(v: uint256) public { + match (v) { +case uint256(w) { +assembly { + sstore(0x00, w) + } +} +} + } + + function getCounter() public returns (uint256) { + let res; + assembly { + res := sload(0x00) + } + return uint256(res); + } + + constructor(x:uint256, y:uint256, z:uint256) + // function myconstructor(x:uint256, y:uint256, z:uint256) -> () + { + log1(x, 0xc1); + log1(y, 0xc2); + log1(z, 0xc3); + setCounter(Add.add(Add.add(x,y),z)); + } + +/* This should desugar to: (check with --dump-dispatch */ + +/* + init_(x:uint256, y:uint256, z:uint256) + // function myconstructor(x:uint256, y:uint256, z:uint256) -> () + { + setCounter(x+y+z); + } + function copy_arguments_for_constructor() -> (uint256, uint256, uint256) { // result type CHANGES + let res : (uint256, uint256, uint256); // type(res) CHANGES + let memoryDataOffset : word; + + assembly { + let programSize := datasize("CounterDeploy") // ${deployerName} where deployerName = contractName <> "Deploy" + let argSize := sub(codesize(), programSize) + memoryDataOffset := mload(64) + mstore(64, add(memoryDataOffset, argSize)) + codecopy(memoryDataOffset, programSize, argSize) + } + + let source : memory(bytes) = memory(memoryDataOffset); + res = abi_decode(source, Proxy:Proxy( (uint256, uint256, uint256) ), Proxy:Proxy(MemoryWordReader)); + return res; + } + + function start() -> () { + assembly { mstore(64, memoryguard(128)) } + + let conargs = copy_arguments_for_constructor(); + // Possible hack: let fn = init; fn(conargs); + // match conargs { | (a1, a2, a3) => myconstructor(a1,a2,a3) ; } + match conargs { | (a1, a2, a3) => init_(a1,a2,a3) ; } + + assembly { + let size := datasize("Counter") + codecopy(0, dataoffset("Counter"), datasize("Counter")) + return(0, size) + } + /* Haskell with Yul QQ (#231) + let cname = "Counter" in Asm [yulBlock| + let size := datasize(`cname`) + codecopy(0, dataoffset(`cname`), datasize(`cname`)) + return(0, size) + |] + */ + return (); + } + */ + + // TODO: remove main, use dispatch instead + function main() returns (uint256) { + return getCounter(); + } + +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/135cons3.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/135cons3.solc deleted file mode 100644 index 9e808a4c..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/135cons3.solc +++ /dev/null @@ -1,97 +0,0 @@ -// test constructor with multiple args -import std.{*}; -// import prelude; - - -forall t.t:Typedef(word) => -function log1(v:t, topic:word) -> () { - let w : word = Typedef.rep(v); - assembly { - mstore(0,w) - log1(0,32,topic) - } -} - -contract Counter { - - // setCounter & getCounter are intentionally low-level to avoid clutter - public function setCounter(v: uint256) -> () { - match v { | uint256(w) => - assembly { - sstore(0x00, w) - } - } - } - - public function getCounter() -> uint256 { - let res; - assembly { - res := sload(0x00) - } - return uint256(res); - } - - constructor(x:uint256, y:uint256, z:uint256) - // function myconstructor(x:uint256, y:uint256, z:uint256) -> () - { - log1(x, 0xc1); - log1(y, 0xc2); - log1(z, 0xc3); - setCounter(Add.add(Add.add(x,y),z)); - } - -/* This should desugar to: (check with --dump-dispatch */ - -/* - init_(x:uint256, y:uint256, z:uint256) - // function myconstructor(x:uint256, y:uint256, z:uint256) -> () - { - setCounter(x+y+z); - } - function copy_arguments_for_constructor() -> (uint256, uint256, uint256) { // result type CHANGES - let res : (uint256, uint256, uint256); // type(res) CHANGES - let memoryDataOffset : word; - - assembly { - let programSize := datasize("CounterDeploy") // ${deployerName} where deployerName = contractName <> "Deploy" - let argSize := sub(codesize(), programSize) - memoryDataOffset := mload(64) - mstore(64, add(memoryDataOffset, argSize)) - codecopy(memoryDataOffset, programSize, argSize) - } - - let source : memory(bytes) = memory(memoryDataOffset); - res = abi_decode(source, Proxy:Proxy( (uint256, uint256, uint256) ), Proxy:Proxy(MemoryWordReader)); - return res; - } - - function start() -> () { - assembly { mstore(64, memoryguard(128)) } - - let conargs = copy_arguments_for_constructor(); - // Possible hack: let fn = init; fn(conargs); - // match conargs { | (a1, a2, a3) => myconstructor(a1,a2,a3) ; } - match conargs { | (a1, a2, a3) => init_(a1,a2,a3) ; } - - assembly { - let size := datasize("Counter") - codecopy(0, dataoffset("Counter"), datasize("Counter")) - return(0, size) - } - /* Haskell with Yul QQ (#231) - let cname = "Counter" in Asm [yulBlock| - let size := datasize(`cname`) - codecopy(0, dataoffset(`cname`), datasize(`cname`)) - return(0, size) - |] - */ - return (); - } - */ - - // TODO: remove main, use dispatch instead - function main() -> uint256 { - return getCounter(); - } - -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/StorageLib.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/StorageLib.sol new file mode 100644 index 00000000..222ac6f1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/StorageLib.sol @@ -0,0 +1,239 @@ +// v4: Simplified Member AccessProxy (no Proxy(offset)) +// variables holding field MAPs + +function add(x : word, y : word) { + let res: word; + assembly { + res := add(x, y) + } + return res; +} + +/////// Construction +trait Typedef { + function rep(x: abs) returns (rep) ; + function abs(x: rep) returns (abs) ; +} + + +enum uint { uint(word) } + +// this does not work :( +/* +forall a +. default instance a:Typedef(a) { + function rep(x:a) -> word { return a; } + function abs(x:a) -> word { return a;} +} +*/ + +impl Typedef { + function rep(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + function abs(x: word) returns (uint) { + return uint(x); + } +} + +enum storage { storage(word) } +enum ContractStorage { ContractStorage(cxt) } + +enum storageRef { storageRef(word) } +enum Proxy { Proxy } + +impl Typedef, word> { + function rep(x: storage) returns (word) { + match (x) { +case storage(y) { +return y; +} +} + } + function abs(x: word) returns (storage) { + return storage(x); + } +} + +impl Typedef, word> { + function rep(x: storageRef) returns (word) { + match (x) { +case storageRef(y) { +return y; +} +} + } + function abs(x: word) returns (storageRef) { + return storageRef(x); + } +} + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +enum ref { ref(a) } + +impl Assign, a> { + function assign(l: ref, r: a) { + // builtin "stack store" + return (); + } +} + +trait StorageType { + function sload(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +trait StorageSize { + function size(x: Proxy) returns (word) ; +} + + +function sload_(x: word) returns (word) { + let res: word; + assembly { + res := sload(x) + } + return res; + } + +function sstore_(a:word, v:word) { + assembly { sstore(a,v) } +} + +impl StorageType { + function sload(ptr: word) returns (word) { + let r:word; + assembly { + r := sload(ptr) + } + return r; + } + function store(ptr: word, value: word) { + assembly { + sstore(ptr, value) + } + } +} + +impl StorageType { + function sload(ptr: word) returns (uint) { + return Typedef.abs(sload_(ptr)); // type annotation needed due to a typechecker bug + } + function store(ptr: word, value: uint) { + return sstore_(ptr, Typedef.rep(value)); + } +} + +impl Assign, a> where a: StorageType { + function assign(l: storageRef, y: a) { + StorageType.store(Typedef.rep(l), y); + } +} + + + +enum MemberAccessProxy { MemberAccessProxy(a, field) } +function memberAccessD1(x: MemberAccessProxy) returns (a) { + match (x) { +case MemberAccessProxy(y,z) { +return y; +} +} +} + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +trait RValueMemberAccess { + function memberAccess(x: self) returns (memberValueType) ; +} + +trait CStructField {} + +enum StructField { StructField(structType) } + +impl LValueMemberAccess, fieldSelector, offsetType>, storageRef> where StructField: CStructField, offsetType: StorageSize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (storageRef) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = StorageSize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return storageRef(ptr); + } +} + +impl StorageSize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} + +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + + +/* +// fails Patterson cond +forall a b . a:Typedef(b), b:StorageSize +=> instance a:StorageSize { + function size(x:Proxy(a)) -> word { + return StorageSize.size(Proxy(b)); + } +} +*/ + +impl StorageSize<(a, b)> where a: StorageSize, b: StorageSize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz:word = StorageSize.size(@a); + let b_sz:word = StorageSize.size(@b); + assembly { + a_sz := add(a_sz, b_sz) + } + return a_sz; + } +} + +pragma no-patterson-condition RValueMemberAccess; // this is due to ContractStorage(cxt); probably not needed once we have local instances +pragma no-coverage-condition LValueMemberAccess, RValueMemberAccess; + +impl LValueMemberAccess, fieldSelector, offsetType>, storageRef> where StructField, fieldSelector>: CStructField, offsetType: StorageSize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (storageRef) { + let ptr:word = 0x100; // forge uses at least 1 storage slot + let offsetSize:word = StorageSize.size(@offsetType); + + assembly { + ptr := add(ptr, offsetSize) + } + return storageRef(ptr); // contract storage starts at 0 + } +} + +impl RValueMemberAccess, fieldSelector, offsetType>, fieldType> where StructField, fieldSelector>: CStructField, fieldType: StorageType, offsetType: StorageSize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (fieldType) { + let ptr:word = 0x100; + let offsetSize:word = StorageSize.size(@offsetType); + return StorageType.sload(add(ptr, offsetSize)); + } +} + +function rval(x: a) returns (b) where a: RValueMemberAccess { + return RValueMemberAccess.memberAccess(x); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/StorageLib.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/StorageLib.solc deleted file mode 100644 index 9047f00a..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/StorageLib.solc +++ /dev/null @@ -1,253 +0,0 @@ -// v4: Simplified Member AccessProxy (no Proxy(offset)) -// variables holding field MAPs - -function add(x : word, y : word) { - let res: word; - assembly { - res := add(x, y) - } - return res; -} - -/////// Construction -forall abs rep. -class abs:Typedef(rep) { - function rep(x:abs) -> rep; - function abs(x:rep) -> abs; -} - - -data uint = uint(word); - -// this does not work :( -/* -forall a -. default instance a:Typedef(a) { - function rep(x:a) -> word { return a; } - function abs(x:a) -> word { return a;} -} -*/ - -instance uint:Typedef(word) { - function rep(x:uint) -> word { - match x { - | uint(y) => return y; - } - } - function abs(x:word) -> uint { - return uint(x); - } -} - -data storage(a) = storage(word); -data ContractStorage(cxt) = ContractStorage(cxt); - -data storageRef(a) = storageRef(word); -data Proxy(a) = Proxy; - -forall a. -instance storage(a):Typedef(word) { - function rep(x:storage(a)) -> word { - match x { - | storage(y) => return y; - } - } - function abs(x:word) -> storage(a) { - return storage(x); - } -} - -forall a. -instance storageRef(a):Typedef(word) { - function rep(x:storageRef(a)) -> word { - match x { - | storageRef(y) => return y; - } - } - function abs(x:word) -> storageRef(a) { - return storageRef(x); - } -} - -forall lhs rhs. -class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -data ref(a) = ref(a); - -forall a. -instance ref(a):Assign(a) { - function assign(l:ref(a), r:a) -> () { - // builtin "stack store" - return (); - } -} - -forall self. -class self:StorageType { - function sload(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -forall self. -class self:StorageSize { - function size(x:Proxy(self)) -> word; -} - - -function sload_(x:word) -> word { - let res: word; - assembly { - res := sload(x) - } - return res; - } - -function sstore_(a:word, v:word) { - assembly { sstore(a,v) } -} - -instance word:StorageType { - function sload(ptr:word) -> word { - let r:word; - assembly { - r := sload(ptr) - } - return r; - } - function store(ptr:word, value:word) -> () { - assembly { - sstore(ptr, value) - } - } -} - -instance uint:StorageType { - function sload(ptr:word) -> uint { - return Typedef.abs(sload_(ptr)):uint; // type annotation needed due to a typechecker bug - } - function store(ptr:word, value:uint) -> () { - return sstore_(ptr, Typedef.rep(value)); - } -} - -forall a . a : StorageType => instance storageRef(a):Assign(a) { - function assign(l:storageRef(a), y:a) -> () { - StorageType.store(Typedef.rep(l), y); - } -} - - - -data MemberAccessProxy(a, field, offset) = MemberAccessProxy(a, field); -forall a field offset . -function memberAccessD1(x:MemberAccessProxy(a, field, offset)) -> a { - match x { - | MemberAccessProxy(y,z) => return y; - } -} - -forall self memberRefType . -class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -forall self memberValueType . -class self:RValueMemberAccess(memberValueType) { - function memberAccess(x:self) -> memberValueType; -} - -forall self fieldType offsetType . -class self:CStructField(fieldType, offsetType) {} - -data StructField(structType, fieldSelector) = StructField(structType); - -forall structType fieldSelector fieldType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , offsetType:StorageSize - => instance MemberAccessProxy(storage(structType), fieldSelector, offsetType):LValueMemberAccess(storageRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(storage(structType), fieldSelector, offsetType)) -> storageRef(fieldType) { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = StorageSize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return storageRef(ptr); - } -} - -instance ():StorageSize { - function size(x:Proxy(())) -> word { - return 0; - } -} - -instance word:StorageSize { - function size(x:Proxy(word)) -> word { - return 1; - } -} - -instance uint:StorageSize { - function size(x:Proxy(uint)) -> word { - return 1; - } -} - - -/* -// fails Patterson cond -forall a b . a:Typedef(b), b:StorageSize -=> instance a:StorageSize { - function size(x:Proxy(a)) -> word { - return StorageSize.size(Proxy(b)); - } -} -*/ - -forall a b . a:StorageSize, b:StorageSize => instance (a,b):StorageSize { - function size(x:Proxy((a,b))) -> word { - let a_sz:word = StorageSize.size(Proxy:Proxy(a)); - let b_sz:word = StorageSize.size(Proxy:Proxy(b)); - assembly { - a_sz := add(a_sz, b_sz) - } - return a_sz; - } -} - -pragma no-patterson-condition RValueMemberAccess; // this is due to ContractStorage(cxt); probably not needed once we have local instances -pragma no-coverage-condition LValueMemberAccess, RValueMemberAccess; - -forall cxt fieldSelector fieldType offsetType - . StructField(ContractStorage(cxt), fieldSelector):CStructField(fieldType, offsetType) - , offsetType:StorageSize - => instance MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType):LValueMemberAccess(storageRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType)) -> storageRef(fieldType) { - let ptr:word = 0x100; // forge uses at least 1 storage slot - let offsetSize:word = StorageSize.size(Proxy:Proxy(offsetType)); - - assembly { - ptr := add(ptr, offsetSize) - } - return storageRef(ptr); // contract storage starts at 0 - } -} - -forall cxt fieldSelector fieldType offsetType - . StructField(ContractStorage(cxt), fieldSelector):CStructField(fieldType, offsetType) - , fieldType:StorageType - , offsetType:StorageSize - => instance MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType):RValueMemberAccess(fieldType) { - function memberAccess(x:MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType)) -> fieldType { - let ptr:word = 0x100; - let offsetSize:word = StorageSize.size(Proxy:Proxy(offsetType)); - return StorageType.sload(add(ptr, offsetSize)):fieldType; - } -} - -forall a b. a:RValueMemberAccess(b) => -function rval(x:a) -> b { - return RValueMemberAccess.memberAccess(x); -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/051expreturn.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/051expreturn.sol new file mode 100644 index 00000000..37be4543 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/051expreturn.sol @@ -0,0 +1,66 @@ +enum Bool { False, True } +enum W { W(Word) } +enum U { U } + +// empty class needed since forall expects a nonempty context +trait Top {} +impl Top {} + +/* For experiments, special handling when emitting code */ +// this does not work, typechecker forces a ~ b +// forall a, b.(a:Top, b:Top) => function ereturn(x:a) -> b { let res: b; return res; } +// we might have +// forall a.(a:Top) => function ereturn(x:a) -> a +// or + +function ereturn(x: a) returns (Unit) where a: Top { let res: Unit; return res; } +// and then cast it to any type using unsafeCast + +/* simulate match expression + x = match { | Bool.False => return 77; | Bool.True => W(22) } +*/ +function elimBool1(b: Bool) returns (Word) { + let x : W; + x = W(1); + match (b) { +// this works + // | Bool.False => x = unsafeCast(ereturn(77)); + // but this does not - unknown intermediate type + // | Bool.False => x = unsafeCast(unsafeCast(ereturn(77))); + // what about "return(return 77)"? + // this works +case Bool.False { +x = unsafeCast(ereturn(ereturn(77))); + // but this does not + // | Bool.False => x = unsafeCast(ereturn(unsafeCast(ereturn(77)))); +} +case Bool.True { +x = W(22); +} +} + + match (x) { +case W(y) { +return y; +} +} + +} + +// "semicolon" +function semi(x: a) returns (U) where a: Top { return U;} + +function unsafeCast(x: a) returns (b) where a: Top, b: Top { + let res: b; return res; +} + + +contract ExpReturn { + + + + function main() public returns (Word) { + return elimBool1(Bool.False); + // return elimBool1(Bool.False); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/051expreturn.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/051expreturn.solc deleted file mode 100644 index 33b372b3..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/051expreturn.solc +++ /dev/null @@ -1,60 +0,0 @@ -data Bool = False | True; -data W = W(Word); -data U = U; - -// empty class needed since forall expects a nonempty context -class a :Top {} -instance a:Top {} - -/* For experiments, special handling when emitting code */ -// this does not work, typechecker forces a ~ b -// forall a, b.(a:Top, b:Top) => function ereturn(x:a) -> b { let res: b; return res; } -// we might have -// forall a.(a:Top) => function ereturn(x:a) -> a -// or - -forall a:Top . function ereturn(x:a) -> Unit { let res: Unit; return res; } -// and then cast it to any type using unsafeCast - -/* simulate match expression - x = match { | Bool.False => return 77; | Bool.True => W(22) } -*/ -function elimBool1(b:Bool) -> Word { - let x : W; - x = W(1); - match b { - // this works - // | Bool.False => x = unsafeCast(ereturn(77)); - // but this does not - unknown intermediate type - // | Bool.False => x = unsafeCast(unsafeCast(ereturn(77))); - // what about "return(return 77)"? - // this works - | Bool.False => x = unsafeCast(ereturn(ereturn(77))); - // but this does not - // | Bool.False => x = unsafeCast(ereturn(unsafeCast(ereturn(77)))); - | Bool.True => x = W(22); - }; - - match x { - | W(y) => return y; - }; - -} - -// "semicolon" -forall a:Top . function semi(x:a) -> U { return U;} - -forall a:Top, b:Top . function unsafeCast(x:a) -> b { - let res: b; return res; -} - - -contract ExpReturn { - - - - public function main() -> Word { - return elimBool1(Bool.False); - // return elimBool1(Bool.False); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/052return.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/052return.sol new file mode 100644 index 00000000..f5cc08c4 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/052return.sol @@ -0,0 +1,62 @@ +enum Bool { False, True } +enum W { W(word) } +enum U { U } + + +/* For experiments, special handling when emitting code */ +// this does not work, typechecker forces a ~ b +// function ereturn(x:a) -> b { let res: b; return res; } +// we might have +// function ereturn(x:a) -> a +// or + +function ereturn(x: a) returns (unit) { let res: unit; return res; } +// and then cast it to any type using unsafeCast + +/* simulate match expression + x = match { | Bool.False => return 77; | Bool.True => W(22) } +*/ +function elimBool1(b: Bool) returns (word) { + let x : W; + x = W(1); + match (b) { +// this works +case Bool.False { +x = unsafeCast(ereturn(77)); + // but this does not - unknown intermediate type + // | Bool.False => x = unsafeCast(unsafeCast(ereturn(77))); + // what about "return(return 77)"? + // this does not work + // | Bool.False => x = ereturn(ereturn(77)); + // this works + // | Bool.False => x = unsafeCast(ereturn(ereturn(77))); + // this does not work (monomorphisation fails): + // | Bool.False => x = unsafeCast(ereturn(unsafeCast(ereturn(77)))); +} +case Bool.True { +x = W(22); +} +} + + match (x) { +case W(y) { +return y; +} +} + +} + +// "semicolon" +function semi(x: a) returns (U) { return U;} + +function unsafeCast(x: a) returns (b) { +let res: b; return res; +} + + +contract ExpReturn { + function main() public returns (word) { + return elimBool1(Bool.False); + // return elimBool1(Bool.True); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/052return.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/052return.solc deleted file mode 100644 index 620987e9..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/052return.solc +++ /dev/null @@ -1,57 +0,0 @@ -data Bool = False | True; -data W = W(word); -data U = U; - - -/* For experiments, special handling when emitting code */ -// this does not work, typechecker forces a ~ b -// function ereturn(x:a) -> b { let res: b; return res; } -// we might have -// function ereturn(x:a) -> a -// or - -function ereturn(x:a) -> unit { let res: unit; return res; } -// and then cast it to any type using unsafeCast - -/* simulate match expression - x = match { | Bool.False => return 77; | Bool.True => W(22) } -*/ -function elimBool1(b:Bool) -> word { - let x : W; - x = W(1); - match b { - // this works - | Bool.False => x = unsafeCast(ereturn(77)); - // but this does not - unknown intermediate type - // | Bool.False => x = unsafeCast(unsafeCast(ereturn(77))); - // what about "return(return 77)"? - // this does not work - // | Bool.False => x = ereturn(ereturn(77)); - // this works - // | Bool.False => x = unsafeCast(ereturn(ereturn(77))); - // this does not work (monomorphisation fails): - // | Bool.False => x = unsafeCast(ereturn(unsafeCast(ereturn(77)))); - - | Bool.True => x = W(22); - }; - - match x { - | W(y) => return y; - }; - -} - -// "semicolon" -function semi(x:a) -> U { return U;} - -function unsafeCast(x:a) -> b { -let res: b; return res; -} - - -contract ExpReturn { - public function main() -> word { - return elimBool1(Bool.False); - // return elimBool1(Bool.True); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/053return.sol b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/053return.sol new file mode 100644 index 00000000..abab6d7e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/053return.sol @@ -0,0 +1,41 @@ +enum Bool { False, True } +enum W { W(word) } + + +/* For experiments, special handling when emitting code */ +function ereturn(x: a) returns (b) { let res: b; return res; } + +/* simulate match expression + x = match { | Bool.False => return 77; | Bool.True => W(22) } +*/ +function elimBool1(b: Bool) returns (word) { + let x : W; + x = W(1); + match (b) { +// this works +case Bool.False { +x = ereturn(77); + // what about "return(return 77)"? + // this does not work (monomorphisation fails) + // | Bool.False => x = ereturn(ereturn(77)); +} +case Bool.True { +x = W(22); +} +} + + match (x) { +case W(y) { +return y; +} +} + +} + + +contract ExpReturn { + function main() public returns (word) { + return elimBool1(Bool.False); + // return elimBool1(Bool.True); + } +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/053return.solc b/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/053return.solc deleted file mode 100644 index 29836b50..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/examples/spec/attic/053return.solc +++ /dev/null @@ -1,36 +0,0 @@ -data Bool = False | True; -data W = W(word); - - -/* For experiments, special handling when emitting code */ -function ereturn(x:a) -> b { let res: b; return res; } - -/* simulate match expression - x = match { | Bool.False => return 77; | Bool.True => W(22) } -*/ -function elimBool1(b:Bool) -> word { - let x : W; - x = W(1); - match b { - // this works - | Bool.False => x = ereturn(77); - // what about "return(return 77)"? - // this does not work (monomorphisation fails) - // | Bool.False => x = ereturn(ereturn(77)); - - | Bool.True => x = W(22); - }; - - match x { - | W(y) => return y; - }; - -} - - -contract ExpReturn { - public function main() -> word { - return elimBool1(Bool.False); - // return elimBool1(Bool.True); - } -} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/imports/select_alias_tail_fail.snap b/crates/parser/tests/fixtures/corpus/fail/test/imports/select_alias_tail_fail.snap deleted file mode 100644 index 34cd0b91..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/imports/select_alias_tail_fail.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/parser/tests/diagnostics.rs -expression: value -input_file: crates/parser/tests/fixtures/corpus/fail/test/imports/select_alias_tail_fail.solc ---- -error[SC0001]: parse error: unexpected `as` - --> /select_alias_tail_fail.solc:1:25 - | -1 | import selectlib.{keep} as keep_; - | ^^ unexpected token -2 | -3 | function main(x: word) -> word { - | - = note: expecting `;` - = note: while parsing import declaration diff --git a/crates/parser/tests/fixtures/corpus/fail/test/imports/select_alias_tail_fail.sol b/crates/parser/tests/fixtures/corpus/fail/test/imports/select_alias_tail_fail.sol new file mode 100644 index 00000000..59d06321 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/fail/test/imports/select_alias_tail_fail.sol @@ -0,0 +1,5 @@ +import {keep} from selectlib; + +function main(x: word) returns (word) { + return keep_(x); +} diff --git a/crates/parser/tests/fixtures/corpus/fail/test/imports/select_alias_tail_fail.solc b/crates/parser/tests/fixtures/corpus/fail/test/imports/select_alias_tail_fail.solc deleted file mode 100644 index ca1765fb..00000000 --- a/crates/parser/tests/fixtures/corpus/fail/test/imports/select_alias_tail_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import selectlib.{keep} as keep_; - -function main(x: word) -> word { - return keep_(x); -} diff --git a/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/duplicate-definition.sol b/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/duplicate-definition.sol new file mode 100644 index 00000000..ea0d4cc1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/duplicate-definition.sol @@ -0,0 +1,3 @@ +function foo() returns (word) { return 1; } +function foo() returns (word) { return 2; } +function main() returns (word) { return foo(); } diff --git a/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/duplicate-definition.solc b/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/duplicate-definition.solc deleted file mode 100644 index 11e1185e..00000000 --- a/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/duplicate-definition.solc +++ /dev/null @@ -1,3 +0,0 @@ -function foo() -> word { return 1; } -function foo() -> word { return 2; } -function main() -> word { return foo(); } diff --git a/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/missing-signature.solc b/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/missing-signature.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/missing-signature.solc rename to crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/missing-signature.sol diff --git a/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/not-polymorphic-enough.sol b/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/not-polymorphic-enough.sol new file mode 100644 index 00000000..9411dc19 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/not-polymorphic-enough.sol @@ -0,0 +1,5 @@ +function fromWord(x: word) returns (a) { + let result; + assembly { result := x } + return result; +} diff --git a/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/not-polymorphic-enough.solc b/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/not-polymorphic-enough.solc deleted file mode 100644 index 7400c26c..00000000 --- a/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/not-polymorphic-enough.solc +++ /dev/null @@ -1,5 +0,0 @@ -forall a . function fromWord(x : word) -> a { - let result; - assembly { result := x } - return result; -} diff --git a/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/type-mismatch.sol b/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/type-mismatch.sol new file mode 100644 index 00000000..2ca138c6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/type-mismatch.sol @@ -0,0 +1 @@ +function main() returns (word) { return true; } diff --git a/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/type-mismatch.solc b/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/type-mismatch.solc deleted file mode 100644 index 64d7ed2c..00000000 --- a/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/type-mismatch.solc +++ /dev/null @@ -1 +0,0 @@ -function main() -> word { return true; } diff --git a/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/undefined-name.sol b/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/undefined-name.sol new file mode 100644 index 00000000..db2d49cd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/undefined-name.sol @@ -0,0 +1 @@ +function main() returns (word) { return missing; } diff --git a/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/undefined-name.solc b/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/undefined-name.solc deleted file mode 100644 index 6aae2ad1..00000000 --- a/crates/parser/tests/fixtures/corpus/known-diagnostic-gaps/test/diagnostics/undefined-name.solc +++ /dev/null @@ -1 +0,0 @@ -function main() -> word { return missing; } diff --git a/crates/parser/tests/fixtures/corpus/ok/std/ABIGeneric.sol b/crates/parser/tests/fixtures/corpus/ok/std/ABIGeneric.sol new file mode 100644 index 00000000..c4406998 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/std/ABIGeneric.sol @@ -0,0 +1,198 @@ +pragma no-patterson-condition ABIAttribs, ABIEncode, ABIDecode; +pragma no-bounded-variable-condition ABIAttribs, ABIEncode, ABIDecode; +pragma no-coverage-condition ABIDecode; + +export { + ABIDeriving, + encode, + decode +}; + +import * from std; +import {mstore} from std.opcodes; +import * from std.Generic; + +// Marker trait. Importing this module brings ABIDeriving into scope, which is +// the signal DeriveGeneric looks for to auto-derive a per-type ABIDecode +// impl for local data types. ABIAttribs / ABIEncode are provided generically +// via the default Generic bridges below, but ABIDecode cannot be a default +// impl (its decode returns the head variable `a` via Generic.to, a +// result-position type variable the specializer cannot monomorphize), so a +// concrete per-type impl is emitted instead — exactly as for storage. +trait ABIDeriving {} + +// ─── ABIAttribs for the primitive sum(f, g) type ───────────────────────── +// headSize = 32 (tag word) + max(headSize(f), headSize(g)) + +impl ABIAttribs> where f: ABIAttribs, g: ABIAttribs { + // Head footprint. A *dynamic* sum occupies a single offset word in the head + // (its tag + branch payload live in the tail), exactly like any other + // dynamic type. Only a fully *static* sum is laid out inline as + // tag + widest branch; there both branches are static, so their headSize is + // their full size and 32 + max(...) is the correct inline footprint. + function headSize(ty: Proxy>) returns (word) { + let pf : Proxy; + let pg : Proxy; + match (and(ABIAttribs.isStatic(pf), ABIAttribs.isStatic(pg))) { +case false { +return 32; +} +case true { +return 32 + maxWord(ABIAttribs.headSize(pf), ABIAttribs.headSize(pg)); +} +} + } + function isStatic(ty: Proxy>) returns (bool) { + let pf : Proxy; + let pg : Proxy; + return and(ABIAttribs.isStatic(pf), ABIAttribs.isStatic(pg)); + } +} + +// ─── ABIEncode for sum(f, g) ───────────────────────────────────────────── +// This is the exact mirror of `ABIDecoder, reader>: ABIDecode` below. +// +// A STATIC sum is laid out inline in the head: +// [offset + 0 .. offset + 31] : tag word (0 = inl, 1 = inr) +// [offset + 32 .. ] : encoded branch payload +// +// A DYNAMIC sum (one whose branch carries a dynamic field) occupies a single +// offset word in the head, like any other dynamic ABI value; its tag + branch +// payload live in the tail: +// head: [offset .. offset + 31] : relative offset (tail - basePtr) to the body +// tail: [tag word][branch head ...][branch tail ...] +// The tail body is itself an inline [tag][branch] sum, so decode follows the +// offset and reads it exactly as it reads a static sum. + +impl ABIEncode> where f: ABIAttribs, f: ABIEncode, g: ABIAttribs, g: ABIEncode { + function encodeInto(x: sum, basePtr: word, offset: word, tail: word) returns (word) { + let prx : Proxy>; + match (ABIAttribs.isStatic(prx)) { +// STATIC sum: inline tag at basePtr+offset, branch at offset + 32. +case true { +match (x) { +case inl(v) { +mstore(basePtr + offset, 0); + return ABIEncode.encodeInto(v, basePtr, offset + 32, tail); +} +case inr(v) { +mstore(basePtr + offset, 1); + return ABIEncode.encodeInto(v, basePtr, offset + 32, tail); +} +} + // DYNAMIC sum: head slot holds a relative offset to the sum body, which + // is laid out inline in the tail. headSize(prx) is 32 here (the offset + // word), so the inline head footprint is computed from the branches: + // 32 (tag) + max(headSize(f), headSize(g)). +} +case false { +let pf : Proxy; + let pg : Proxy; + mstore(basePtr + offset, tail - basePtr); + let newBase = tail; + let innerHead = 32 + maxWord(ABIAttribs.headSize(pf), ABIAttribs.headSize(pg)); + let newTail = tail + innerHead; + match (x) { +case inl(v) { +mstore(newBase, 0); + return ABIEncode.encodeInto(v, newBase, 32, newTail); +} +case inr(v) { +mstore(newBase, 1); + return ABIEncode.encodeInto(v, newBase, 32, newTail); +} +} +} +} + } +} + +// ─── ABIDecode for sum(f, g) ───────────────────────────────────────────── +// A STATIC sum is laid out inline: read the tag word at headOffset, dispatch to +// the branch decoder at headOffset + 32. +// +// A DYNAMIC sum (one whose branch carries a dynamic field) is, like any dynamic +// ABI value, referenced by a 32-byte offset: read that offset at headOffset, +// rebase a decoder onto the sum's start, then read [tag][branch] inline there. +// Following the offset here (rather than at the call site) is what lets a +// dynamic sum be decoded uniformly wherever a dynamic value can appear — as a +// field, or as a `T[]` element alongside a bare `bytes`/`string` leaf, which +// follows its offset the same way. + +impl ABIDecode, reader>, sum> where reader: WordReader, f: ABIAttribs, g: ABIAttribs, ABIDecoder: ABIDecode, ABIDecoder: ABIDecode { + function decode(ptr: ABIDecoder, reader>, headOffset: word) returns (sum) { + match (ptr) { +case ABIDecoder(rdr) { +let prx : Proxy>; + // Byte offset (relative to rdr) of this sum's own start. A static sum + // is inline at headOffset; a dynamic sum's head slot holds a 32-byte + // offset to it, which we follow. We then rebase a decoder onto the + // sum start and read [tag][branch] inline — so the tag match (and its + // inl/inr) has a single, uniform shape regardless of static/dynamic. + let sumStartOff : word; + match (ABIAttribs.isStatic(prx)) { +case true { +sumStartOff = headOffset; +} +case false { +sumStartOff = WordReader.read(WordReader.advance(rdr, headOffset)); +} +} + let sumRdr = WordReader.advance(rdr, sumStartOff); + let tag = WordReader.read(sumRdr); + match (tag) { +case 0 { +let dec_f : ABIDecoder = ABIDecoder(sumRdr); + return inl(ABIDecode.decode(dec_f, 32)); +} +default { +let dec_g : ABIDecoder = ABIDecoder(sumRdr); + return inr(ABIDecode.decode(dec_g, 32)); +} +} +} +} + } +} + +// ─── Default bridges: ABIAttribs and ABIEncode via Generic ─────────────── +// Any type `a` with `a: Generic` inherits its ABI layout from `rep`. + +default impl ABIAttribs where a: Generic, rep: ABIAttribs { + function headSize(ty: Proxy) returns (word) { + let prx : Proxy; + return ABIAttribs.headSize(prx); + } + function isStatic(ty: Proxy) returns (bool) { + let prx : Proxy; + return ABIAttribs.isStatic(prx); + } +} + +default impl ABIEncode where a: Generic, rep: ABIAttribs, rep: ABIEncode { + function encodeInto(x: a, basePtr: word, offset: word, tail: word) returns (word) { + return ABIEncode.encodeInto(Generic.from(x), basePtr, offset, tail); + } +} + +// ─── Top-level generic encode function ─────────────────────────────────── +// Serialises any `a` that has a `Generic` impl. +// Only the Generic impl is required — ABIEncode is resolved via the bridge. + +function encode(x: a, basePtr: word, offset: word, tail: word) returns (word) where a: Generic, rep: ABIAttribs, rep: ABIEncode { + let xrep : rep = Generic.from(x); + return ABIEncode.encodeInto(xrep, basePtr, offset, tail); +} + +// ─── Top-level generic decode function ─────────────────────────────────── +// Deserialises any `a` that has a `Generic` impl. +// Only the Generic impl is required — ABIDecode is resolved via the bridge. + +function decode(ptr: ABIDecoder, headOffset: word) returns (a) where a: Generic, reader: WordReader, ABIDecoder: ABIDecode { + match (ptr) { +case ABIDecoder(rdr) { +let rep_ptr : ABIDecoder = ABIDecoder(rdr); + return Generic.to(ABIDecode.decode(rep_ptr, headOffset)); +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/ABIGeneric.solc b/crates/parser/tests/fixtures/corpus/ok/std/ABIGeneric.solc deleted file mode 100644 index 84349cd5..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/std/ABIGeneric.solc +++ /dev/null @@ -1,195 +0,0 @@ -pragma no-patterson-condition ABIAttribs, ABIEncode, ABIDecode; -pragma no-bounded-variable-condition ABIAttribs, ABIEncode, ABIDecode; -pragma no-coverage-condition ABIDecode; - -export { - ABIDeriving, - encode, - decode -}; - -import std.{*}; -import std.opcodes.{mstore}; -import std.Generic.{*}; - -// Marker class. Importing this module brings ABIDeriving into scope, which is -// the signal DeriveGeneric looks for to auto-derive a per-type ABIDecode -// instance for local data types. ABIAttribs / ABIEncode are provided generically -// via the default Generic bridges below, but ABIDecode cannot be a default -// instance (its decode returns the head variable `a` via Generic.to, a -// result-position type variable the specializer cannot monomorphize), so a -// concrete per-type instance is emitted instead — exactly as for storage. -forall self. class self : ABIDeriving {} - -// ─── ABIAttribs for the primitive sum(f, g) type ───────────────────────── -// headSize = 32 (tag word) + max(headSize(f), headSize(g)) - -forall f g . f:ABIAttribs, g:ABIAttribs => -instance sum(f, g) : ABIAttribs { - // Head footprint. A *dynamic* sum occupies a single offset word in the head - // (its tag + branch payload live in the tail), exactly like any other - // dynamic type. Only a fully *static* sum is laid out inline as - // tag + widest branch; there both branches are static, so their headSize is - // their full size and 32 + max(...) is the correct inline footprint. - function headSize(ty : Proxy(sum(f, g))) -> word { - let pf : Proxy(f); - let pg : Proxy(g); - match and(ABIAttribs.isStatic(pf), ABIAttribs.isStatic(pg)) { - | false => return 32; - | true => return 32 + maxWord(ABIAttribs.headSize(pf), ABIAttribs.headSize(pg)); - } - } - function isStatic(ty : Proxy(sum(f, g))) -> bool { - let pf : Proxy(f); - let pg : Proxy(g); - return and(ABIAttribs.isStatic(pf), ABIAttribs.isStatic(pg)); - } -} - -// ─── ABIEncode for sum(f, g) ───────────────────────────────────────────── -// This is the exact mirror of `ABIDecoder(sum(f, g), reader):ABIDecode` below. -// -// A STATIC sum is laid out inline in the head: -// [offset + 0 .. offset + 31] : tag word (0 = inl, 1 = inr) -// [offset + 32 .. ] : encoded branch payload -// -// A DYNAMIC sum (one whose branch carries a dynamic field) occupies a single -// offset word in the head, like any other dynamic ABI value; its tag + branch -// payload live in the tail: -// head: [offset .. offset + 31] : relative offset (tail - basePtr) to the body -// tail: [tag word][branch head ...][branch tail ...] -// The tail body is itself an inline [tag][branch] sum, so decode follows the -// offset and reads it exactly as it reads a static sum. - -forall f g . f:ABIAttribs, f:ABIEncode, g:ABIAttribs, g:ABIEncode => -instance sum(f, g) : ABIEncode { - function encodeInto(x : sum(f, g), basePtr : word, offset : word, tail : word) -> word { - let prx : Proxy(sum(f, g)); - match ABIAttribs.isStatic(prx) { - // STATIC sum: inline tag at basePtr+offset, branch at offset + 32. - | true => - match x { - | inl(v) => - mstore(basePtr + offset, 0); - return ABIEncode.encodeInto(v, basePtr, offset + 32, tail); - | inr(v) => - mstore(basePtr + offset, 1); - return ABIEncode.encodeInto(v, basePtr, offset + 32, tail); - } - // DYNAMIC sum: head slot holds a relative offset to the sum body, which - // is laid out inline in the tail. headSize(prx) is 32 here (the offset - // word), so the inline head footprint is computed from the branches: - // 32 (tag) + max(headSize(f), headSize(g)). - | false => - let pf : Proxy(f); - let pg : Proxy(g); - mstore(basePtr + offset, tail - basePtr); - let newBase = tail; - let innerHead = 32 + maxWord(ABIAttribs.headSize(pf), ABIAttribs.headSize(pg)); - let newTail = tail + innerHead; - match x { - | inl(v) => - mstore(newBase, 0); - return ABIEncode.encodeInto(v, newBase, 32, newTail); - | inr(v) => - mstore(newBase, 1); - return ABIEncode.encodeInto(v, newBase, 32, newTail); - } - } - } -} - -// ─── ABIDecode for sum(f, g) ───────────────────────────────────────────── -// A STATIC sum is laid out inline: read the tag word at headOffset, dispatch to -// the branch decoder at headOffset + 32. -// -// A DYNAMIC sum (one whose branch carries a dynamic field) is, like any dynamic -// ABI value, referenced by a 32-byte offset: read that offset at headOffset, -// rebase a decoder onto the sum's start, then read [tag][branch] inline there. -// Following the offset here (rather than at the call site) is what lets a -// dynamic sum be decoded uniformly wherever a dynamic value can appear — as a -// field, or as a `T[]` element alongside a bare `bytes`/`string` leaf, which -// follows its offset the same way. - -forall f g reader . - reader : WordReader, - f : ABIAttribs, - g : ABIAttribs, - ABIDecoder(f, reader) : ABIDecode(f), - ABIDecoder(g, reader) : ABIDecode(g) => -instance ABIDecoder(sum(f, g), reader) : ABIDecode(sum(f, g)) { - function decode(ptr : ABIDecoder(sum(f, g), reader), headOffset : word) -> sum(f, g) { - match ptr { - | ABIDecoder(rdr) => - let prx : Proxy(sum(f, g)); - // Byte offset (relative to rdr) of this sum's own start. A static sum - // is inline at headOffset; a dynamic sum's head slot holds a 32-byte - // offset to it, which we follow. We then rebase a decoder onto the - // sum start and read [tag][branch] inline — so the tag match (and its - // inl/inr) has a single, uniform shape regardless of static/dynamic. - let sumStartOff : word; - match ABIAttribs.isStatic(prx) { - | true => sumStartOff = headOffset; - | false => sumStartOff = WordReader.read(WordReader.advance(rdr, headOffset)); - } - let sumRdr = WordReader.advance(rdr, sumStartOff); - let tag = WordReader.read(sumRdr); - match tag { - | 0 => - let dec_f : ABIDecoder(f, reader) = ABIDecoder(sumRdr); - return inl(ABIDecode.decode(dec_f, 32)); - | _ => - let dec_g : ABIDecoder(g, reader) = ABIDecoder(sumRdr); - return inr(ABIDecode.decode(dec_g, 32)); - } - } - } -} - -// ─── Default bridges: ABIAttribs and ABIEncode via Generic ─────────────── -// Any type 'a' with Generic(rep) inherits its ABI layout from rep. - -forall a rep . a:Generic(rep), rep:ABIAttribs => -default instance a : ABIAttribs { - function headSize(ty : Proxy(a)) -> word { - let prx : Proxy(rep); - return ABIAttribs.headSize(prx); - } - function isStatic(ty : Proxy(a)) -> bool { - let prx : Proxy(rep); - return ABIAttribs.isStatic(prx); - } -} - -forall a rep . a:Generic(rep), rep:ABIAttribs, rep:ABIEncode => -default instance a : ABIEncode { - function encodeInto(x : a, basePtr : word, offset : word, tail : word) -> word { - return ABIEncode.encodeInto(Generic.from(x), basePtr, offset, tail); - } -} - -// ─── Top-level generic encode function ─────────────────────────────────── -// Serialises any 'a' that has a Generic(rep) instance. -// Only the Generic instance is required — ABIEncode is resolved via the bridge. - -forall a rep . a:Generic(rep), rep:ABIAttribs, rep:ABIEncode => -function encode(x : a, basePtr : word, offset : word, tail : word) -> word { - let xrep : rep = Generic.from(x); - return ABIEncode.encodeInto(xrep, basePtr, offset, tail); -} - -// ─── Top-level generic decode function ─────────────────────────────────── -// Deserialises any 'a' that has a Generic(rep) instance. -// Only the Generic instance is required — ABIDecode is resolved via the bridge. - -forall a rep reader . - a : Generic(rep), - reader : WordReader, - ABIDecoder(rep, reader) : ABIDecode(rep) => -function decode(ptr : ABIDecoder(a, reader), headOffset : word) -> a { - match ptr { - | ABIDecoder(rdr) => - let rep_ptr : ABIDecoder(rep, reader) = ABIDecoder(rdr); - return Generic.to(ABIDecode.decode(rep_ptr, headOffset)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/Generic.sol b/crates/parser/tests/fixtures/corpus/ok/std/Generic.sol new file mode 100644 index 00000000..46a56996 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/std/Generic.sol @@ -0,0 +1,16 @@ +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +export { Generic }; + +import * from std; + +// MPTC: isomorphism between a user type and its SOP representation. +// The representation 'rep' is built from primitive Solcore types: +// sum(f, g) with constructors inl / inr +// (f, g) pair (product) +// () unit +trait Generic { + function from(x: a) returns (rep) ; + function to(x: rep) returns (a) ; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/Generic.solc b/crates/parser/tests/fixtures/corpus/ok/std/Generic.solc deleted file mode 100644 index ba30049d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/std/Generic.solc +++ /dev/null @@ -1,17 +0,0 @@ -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -export { Generic }; - -import std.{*}; - -// MPTC: isomorphism between a user type and its SOP representation. -// The representation 'rep' is built from primitive Solcore types: -// sum(f, g) with constructors inl / inr -// (f, g) pair (product) -// () unit -forall a rep. -class a : Generic(rep) { - function from(x : a) -> rep; - function to(x : rep) -> a; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/StorageGeneric.sol b/crates/parser/tests/fixtures/corpus/ok/std/StorageGeneric.sol new file mode 100644 index 00000000..631b665f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/std/StorageGeneric.sol @@ -0,0 +1,258 @@ +pragma no-patterson-condition StorageType; +pragma no-bounded-variable-condition StorageType; + +export { + StorageDeriving, + loadGeneric, + storeGeneric +}; + +import * from std; +import {sload, sstore} from std.opcodes; +import * from std.Generic; + +// Marker trait. Importing this module brings StorageDeriving into scope, which +// is the signal DeriveGeneric looks for to auto-derive StorageSize / CanStore +// impls for local data types (alongside their Generic impl). It carries +// no methods — its mere visibility enables storage derivation. +trait StorageDeriving {} + +// ─── Storage layout for algebraic data types ───────────────────────────── +// +// This module is the storage analogue of std.ABIGeneric: it teaches the +// StorageSize / StorageType / CanStore traits how to deal with the +// primitive SOP types that `Generic` maps user data types onto +// sum(f, g) with constructors inl / inr (choice / tagged union) +// (f, g) pair (product) +// () unit +// and then bridges every type with a `Generic` impl to those +// layouts. `Generic` impls are auto-derived for local data types, so +// no per-type boilerplate is needed at the use site. + +// ─── StorageSize for the primitive sum(f, g) type ──────────────────────── +// A tagged union occupies one slot for the tag plus enough slots for the +// largest branch: size = 1 + max(size(f), size(g)). +// (StorageSize for () and (a, b) is already provided by std.) + +impl StorageSize> where f: StorageSize, g: StorageSize { + function size(x: Proxy>) returns (word) { + let f_sz : word = StorageSize.size(@f); + let g_sz : word = StorageSize.size(@g); + return 1 + maxWord(f_sz, g_sz); + } +} + +// ─── StorageType for () ────────────────────────────────────────────────── +// The unit type occupies no slots, so load/store are no-ops. + +impl StorageType<()> { + function load(ptr: word) { + return (); + } + function store(ptr: word, value: ()) { + return (); + } +} + +// ─── StorageType for the primitive product (a, b) ──────────────────────── +// Layout: [ptr .. ptr + size(a) - 1] : a +// [ptr + size(a) .. ] : b + +impl StorageType<(a, b)> where a: StorageType, a: StorageSize, b: StorageType { + function load(ptr: word) returns (a, b) { + let a_sz : word = StorageSize.size(@a); + let x : a = StorageType.load(ptr); + let y : b = StorageType.load(ptr + a_sz); + return (x, y); + } + function store(ptr: word, value: (a, b)) { + match (value) { +case (x, y) { +let a_sz : word = StorageSize.size(@a); + StorageType.store(ptr, x); + StorageType.store(ptr + a_sz, y); +} +} + } +} + +// ─── StorageType for the primitive sum(f, g) ───────────────────────────── +// Slot layout (static sums): +// [ptr] : tag word (0 = inl, 1 = inr) +// [ptr + 1 .. ] : encoded branch payload + +impl StorageType> where f: StorageType, g: StorageType { + function load(ptr: word) returns (sum) { + let tag : word = sload(ptr); + match (tag) { +case 0 { +let v : f = StorageType.load(ptr + 1); + return inl(v); +} +default { +let v : g = StorageType.load(ptr + 1); + return inr(v); +} +} + } + function store(ptr: word, value: sum) { + match (value) { +case inl(v) { +sstore(ptr, 0); + StorageType.store(ptr + 1, v); +} +case inr(v) { +sstore(ptr, 1); + StorageType.store(ptr + 1, v); +} +} + } +} + +// ─── Storage layout via CanStore ───────────────────────────────────────── +// +// The structural impls above teach StorageType the fixed-slot encoding of +// the SOP primitives. But StorageType can only describe word-packed types: a +// dynamically-sized field such as memory has a StorageSize (one slot, +// Solidity-style) and a CanStore impl (`storage: CanStore>`) +// but NO StorageType impl. Routing an ADT's storage through StorageType +// therefore rejects any data type carrying such a field, even though the field +// is perfectly storable. +// +// So we give CanStore the same structural treatment, decomposing the SOP +// representation and storing each leaf through the leaf's OWN CanStore impl. +// Fixed leaves resolve to storage/storage/… (which delegate to +// StorageType); dynamic leaves resolve to storage/storage. Each +// field occupies StorageSize-many slots, so offsets are computed exactly as in +// the StorageType layout. The slot handle for a value of type `t` is uniformly +// `storage`, which is why the dynamic leaves below are mirrored at that +// handle. + +// The unit type occupies no slots. +impl CanStore, ()> { + function store(r: storage<()>, v: ()) { + return (); + } + function load(r: storage<()>) { + return (); + } +} + +// Product: store `a` at the base slot, `b` size(a) slots later. +impl CanStore, (a, b)> where storage: CanStore, a: StorageSize, storage: CanStore { + function store(r: storage<(a, b)>, v: (a, b)) { + match (v) { +case (x, y) { +let base : word = Typedef.rep(r); + let a_sz : word = StorageSize.size(@a); + let xSlot : storage = storage(base); + let ySlot : storage = storage(base + a_sz); + CanStore.store(xSlot, x); + CanStore.store(ySlot, y); +} +} + } + function load(r: storage<(a, b)>) returns (a, b) { + let base : word = Typedef.rep(r); + let a_sz : word = StorageSize.size(@a); + let xSlot : storage = storage(base); + let ySlot : storage = storage(base + a_sz); + let x : a = CanStore.load(xSlot); + let y : b = CanStore.load(ySlot); + return (x, y); + } +} + +// Tagged union: slot 0 holds the tag, the branch payload follows. +impl CanStore>, sum> where storage: CanStore, storage: CanStore { + function store(r: storage>, v: sum) { + let base : word = Typedef.rep(r); + match (v) { +case inl(x) { +sstore(base, 0); + let slot : storage = storage(base + 1); + CanStore.store(slot, x); +} +case inr(y) { +sstore(base, 1); + let slot : storage = storage(base + 1); + CanStore.store(slot, y); +} +} + } + function load(r: storage>) returns (sum) { + let base : word = Typedef.rep(r); + let tag : word = sload(base); + // NOTE: the loaded payload is inlined directly into inl(...) / inr(...) + // rather than bound to a `let x : f` / `let y : g` first. Binding the + // payload to an intermediate of the branch type (f or g) makes the + // compiler infer the *branch* type for the inl/inr application instead + // of the full sum(f, g), so it emits e.g. `inr(y)` and Yul codegen + // rejects it (sum nesting off by one). Inlining matches the working + // ABIGeneric.decode pattern, so inl/inr pick up the full sum(f, g). + match (tag) { +case 0 { +let slot : storage = storage(base + 1); +return inl(CanStore.load(slot)); +} +default { +let slot : storage = storage(base + 1); +return inr(CanStore.load(slot)); +} +} + } +} + +// Dynamic leaves at the uniform storage handle. std provides the storage +// / storage impls (data lives at keccak(slot)); these mirror them at +// the storage> / storage> handle the structural +// decomposition asks for, so a memory field inside an ADT is storable. +impl CanStore>, memory> { + function store(r: storage>, v: memory) { + let slot : storage = storage(Typedef.rep(r)); + CanStore.store(slot, v); + } + function load(r: storage>) returns (memory) { + let slot : storage = storage(Typedef.rep(r)); + return CanStore.load(slot); + } +} + +impl CanStore>, memory> { + function store(r: storage>, v: memory) { + let slot : storage = storage(Typedef.rep(r)); + CanStore.store(slot, v); + } + function load(r: storage>) returns (memory) { + let slot : storage = storage(Typedef.rep(r)); + return CanStore.load(slot); + } +} + +// StorageType / CanStore for an ADT are NOT provided here as blanket bridges. +// +// A `default impl StorageType` would have its `load` return the head +// variable `a` via Generic.to — but the specializer cannot monomorphize a +// result-position type variable of a default impl (it is not pinned by the +// arguments), so loads panic. Likewise a tyvar-headed `default a:CanStore(b)` +// is non-functional (accepts any storable b), so contract field access cannot +// infer the stored type from the slot type. +// +// Instead, DeriveGeneric emits a concrete, per-type +// `storage: CanStore` impl (see Solcore.Desugarer.DeriveGeneric) where the +// data type is fixed in the impl head; it delegates to the structural CanStore impls above +// via the type's Generic representation. StorageSize is likewise derived +// per-type for the field layout. + +// ─── Top-level helpers ─────────────────────────────────────────────────── +// Convenience wrappers mirroring std.ABIGeneric's encode / decode: persist or +// read back any `a` that has a `Generic` impl at a raw storage slot. + +function storeGeneric(slot: word, value: a) where a: Generic, rep: StorageType { + StorageType.store(slot, Generic.from(value)); +} + +function loadGeneric(slot: word) returns (a) where a: Generic, rep: StorageType { + let r : rep = StorageType.load(slot); + return Generic.to(r); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/StorageGeneric.solc b/crates/parser/tests/fixtures/corpus/ok/std/StorageGeneric.solc deleted file mode 100644 index 38e855c5..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/std/StorageGeneric.solc +++ /dev/null @@ -1,243 +0,0 @@ -pragma no-patterson-condition StorageType; -pragma no-bounded-variable-condition StorageType; - -export { - StorageDeriving, - loadGeneric, - storeGeneric -}; - -import std.{*}; -import std.opcodes.{sload, sstore}; -import std.Generic.{*}; - -// Marker class. Importing this module brings StorageDeriving into scope, which -// is the signal DeriveGeneric looks for to auto-derive StorageSize / CanStore -// instances for local data types (alongside their Generic instance). It carries -// no methods — its mere visibility enables storage derivation. -forall self. class self : StorageDeriving {} - -// ─── Storage layout for algebraic data types ───────────────────────────── -// -// This module is the storage analogue of std.ABIGeneric: it teaches the -// StorageSize / StorageType / CanStore classes how to deal with the -// primitive SOP types that `Generic` maps user data types onto -// sum(f, g) with constructors inl / inr (choice / tagged union) -// (f, g) pair (product) -// () unit -// and then bridges every type with a `Generic(rep)` instance to those -// layouts. `Generic` instances are auto-derived for local data types, so -// no per-type boilerplate is needed at the use site. - -// ─── StorageSize for the primitive sum(f, g) type ──────────────────────── -// A tagged union occupies one slot for the tag plus enough slots for the -// largest branch: size = 1 + max(size(f), size(g)). -// (StorageSize for () and (a, b) is already provided by std.) - -forall f g . f:StorageSize, g:StorageSize => -instance sum(f, g):StorageSize { - function size(x : Proxy(sum(f, g))) -> word { - let f_sz : word = StorageSize.size(Proxy : Proxy(f)); - let g_sz : word = StorageSize.size(Proxy : Proxy(g)); - return 1 + maxWord(f_sz, g_sz); - } -} - -// ─── StorageType for () ────────────────────────────────────────────────── -// The unit type occupies no slots, so load/store are no-ops. - -instance ():StorageType { - function load(ptr : word) -> () { - return (); - } - function store(ptr : word, value : ()) -> () { - return (); - } -} - -// ─── StorageType for the primitive product (a, b) ──────────────────────── -// Layout: [ptr .. ptr + size(a) - 1] : a -// [ptr + size(a) .. ] : b - -forall a b . a:StorageType, a:StorageSize, b:StorageType => -instance (a, b):StorageType { - function load(ptr : word) -> (a, b) { - let a_sz : word = StorageSize.size(Proxy : Proxy(a)); - let x : a = StorageType.load(ptr); - let y : b = StorageType.load(ptr + a_sz); - return (x, y); - } - function store(ptr : word, value : (a, b)) -> () { - match value { - | (x, y) => - let a_sz : word = StorageSize.size(Proxy : Proxy(a)); - StorageType.store(ptr, x); - StorageType.store(ptr + a_sz, y); - } - } -} - -// ─── StorageType for the primitive sum(f, g) ───────────────────────────── -// Slot layout (static sums): -// [ptr] : tag word (0 = inl, 1 = inr) -// [ptr + 1 .. ] : encoded branch payload - -forall f g . f:StorageType, g:StorageType => -instance sum(f, g):StorageType { - function load(ptr : word) -> sum(f, g) { - let tag : word = sload(ptr); - match tag { - | 0 => - let v : f = StorageType.load(ptr + 1); - return inl(v); - | _ => - let v : g = StorageType.load(ptr + 1); - return inr(v); - } - } - function store(ptr : word, value : sum(f, g)) -> () { - match value { - | inl(v) => - sstore(ptr, 0); - StorageType.store(ptr + 1, v); - | inr(v) => - sstore(ptr, 1); - StorageType.store(ptr + 1, v); - } - } -} - -// ─── Storage layout via CanStore ───────────────────────────────────────── -// -// The structural instances above teach StorageType the fixed-slot encoding of -// the SOP primitives. But StorageType can only describe word-packed types: a -// dynamically-sized field such as memory(bytes) has a StorageSize (one slot, -// Solidity-style) and a CanStore instance (storage(bytes):CanStore(memory(bytes))) -// but NO StorageType instance. Routing an ADT's storage through StorageType -// therefore rejects any data type carrying such a field, even though the field -// is perfectly storable. -// -// So we give CanStore the same structural treatment, decomposing the SOP -// representation and storing each leaf through the leaf's OWN CanStore instance. -// Fixed leaves resolve to storage(word)/storage(uint256)/… (which delegate to -// StorageType); dynamic leaves resolve to storage(bytes)/storage(string). Each -// field occupies StorageSize-many slots, so offsets are computed exactly as in -// the StorageType layout. The slot handle for a value of type `t` is uniformly -// `storage(t)`, which is why the dynamic leaves below are mirrored at that -// handle. - -// The unit type occupies no slots. -instance storage(()) : CanStore(()) { - function store(r : storage(()), v : ()) -> () { - return (); - } - function load(r : storage(())) -> () { - return (); - } -} - -// Product: store `a` at the base slot, `b` size(a) slots later. -forall a b . storage(a):CanStore(a), a:StorageSize, storage(b):CanStore(b) => -instance storage((a, b)) : CanStore((a, b)) { - function store(r : storage((a, b)), v : (a, b)) -> () { - match v { - | (x, y) => - let base : word = Typedef.rep(r); - let a_sz : word = StorageSize.size(Proxy : Proxy(a)); - CanStore.store(storage(base) : storage(a), x); - CanStore.store(storage(base + a_sz) : storage(b), y); - } - } - function load(r : storage((a, b))) -> (a, b) { - let base : word = Typedef.rep(r); - let a_sz : word = StorageSize.size(Proxy : Proxy(a)); - let x : a = CanStore.load(storage(base) : storage(a)); - let y : b = CanStore.load(storage(base + a_sz) : storage(b)); - return (x, y); - } -} - -// Tagged union: slot 0 holds the tag, the branch payload follows. -forall f g . storage(f):CanStore(f), storage(g):CanStore(g) => -instance storage(sum(f, g)) : CanStore(sum(f, g)) { - function store(r : storage(sum(f, g)), v : sum(f, g)) -> () { - let base : word = Typedef.rep(r); - match v { - | inl(x) => - sstore(base, 0); - CanStore.store(storage(base + 1) : storage(f), x); - | inr(y) => - sstore(base, 1); - CanStore.store(storage(base + 1) : storage(g), y); - } - } - function load(r : storage(sum(f, g))) -> sum(f, g) { - let base : word = Typedef.rep(r); - let tag : word = sload(base); - // NOTE: the loaded payload is inlined directly into inl(...) / inr(...) - // rather than bound to a `let x : f` / `let y : g` first. Binding the - // payload to an intermediate of the branch type (f or g) makes the - // compiler infer the *branch* type for the inl/inr application instead - // of the full sum(f, g), so it emits e.g. `inr(y)` and Yul codegen - // rejects it (sum nesting off by one). Inlining matches the working - // ABIGeneric.decode pattern, so inl/inr pick up the full sum(f, g). - match tag { - | 0 => - return inl(CanStore.load(storage(base + 1) : storage(f))); - | _ => - return inr(CanStore.load(storage(base + 1) : storage(g))); - } - } -} - -// Dynamic leaves at the uniform storage(t) handle. std provides the storage(bytes) -// / storage(string) instances (data lives at keccak(slot)); these mirror them at -// the storage(memory(bytes)) / storage(memory(string)) handle the structural -// decomposition asks for, so a memory(bytes) field inside an ADT is storable. -instance storage(memory(bytes)) : CanStore(memory(bytes)) { - function store(r : storage(memory(bytes)), v : memory(bytes)) -> () { - CanStore.store(storage(Typedef.rep(r)) : storage(bytes), v); - } - function load(r : storage(memory(bytes))) -> memory(bytes) { - return CanStore.load(storage(Typedef.rep(r)) : storage(bytes)); - } -} - -instance storage(memory(string)) : CanStore(memory(string)) { - function store(r : storage(memory(string)), v : memory(string)) -> () { - CanStore.store(storage(Typedef.rep(r)) : storage(string), v); - } - function load(r : storage(memory(string))) -> memory(string) { - return CanStore.load(storage(Typedef.rep(r)) : storage(string)); - } -} - -// StorageType / CanStore for an ADT are NOT provided here as blanket bridges. -// -// A `default instance a:StorageType` would have its `load` return the head -// variable `a` via Generic.to — but the specializer cannot monomorphize a -// result-position type variable of a default instance (it is not pinned by the -// arguments), so loads panic. Likewise a tyvar-headed `default a:CanStore(b)` -// is non-functional (accepts any storable b), so contract field access cannot -// infer the stored type from the slot type. -// -// Instead, DeriveGeneric emits a concrete, per-type storage(T):CanStore(T) -// instance (see Solcore.Desugarer.DeriveGeneric) where the data type is fixed -// in the instance head; it delegates to the structural CanStore instances above -// via the type's Generic representation. StorageSize is likewise derived -// per-type for the field layout. - -// ─── Top-level helpers ─────────────────────────────────────────────────── -// Convenience wrappers mirroring std.ABIGeneric's encode / decode: persist or -// read back any 'a' that has a Generic(rep) instance at a raw storage slot. - -forall a rep . a:Generic(rep), rep:StorageType => -function storeGeneric(slot : word, value : a) -> () { - StorageType.store(slot, Generic.from(value)); -} - -forall a rep . a:Generic(rep), rep:StorageType => -function loadGeneric(slot : word) -> a { - let r : rep = StorageType.load(slot); - return Generic.to(r); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/dispatch.sol b/crates/parser/tests/fixtures/corpus/ok/std/dispatch.sol new file mode 100644 index 00000000..408c1415 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/std/dispatch.sol @@ -0,0 +1,300 @@ +import * from std; +import {callvalue, calldatasize, calldataload, shr, return_} from std.opcodes; +import * from std.Generic; + +export { + ABIString, + Contract(*), + ExecMethod, + Fallback(*), + Method(*), + MethodLevelCallvalueCheck, + NonPayable, + Payable, + RunContract, + RunDispatch, + Selector, + SigString, + do_exec, + fallback_default_implementation, + selector_matches, + sigStr +}; + +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +// --- Core Data Types --- + +// A contract contains a tuple of methods and a single fallback +// TODO: implement receive() +enum Contract { Contract(methods, fb) } + +// A method contains an implementation (fn) as well as it's name and type signature +enum Method { Method(Proxy, Proxy, Proxy, Proxy, fn) } + +// Contains the implementation for the fallback (fn) as well as it's type signature +enum Fallback { Fallback(Proxy, Proxy, Proxy, fn) } + +// --- Method Selectors --- + +trait ABIString { // deprecated + function append(head: word, tail: word, prx: Proxy) returns (word) ; +} + +trait SigString { function sigStr(x: Proxy) returns (string) ; } + +function sigStr(p: Proxy) returns (string) where t: SigString { SigString.sigStr(p) } + +impl SigString { function sigStr(x: Proxy) returns (string) { "uint256" }} +impl SigString { function sigStr(x: Proxy) returns (string) { "bytes32" }} +impl SigString { function sigStr(x: Proxy) returns (string) { "bytes4" }} +impl SigString
{ function sigStr(x: Proxy
) returns (string) { "address" }} +impl SigString { function sigStr(x: Proxy) returns (string) { "bool" }} +impl SigString> { function sigStr(x: Proxy>) returns (string) { "string" }} +impl SigString> { function sigStr(x: Proxy>) returns (string) { "bytes" }} +impl SigString<()> { function sigStr(x: Proxy<()>) returns (string) { "" } } + +impl SigString<(a, b)> where a: SigString, b: SigString { + function sigStr(x: Proxy<(a, b)>) returns (string) { + SigString.sigStr( @a ) + "," + SigString.sigStr( @b ) + } +} + +// A tagged union (the SOP form of an ADT with several constructors). There is no +// standard ABI type for sums, so this signature is structural: the branch +// signatures wrapped in a `sum(l,r)` constructor. The explicit `sum(...)` wrapper +// keeps sums from colliding with the comma-joined product `(a,b)` signature, so +// `sum(uint256,uint256)` and `(uint256,uint256)` hash to distinct selectors. It +// makes ADT-typed parameters produce a deterministic selector; refine here if a +// specific on-the-wire sum convention is needed. +impl SigString> where f: SigString, g: SigString { + function sigStr(x: Proxy>) returns (string) { + "sum(" + SigString.sigStr( @f ) + "," + SigString.sigStr( @g ) + ")" + } +} + +// A dynamic array signs as `[]`, matching Solidity's `T[]` convention. +// The element carries its own (structural, for ADTs) signature, so an array of a +// sum type reads `sum(l,r)[]`. Location is transparent to the ABI, so this keys +// on the calldata form the dispatch decodes from. +impl SigString>> where t: SigString { + function sigStr(x: Proxy>>) returns (string) { + SigString.sigStr( @t ) + "[]" + } +} + +// Any data type inherits its ABI signature from its Generic representation, the +// same way ABIAttribs / ABIEncode bridge through Generic in std.ABIGeneric. This +// lets the dispatch take ADT-typed parameters (e.g. a Signature) without a +// hand-written SigString impl per type. +default impl SigString where a: Generic, rep: SigString { + function sigStr(x: Proxy) returns (string) { + SigString.sigStr( @rep ) + } +} + +impl SigString> where f: invokable, name: SigString, args: SigString, rets: SigString { + function sigStr(x: Proxy>) returns (string) { + sigStr(@name) + "(" + sigStr(@args) + ")" + } +} + + +trait Selector { + function compute(prx: Proxy) returns (bytes4) ; +} + +// Computes the selector hash for a given method +// This trait has a single impl, which keeps downstream definitions simpler. +// NOTE: for efficiency purposes this leaves dirty data past the end of the free memory pointer +impl Selector> where name: SigString, args: SigString { + function compute(prx: Proxy>) returns (bytes4) { + // let hash : word = keccakLit(sigStr(prx)); + let hash = keccakLit(sigStr(@name) + "(" + sigStr(@args) + ")"); + return bytes4(shr(224, hash)); + } +} + +// --- Method Execution --- + +// Describes how to execute a given method / fallback +trait ExecMethod { + function exec(x: ty) ; +} + +// If fn matches the provided args/ret types, then we can execute any non-payable method +impl ExecMethod> where fn: invokable, args: ABIAttribs, rets: ABIAttribs, ABIDecoder: ABIDecode, rets: ABIEncode { + function exec(m: Method) { + match (m) { +case Method(pnm,ppayability,pargs,prets,fn) { +// non-payable methods must reject any callvalue before running + MethodLevelCallvalueCheck.checkCallvalue(@NonPayable); + do_exec(pargs, prets, fn); +} +} + } +} + +// If fn matches the provided args/ret types, then we can execute any payable method +// payable methods skip the callvalue check entirely +impl ExecMethod> where fn: invokable, args: ABIAttribs, rets: ABIAttribs, ABIDecoder: ABIDecode, rets: ABIEncode { + function exec(m: Method) { + match (m) { +case Method(pnm,ppayability,pargs,prets,fn) { +do_exec(pargs, prets, fn); +} +} + } +} + +// Fallbacks have no ABI-decoded inputs or outputs, so the impl is +// specialised to args = rets = () and bypasses the calldata length check +// and ABI decode/encode entirely. +impl ExecMethod> where fn: invokable<(), ()>, payability: MethodLevelCallvalueCheck { + function exec(fb: Fallback) { + match (fb) { +case Fallback(ppayability, pargs, prets, fn) { +MethodLevelCallvalueCheck.checkCallvalue(@payability); + fn(()); + assembly { + stop() + } +} +} + } +} + +function do_exec(pargs: Proxy, prets: Proxy, fn: fn) where fn: invokable, args: ABIAttribs, rets: ABIAttribs, ABIDecoder: ABIDecode, rets: ABIEncode { + // check we have enough calldata for the head of args + require(calldatasize() >= (ABIAttribs.headSize(pargs) + 4), Error(0x08638556)); // ABIInputTruncated() + + // TODO: calldatasize checks for dynamic types + + // abi decode args from calldata + let ptr : calldata = calldata(4); + + // TODO: this needs entirely too many type annotations + let args : args = abi_decode(ptr, pargs, @CalldataWordReader); + + // call fn with args + // TODO: why are type annotations needed here? + let rets : rets = fn(args); + + // abi encode rets to memory + let ptr = abi_encode(rets); + return_(MemoryPointer.ptr(ptr), MemorySize.len(ptr)); +} + +// --- Method Dispatch --- + +// For a given tuple of methods this executes the method specified by the first four bytes of calldata +trait RunDispatch { + function go(methods: ty) ; +} + +// We can dispatch to a single executable method with a known selector +impl RunDispatch> where Method: ExecMethod, Method: Selector { + function go(method: Method) { + match (selector_matches(@Method)) { +case true { +ExecMethod.exec(method); +} +case false { +return (); +} +} + } +} + +// Base case: a contract with no methods has nothing to dispatch to +impl RunDispatch<()> { + function go(methods: ()) { } +} + +// Recursive impl. +impl RunDispatch<(n, m)> where n: ExecMethod, n: Selector, m: RunDispatch { + function go(methods: (n, m)) { + match (methods) { +case (method_n, rest) { +match (selector_matches(@n)) { +case true { +ExecMethod.exec(method_n); +} +case false { +RunDispatch.go(rest); +} +} +} +} + } +} + +// TODO: we only wanna do the calldataload once +// Given evidence of a type with a known selector, we can check if it matches the selector in the first four bytes of calldata +function selector_matches(prx: Proxy) returns (bool) where ty: Selector { + let candidate = Typedef.rep(Selector.compute(prx)); + let selector = shr(224, calldataload(0)); + return selector == candidate; +} + +// --- Callvalue Checks --- + +enum Payable {} +enum NonPayable {} + +trait MethodLevelCallvalueCheck { + function checkCallvalue(pty: Proxy) ; +} + +// no callvalue check for Payable methods +impl MethodLevelCallvalueCheck { + function checkCallvalue(prx: Proxy) { } +} +// NonPayable methods revert if passed value +impl MethodLevelCallvalueCheck { + function checkCallvalue(prx: Proxy) { + let NonPayableReceivedValue = Error(0xb5988ea3); + require(callvalue() == 0, NonPayableReceivedValue); + } +} + +// --- Contract Execution --- + +// Describes how to execute a given contract +trait RunContract { + function exec(v: c) ; +} + +// If we have a dispatch for the contracts methods, and we know how to execute it's fallback, then we can define an entrypoint +impl RunContract> where methods: RunDispatch, fb: ExecMethod { + function exec(c: Contract) { + match (c) { +case Contract(ms, fb) { +// TODO: if all methods are non payable then we should life the callvalue check here + + // set free memory pointer to the output of memoryguard + // https://docs.soliditylang.org/en/v0.8.30/yul.html#memoryguard + // TODO: we will need to consider immutables here at some point... + assembly { mstore(0x40, memoryguard(128)) } + + // calldata shorter than 4 bytes can't contain a selector — skip + // dispatch and invoke the fallback directly (matches Solidity) + if (calldatasize() >= 4) { + // dispatch to method based on selector + RunDispatch.go(ms); + } + // fallthrough to fallback -- this will be reached upon short input + // or no matching selector + ExecMethod.exec(fb); +} +} + } +} + +// This is the default fallback used if none is defined. +function fallback_default_implementation() { + let NoSelectorMatchedWithoutFallback = Error(0x4924aef0); + revertWithError(NoSelectorMatchedWithoutFallback); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/dispatch.solc b/crates/parser/tests/fixtures/corpus/ok/std/dispatch.solc deleted file mode 100644 index ef9672a3..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/std/dispatch.solc +++ /dev/null @@ -1,322 +0,0 @@ -import std.{*}; -import std.opcodes.{callvalue, calldatasize, calldataload, shr, return_}; -import std.Generic.{*}; - -export { - ABIString, - Contract(*), - ExecMethod, - Fallback(*), - Method(*), - MethodLevelCallvalueCheck, - NonPayable, - Payable, - RunContract, - RunDispatch, - Selector, - SigString, - do_exec, - fallback_default_implementation, - selector_matches, - sigStr -}; - -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -// --- Core Data Types --- - -// A contract contains a tuple of methods and a single fallback -// TODO: implement receive() -data Contract(methods, fb) = Contract(methods,fb); - -// A method contains an implementation (fn) as well as it's name and type signature -data Method(name, payability, args, rets, fn) = Method(Proxy(name), Proxy(payability), Proxy(args), Proxy(rets), fn); - -// Contains the implementation for the fallback (fn) as well as it's type signature -data Fallback(payability, args, rets, fn) = Fallback(Proxy(payability), Proxy(args), Proxy(rets), fn); - -// --- Method Selectors --- - -forall ty . class ty:ABIString { // deprecated - function append(head : word, tail : word, prx : Proxy(ty)) -> word; -} - -forall t.class t:SigString { function sigStr(x:Proxy(t)) -> string; } - -forall t. t: SigString => -function sigStr(p:Proxy(t)) -> string { SigString.sigStr(p) } - -instance uint256 : SigString { function sigStr(x:Proxy(uint256)) -> string { "uint256" }} -instance bytes32 : SigString { function sigStr(x:Proxy(bytes32)) -> string { "bytes32" }} -instance bytes4 : SigString { function sigStr(x:Proxy(bytes4)) -> string { "bytes4" }} -instance address : SigString { function sigStr(x:Proxy(address)) -> string { "address" }} -instance bool : SigString { function sigStr(x:Proxy(bool)) -> string { "bool" }} -instance memory(string) : SigString { function sigStr(x:Proxy(memory(string))) -> string { "string" }} -instance memory(bytes) : SigString { function sigStr(x:Proxy(memory(bytes))) -> string { "bytes" }} -instance ():SigString { function sigStr(x:Proxy(())) -> string { "" } } - -forall a b. a:SigString, b: SigString => -instance (a,b):SigString { - function sigStr(x:Proxy((a,b))) -> string { - SigString.sigStr( Proxy:Proxy(a) ) + "," + SigString.sigStr( Proxy:Proxy(b) ) - } -} - -// A tagged union (the SOP form of an ADT with several constructors). There is no -// standard ABI type for sums, so this signature is structural: the branch -// signatures wrapped in a `sum(l,r)` constructor. The explicit `sum(...)` wrapper -// keeps sums from colliding with the comma-joined product `(a,b)` signature, so -// `sum(uint256,uint256)` and `(uint256,uint256)` hash to distinct selectors. It -// makes ADT-typed parameters produce a deterministic selector; refine here if a -// specific on-the-wire sum convention is needed. -forall f g. f:SigString, g: SigString => -instance sum(f,g):SigString { - function sigStr(x:Proxy(sum(f,g))) -> string { - "sum(" + SigString.sigStr( Proxy:Proxy(f) ) + "," + SigString.sigStr( Proxy:Proxy(g) ) + ")" - } -} - -// A dynamic array signs as `[]`, matching Solidity's `T[]` convention. -// The element carries its own (structural, for ADTs) signature, so an array of a -// sum type reads `sum(l,r)[]`. Location is transparent to the ABI, so this keys -// on the calldata form the dispatch decodes from. -forall t. t:SigString => -instance calldata(array(t)):SigString { - function sigStr(x:Proxy(calldata(array(t)))) -> string { - SigString.sigStr( Proxy:Proxy(t) ) + "[]" - } -} - -// Any data type inherits its ABI signature from its Generic representation, the -// same way ABIAttribs / ABIEncode bridge through Generic in std.ABIGeneric. This -// lets the dispatch take ADT-typed parameters (e.g. a Signature) without a -// hand-written SigString instance per type. -forall a rep. a:Generic(rep), rep:SigString => -default instance a:SigString { - function sigStr(x:Proxy(a)) -> string { - SigString.sigStr( Proxy:Proxy(rep) ) - } -} - -forall name f args rets payability. - f: invokable(args,rets), name:SigString, args:SigString, rets:SigString => -instance Method(name,payability,args,rets,f):SigString { - function sigStr(x:Proxy(Method(name,payability,args,rets,f))) -> string { - sigStr(Proxy:Proxy(name)) + "(" + sigStr(Proxy:Proxy(args)) + ")" - } -} - - -forall ty . class ty:Selector { - function compute(prx : Proxy(ty)) -> bytes4; -} - -// Computes the selector hash for a given method -// this is a class with a single instance since it made some of the downstream definitions a bit cleaner to define -// NOTE: for efficiency purposes this leaves dirty data past the end of the free memory pointer -forall name payability args rets fn - . name:SigString - , args:SigString -=> instance Method(name,payability,args,rets,fn):Selector { - function compute(prx : Proxy(Method(name,payability,args,rets,fn))) -> bytes4 { - // let hash : word = keccakLit(sigStr(prx)); - let hash = keccakLit(sigStr(Proxy:Proxy(name)) + "(" + sigStr(Proxy:Proxy(args)) + ")"); - return bytes4(shr(224, hash)); - } -} - -// --- Method Execution --- - -// Describes how to execute a given method / fallback -forall ty . class ty:ExecMethod { - function exec(x: ty) -> (); -} - -// If fn matches the provided args/ret types, then we can execute any non-payable method -forall name args rets fn - . fn:invokable(args,rets) - , args:ABIAttribs - , rets:ABIAttribs - , ABIDecoder(args,CalldataWordReader):ABIDecode(args) - , rets:ABIEncode -=> instance Method(name,NonPayable,args,rets,fn):ExecMethod { - function exec(m : Method(name,NonPayable,args,rets,fn)) -> () { - match m { - | Method(pnm,ppayability,pargs,prets,fn) => - // non-payable methods must reject any callvalue before running - MethodLevelCallvalueCheck.checkCallvalue(Proxy : Proxy(NonPayable)); - do_exec(pargs, prets, fn); - } - } -} - -// If fn matches the provided args/ret types, then we can execute any payable method -// payable methods skip the callvalue check entirely -forall name args rets fn - . fn:invokable(args,rets) - , args:ABIAttribs - , rets:ABIAttribs - , ABIDecoder(args,CalldataWordReader):ABIDecode(args) - , rets:ABIEncode -=> instance Method(name,Payable,args,rets,fn):ExecMethod { - function exec(m : Method(name,Payable,args,rets,fn)) -> () { - match m { - | Method(pnm,ppayability,pargs,prets,fn) => - do_exec(pargs, prets, fn); - } - } -} - -// Fallbacks have no ABI-decoded inputs or outputs, so the instance is -// specialised to args = rets = () and bypasses the calldata length check -// and ABI decode/encode entirely. -forall payability fn - . fn:invokable((),()) - , payability:MethodLevelCallvalueCheck -=> instance Fallback(payability,(),(),fn):ExecMethod { - function exec(fb : Fallback(payability,(),(),fn)) -> () { - match fb { - | Fallback(ppayability, pargs, prets, fn) => - MethodLevelCallvalueCheck.checkCallvalue(Proxy : Proxy(payability)); - fn(()); - assembly { - stop() - } - } - } -} - -forall args rets fn - . fn:invokable(args,rets) - , args:ABIAttribs - , rets:ABIAttribs - , ABIDecoder(args,CalldataWordReader):ABIDecode(args) - , rets:ABIEncode -=> function do_exec(pargs : Proxy(args), prets : Proxy(rets), fn : fn) -> () { - // check we have enough calldata for the head of args - require(calldatasize() >= (ABIAttribs.headSize(pargs) + 4), Error(0x08638556)); // ABIInputTruncated() - - // TODO: calldatasize checks for dynamic types - - // abi decode args from calldata - let ptr : calldata(bytes) = calldata(4); - - // TODO: this needs entirely too many type annotations - let args : args = abi_decode(ptr, pargs, Proxy : Proxy(CalldataWordReader)); - - // call fn with args - // TODO: why are type annotations needed here? - let rets : rets = fn(args); - - // abi encode rets to memory - let ptr = abi_encode(rets); - return_(MemoryPointer.ptr(ptr), MemorySize.len(ptr)); -} - -// --- Method Dispatch --- - -// For a given tuple of methods this executes the method specified by the first four bytes of calldata -forall ty . class ty:RunDispatch { - function go(methods : ty) -> (); -} - -// We can dispatch to a single executable method with a known selector -forall name payability args rets fn - . Method(name,payability,args,rets,fn):ExecMethod - , Method(name,payability,args,rets,fn):Selector -=> instance Method(name,payability,args,rets,fn):RunDispatch { - function go(method : Method(name,payability,args,rets,fn)) -> () { - match selector_matches(Proxy : Proxy(Method(name,payability,args,rets,fn))) { - | true => ExecMethod.exec(method); - | false => return (); - } - } -} - -// Base case: a contract with no methods has nothing to dispatch to -instance ():RunDispatch { - function go(methods : ()) -> () { } -} - -// Recursive instance -forall n m . n:ExecMethod, n:Selector, m:RunDispatch => instance (n,m):RunDispatch { - function go(methods : (n,m)) -> () { - match methods { - | (method_n, rest) => - match selector_matches(Proxy : Proxy(n)) { - | true => ExecMethod.exec(method_n); - | false => RunDispatch.go(rest); - } - } - } -} - -// TODO: we only wanna do the calldataload once -// Given evidence of a type with a known selector, we can check if it matches the selector in the first four bytes of calldata -forall ty . ty:Selector => function selector_matches(prx : Proxy(ty)) -> bool { - let candidate = Typedef.rep(Selector.compute(prx)); - let selector = shr(224, calldataload(0)); - return selector == candidate; -} - -// --- Callvalue Checks --- - -data Payable; -data NonPayable; - -forall ty . class ty:MethodLevelCallvalueCheck { - function checkCallvalue(pty : Proxy(ty)) -> (); -} - -// no callvalue check for Payable methods -instance Payable:MethodLevelCallvalueCheck { - function checkCallvalue(prx : Proxy(Payable)) -> () { } -} -// NonPayable methods revert if passed value -instance NonPayable:MethodLevelCallvalueCheck { - function checkCallvalue(prx : Proxy(NonPayable)) -> () { - let NonPayableReceivedValue = Error(0xb5988ea3); - require(callvalue() == 0, NonPayableReceivedValue); - } -} - -// --- Contract Execution --- - -// Describes how to execute a given contract -forall c . class c:RunContract { - function exec(v : c) -> (); -} - -// If we have a dispatch for the contracts methods, and we know how to execute it's fallback, then we can define an entrypoint -forall methods fb . methods:RunDispatch, fb:ExecMethod => instance Contract(methods, fb):RunContract { - function exec(c : Contract(methods, fb)) -> () { - match c { - | Contract(ms, fb) => - - // TODO: if all methods are non payable then we should life the callvalue check here - - // set free memory pointer to the output of memoryguard - // https://docs.soliditylang.org/en/v0.8.30/yul.html#memoryguard - // TODO: we will need to consider immutables here at some point... - assembly { mstore(0x40, memoryguard(128)) } - - // calldata shorter than 4 bytes can't contain a selector — skip - // dispatch and invoke the fallback directly (matches Solidity) - if (calldatasize() >= 4) { - // dispatch to method based on selector - RunDispatch.go(ms); - } - // fallthrough to fallback -- this will be reached upon short input - // or no matching selector - ExecMethod.exec(fb); - } - } -} - -// This is the default fallback used if none is defined. -function fallback_default_implementation() -> () { - let NoSelectorMatchedWithoutFallback = Error(0x4924aef0); - revertWithError(NoSelectorMatchedWithoutFallback); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/eip712.sol b/crates/parser/tests/fixtures/corpus/ok/std/eip712.sol new file mode 100644 index 00000000..7218ef29 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/std/eip712.sol @@ -0,0 +1,52 @@ +import * from std; +import {mstore, keccak256, shl} from std.opcodes; + +export { + eip712Digest, + eip712DomainSeparator +}; + +// --- EIP-712 (typed structured data hashing & signing) --- +// https://eips.ethereum.org/EIPS/eip-712 +// +// The digest a wallet signs is +// keccak256(0x19 0x01 ‖ domainSeparator ‖ hashStruct(message)) +// where every `hashStruct(s)` is `keccak256(typeHash ‖ encodeData(s))` and the +// domain separator is the `hashStruct` of the standard EIP712Domain struct. +// +// Encoding a struct's members is application specific (it depends on which +// members the struct has and whether they are atomic or dynamic), so the +// message struct hash is built by the caller. The two reusable pieces live +// here: the domain separator for the common `EIP712Domain(string name,string +// version,uint256 chainId,address verifyingContract)` shape, and the `0x1901` +// digest combinator that binds a domain separator to a message struct hash. + +// hashStruct of the standard EIP712Domain. `nameHash` / `versionHash` are the +// keccak256 of the (dynamic) name / version strings — typically compile-time +// constants produced with `keccakLit`. `chainId` / `verifyingContract` are +// encoded as their left-padded 32-byte words. +function eip712DomainSeparator(nameHash: bytes32, versionHash: bytes32, chainId: uint256, verifyingContract: address) returns (bytes32) { + let typeHash = keccakLit("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); + // Lay the five 32-byte words out contiguously and hash them. We borrow the + // area above the free-memory pointer as scratch (as `ecrecover` does): the + // preimage is consumed immediately by keccak256 and never needs to persist, + // so there is no need to bump the free pointer. + let ptr = get_free_memory(); + mstore(ptr, typeHash); + mstore(ptr + 32, Typedef.rep(nameHash)); + mstore(ptr + 64, Typedef.rep(versionHash)); + mstore(ptr + 96, Typedef.rep(chainId)); + mstore(ptr + 128, Typedef.rep(verifyingContract)); + return bytes32(keccak256(ptr, 160)); +} + +// Binds a domain separator to a message's struct hash, yielding the final +// EIP-712 digest: keccak256(0x19 0x01 ‖ domainSeparator ‖ structHash). The +// two-byte 0x1901 prefix occupies the leading bytes of the first word. +function eip712Digest(domainSeparator: bytes32, structHash: bytes32) returns (bytes32) { + let ptr = get_free_memory(); + mstore(ptr, shl(240, 0x1901)); // 0x1901 in the leading two bytes + mstore(ptr + 2, Typedef.rep(domainSeparator)); + mstore(ptr + 34, Typedef.rep(structHash)); + return bytes32(keccak256(ptr, 66)); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/eip712.solc b/crates/parser/tests/fixtures/corpus/ok/std/eip712.solc deleted file mode 100644 index 9e6f687b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/std/eip712.solc +++ /dev/null @@ -1,57 +0,0 @@ -import std.{*}; -import std.opcodes.{mstore, keccak256, shl}; - -export { - eip712Digest, - eip712DomainSeparator -}; - -// --- EIP-712 (typed structured data hashing & signing) --- -// https://eips.ethereum.org/EIPS/eip-712 -// -// The digest a wallet signs is -// keccak256(0x19 0x01 ‖ domainSeparator ‖ hashStruct(message)) -// where every `hashStruct(s)` is `keccak256(typeHash ‖ encodeData(s))` and the -// domain separator is the `hashStruct` of the standard EIP712Domain struct. -// -// Encoding a struct's members is application specific (it depends on which -// members the struct has and whether they are atomic or dynamic), so the -// message struct hash is built by the caller. The two reusable pieces live -// here: the domain separator for the common `EIP712Domain(string name,string -// version,uint256 chainId,address verifyingContract)` shape, and the `0x1901` -// digest combinator that binds a domain separator to a message struct hash. - -// hashStruct of the standard EIP712Domain. `nameHash` / `versionHash` are the -// keccak256 of the (dynamic) name / version strings — typically compile-time -// constants produced with `keccakLit`. `chainId` / `verifyingContract` are -// encoded as their left-padded 32-byte words. -function eip712DomainSeparator( - nameHash: bytes32, - versionHash: bytes32, - chainId: uint256, - verifyingContract: address -) -> bytes32 { - let typeHash = keccakLit("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - // Lay the five 32-byte words out contiguously and hash them. We borrow the - // area above the free-memory pointer as scratch (as `ecrecover` does): the - // preimage is consumed immediately by keccak256 and never needs to persist, - // so there is no need to bump the free pointer. - let ptr = get_free_memory(); - mstore(ptr, typeHash); - mstore(ptr + 32, Typedef.rep(nameHash)); - mstore(ptr + 64, Typedef.rep(versionHash)); - mstore(ptr + 96, Typedef.rep(chainId)); - mstore(ptr + 128, Typedef.rep(verifyingContract)); - return bytes32(keccak256(ptr, 160)); -} - -// Binds a domain separator to a message's struct hash, yielding the final -// EIP-712 digest: keccak256(0x19 0x01 ‖ domainSeparator ‖ structHash). The -// two-byte 0x1901 prefix occupies the leading bytes of the first word. -function eip712Digest(domainSeparator: bytes32, structHash: bytes32) -> bytes32 { - let ptr = get_free_memory(); - mstore(ptr, shl(240, 0x1901)); // 0x1901 in the leading two bytes - mstore(ptr + 2, Typedef.rep(domainSeparator)); - mstore(ptr + 34, Typedef.rep(structHash)); - return bytes32(keccak256(ptr, 66)); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/eip7951.sol b/crates/parser/tests/fixtures/corpus/ok/std/eip7951.sol new file mode 100644 index 00000000..a5ae3fe4 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/std/eip7951.sol @@ -0,0 +1,38 @@ +import * from std; +import {mstore, mload, gas, staticcall} from std.opcodes; + +export { p256verify }; + +// Perform secp256r1 recovery and check using the P256VERIFY precompile at address +// 0x100, introduced by EIP-7951 / RIP-7212. +// Input layout (160 bytes): hash | r | s | qx | qy. The precompile +// returns a 32-byte word equal to 1 on a valid signature and empty output on an +// invalid one; we pre-clear the [0, 32) scratch slot so the failing case reads +// back as 0. +function p256verify(hash: bytes32, r: bytes32, s: bytes32, qx: bytes32, qy: bytes32) returns (bool) { + let hash_ = Typedef.rep(hash); + let r_ = Typedef.rep(r); + let s_ = Typedef.rep(s); + let qx_ = Typedef.rep(qx); + let qy_ = Typedef.rep(qy); + let ptr = get_free_memory(); + mstore(ptr, hash_); + mstore(ptr + 32, r_); + mstore(ptr + 64, s_); + mstore(ptr + 96, qx_); + mstore(ptr + 128, qy_); + // We assume the [0, 32] scratch space is reserved. + // Clear the scratch slot so an empty (verification-failed) response reads as 0. + mstore(0, 0); + let ret = staticcall(gas(), 0x100, ptr, 160, 0, 32); + require(ret != 0, Error(0x1fb6bf04)); // P256VerifyCallFailed() + // NOTE: we are doing the inverse check here for safety, so not using tobool() + match (mload(0)) { +case 1 { +return true; +} +default { +return false; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/eip7951.solc b/crates/parser/tests/fixtures/corpus/ok/std/eip7951.solc deleted file mode 100644 index 16f2ca50..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/std/eip7951.solc +++ /dev/null @@ -1,34 +0,0 @@ -import std.{*}; -import std.opcodes.{mstore, mload, gas, staticcall}; - -export { p256verify }; - -// Perform secp256r1 recovery and check using the P256VERIFY precompile at address -// 0x100, introduced by EIP-7951 / RIP-7212. -// Input layout (160 bytes): hash | r | s | qx | qy. The precompile -// returns a 32-byte word equal to 1 on a valid signature and empty output on an -// invalid one; we pre-clear the [0, 32) scratch slot so the failing case reads -// back as 0. -function p256verify(hash: bytes32, r: bytes32, s: bytes32, qx: bytes32, qy: bytes32) -> bool { - let hash_ = Typedef.rep(hash); - let r_ = Typedef.rep(r); - let s_ = Typedef.rep(s); - let qx_ = Typedef.rep(qx); - let qy_ = Typedef.rep(qy); - let ptr = get_free_memory(); - mstore(ptr, hash_); - mstore(ptr + 32, r_); - mstore(ptr + 64, s_); - mstore(ptr + 96, qx_); - mstore(ptr + 128, qy_); - // We assume the [0, 32] scratch space is reserved. - // Clear the scratch slot so an empty (verification-failed) response reads as 0. - mstore(0, 0); - let ret = staticcall(gas(), 0x100, ptr, 160, 0, 32); - require(ret != 0, Error(0x1fb6bf04)); // P256VerifyCallFailed() - // NOTE: we are doing the inverse check here for safety, so not using tobool() - match mload(0) { - | 1 => return true; - | _ => return false; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/opcodes.sol b/crates/parser/tests/fixtures/corpus/ok/std/opcodes.sol new file mode 100644 index 00000000..a8193fb5 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/std/opcodes.sol @@ -0,0 +1,693 @@ +// Generated by scripts/gen-std-opcodes.py. Run the script to regenerate. + +export { + stop, + add, + mul, + sub, + div, + sdiv, + mod, + smod, + addmod, + mulmod, + exp, + signextend, + lt, + gt, + slt, + sgt, + eq, + iszero, + and, + or, + xor, + not, + byte, + shl, + shr, + sar, + clz, + keccak256, + address, + balance, + origin, + caller, + callvalue, + calldataload, + calldatasize, + calldatacopy, + codesize, + codecopy, + gasprice, + extcodesize, + extcodecopy, + returndatasize, + returndatacopy, + extcodehash, + blockhash, + coinbase, + timestamp, + number, + prevrandao, + gaslimit, + chainid, + selfbalance, + basefee, + blobhash, + blobbasefee, + pop, + mload, + mstore, + mstore8, + sload, + sstore, + msize, + gas, + tload, + tstore, + mcopy, + log0, + log1, + log2, + log3, + log4, + create, + call, + callcode, + return_, + delegatecall, + create2, + staticcall, + revert, + invalid, + selfdestruct +}; + +function stop() { + assembly { + stop() + } +} + +function add(a: word, b: word) returns (word) { + let res; + assembly { + res := add(a, b) + } + return res; +} + +function mul(a: word, b: word) returns (word) { + let res; + assembly { + res := mul(a, b) + } + return res; +} + +function sub(a: word, b: word) returns (word) { + let res; + assembly { + res := sub(a, b) + } + return res; +} + +function div(a: word, b: word) returns (word) { + let res; + assembly { + res := div(a, b) + } + return res; +} + +function sdiv(a: word, b: word) returns (word) { + let res; + assembly { + res := sdiv(a, b) + } + return res; +} + +function mod(a: word, b: word) returns (word) { + let res; + assembly { + res := mod(a, b) + } + return res; +} + +function smod(a: word, b: word) returns (word) { + let res; + assembly { + res := smod(a, b) + } + return res; +} + +function addmod(a: word, b: word, c: word) returns (word) { + let res; + assembly { + res := addmod(a, b, c) + } + return res; +} + +function mulmod(a: word, b: word, c: word) returns (word) { + let res; + assembly { + res := mulmod(a, b, c) + } + return res; +} + +function exp(a: word, b: word) returns (word) { + let res; + assembly { + res := exp(a, b) + } + return res; +} + +function signextend(a: word, b: word) returns (word) { + let res; + assembly { + res := signextend(a, b) + } + return res; +} + +function lt(a: word, b: word) returns (word) { + let res; + assembly { + res := lt(a, b) + } + return res; +} + +function gt(a: word, b: word) returns (word) { + let res; + assembly { + res := gt(a, b) + } + return res; +} + +function slt(a: word, b: word) returns (word) { + let res; + assembly { + res := slt(a, b) + } + return res; +} + +function sgt(a: word, b: word) returns (word) { + let res; + assembly { + res := sgt(a, b) + } + return res; +} + +function eq(a: word, b: word) returns (word) { + let res; + assembly { + res := eq(a, b) + } + return res; +} + +function iszero(a: word) returns (word) { + let res; + assembly { + res := iszero(a) + } + return res; +} + +function and(a: word, b: word) returns (word) { + let res; + assembly { + res := and(a, b) + } + return res; +} + +function or(a: word, b: word) returns (word) { + let res; + assembly { + res := or(a, b) + } + return res; +} + +function xor(a: word, b: word) returns (word) { + let res; + assembly { + res := xor(a, b) + } + return res; +} + +function not(a: word) returns (word) { + let res; + assembly { + res := not(a) + } + return res; +} + +function byte(a: word, b: word) returns (word) { + let res; + assembly { + res := byte(a, b) + } + return res; +} + +function shl(a: word, b: word) returns (word) { + let res; + assembly { + res := shl(a, b) + } + return res; +} + +function shr(a: word, b: word) returns (word) { + let res; + assembly { + res := shr(a, b) + } + return res; +} + +function sar(a: word, b: word) returns (word) { + let res; + assembly { + res := sar(a, b) + } + return res; +} + +function clz(a: word) returns (word) { + let res; + assembly { + res := clz(a) + } + return res; +} + +function keccak256(a: word, b: word) returns (word) { + let res; + assembly { + res := keccak256(a, b) + } + return res; +} + +function address() returns (word) { + let res; + assembly { + res := address() + } + return res; +} + +function balance(a: word) returns (word) { + let res; + assembly { + res := balance(a) + } + return res; +} + +function origin() returns (word) { + let res; + assembly { + res := origin() + } + return res; +} + +function caller() returns (word) { + let res; + assembly { + res := caller() + } + return res; +} + +function callvalue() returns (word) { + let res; + assembly { + res := callvalue() + } + return res; +} + +function calldataload(a: word) returns (word) { + let res; + assembly { + res := calldataload(a) + } + return res; +} + +function calldatasize() returns (word) { + let res; + assembly { + res := calldatasize() + } + return res; +} + +function calldatacopy(a: word, b: word, c: word) { + assembly { + calldatacopy(a, b, c) + } +} + +function codesize() returns (word) { + let res; + assembly { + res := codesize() + } + return res; +} + +function codecopy(a: word, b: word, c: word) { + assembly { + codecopy(a, b, c) + } +} + +function gasprice() returns (word) { + let res; + assembly { + res := gasprice() + } + return res; +} + +function extcodesize(a: word) returns (word) { + let res; + assembly { + res := extcodesize(a) + } + return res; +} + +function extcodecopy(a: word, b: word, c: word, d: word) { + assembly { + extcodecopy(a, b, c, d) + } +} + +function returndatasize() returns (word) { + let res; + assembly { + res := returndatasize() + } + return res; +} + +function returndatacopy(a: word, b: word, c: word) { + assembly { + returndatacopy(a, b, c) + } +} + +function extcodehash(a: word) returns (word) { + let res; + assembly { + res := extcodehash(a) + } + return res; +} + +function blockhash(a: word) returns (word) { + let res; + assembly { + res := blockhash(a) + } + return res; +} + +function coinbase() returns (word) { + let res; + assembly { + res := coinbase() + } + return res; +} + +function timestamp() returns (word) { + let res; + assembly { + res := timestamp() + } + return res; +} + +function number() returns (word) { + let res; + assembly { + res := number() + } + return res; +} + +function prevrandao() returns (word) { + let res; + assembly { + res := prevrandao() + } + return res; +} + +function gaslimit() returns (word) { + let res; + assembly { + res := gaslimit() + } + return res; +} + +function chainid() returns (word) { + let res; + assembly { + res := chainid() + } + return res; +} + +function selfbalance() returns (word) { + let res; + assembly { + res := selfbalance() + } + return res; +} + +function basefee() returns (word) { + let res; + assembly { + res := basefee() + } + return res; +} + +function blobhash(a: word) returns (word) { + let res; + assembly { + res := blobhash(a) + } + return res; +} + +function blobbasefee() returns (word) { + let res; + assembly { + res := blobbasefee() + } + return res; +} + +function pop(a: word) { + assembly { + pop(a) + } +} + +function mload(a: word) returns (word) { + let res; + assembly { + res := mload(a) + } + return res; +} + +function mstore(a: word, b: word) { + assembly { + mstore(a, b) + } +} + +function mstore8(a: word, b: word) { + assembly { + mstore8(a, b) + } +} + +function sload(a: word) returns (word) { + let res; + assembly { + res := sload(a) + } + return res; +} + +function sstore(a: word, b: word) { + assembly { + sstore(a, b) + } +} + +function msize() returns (word) { + let res; + assembly { + res := msize() + } + return res; +} + +function gas() returns (word) { + let res; + assembly { + res := gas() + } + return res; +} + +function tload(a: word) returns (word) { + let res; + assembly { + res := tload(a) + } + return res; +} + +function tstore(a: word, b: word) { + assembly { + tstore(a, b) + } +} + +function mcopy(a: word, b: word, c: word) { + assembly { + mcopy(a, b, c) + } +} + +function log0(a: word, b: word) { + assembly { + log0(a, b) + } +} + +function log1(a: word, b: word, c: word) { + assembly { + log1(a, b, c) + } +} + +function log2(a: word, b: word, c: word, d: word) { + assembly { + log2(a, b, c, d) + } +} + +function log3(a: word, b: word, c: word, d: word, e: word) { + assembly { + log3(a, b, c, d, e) + } +} + +function log4(a: word, b: word, c: word, d: word, e: word, f: word) { + assembly { + log4(a, b, c, d, e, f) + } +} + +function create(a: word, b: word, c: word) returns (word) { + let res; + assembly { + res := create(a, b, c) + } + return res; +} + +function call(a: word, b: word, c: word, d: word, e: word, f: word, g: word) returns (word) { + let res; + assembly { + res := call(a, b, c, d, e, f, g) + } + return res; +} + +function callcode(a: word, b: word, c: word, d: word, e: word, f: word, g: word) returns (word) { + let res; + assembly { + res := callcode(a, b, c, d, e, f, g) + } + return res; +} + +function return_(a: word, b: word) { + assembly { + return(a, b) + } +} + +function delegatecall(a: word, b: word, c: word, d: word, e: word, f: word) returns (word) { + let res; + assembly { + res := delegatecall(a, b, c, d, e, f) + } + return res; +} + +function create2(a: word, b: word, c: word, d: word) returns (word) { + let res; + assembly { + res := create2(a, b, c, d) + } + return res; +} + +function staticcall(a: word, b: word, c: word, d: word, e: word, f: word) returns (word) { + let res; + assembly { + res := staticcall(a, b, c, d, e, f) + } + return res; +} + +function revert(a: word, b: word) { + assembly { + revert(a, b) + } +} + +function invalid() { + assembly { + invalid() + } +} + +function selfdestruct(a: word) { + assembly { + selfdestruct(a) + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/opcodes.solc b/crates/parser/tests/fixtures/corpus/ok/std/opcodes.solc deleted file mode 100644 index 991d18eb..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/std/opcodes.solc +++ /dev/null @@ -1,693 +0,0 @@ -// Generated by scripts/gen-std-opcodes.py. Run the script to regenerate. - -export { - stop, - add, - mul, - sub, - div, - sdiv, - mod, - smod, - addmod, - mulmod, - exp, - signextend, - lt, - gt, - slt, - sgt, - eq, - iszero, - and, - or, - xor, - not, - byte, - shl, - shr, - sar, - clz, - keccak256, - address, - balance, - origin, - caller, - callvalue, - calldataload, - calldatasize, - calldatacopy, - codesize, - codecopy, - gasprice, - extcodesize, - extcodecopy, - returndatasize, - returndatacopy, - extcodehash, - blockhash, - coinbase, - timestamp, - number, - prevrandao, - gaslimit, - chainid, - selfbalance, - basefee, - blobhash, - blobbasefee, - pop, - mload, - mstore, - mstore8, - sload, - sstore, - msize, - gas, - tload, - tstore, - mcopy, - log0, - log1, - log2, - log3, - log4, - create, - call, - callcode, - return_, - delegatecall, - create2, - staticcall, - revert, - invalid, - selfdestruct -}; - -function stop() -> () { - assembly { - stop() - } -} - -function add(a: word, b: word) -> word { - let res; - assembly { - res := add(a, b) - } - return res; -} - -function mul(a: word, b: word) -> word { - let res; - assembly { - res := mul(a, b) - } - return res; -} - -function sub(a: word, b: word) -> word { - let res; - assembly { - res := sub(a, b) - } - return res; -} - -function div(a: word, b: word) -> word { - let res; - assembly { - res := div(a, b) - } - return res; -} - -function sdiv(a: word, b: word) -> word { - let res; - assembly { - res := sdiv(a, b) - } - return res; -} - -function mod(a: word, b: word) -> word { - let res; - assembly { - res := mod(a, b) - } - return res; -} - -function smod(a: word, b: word) -> word { - let res; - assembly { - res := smod(a, b) - } - return res; -} - -function addmod(a: word, b: word, c: word) -> word { - let res; - assembly { - res := addmod(a, b, c) - } - return res; -} - -function mulmod(a: word, b: word, c: word) -> word { - let res; - assembly { - res := mulmod(a, b, c) - } - return res; -} - -function exp(a: word, b: word) -> word { - let res; - assembly { - res := exp(a, b) - } - return res; -} - -function signextend(a: word, b: word) -> word { - let res; - assembly { - res := signextend(a, b) - } - return res; -} - -function lt(a: word, b: word) -> word { - let res; - assembly { - res := lt(a, b) - } - return res; -} - -function gt(a: word, b: word) -> word { - let res; - assembly { - res := gt(a, b) - } - return res; -} - -function slt(a: word, b: word) -> word { - let res; - assembly { - res := slt(a, b) - } - return res; -} - -function sgt(a: word, b: word) -> word { - let res; - assembly { - res := sgt(a, b) - } - return res; -} - -function eq(a: word, b: word) -> word { - let res; - assembly { - res := eq(a, b) - } - return res; -} - -function iszero(a: word) -> word { - let res; - assembly { - res := iszero(a) - } - return res; -} - -function and(a: word, b: word) -> word { - let res; - assembly { - res := and(a, b) - } - return res; -} - -function or(a: word, b: word) -> word { - let res; - assembly { - res := or(a, b) - } - return res; -} - -function xor(a: word, b: word) -> word { - let res; - assembly { - res := xor(a, b) - } - return res; -} - -function not(a: word) -> word { - let res; - assembly { - res := not(a) - } - return res; -} - -function byte(a: word, b: word) -> word { - let res; - assembly { - res := byte(a, b) - } - return res; -} - -function shl(a: word, b: word) -> word { - let res; - assembly { - res := shl(a, b) - } - return res; -} - -function shr(a: word, b: word) -> word { - let res; - assembly { - res := shr(a, b) - } - return res; -} - -function sar(a: word, b: word) -> word { - let res; - assembly { - res := sar(a, b) - } - return res; -} - -function clz(a: word) -> word { - let res; - assembly { - res := clz(a) - } - return res; -} - -function keccak256(a: word, b: word) -> word { - let res; - assembly { - res := keccak256(a, b) - } - return res; -} - -function address() -> word { - let res; - assembly { - res := address() - } - return res; -} - -function balance(a: word) -> word { - let res; - assembly { - res := balance(a) - } - return res; -} - -function origin() -> word { - let res; - assembly { - res := origin() - } - return res; -} - -function caller() -> word { - let res; - assembly { - res := caller() - } - return res; -} - -function callvalue() -> word { - let res; - assembly { - res := callvalue() - } - return res; -} - -function calldataload(a: word) -> word { - let res; - assembly { - res := calldataload(a) - } - return res; -} - -function calldatasize() -> word { - let res; - assembly { - res := calldatasize() - } - return res; -} - -function calldatacopy(a: word, b: word, c: word) -> () { - assembly { - calldatacopy(a, b, c) - } -} - -function codesize() -> word { - let res; - assembly { - res := codesize() - } - return res; -} - -function codecopy(a: word, b: word, c: word) -> () { - assembly { - codecopy(a, b, c) - } -} - -function gasprice() -> word { - let res; - assembly { - res := gasprice() - } - return res; -} - -function extcodesize(a: word) -> word { - let res; - assembly { - res := extcodesize(a) - } - return res; -} - -function extcodecopy(a: word, b: word, c: word, d: word) -> () { - assembly { - extcodecopy(a, b, c, d) - } -} - -function returndatasize() -> word { - let res; - assembly { - res := returndatasize() - } - return res; -} - -function returndatacopy(a: word, b: word, c: word) -> () { - assembly { - returndatacopy(a, b, c) - } -} - -function extcodehash(a: word) -> word { - let res; - assembly { - res := extcodehash(a) - } - return res; -} - -function blockhash(a: word) -> word { - let res; - assembly { - res := blockhash(a) - } - return res; -} - -function coinbase() -> word { - let res; - assembly { - res := coinbase() - } - return res; -} - -function timestamp() -> word { - let res; - assembly { - res := timestamp() - } - return res; -} - -function number() -> word { - let res; - assembly { - res := number() - } - return res; -} - -function prevrandao() -> word { - let res; - assembly { - res := prevrandao() - } - return res; -} - -function gaslimit() -> word { - let res; - assembly { - res := gaslimit() - } - return res; -} - -function chainid() -> word { - let res; - assembly { - res := chainid() - } - return res; -} - -function selfbalance() -> word { - let res; - assembly { - res := selfbalance() - } - return res; -} - -function basefee() -> word { - let res; - assembly { - res := basefee() - } - return res; -} - -function blobhash(a: word) -> word { - let res; - assembly { - res := blobhash(a) - } - return res; -} - -function blobbasefee() -> word { - let res; - assembly { - res := blobbasefee() - } - return res; -} - -function pop(a: word) -> () { - assembly { - pop(a) - } -} - -function mload(a: word) -> word { - let res; - assembly { - res := mload(a) - } - return res; -} - -function mstore(a: word, b: word) -> () { - assembly { - mstore(a, b) - } -} - -function mstore8(a: word, b: word) -> () { - assembly { - mstore8(a, b) - } -} - -function sload(a: word) -> word { - let res; - assembly { - res := sload(a) - } - return res; -} - -function sstore(a: word, b: word) -> () { - assembly { - sstore(a, b) - } -} - -function msize() -> word { - let res; - assembly { - res := msize() - } - return res; -} - -function gas() -> word { - let res; - assembly { - res := gas() - } - return res; -} - -function tload(a: word) -> word { - let res; - assembly { - res := tload(a) - } - return res; -} - -function tstore(a: word, b: word) -> () { - assembly { - tstore(a, b) - } -} - -function mcopy(a: word, b: word, c: word) -> () { - assembly { - mcopy(a, b, c) - } -} - -function log0(a: word, b: word) -> () { - assembly { - log0(a, b) - } -} - -function log1(a: word, b: word, c: word) -> () { - assembly { - log1(a, b, c) - } -} - -function log2(a: word, b: word, c: word, d: word) -> () { - assembly { - log2(a, b, c, d) - } -} - -function log3(a: word, b: word, c: word, d: word, e: word) -> () { - assembly { - log3(a, b, c, d, e) - } -} - -function log4(a: word, b: word, c: word, d: word, e: word, f: word) -> () { - assembly { - log4(a, b, c, d, e, f) - } -} - -function create(a: word, b: word, c: word) -> word { - let res; - assembly { - res := create(a, b, c) - } - return res; -} - -function call(a: word, b: word, c: word, d: word, e: word, f: word, g: word) -> word { - let res; - assembly { - res := call(a, b, c, d, e, f, g) - } - return res; -} - -function callcode(a: word, b: word, c: word, d: word, e: word, f: word, g: word) -> word { - let res; - assembly { - res := callcode(a, b, c, d, e, f, g) - } - return res; -} - -function return_(a: word, b: word) -> () { - assembly { - return(a, b) - } -} - -function delegatecall(a: word, b: word, c: word, d: word, e: word, f: word) -> word { - let res; - assembly { - res := delegatecall(a, b, c, d, e, f) - } - return res; -} - -function create2(a: word, b: word, c: word, d: word) -> word { - let res; - assembly { - res := create2(a, b, c, d) - } - return res; -} - -function staticcall(a: word, b: word, c: word, d: word, e: word, f: word) -> word { - let res; - assembly { - res := staticcall(a, b, c, d, e, f) - } - return res; -} - -function revert(a: word, b: word) -> () { - assembly { - revert(a, b) - } -} - -function invalid() -> () { - assembly { - invalid() - } -} - -function selfdestruct(a: word) -> () { - assembly { - selfdestruct(a) - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/std.sol b/crates/parser/tests/fixtures/corpus/ok/std/std.sol new file mode 100644 index 00000000..dc16c75e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/std/std.sol @@ -0,0 +1,2901 @@ +import {add, sub, mul, div, mod, addmod as addmod_, mulmod as mulmod_, and as and_, or as or_, xor as xor_, shl, shr, eq, not as not_, gt as gt_, iszero, keccak256, mstore, mload, mcopy, sstore, sload, gas, calldataload, calldatacopy, returndatasize, returndatacopy, log1 as log1_, call, staticcall, revert as revert_, invalid} from std.opcodes; + +pragma no-patterson-condition ABIEncode, Num, Array, ArrayPush, Eq, Ord; +pragma no-coverage-condition ABIDecode, MemoryType, Array, ArrayPush, RValueIdxAccess; + +export { + ABIAttribs, + ABIDecode, + ABIDecoder(*), + ABIEncode, + ABITuple(*), + Add, + Array, + ArrayPush, + Assign, + BitAnd, + BitNot, + BitOr, + BitXor, + Bounded, + CalldataWordReader(*), + CanStore, + ContractStorage(*), + Div, + DynArray, + Error(*), + Eq, + HasWordReader, + IndexAccess, + LVA, + LValueIdxAccess, + Length, + MemberAccessProxy(*), + MemoryEncode, + MemoryPointer, + MemorySize, + MemoryType, + MemoryWordReader(*), + Mod, + Mul, + Num, + Ord, + Proxy(*), + RVA, + RValueIdxAccess, + StorageCopy, + StorageSize, + StorageType, + StructField(*), + Sub, + Typedef, + WordReader, + abi_decode, + abi_encode, + absurd, + addWord, + addmod, + allocateDynamicArray, + address(*), + allocate_memory, + allocate_zeroed_memory, + and, + array(*), + arrayLitInit, + arrayLitNew, + assert, + byte(*), + bytes, + bytes4(*), + bytes32(*), + bandWord, + borWord, + bxorWord, + bnotWord, + bshlWord, + bshrWord, + calldata(*), + concat, + concatLit, + ecrecover, + empty(*), + eqWord, + erc7201, + frombool, + ge, + getReader, + get_free_memory, + gt, + gtWord, + hash1, + hash2, + keccak256_, + keccakLit, + keccakWordLit, + le, + lidx, + loadBytesFromStorage, + log1, + lt, + mapping(*), + maxVal, + maxWord, + memberAccessBase, + memory(*), + memory_ref, + minWord, + mulmod, + ne, + not, + or, + out_of_bounds, + raw_call, + readStorage, + returndata(*), + revertLit, + revertEmpty, + revertWithError, + require, + ridx, + ripemd160, + round_up_to_mul_of_32, + rval, + set_free_memory, + sha256, + slice(*), + slice_, + storage(*), + storeArrayLit, + storeBytesFromMemory, + string, + strlen, + strlenLit, + subWord, + truncate, + toWord, + to_bytes, + tobool, + uint256(*), + unimplemented, + zeroize_memory +}; + +/* +- features + - primitive word eq + - include stdlib + - MPTC + optional weak args (MPTC formalization?) + - surface for loops + - better inference for Typedef.rep() calls (have to annotate atm?) + - boolean short circuiting +- sugar + - Proxy (e.g. `@t ==> Proxy : Proxy t` + - IndexAccess reads (e.g. `x[i] ==> IndexAccess.get(x, i)`) + - auto typedef instances +- syntax + - order of type args + - braces for blocks in matches + - trait / impl vs class / instance + - function -> fn? + - assembly vs high level return? +- todo + - abi decoding + - contract desugaring + - mappings + - strings + - full range of uintX / intX / bytesX types + - address types + - statically sized arrays + - tuple field access + - structs + - define numeric tower + - fixed point types + - fixed point numeric routines + - memory vectors +*/ + + +function log1(v: t, topic: word) where t: Typedef { + let w : word = Typedef.rep(v); + mstore(0, w); + log1_(0, 32, topic); +} + +function unimplemented() { + let Unimplemented = Error(0x6e128399); + revertWithError(Unimplemented); +} + +function out_of_bounds() { + let OutOfBounds = Error(0xb4120f14); + revertWithError(OutOfBounds); +} + +// ------------------------------------------------------------------ +// High-level revert helper +// ------------------------------------------------------------------ +// EmitHull has special handling for `revertLit("...")` after MastEval has +// constant-folded the argument to a string literal. +function revertLit(comptime s: string) { + unimplemented(); // Sanity check if folding ignores it. + return (); +} + +// Empty revert. +function revertEmpty() { + revert_(0, 0); +} + +// Bottom: a value of any type. absurd never returns, it reverts, so it can +// stand in for a result of any type. Used to derive trait impls for empty +// data types (which have no values, so the method bodies are unreachable). The +// recursive tail satisfies the generic result type `a`; execution never +// reaches it because revertEmpty() aborts first. +function absurd() returns (a) { + // Despite looking like an infinite loop, this reverts: revertEmpty() + // aborts execution on the first line, so the recursive return absurd() + // is never actually run. The recursion exists only to give the body a + // value of type `a`, satisfying the generic result type. + revertEmpty(); + return absurd(); +} + +// TODO: use bytes4 +enum Error { Error(word), Empty, Msg(memory) } + +// A string literal can be used as an Error: `require(cond, "message")` reverts +// with the message. The literal is materialized into memory here; MastEval +// erases the comptime-only parameter by cloning this method per literal, so +// the materializer sees a literal rather than a parameter. +impl Str { + function fromString(s: string) returns (Error) { + return Error.Msg(Str.fromString(s)); + } +} + +// Revert with Error selector. +function revertWithError(e: Error) { + match (e) { +case .Error(selector) { +mstore(0, selector); + // We only care about the BE MSB. + revert_(28, 4); +} +case .Empty { +revert_(0, 0); +} +case .Msg(msg) { +let msg_ = Typedef.rep(msg); + revert_(msg_ + 32, mload(msg_)); +} +} +} + +function assert(cond: bool) { + if (!cond) { + invalid(); + } +} + +function require(cond: bool, e: Error) { + if (!cond) { + revertWithError(e); + } +} + +// --- booleans --- + +// TODO: this should short circuit. probably needs some compiler magic to do so. +function and(x: bool, y: bool) returns (bool) { + match (x, y) { +case (true, y) { +return y; +} +case (false, _) { +return false; +} +} +} + +// TODO: this should short circuit. probably needs some compiler magic to do so. +function or(x: bool, y: bool) returns (bool) { + match (x, y) { +case (true, _) { +return true; +} +case (false, y) { +return y; +} +} +} + +function not(b: bool) returns (bool) { + match (b) { +case false { +return true; +} +case true { +return false; +} +} +} + +function frombool(b: bool) returns (word) { + match (b) { +case false { +return 0; +} +case true { +return 1; +} +} +} + +function tobool(x: word) returns (bool) { + match (x) { +case 0 { +return false; +} +default { +return true; +} +} +} + +// --- Tuple projections --- + +function fst(p: (a, b)) returns (a) { + match (p) { +case (a, _) { +return a; +} +} +} + +function snd(p: (a, b)) returns (b) { + match (p) { +case (_, b) { +return b; +} +} +} + +// --- Proxy --- + +// Proxy is a unit type that can be used to pass Types as paramaters at runtime +enum Proxy { Proxy } + +// --- Type Abstraction --- + +trait Typedef { + function abs(x: rep) returns (abs) ; + function rep(x: abs) returns (rep) ; +} + +default impl Typedef { + function abs(x: t) returns (t) { return x; } + function rep(x: t) returns (t) { return x; } +} + +// --- Equality --- +// Note: All these are used by the compiler by name. + +trait Eq { + function eq(x: a, y: a) returns (bool) ; +} + +function ne(x: a, y: a) returns (bool) where a: Eq { + return not(Eq.eq(x,y)); +} + +// --- Ordering --- +// Note: All these are used by the compiler by name. + +trait Ord where a: Eq { + function gt(x: a, y: a) returns (bool) ; +} + +function gt(x: a, y: a) returns (bool) where a: Ord { + return Ord.gt(x,y); +} + +function le(x: a, y: a) returns (bool) where a: Ord { + return not(Ord.gt(x,y)); +} + +function ge(x: a, y: a) returns (bool) where a: Ord { + return le(y,x); +} + +function lt(x: a, y: a) returns (bool) where a: Ord { + return Ord.gt(y,x); +} + +// --- Generic deriving: structural impls over the representation universe --- +// These let `#[derive(Eq)]` / `#[derive(Ord)]` work for any data type through +// its `Generic` impl, where `rep` is built from `()`, `sum` and +// `(f, g)`. + +impl Eq<()> { + function eq(x: (), y: ()) returns (bool) { + return true; + } +} + +impl Eq> where f: Eq, g: Eq { + function eq(x: sum, y: sum) returns (bool) { + match (x) { +case inl(a) { +match (y) { +case inl(b) { +return Eq.eq(a, b); +} +case inr(b) { +return false; +} +} +} +case inr(a) { +match (y) { +case inl(b) { +return false; +} +case inr(b) { +return Eq.eq(a, b); +} +} +} +} + } +} + +impl Eq<(f, g)> where f: Eq, g: Eq { + function eq(x: (f, g), y: (f, g)) returns (bool) { + match (x) { +case (a1, b1) { +match (y) { +case (a2, b2) { +match (Eq.eq(a1, a2)) { +case true { +return Eq.eq(b1, b2); +} +case false { +return false; +} +} +} +} +} +} + } +} + +impl Ord<()> { + function gt(x: (), y: ()) returns (bool) { + return false; + } +} + +impl Ord> where f: Ord, g: Ord { + function gt(x: sum, y: sum) returns (bool) { + match (x) { +case inl(a) { +match (y) { +case inl(b) { +return Ord.gt(a, b); +} +case inr(b) { +return false; +} +} +} +case inr(a) { +match (y) { +case inl(b) { +return true; +} +case inr(b) { +return Ord.gt(a, b); +} +} +} +} + } +} + +impl Ord<(f, g)> where f: Ord, g: Ord { + function gt(x: (f, g), y: (f, g)) returns (bool) { + match (x) { +case (a1, b1) { +match (y) { +case (a2, b2) { +match (Ord.gt(a1, a2)) { +case true { +return true; +} +case false { +match (Eq.eq(a1, a2)) { +case true { +return Ord.gt(b1, b2); +} +case false { +return false; +} +} +} +} +} +} +} +} + } +} + +// --- Arithmetic --- +// Note: All these are used by the compiler by name. + +trait Add { + function add(l: t, r: t) returns (t) ; +} + +trait Sub { + function sub(l: t, r: t) returns (t) ; +} + +trait Mul { + function mul(l: t, r: t) returns (t) ; +} + +trait Div { + function div(l: t, r: t) returns (t) ; +} + +trait Mod { + function mod(l: t, r: t) returns (t) ; +} + +trait BitAnd { + function band(l: t, r: t) returns (t) ; +} + +trait BitOr { + function bor(l: t, r: t) returns (t) ; +} + +trait BitXor { + function bxor(l: t, r: t) returns (t) ; +} + +trait BitNot { + function bnot(x: t) returns (t) ; +} + +trait Bounded { + function minVal() returns (t) ; + function maxVal() returns (t) ; +} + +function maxVal() returns (t) where t: Bounded { return Bounded.maxVal(); } + +// Umbrella trait. +trait Num where a: Add, a: Sub, a: Bounded, a: Eq, a: Ord, a: Typedef { + function maxVal() returns (a) ; + function toWord(x: a) returns (word) ; + function fromWord(x: word) returns (a) ; + function fromInteger(comptime x: integer) returns (comptime) ; + function add(x: a, y: a) returns (a) ; + function sub(x: a, y: a) returns (a) ; + function gt(x: a, y: a) returns (bool) ; +} + +default impl Num where a: Add, a: Sub, a: Bounded, a: Eq, a: Ord, a: Typedef { + function maxVal() returns (a) { return Bounded.maxVal(); } + function toWord(x: a) returns (word) { return Typedef.rep(x); } + function fromWord(x: word) returns (a) { return Typedef.abs(x); } + function fromInteger(comptime x: integer) returns (comptime) { return Typedef.abs(wordFromInteger(x)); } + function add(x: a, y: a) returns (a) { return Add.add(x,y); } + function sub(x: a, y: a) returns (a) { return Sub.sub(x,y); } + function gt(x: a, y: a) returns (bool) { return Ord.gt(x, y); } +} + +// --- Word Arithmetic & Logic --- +// TODO: make these checked + +// These are intended to be folded by MastEval when their arguments are +// statically known word values. +function eqWord(x: word, y: word) returns (bool) { + return tobool(eq(x, y)); +} + +function gtWord(x: word, y: word) returns (bool) { + return tobool(gt_(x, y)); +} + +function maxWord(a: word, b: word) returns (word) { + match (gtWord(a, b)) { +case true { +return a; +} +case false { +return b; +} +} +} + +function minWord(a: word, b: word) returns (word) { + match (gtWord(a, b)) { +case true { +return b; +} +case false { +return a; +} +} +} + +function addWord(l: word, r: word) returns (word) { + return add(l, r); +} + +function subWord(l: word, r: word) returns (word) { + return sub(l, r); +} + +// Bitwise AND +function bandWord(x: word, y: word) returns (word) { + return and_(x, y); +} + +// Bitwise OR +function borWord(x: word, y: word) returns (word) { + return or_(x, y); +} + +// Bitwise XOR +function bxorWord(x: word, y: word) returns (word) { + return xor_(x, y); +} + +// Bitwise NOT +function bnotWord(x: word) returns (word) { + return not_(x); +} + +// Bitwise SHL +function bshlWord(x: word, y: word) returns (word) { + return shl(x, y); +} + +// Bitwise SHR +function bshrWord(x: word, y: word) returns (word) { + return shr(x, y); +} + +impl Eq { + function eq(x: word, y: word) returns (bool) { + return eqWord(x, y); + } +} + +impl Ord { + function gt(x: word, y: word) returns (bool) { + return gtWord(x, y); + } +} + +impl Add { + function add(l: word, r: word) returns (word) { + return addWord(l, r); + } +} + +impl Sub { + function sub(l: word, r: word) returns (word) { + return subWord(l, r); + } +} + +function mulWord(l: word, r: word) returns (word) { + return mul(l, r); +} + +impl Mul { + function mul(l: word, r: word) returns (word) { + return mulWord(l, r); + } +} + +impl Div { + function div(l: word, r: word) returns (word) { + return div(l, r); + } +} + +impl Mod { + function mod(l: word, r: word) returns (word) { + return mod(l, r); + } +} + +impl BitAnd { + function band(l: word, r: word) returns (word) { + return bandWord(l, r); + } +} + +impl BitOr { + function bor(l: word, r: word) returns (word) { + return borWord(l, r); + } +} + +impl BitXor { + function bxor(l: word, r: word) returns (word) { + return bxorWord(l, r); + } +} + +impl BitNot { + function bnot(x: word) returns (word) { + return bnotWord(x); + } +} + +impl Eq { + function eq(x: integer, y: integer) returns (bool) { + return integerEq(x, y); + } +} + +impl Ord { + function gt(x: integer, y: integer) returns (bool) { + return integerLt(y, x); + } +} + +impl Add { + function add(l: integer, r: integer) returns (integer) { + return integerAdd(l, r); + } +} + +impl Sub { + function sub(l: integer, r: integer) returns (integer) { + return integerSub(l, r); + } +} + +impl Mul { + function mul(l: integer, r: integer) returns (integer) { + return integerMul(l, r); + } +} + +impl Bounded { + function maxVal() returns (word) { + return 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + } + function minVal() returns (word) { + return 0; + } +} + +function hash1(x: word) returns (word) { + mstore(0, x); + return keccak256(0, 32); +} + +function hash2(x: word, y: word) returns (word) { + mstore(0, x); + mstore(32, y); + return keccak256(0, 64); +} + +// --- Value Types --- + +function toWord(x: t) returns (word) where t: Typedef { return Typedef.rep(x); } + +enum uint256 { uint256(word) } +impl Typedef { + function abs(w: word) returns (uint256) { + return uint256(w); + } + + function rep(x: uint256) returns (word) { + match (x) { +case uint256(w) { +return w; +} +} + } +} +impl Add { + function add(x: uint256, y: uint256) returns (uint256) { + return Typedef.abs(Add.add(Typedef.rep(x), Typedef.rep(y))); + } +} + +impl Sub { + function sub(x: uint256, y: uint256) returns (uint256) { + return Typedef.abs(Sub.sub(Typedef.rep(x), Typedef.rep(y))); + } +} + +impl Mul { + function mul(x: uint256, y: uint256) returns (uint256) { + return Typedef.abs(Mul.mul(Typedef.rep(x), Typedef.rep(y))); + } +} + +impl Div { + function div(x: uint256, y: uint256) returns (uint256) { + return Typedef.abs(Div.div(Typedef.rep(x), Typedef.rep(y))); + } +} + +impl Mod { + function mod(x: uint256, y: uint256) returns (uint256) { + return Typedef.abs(Mod.mod(Typedef.rep(x), Typedef.rep(y))); + } +} + +impl BitAnd { + function band(x: uint256, y: uint256) returns (uint256) { + return Typedef.abs(BitAnd.band(Typedef.rep(x), Typedef.rep(y))); + } +} + +impl BitOr { + function bor(x: uint256, y: uint256) returns (uint256) { + return Typedef.abs(BitOr.bor(Typedef.rep(x), Typedef.rep(y))); + } +} + +impl BitXor { + function bxor(x: uint256, y: uint256) returns (uint256) { + return Typedef.abs(BitXor.bxor(Typedef.rep(x), Typedef.rep(y))); + } +} + +impl BitNot { + function bnot(x: uint256) returns (uint256) { + return Typedef.abs(BitNot.bnot(Typedef.rep(x))); + } +} + +impl Eq { + function eq(x: uint256, y: uint256) returns (bool) { + return Eq.eq(Typedef.rep(x), Typedef.rep(y)); + } +} + +impl Ord { + function gt(x: uint256, y: uint256) returns (bool) { + return Ord.gt(Typedef.rep(x), Typedef.rep(y)); + } +} + +impl Bounded { + function maxVal() returns (uint256) { + return uint256(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); + } + function minVal() returns (uint256) { + return uint256(0); + } +} + +impl Int { + function fromInteger(x: integer) returns (uint256) { + return uint256(wordFromInteger(x)); + } +} + +function addmod(x: uint256, y: uint256, k: uint256) returns (uint256) { + require(k != uint256(0), Error(0x7125cbb9)); // AddModWithZero() + return Typedef.abs(addmod_(Typedef.rep(x), Typedef.rep(y), Typedef.rep(k))); +} + +function mulmod(x: uint256, y: uint256, k: uint256) returns (uint256) { + require(k != uint256(0), Error(0xdaea23b9)); // MulModWithZero() + return Typedef.abs(mulmod_(Typedef.rep(x), Typedef.rep(y), Typedef.rep(k))); +} + +enum byte { byte(word) } +impl Typedef { + function abs(w: word) returns (byte) { + return byte(w); + } + + function rep(x: byte) returns (word) { + match (x) { +case byte(w) { +return w; +} +} + } +} + +// --- Address --- +enum address { address(word) } + +impl Typedef { + function rep(x: address) returns (word) { + match (x) { +case address(y) { +return y; +} +} + } + function abs(x: word) returns (address) { + return address(x); + } +} + +impl Eq
{ + function eq(x: address, y: address) returns (bool) { + return Eq.eq(Typedef.rep(x), Typedef.rep(y)); + } +} + +// --- Bytes4 --- + +enum bytes4 { bytes4(word) } + +impl Typedef { + function rep(b: bytes4) returns (word) { + match (b) { +case bytes4(w) { +return w; +} +} + } + function abs(w: word) returns (bytes4) { + return bytes4(w); + } +} + +// --- Bytes32 --- + +enum bytes32 { bytes32(word) } + +impl Typedef { + function rep(b: bytes32) returns (word) { + match (b) { +case bytes32(w) { +return w; +} +} + } + function abs(w: word) returns (bytes32) { + return bytes32(w); + } +} + +impl Eq { + function eq(x: bytes32, y: bytes32) returns (bool) { + return Eq.eq(Typedef.rep(x), Typedef.rep(y)); + } +} + +impl Ord { + function gt(x: bytes32, y: bytes32) returns (bool) { + return Ord.gt(Typedef.rep(x), Typedef.rep(y)); + } +} + +// --- Pointers --- + +enum memory { memory(word) } +impl Typedef, word> { + function abs(x: word) returns (memory) { + return memory(x); + } + + function rep(x: memory) returns (word) { + match (x) { +case memory(w) { +return w; +} +} + } +} + +enum storage { storage(word) } +impl Typedef, word> { + function abs(x: word) returns (storage) { + return storage(x); + } + + function rep(x: storage) returns (word) { + match (x) { +case storage(w) { +return w; +} +} + } +} + +enum calldata { calldata(word) } +impl Typedef, word> { + function abs(x: word) returns (calldata) { + return calldata(x); + } + + function rep(x: calldata) returns (word) { + match (x) { +case calldata(w) { +return w; +} +} + } +} + +enum returndata { returndata(word) } +impl Typedef, word> { + function abs(x: word) returns (returndata) { + return returndata(x); + } + + function rep(x: returndata) returns (word) { + match (x) { +case returndata(w) { +return w; +} +} + } +} + +enum mapping { mapping(word) } + +enum array { array(word) } + +// --- Low-level memory ops + +function strlen(s: memory) returns (word) { + match (s) { +case memory(a) { +return mload(a); +} +} +} + +// --- Memory Utilities --- + +// Memory in solidity is bump allocated in a single arena +// The word stored in memory at index 0x40 is used to store the start of the currently unused memory region + +// returns the value stored in memory(0x40) +function get_free_memory() returns (word) { + return mload(0x40); +} + +// set the value stored in memory(0x40) +function set_free_memory(loc: word) { + mstore(0x40, loc); +} + +// Allocate memory and update the memory pointer. +function allocate_memory(size: word) returns (word) { + let ptr = get_free_memory(); + set_free_memory(ptr + size); + return ptr; +} + +function allocate_zeroed_memory(size: word) returns (word) { + let ptr = allocate_memory(size); + zeroize_memory(ptr, size); + return ptr; +} + +// Clears a memory area. +function zeroize_memory(ptr: word, len: word) { + let end_ptr = ptr + len; + + // Zero out 32-byte words. + for (let i = 0; i < len / 32; i += 1, ptr += 32) { + mstore(ptr, 0); + } + + // Zero out trailing bytes. We rely on the zero-slot (0x60-0x7f). + mcopy(ptr, 0x60, end_ptr - ptr); +} + +// --- Indexable Types --- + +// types that can be written to and read from at a uint256 index +// TODO: this needs to be split into LValue / RValue variants for `=` desugaring +trait IndexAccess { + function get(c: t, i: uint256) returns (val) ; + function set(c: t, i: uint256, v: val) ; +} + +// --- DynArray --- + +// Word arrays with a size known only at runtime +// types with a size smaller than `word` will not be packed, so a `DynArray(byte)` will waste a lot of space +// TODO: storage representation +enum DynArray {} + +// Layout: the length lives at `loc`, so element i lives at `loc + 32 + i*32`. +// An index is in bounds when i < length. +impl IndexAccess>, t> where t: Typedef { + function get(ptr: memory>, i: uint256) returns (t) { + let i_: word = Typedef.rep(i); + let loc : word = Typedef.rep(ptr); + if (i_ >= mload(loc)) { out_of_bounds(); } + return Typedef.abs(mload(loc + 32 + (i_ * 32))); + } + function set(arr: memory>, i: uint256, val: t) { + let i_ : word = Typedef.rep(i); + let loc : word = Typedef.rep(arr); + if (i_ >= mload(loc)) { out_of_bounds(); } + mstore(loc + 32 + (i_ * 32), Typedef.rep(val)); + } +} + +// --- Array literals --- +// +// `[e1, ..., en]` is desugared, after type checking, into +// arrayLitInit(... arrayLitInit(arrayLitNew(n), 0, e1) ..., n-1, en) +// The chain is a plain expression: each step returns the array it wrote to. + +function arrayLitNew(n: uint256) returns (memory>) where t: Typedef { + let prx : Proxy; + return allocateDynamicArray(prx, Typedef.rep(n)); +} + +function arrayLitInit(arr: memory>, i: uint256, v: t) returns (memory>) where t: Typedef { + IndexAccess.set(arr, i, v); + return arr; +} + +function allocateDynamicArray(prx: Proxy, length: word) returns (memory>) { + // size of allocation in bytes + let sz : word = (length + 1) * 32; + + // get start of array & increment free by sz + let free : word = get_free_memory(); + set_free_memory(free + sz); + + // write array length and return + mstore(free, length); + let res : memory> = Typedef.abs(free); + return res; +} + +// --- bytes --- + +// tightly packed byte arrays +// bytes does not have a runtime representation since it can only ever exist in +// memory / calldata / storage and serves only as a type tag for pointer types +// TODO: IndexAccess for memory +// TODO: IndexAccess for calldata +// TODO: IndexAccess for storage +enum bytes {} + +// --- strings --- + +// TODO: should this be a typedef over `bytes`? +enum string {} + +impl Add { + function add(l: string, r: string) returns (string) { + return concatLit(l, r); + } +} + +// ------------------------------------------------------------------ +// Compile-time string literal builtins +// ------------------------------------------------------------------ +// These are intended to be folded by MastEval when their arguments are +// statically known string literals. + +function concatLit(comptime a: string, comptime b: string) returns (string) { + unimplemented(); // Sanity check if folding ignores it. + return ""; +} + +function strlenLit(comptime a: string) returns (word) { + unimplemented(); // Sanity check if folding ignores it. + return 0; +} + +// Keccak-256 hash of the string-literal as UTF-8 bytes. +function keccakLit(comptime a: string) returns (word) { + unimplemented(); // Sanity check if folding ignores it. + return 0; +} + +// Keccak-256 hash of a word's 32-byte big-endian representation. +// NOTE: this could be deprecated if we have comptime `to_bytes`. +function keccakWordLit(comptime a: word) returns (word) { + unimplemented(); // Sanity check if folding ignores it. + return 0; +} + +// --- slices (sized pointers) --- + +// A slice is a wrapper around an existing pointer type that extends the +// underlying type with information about the size of the data pointed to by `t` +enum slice { slice(ptr, word) } + +// --- Word Reader --- + +// A WordReader is an abstraction over byte indexed structure that can be read in word sized chunks (e.g. calldata / memory) +// These let us use the same abi decoding routines for calldata / memory +trait WordReader { + // returns the word currently pointed to by the WordReader + function read(reader: ty) returns (word) ; + // returns a new WordReader that points to a location `offset` bytes further into the array + function advance(reader: ty, offset: word) returns (ty) ; + // copies a block from the underlying source to memory + function copyToMem(reader: ty, dst: word, cnt: word) ; +} + +// WordReader for memory +enum MemoryWordReader { MemoryWordReader(word) } +impl WordReader { + function read(reader: MemoryWordReader) returns (word) { + match (reader) { +case MemoryWordReader(ptr) { +return mload(ptr); +} +} + } + function advance(reader: MemoryWordReader, offset: word) returns (MemoryWordReader) { + match (reader) { +case MemoryWordReader(ptr) { +return MemoryWordReader(ptr + offset); +} +} + } + function copyToMem(reader: MemoryWordReader, dst: word, cnt: word) { + match (reader) { +case MemoryWordReader(ptr) { +mcopy(dst, ptr, cnt); +} +} + } +} + +// WordReader for calldata +enum CalldataWordReader { CalldataWordReader(word) } + +impl Typedef { + function abs(a: word) returns (CalldataWordReader) { return CalldataWordReader(a); } + function rep(r: CalldataWordReader) returns (word) { + match (r) { +case CalldataWordReader(a) { +return a; +} +} + } +} + +impl WordReader { + function read(reader: CalldataWordReader) returns (word) { + match (reader) { +case CalldataWordReader(ptr) { +return calldataload(ptr); +} +} + } + function advance(reader: CalldataWordReader, offset: word) returns (CalldataWordReader) { + match (reader) { +case CalldataWordReader(ptr) { +return CalldataWordReader(ptr + offset); +} +} + } + function copyToMem(reader: CalldataWordReader, dst: word, cnt: word) { + match (reader) { +case CalldataWordReader(ptr) { +calldatacopy(dst, ptr, cnt); +} +} + } +} + +// --- HasWordReader --- + +// The HasWordReader trait defines the types for which a WordReader can be produced. +// We define impls for memory and calldata. +trait HasWordReader { + function getWordReader(x: self) returns (reader) ; +} + +impl HasWordReader, MemoryWordReader> { + function getWordReader(x: memory) returns (MemoryWordReader) { + return MemoryWordReader(Typedef.rep(x)); + } +} + +impl HasWordReader, CalldataWordReader> { + function getWordReader(x: calldata) returns (CalldataWordReader) { + return CalldataWordReader(Typedef.rep(x)); + } +} + +// --- MemoryType --- + +// A MemoryType impl abstracts over type-specific memory layout, allowing us to +// write code that is generic over the type held in memory. +trait MemoryType { + // Proxy is needed because trait methods must mention strong type parameters. + // Loads a `loadedType` value from a `self` value located at `loc` in memory. + function loadFromMemory(p: Proxy, loc: word) returns (loadedType) ; +} + +// A uint256 can be loaded from memory and pushed straight onto the stack +impl MemoryType { + function loadFromMemory(p: Proxy, loc: word) returns (uint256) { + return uint256(mload(loc)); + } +} + +// We load a DynArray into a sized pointer to the first element +/* +impl MemoryType, slice>> where ty: MemoryType { + function loadFromMemory(p: Proxy>, loc: word) returns (slice>) { + let length = mload(loc); + let ptr: memory = memory(Typedef.abs(loc)); + return slice(ptr, length); + } +} +*/ + +// FAIL: patterson +// FAIL: bound variable +// If `ty: MemoryType` and `deref: ABIEncode`, then memory can be +// encoded by loading and encoding its dereferenced value. +// by loading it and then running the ABI encoding for the loaded value +/* +impl ABIEncode> where ty: MemoryType, deref: ABIEncode { + function encodeInto(x: memory, basePtr: word, offset: word, tail: word) returns (word) { + let prx: Proxy; // FIXED: before was Proxy + return ABIEncode.encodeInto(MemoryType.loadFromMemory(prx, Typedef.rep(x)): deref, basePtr, offset, tail); + } +} +*/ +// --- ABI Tuples --- + +// Tuples in Solidity are always desugared to nested pairs (to allow for +// inductive trait-impl constructions). +// This is an issue for the ABI routines since the ABI spec differentiates +// between `(1,1,1)` and `(1,(1,1))`, but the language treats both identically. +// The ABITuple type lets us reiintroduce this distinction: +// `ABITuple((1,(1,1))` should be treated as `(1,1,1)` for the purposes of ABI +// encoding / decoding. +enum ABITuple { ABITuple(tuple) } + +impl Typedef, t> { + function abs(t: t) returns (ABITuple) { + return ABITuple(t); + } + + function rep(x: ABITuple) returns (t) { + match (x) { +case ABITuple(v) { +return v; +} +} + } +} + +// --- ABI Metadata --- + +// Statically knowable ABI related metadata about `self` +trait ABIAttribs { + // how many bytes should be used for the head portion of the abi encoding of `self` + function headSize(ty: Proxy) returns (word) ; + // whether or not `self` is a fully static type + function isStatic(ty: Proxy) returns (bool) ; +} + +default impl ABIAttribs { + function headSize(ty: Proxy) returns (word) { return 32; } + function isStatic(ty: Proxy) returns (bool) { return true; } +} + +impl ABIAttribs<()> { + function headSize(ty: Proxy<()>) returns (word) { return 0; } + function isStatic(ty: Proxy<()>) returns (bool) { return true; } +} +impl ABIAttribs { + function headSize(ty: Proxy) returns (word) { return 32; } + function isStatic(ty: Proxy) returns (bool) { return true; } +} +impl ABIAttribs
{ + function headSize(ty: Proxy
) returns (word) { return 32; } + function isStatic(ty: Proxy
) returns (bool) { return true; } +} +impl ABIAttribs> { + function headSize(ty: Proxy>) returns (word) { return 32; } + function isStatic(ty: Proxy>) returns (bool) { return false; } +} +// A dynamic array is encoded head-first as a 32-byte offset into the tail, so +// its head is one word and it is never static (matching DynArray above). This +// covers `array` under any location qualifier via the `calldata` / +// `memory` ABIAttribs bridges. +impl ABIAttribs> { + function headSize(ty: Proxy>) returns (word) { return 32; } + function isStatic(ty: Proxy>) returns (bool) { return false; } +} +impl ABIAttribs { + function headSize(ty: Proxy) returns (word) { return 32; } + function isStatic(ty: Proxy) returns (bool) { return false; } +} +// bytes is dynamic, exactly like string — without this impl it falls to the +// default (isStatic = true), which wrongly marks memory (and any ADT +// carrying it) static, so calldata arrays/sums take the inline decode path over +// what is really an offset-referenced value. +impl ABIAttribs { + function headSize(ty: Proxy) returns (word) { return 32; } + function isStatic(ty: Proxy) returns (bool) { return false; } +} + +// computes the attribs for a pair of two types that implement attribs +impl ABIAttribs<(a, b)> where a: ABIAttribs, b: ABIAttribs { + function headSize(ty: Proxy<(a, b)>) returns (word) { + let pa : Proxy; + let pb : Proxy; + let sza = ABIAttribs.headSize(pa); + let szb = ABIAttribs.headSize(pb); + return sza + szb; + } + function isStatic(ty: Proxy<(a, b)>) returns (bool) { + let pa : Proxy; + let pb : Proxy; + return and(ABIAttribs.isStatic(pa), ABIAttribs.isStatic(pb)); + } +} + +// if an abi tuple contains dynamic elems we store it in the tail, otherwise we +// treat it the same as a series of nested pairs +impl ABIAttribs> where tuple: ABIAttribs { + function headSize(ty: Proxy>) returns (word) { + let px : Proxy; + match (ABIAttribs.isStatic(px)) { +case true { +return ABIAttribs.headSize(px); +} +case false { +return 32; +} +} + } + function isStatic(ty: Proxy>) returns (bool) { + let px : Proxy; + return ABIAttribs.isStatic(px); + } +} + +// for pointer types we fetch the attribs of the pointed to type, not the pointer itself +impl ABIAttribs> where ty: ABIAttribs { + function headSize(p: Proxy>) returns (word) { + let px : Proxy; + return ABIAttribs.headSize(px); + } + function isStatic(p: Proxy>) returns (bool) { + let px : Proxy; + return ABIAttribs.isStatic(px); + } +} +impl ABIAttribs> where ty: ABIAttribs { + function headSize(p: Proxy>) returns (word) { + let px : Proxy; + return ABIAttribs.headSize(px); + } + function isStatic(ty: Proxy>) returns (bool) { + let px : Proxy; + return ABIAttribs.isStatic(px); + } +} + +// --- ABI Encoding --- +// TODO: make these generic over the location being written to (i.e. memory or returndata) + +// top level encoding function. +// ABI-encodes a `ty` value and returns a pointer to the result. +function abi_encode(val: ty) returns (memory) where ty: ABIAttribs, ty: ABIEncode { + let ret = get_free_memory(); + let start = ret + 32; + let tail = ABIEncode.encodeInto(val, start, 0, start + ABIAttribs.headSize(@ty)); + mstore(ret, tail - start); + set_free_memory(tail); + return memory(ret); +} + +// types that can be abi encoded +trait ABIEncode { + // ABI-encodes a `self` value into a memory region starting at basePtr. + // offset gives the offset in memory from basePtr to the first empty byte of the head + // tail gives the index in memory of the first empty byte of the tail + function encodeInto(x: self, basePtr: word, offset: word, tail: word) returns (word) ; +} + +impl ABIEncode { + // a unit256 is written directly into the head + function encodeInto(x: uint256, basePtr: word, offset: word, tail: word) returns (word) { + let repx : word = Typedef.rep(x); + mstore(basePtr + offset, repx); + return tail; + } +} + +impl ABIEncode
{ + // an address is written directly into the head (into a full 32-byte slot) + function encodeInto(x: address, basePtr: word, offset: word, tail: word) returns (word) { + let repx : word = Typedef.rep(x); + mstore(basePtr + offset, repx); + return tail; + } +} + +impl ABIEncode { + // a bytes32 is written directly into the head + function encodeInto(x: bytes32, basePtr: word, offset: word, tail: word) returns (word) { + let repx : word = Typedef.rep(x); + mstore(basePtr + offset, repx); + return tail; + } +} + +impl ABIEncode { + // bytes4's word rep is right-aligned (e.g. `bytes4(shr(224, h))`), + // so it is written directly into the head like bytes32 + function encodeInto(x: bytes4, basePtr: word, offset: word, tail: word) returns (word) { + let repx : word = Typedef.rep(x); + mstore(basePtr + offset, repx); + return tail; + } +} + +impl ABIEncode { + function encodeInto(x: bool, basePtr: word, offset: word, tail: word) returns (word) { + let repx : word = frombool(x); + mstore(basePtr + offset, repx); + return tail; + } +} + +function round_up_to_mul_of_32(value: word) returns (word) { + return (value + 31) & ~31; +} + +function encodeIntoFromBytesLike(srcPtr: word, basePtr: word, offset: word, tail: word) returns (word) { + let length = mload(srcPtr); + let total = length + 32; + mstore(basePtr + offset, tail - basePtr); + mcopy(tail, srcPtr, total); + let rounded = round_up_to_mul_of_32(total); + zeroize_memory(tail + total, rounded - total); + return tail + rounded; +} + +impl ABIEncode> { + function encodeInto(x: memory, basePtr: word, offset: word, tail: word) returns (word) { + return encodeIntoFromBytesLike(Typedef.rep(x), basePtr, offset, tail); + } +} + +impl ABIEncode> { + function encodeInto(x: memory, basePtr: word, offset: word, tail: word) returns (word) { + return encodeIntoFromBytesLike(Typedef.rep(x), basePtr, offset, tail); + } +} + +// ABI encoding for a memory dynamic array whose elements fit in a single word. +// Assumes memory layout `[ length | elem_0 | elem_1 | ... ]`, which matches the +// on-the-wire tail of `t[]` so the body can be `mcopy`d verbatim. +// `memory>: ABIAttribs` is already derivable from the generic +// `memory: ABIAttribs` + `DynArray: ABIAttribs` impls above. +impl ABIEncode>> where t: Typedef { + function encodeInto(x: memory>, basePtr: word, offset: word, tail: word) returns (word) { + let srcPtr : word = Typedef.rep(x); + let len : word = mload(srcPtr); + let totalBytes : word = (len + 1) * 32; + + // head slot: relative pointer from basePtr to tail + mstore(basePtr + offset, tail - basePtr); + + // copy length + elements verbatim into the tail + let s : word = srcPtr; + let t_ : word = tail; + let n : word = totalBytes; + mcopy(t_, s, n); + return tail + totalBytes; + } +} + +impl ABIEncode<()> { + // a unit256 is written directly into the head + function encodeInto(x: (), basePtr: word, offset: word, tail: word) returns (word) { + return tail; + } +} + +// abi encoding for a pair of two encodable types +impl ABIEncode<(a, b)> where a: ABIAttribs, a: ABIEncode, b: ABIEncode { + function encodeInto(x: (a, b), basePtr: word, offset: word, tail: word) returns (word) { + match (x) { +case (l,r) { +let newTail = ABIEncode.encodeInto(l, basePtr, offset, tail); + let pa : Proxy; + let a_sz = ABIAttribs.headSize(pa); + return ABIEncode.encodeInto(r, basePtr, offset + a_sz, newTail); +} +} + } +} + + +// abi encoding for an ABITuple of encodable types +// TODO: is this correct? +impl ABIEncode> where tuple: ABIEncode, tuple: ABIAttribs { + function encodeInto(x: ABITuple, basePtr: word, offset: word, tail: word) returns (word) { + let prx : Proxy; + match (ABIAttribs.isStatic(prx)) { +// if the tuple contains only static elements then we encode it in the head +case true { +return ABIEncode.encodeInto(Typedef.rep(x), basePtr, offset, tail); + // if the tuple contains dynamically sized elements then we store a + // pointer in the head, and encode the tuple into the tail +} +case false { +// store the length of the head in basePtr + mstore(basePtr, tail - basePtr); + + // encode the underlying tuple into the tail + let headSize = ABIAttribs.headSize(@tuple); + basePtr = tail; + tail += headSize; + return ABIEncode.encodeInto(Typedef.rep(x), basePtr, 0, tail); +} +} + } +} + +// --- ABI Decoding --- + +// Top level decoding function. +// ABI-decodes a `decodable` value into a `ty` value. +function abi_decode(decodable: decodable, pty: Proxy, prdr: Proxy) returns (decoded) where decodable: HasWordReader, ABIDecoder: ABIDecode { + let decoder : ABIDecoder = ABIDecoder(HasWordReader.getWordReader(decodable)); + return ABIDecode.decode(decoder, 0); +} + + +trait ABIDecode { + function decode(ptr: decoder, currentHeadOffset: word) returns (decoded) ; +} + +// An ABI Decoder for `ty` from `reader` +// This lets us abstract over memory and calldata when decoding +enum ABIDecoder { ABIDecoder(reader) } + +// If `reader` is a `WordReader` then so is our `ABIDecoder` +impl WordReader> where reader: WordReader { + function read(decoder: ABIDecoder) returns (word) { + match (decoder) { +case ABIDecoder(ptr) { +return WordReader.read(ptr); +} +} + } + function advance(decoder: ABIDecoder, offset: word) returns (ABIDecoder) { + match (decoder) { +case ABIDecoder(ptr) { +return ABIDecoder(WordReader.advance(ptr, offset)); +} +} + } + function copyToMem(decoder: ABIDecoder, dst: word, cnt: word) { + match (decoder) { +case ABIDecoder(ptr) { +WordReader.copyToMem(ptr, dst, cnt); +} +} + } +} + +// ABI Decoding for uint256 +impl ABIDecode, uint256> where reader: WordReader { + function decode(ptr: ABIDecoder, currentHeadOffset: word) returns (uint256) { + return Typedef.abs(WordReader.read(WordReader.advance(ptr, currentHeadOffset))) ; + } +} + +// ABI Decoding for bytes32 +impl ABIDecode, bytes32> where reader: WordReader { + function decode(ptr: ABIDecoder, currentHeadOffset: word) returns (bytes32) { + return Typedef.abs(WordReader.read(WordReader.advance(ptr, currentHeadOffset))) ; + } +} + +// ABI Decoding for bytes4 +impl ABIDecode, bytes4> where reader: WordReader { + function decode(ptr: ABIDecoder, currentHeadOffset: word) returns (bytes4) { + return Typedef.abs(WordReader.read(WordReader.advance(ptr, currentHeadOffset))) ; + } +} + +// ABI Decoding for bool +// bool is a builtin (not a Typedef(word)), so it round-trips through word via +// tobool, mirroring the `bool: ABIEncode` impl which uses frombool. +impl ABIDecode, bool> where reader: WordReader { + function decode(ptr: ABIDecoder, currentHeadOffset: word) returns (bool) { + let v = WordReader.read(WordReader.advance(ptr, currentHeadOffset)); + require(v <= 1, Error(0x0557dbbf)); // DirtyHigherBitsForBool() + return tobool(v); + } +} + +// ABI Decoding for address +impl ABIDecode, address> where reader: WordReader { + function decode(ptr: ABIDecoder, currentHeadOffset: word) returns (address) { + let raw = WordReader.read(WordReader.advance(ptr, currentHeadOffset)); + require(shr(160, raw) == 0, Error(0x7cc04fa7)); // DirtyHigherBitsForAddress() + return Typedef.abs(raw) ; + } +} + +impl ABIDecode, ()> where reader: WordReader { + function decode(ptr: ABIDecoder<(), reader>, currentHeadOffset: word) { + return (); + } +} + +// ABI decoding for bytes/strings (only in memory) +function decodeBytesLike(ptr: ABIDecoder, reader>, currentHeadOffset: word) returns (memory) where reader: WordReader { + let tmp:word; + let headRdr = WordReader.advance(ptr, currentHeadOffset); + let tailPtr : word = WordReader.read(headRdr); + + let src = WordReader.advance(ptr, tailPtr); + let srcRdr = getReader(src); + let length = WordReader.read(src); + let total = length + 32; + let rounded = round_up_to_mul_of_32(total); + let resultPtr : word = allocate_memory(rounded); + WordReader.copyToMem(srcRdr, resultPtr, total); + return memory(resultPtr); +} + +// ABI decoding for strings (only in memory) +impl ABIDecode, reader>, memory> where reader: WordReader { + function decode(ptr: ABIDecoder, reader>, currentHeadOffset: word) returns (memory) { + return decodeBytesLike(ptr, currentHeadOffset); + } +} + +// ABI decoding for bytes (only in memory) +impl ABIDecode, reader>, memory> where reader: WordReader { + function decode(ptr: ABIDecoder, reader>, currentHeadOffset: word) returns (memory) { + return decodeBytesLike(ptr, currentHeadOffset); + } +} + +// ABI decoding for a pair of decodable values +// FAIL: Coverage +impl ABIDecode, (a_decoded, b_decoded)> where reader: WordReader, ABIDecoder: ABIDecode, ABIDecoder: ABIDecode, a: ABIAttribs { + function decode(ptr: ABIDecoder<(a, b), reader>, currentHeadOffset: word) returns (a_decoded, b_decoded) { + match (ptr) { +case ABIDecoder(rdr) { +let prx : Proxy; + let decoder_a : ABIDecoder = ABIDecoder(rdr); + let decoder_b : ABIDecoder = ABIDecoder(rdr); + let a_val : a_decoded = ABIDecode.decode(decoder_a, currentHeadOffset); + let b_val : b_decoded = ABIDecode.decode(decoder_b, currentHeadOffset + ABIAttribs.headSize(prx)); + return (a_val, b_val); +} +} + } +} + +impl ABIDecode, reader>, tuple_decoded> where reader: WordReader, tuple: ABIDecode, tuple: ABIAttribs { + function decode(ptr: ABIDecoder, reader>, currentHeadOffset: word) returns (tuple_decoded) { + let prx : Proxy; + match (ABIAttribs.isStatic(prx)) { +case true { +return ABIDecode.decode(WordReader.advance(ptr, currentHeadOffset), 0); +} +case false { +let tailPtr = WordReader.read(ptr); + return ABIDecode.decode(WordReader.advance(ptr, tailPtr), 0); +} +} + } +} + + +impl ABIDecode>, reader>, memory> where reader: WordReader, tuple: ABIDecode, tuple: ABIAttribs { + function decode(ptr: ABIDecoder>, reader>, currentHeadOffset: word) returns (memory) { + let prx : Proxy; + match (ABIAttribs.isStatic(prx)) { +case true { +return ABIDecode.decode(WordReader.advance(ptr, currentHeadOffset), 0); +} +case false { +let tailPtr = WordReader.read(ptr); + return ABIDecode.decode(WordReader.advance(ptr, tailPtr), 0); +} +} + } +} + +impl ABIDecode>, reader>, memory>> where baseType: ABIAttribs, reader: WordReader, ABIDecoder: ABIDecode { + function decode(ptr: ABIDecoder>, reader>, currentHeadOffset: word) returns (memory>) { + let arrayPtr = WordReader.advance(ptr, currentHeadOffset); + let length = WordReader.read(arrayPtr); + // this trigger a missing typedef constraint + // let elementPtr:ABIDecoder(baseType, reader) = Typedef.abs(WordReader.advance(arrayPtr, 32)); + arrayPtr = WordReader.advance(arrayPtr, 32); + let prx : Proxy; + let result : memory> = allocateDynamicArray(prx, length); + let offset : word = 0; + let prx : Proxy; + let elementHeadSize : word = ABIAttribs.headSize(prx); + + // TODO: surface level loops + // TODO: sugar for assigning to indexAccess types (result[i]) + //for(let i = 0; i < length; i++) { + //result[i] = ABIDecode.decode(elementPtr, offset); + //assembly { offset := add(offset, elementHeadSize) } + //} + + return result; + } +} + +function getReader(d: ABIDecoder) returns (reader) { + match (d) { +case ABIDecoder(rdr) { +return rdr; +} +} +} + +impl ABIDecode>, CalldataWordReader>, calldata>> where ABIDecoder: ABIDecode, baseType: WordReader { + function decode(ptr: ABIDecoder>, CalldataWordReader>, currentHeadOffset: word) returns (calldata>) { + let newptr = WordReader.advance(ptr, currentHeadOffset); + let reader: CalldataWordReader = getReader(newptr); + let addr: word = Typedef.rep(reader); + return Typedef.abs(addr); + } + } + +// ─── Lazy ABI decode of a calldata dynamic array ───────────────────────────── +// The head slot holds the (args-relative) byte offset to the array data; +// following it lands on the length word. The decoded value is a calldata handle +// to that length word, so the elements are left in calldata and decoded on +// demand (abiArrayLength / abiArrayGet). Because nothing is materialised here, +// this works for any decodable element type — including multi-word ADTs such as +// a `sum<...>` — which the word-per-slot `memory>` path cannot hold. +impl ABIDecode>, CalldataWordReader>, calldata>> where ABIDecoder: ABIDecode { + function decode(ptr: ABIDecoder>, CalldataWordReader>, currentHeadOffset: word) returns (calldata>) { + let headRdr = WordReader.advance(ptr, currentHeadOffset); + let dataOffset : word = WordReader.read(headRdr); + let dataRdr = WordReader.advance(ptr, dataOffset); + let rdr : CalldataWordReader = getReader(dataRdr); + let addr : word = Typedef.rep(rdr); + return Typedef.abs(addr); + } + } + +// Length of a decoded calldata array: the handle points at the length word. +function abiArrayLength(a: calldata>) returns (uint256) { + let rdr : CalldataWordReader = CalldataWordReader(Typedef.rep(a)); + return uint256(WordReader.read(rdr)); +} + +// Decode element `i` of a calldata array on demand. The element region starts +// one word after the handle (past the length word). Two layouts, per the ABI: +// +// * static element type -> elements sit inline, each headSize(t) bytes, so +// element i starts at (handle + 32) + i * headSize(t). The element decoder +// is aimed at the region base and the per-element offset is threaded as the +// head offset. +// +// * dynamic element type -> the region holds a table of 32-byte offsets (one +// per element, relative to the region base), each pointing at that +// element's own encoding (standard-ABI T[] for dynamic T). The element +// decoder is aimed at the region base and given element i's slot as its +// head offset; the element's own dynamic decoder follows that offset. This +// is uniform across element kinds: a dynamic sum follows it and rebases to +// the element start, a bare bytes/string leaf follows it to its length word. +function abiArrayGet(a: calldata>, i: uint256) returns (t_decoded) where t: ABIAttribs, ABIDecoder: ABIDecode { + // Bounds check: valid indices are [0, length); i == length is already past + // the last element, so reject i >= length (mirrors the storage-array guard). + require(i < abiArrayLength(a), Error(0x7f52b2bf)); // ArrayOutOfBounds() + let base : word = Typedef.rep(a); + let elemRegion : word = base + 32; + let prx : Proxy; + let idx : word = Typedef.rep(i); + match (ABIAttribs.isStatic(prx)) { +case true { +let elemRdr : CalldataWordReader = CalldataWordReader(elemRegion); + let dec : ABIDecoder = ABIDecoder(elemRdr); + return ABIDecode.decode(dec, idx * ABIAttribs.headSize(prx)); +} +case false { +// Dynamic elements: the region is a table of 32-byte offsets (relative + // to the region base), one per element. Hand the element decoder the + // region base and element i's slot as its head offset; the element's own + // (dynamic) decoder follows that offset — uniformly for a dynamic sum + // element or a bare bytes/string element (`calldata>`). + let elemRdr : CalldataWordReader = CalldataWordReader(elemRegion); + let dec : ABIDecoder = ABIDecoder(elemRdr); + return ABIDecode.decode(dec, idx * 32); +} +} +} + + +// --- Assignment --- + +/* +# Types and classes for assignemnt desugaring +- access proxy types +- LValue and RValue access classes (LVA, RVA) +- Assign class +*/ + + +pragma no-patterson-condition RVA, Assign; +pragma no-coverage-condition MemberAccessProxy, LVA, RVA, CStructField, Assign; +pragma no-bounded-variable-condition LVA, RVA; + +// --- Storage --- + +// Zeroes the storage slots in [start, endSlot). Mirrors solc's +// clear_storage_range, used when a dynamic array shrinks so that regrowing it +// cannot resurrect the old elements. +function clearStorageRange(start: word, endSlot: word) { + for (; start < endSlot; start += 1) { + sstore(start, 0); + } +} + +trait StorageSize { + function size(x: Proxy) returns (word) ; +} + + +default impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + +impl StorageSize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} + +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} +/* +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} +*/ +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + +impl StorageSize
{ + function size(x: Proxy
) returns (word) { + return 1; + } +} + +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + +impl StorageSize> { + function size(x: Proxy>) returns (word) { + return 1; + } +} + +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + +impl StorageSize> { + function size(x: Proxy>) returns (word) { + return 1; + } +} + +impl StorageSize<(a, b)> where a: StorageSize, b: StorageSize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz:word = StorageSize.size(@a); + let b_sz:word = StorageSize.size(@b); + return a_sz + b_sz; + } +} + +trait StorageType { + function load(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +// How to copy one element of type self from one storage slot to another. +// Whole-array assignment (a = b) copies element by element through this trait, +// the way solc's copy_array_to_storage calls the element's own copy routine. +// The constraint lives on the *element* type, so it can gate CanStore.store for +// storage> without also gating CanStore.load, which must stay +// unconstrained, a field read has to yield the array's storage reference. +// Impls live below, next to the CanStore impls the dynamic ones rely on. +trait StorageCopy { + function copySlot(dst: storage, src: storage) ; +} + +impl StorageType { + function load(ptr: word) returns (word) { + return sload(ptr); + } + function store(ptr: word, value: word) { + sstore(ptr, value); + } +} + +impl StorageType { + function load(ptr: word) returns (uint256) { return uint256(StorageType.load(ptr)); } + function store(ptr: word, value: uint256) { StorageType.store(ptr, Typedef.rep(value)); } +} + +impl StorageType { + function load(ptr: word) returns (bytes32) { return bytes32(StorageType.load(ptr)); } + function store(ptr: word, value: bytes32) { StorageType.store(ptr, Typedef.rep(value)); } +} + +impl StorageType
{ + function load(ptr: word) returns (address) { return address(StorageType.load(ptr)); } + function store(ptr: word, value: address) { StorageType.store(ptr, Typedef.rep(value)); } +} + +// -- structure fields (including contract fields) + +trait CStructField {} +enum StructField { StructField(structType) } + + +enum MemberAccessProxy { MemberAccessProxy(a, field) } + +function memberAccessBase(x: MemberAccessProxy) returns (a) { + match (x) { +case MemberAccessProxy(y,z) { +return y; +} +} +} + + +// ------------------------------------------------------------------ +// Contract field access +// ------------------------------------------------------------------ + +impl LVA, fieldSelector, loadType, offsetType>, storage> where StructField, fieldSelector>: CStructField, offsetType>, offsetType: StorageSize, storage: CanStore { + function acc(x: MemberAccessProxy, fieldSelector, loadType, offsetType>) returns (storage) { + let offset : word = StorageSize.size(@offsetType) ; + let result : storage = storage(offset); + return result; + } +} + +impl RVA, fieldSelector, loadType, offsetType>, loadType> where StructField, fieldSelector>: CStructField, offsetType>, storage: CanStore, offsetType: StorageSize { + function acc(x: MemberAccessProxy, fieldSelector, loadType, offsetType>) returns (loadType) { + let offset:word = StorageSize.size(@offsetType); + let slot : storage = storage(offset); + return CanStore.load(slot); + } +} + +// TODO: structures other than contract context +/* +impl + LVA, fieldSelector, fieldType, offsetType>, storage> + where StructField: CStructField, + offsetType: StorageSize { + function acc(x: MemberAccessProxy, fieldSelector, fieldType, offsetType>) returns (storage) { + let ptr:word = Typedef.rep(memberAccessBase(x)); + let size:word = StorageSize.size(@offsetType); + return storage(ptr + size); + } +} + +impl + RVA, fieldSelector, fieldType, offsetType>, fieldType> + where StructField: CStructField, + offsetType: StorageSize, + fieldType: StorageType { + function acc(x: MemberAccessProxy, fieldSelector, fieldType, offsetType>) returns (fieldType) { + let ptr:word = Typedef.rep(memberAccessBase(x)); + let size:word = StorageSize.size(@offsetType); + let field: storage = storage(ptr + size); + return CanStore.load(field); + } +} +*/ + + + +enum ContractStorage { ContractStorage(cxt) } + + +impl Typedef member), word> { + function rep(x: mapping(index => member)) returns (word) { + match (x) { +case mapping(y) { +return y; +} +} + } + function abs(x: word) returns (mapping(index => member)) { + return mapping(x); + } +} + + +// cf https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays +impl StorageSize member)> { + function size(x: Proxy member)>) returns (word) { + return 1; + } +} + +impl Typedef, word> { + function rep(x: array) returns (word) { + match (x) { +case array(y) { +return y; +} +} + } + function abs(x: word) returns (array) { + return array(x); + } +} + +// cf https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays +// the slot itself stores the array length; elements live at keccak256(slot) + i +impl StorageSize> { + function size(x: Proxy>) returns (word) { + return 1; + } +} + +trait Length { + function length(arr: self) returns (uint256) ; +} + +// Dynamic storage arrays carry their length at the slot itself (matching the +// Solidity convention) while elements live at keccak256(slot) + i. +trait Array { + function setLength(arr: self, n: uint256) ; + function pop(arr: self) ; +} + +// push is split into its own MPTC so its element type only shows up where it +// actually matters (the value being appended), without forcing `length`/ +// `setLength`/`pop` to drag along an unconstrained `elem` parameter. +trait ArrayPush { + function push(arr: self, val: elem) ; +} + +impl Length>> { + function length(arr: storage>) returns (uint256) { + return uint256(sload(Typedef.rep(arr))); + } +} + +// A lazily-decoded calldata array reports its length from the head length-word +// of its handle (see abiArrayLength), so `arr.length()` resolves through the +// same Length trait / UFCS as storage arrays. +impl Length>> { + function length(arr: calldata>) returns (uint256) { + return abiArrayLength(arr); + } +} + +impl Array>> { + // Shrinking clears the abandoned slots, matching solc's resize_array. + // For string/bytes elements this zeroes the inline slot, which makes any + // keccak-derived tail unreachable (reads are governed by the length word) but + // does not reclaim it. + function setLength(arr: storage>, n: uint256) { + let slot : word = Typedef.rep(arr); + let oldLen : word = sload(slot); + let newLen : word = Typedef.rep(n); + if (newLen < oldLen) { + let base : word = hash1(slot); + clearStorageRange(base + newLen, base + oldLen); + } + sstore(slot, newLen); + } + // Zeroes the removed element before decrementing, as solc's array_pop does. + function pop(arr: storage>) { + let slot : word = Typedef.rep(arr); + let n : word = sload(slot); + if (n == 0) { out_of_bounds(); } + sstore(hash1(slot) + (n - 1), 0); + sstore(slot, n - 1); + } +} + +// The value pushed is whatever the element's storage reference can store, rather +// than the element tag type itself. That is what lets array accept a +// memory, via `storage: CanStore>`. For word-sized +// elements v collapses to the element type and CanStore.store delegates to +// StorageType.store, so the generated code is unchanged. +impl ArrayPush>, v> where storage: CanStore { + function push(arr: storage>, val: v) { + let slot : word = Typedef.rep(arr); + let n : word = sload(slot); + let element : storage = storage(hash1(slot) + n); + CanStore.store(element, val); + sstore(slot, n + 1); + } +} + +trait LVA { + function acc(x: self) returns (memberRefType) ; +} + + +trait RVA { + function acc(x: self) returns (member) ; +} + +function rval(x: a) returns (b) where a: RVA { + return RVA.acc(x); +} + + +// TODO: consider merging CanStore and Assign +trait Assign { + function assign(l: lhs, r: rhs) ; +} + + +// `a` can store `b`; e.g. `storage: CanStore>`. +trait CanStore { + function store(r: a, v: b) ; + function load(r: a) returns (b) ; +} + + +impl Assign where a: CanStore { + function assign(l: a, r: b) { + CanStore.store(l, r); + } +} + +/* +default impl CanStore, a> where a: StorageType { + function store(l: storage, r: a) { + StorageType.store(Typedef.rep(l), r); + } + function load(l: storage) returns (a) { + return StorageType.load(Typedef.rep(l)); + } +} +*/ + + impl CanStore, word> { + function store(l: storage, r: word) { + StorageType.store(Typedef.rep(l), r); + } + function load(l: storage) returns (word) { + return StorageType.load(Typedef.rep(l)); + } +} + + impl CanStore, uint256> { + function store(l: storage, r: uint256) { + StorageType.store(Typedef.rep(l), r); + } + function load(l: storage) returns (uint256) { + return StorageType.load(Typedef.rep(l)); + } +} + + impl CanStore, bytes32> { + function store(l: storage, r: bytes32) { + StorageType.store(Typedef.rep(l), r); + } + function load(l: storage) returns (bytes32) { + return StorageType.load(Typedef.rep(l)); + } +} + + impl CanStore, address> { + function store(l: storage
, r: address) { + StorageType.store(Typedef.rep(l), r); + } + function load(l: storage
) returns (address) { + return StorageType.load(Typedef.rep(l)); + } +} + +// bool has no StorageType impl (it is a builtin, not a Typedef), but it +// round-trips through word via frombool / tobool, so it can still be stored. +impl CanStore, bool> { + function store(l: storage, r: bool) { + StorageType.store(Typedef.rep(l), frombool(r)); + } + function load(l: storage) returns (bool) { + return tobool(StorageType.load(Typedef.rep(l))); + } +} + +impl CanStore v)>, storage v)>> { + function store(l: storage v)>, r: storage v)>) { + // StorageType.store(Typedef.rep(l), r); + unimplemented(); + } + function load(l: storage v)>) returns (storage v)>) { + // "Loading" a storage mapping field yields its storage reference (the + // slot); indexed access / method calls consume that reference directly. + return l; + } +} + +impl CanStore>, storage>> where v: StorageCopy { + // Whole-array assignment is a deep copy, as in Solidity: a = b resizes a + // to b's length and then copies every + // element. Assigning an array to itself is a no-op. A *local* bound to an + // array field stays an alias, because a let is not an Assign.assign. + function store(l: storage>, r: storage>) { + let dst : word = Typedef.rep(l); + let src : word = Typedef.rep(r); + if (dst != src) { + let oldLen : word = sload(dst); + let newLen : word = sload(src); + let dstBase : word = hash1(dst); + if (newLen < oldLen) { + clearStorageRange(dstBase + newLen, dstBase + oldLen); + } + sstore(dst, newLen); + let srcBase : word = hash1(src); + for (let i = 0; i < newLen; i += 1) { + let dstSlot : storage = storage(dstBase + i); + let srcSlot : storage = storage(srcBase + i); + StorageCopy.copySlot(dstSlot, srcSlot); + } + } + } + function load(l: storage>) returns (storage>) { + // "Loading" a storage array field yields its storage reference (the + // slot). push / pop / length / arr[i] all consume that reference, so a + // field read like `ArrayPush.push(members, x)` must return the slot, + // not a copy. + return l; + } +} + +// Assigning an array literal to a storage array field: `xs = [1,2,3]`. +// +// This is Solidity's memory -> storage array copy. It is a plain function, not +// a CanStore impl, on purpose: impl overlap is decided by the main type alone, +// so a second CanStore impl for storage> would clash with +// the deep-copy one above. FieldAccess routes `field = ` here +// instead of through Assign.assign. +// +// Array.setLength resizes and clears the abandoned tail, so old elements never +// resurrect. The element types differ: `t` is the storage element tag and `v` +// what a value of it looks like in memory (they coincide for word-sized +// elements; for array, t = string and v = memory). +function storeArrayLit(dst: storage>, src: memory>) where storage: CanStore, v: Typedef { + let n : word = mload(Typedef.rep(src)); + Array.setLength(dst, uint256(n)); + let base : word = hash1(Typedef.rep(dst)); + let i : word = 0; + for (; i < n; i += 1) { + let element : storage = storage(base + i); + CanStore.store(element, IndexAccess.get(src, uint256(i))); + } +} + +impl CanStore, memory> { + function store(dst: storage, src: memory) { + let srcPtr : word = Typedef.rep(src); + let slot = Typedef.rep(dst); + storeBytesFromMemory(slot, srcPtr); + } + + function load(src: storage) returns (memory) { + let srcPtr : word = Typedef.rep(src); + let dstPtr : word = get_free_memory(); + let endPtr = loadBytesFromStorage(srcPtr, dstPtr); + set_free_memory(endPtr); + return memory(dstPtr); + } +} + +// bytes share the same storage layout as string, so the same +// storeBytesFromMemory / loadBytesFromStorage helpers apply. +impl CanStore, memory> { + function store(dst: storage, src: memory) { + let srcPtr : word = Typedef.rep(src); + let slot = Typedef.rep(dst); + storeBytesFromMemory(slot, srcPtr); + } + + function load(src: storage) returns (memory) { + let srcPtr : word = Typedef.rep(src); + let dstPtr : word = get_free_memory(); + let endPtr = loadBytesFromStorage(srcPtr, dstPtr); + set_free_memory(endPtr); + return memory(dstPtr); + } +} + +// --- StorageCopy: per-element copy used by whole-array assignment --- + +// Word-sized elements are self-contained: the slot is the value. +impl StorageCopy { + function copySlot(dst: storage, src: storage) { + sstore(Typedef.rep(dst), sload(Typedef.rep(src))); + } +} +impl StorageCopy { + function copySlot(dst: storage, src: storage) { + sstore(Typedef.rep(dst), sload(Typedef.rep(src))); + } +} +impl StorageCopy { + function copySlot(dst: storage, src: storage) { + sstore(Typedef.rep(dst), sload(Typedef.rep(src))); + } +} +impl StorageCopy
{ + function copySlot(dst: storage
, src: storage
) { + sstore(Typedef.rep(dst), sload(Typedef.rep(src))); + } +} + +// Dynamic elements keep their payload at keccak256(elementSlot), so copying the +// inline slot alone would leave the destination pointing at the *source's* tail. +// Round-tripping through memory copies the payload too. +impl StorageCopy { + function copySlot(dst: storage, src: storage) { + let value : memory = CanStore.load(src); + CanStore.store(dst, value); + } +} +impl StorageCopy { + function copySlot(dst: storage, src: storage) { + let value : memory = CanStore.load(src); + CanStore.store(dst, value); + } +} + +// Nested arrays recurse into the array CanStore impl above. The recursion is +// on the element type, so it terminates with the type's structure. +impl StorageCopy> where t: StorageCopy { + function copySlot(dst: storage>, src: storage>) { + CanStore.store(dst, src); + } +} + +// Shamelessly stolen from function copy_byte_array_to_storage_from_t_bytes_memory_ptr_to_t_bytes_storage +// TODO: consider wrapping behaviour at end of storage +function storeBytesFromMemory(slot: word, src: word) { + assembly { + let newLen := mload(src) + // TODO: check old len, cleanup etc + let srcOffset := 32 + switch gt(newLen, 31) + case 1 { + mstore(0,slot) + let dstPtr := keccak256(0,32) + let loopEnd := and(newLen, not(0x1f)) + let i := 0 + for { } lt(i, loopEnd) { i := add(i, 0x20) } { + sstore(dstPtr, mload(add(src, srcOffset))) + dstPtr := add(dstPtr, 1) + srcOffset := add(srcOffset, 32) + } + if lt(loopEnd, newLen) { + let lastValue := mload(add(src, srcOffset)) + let lastLen := and(newLen, 0x1f) + let mask := not(shr(mul(8, lastLen), not(0))) + let nudata := and(lastValue, mask) // a Yul variable cannot be called "data". Go figure. + sstore(dstPtr, nudata) + } + sstore(slot, add(mul(newLen, 2), 1)) + } + default { + let value := 0 + if newLen { + value := mload(add(src, srcOffset)) + } + let mask := not(shr(mul(8, newLen), not(0))) + let nudata := and(value, mask) + let used := or(nudata, mul(2, newLen)) + sstore(slot,used) + } + } +} + + +// shamelessly stolen from abi_encode_t_string_storage_to_t_string_memory_ptr +function loadBytesFromStorage(slot: word, memPtr: word) returns (word) { + let pos = memPtr; + let slotValue = sload(slot); + let length = slotValue / 2; + let outOfPlaceEncoding = tobool(slotValue & 1); + if (!outOfPlaceEncoding) { + length &= 0x7f; + } + mstore(pos, length); + pos += 32; + match (outOfPlaceEncoding) { +case false { +// Short byte array + mstore(pos, slotValue & ~0xff); + let empty = iszero(length); + let notzero = iszero(empty); + return pos + (notzero * 32); +} +case true { +// Long byte array + let dataPos = hash1(slot); + let i = 0; + for (; i < length; i += 32, dataPos += 1) { + mstore(pos + i, sload(dataPos)); + } + return pos + i; +} +} +} + + +// -- Tuple-based indexed access: + +trait RValueIdxAccess { + function lookup(ci: col_idx) returns (val) ; +} + +trait LValueIdxAccess { + function lookup(ci: col_idx) returns (ref) ; +} + +impl LValueIdxAccess<(storage a)>, i), storage> where i: Typedef { + function lookup(xi: (storage a)>, i)) returns (storage) { + match (xi) { +case (x, i) { +return storage(hash2(Typedef.rep(x), Typedef.rep(i))); +} +} + } +} + +impl RValueIdxAccess<(storage a)>, i), a> where storage: CanStore, i: Typedef { + function lookup(xi: (storage a)>, i)) returns (a) { + /* + match(xi) { + | (x, i) => return StorageType.load(hash2(Typedef.rep(x), Typedef.rep(i))); + } + */ + return readStorage(LValueIdxAccess.lookup(xi)); + } +} + +impl LValueIdxAccess<(storage>, i), storage> where i: Typedef { + function lookup(xi: (storage>, i)) returns (storage) { + match (xi) { +case (x, i) { +let slot : word = Typedef.rep(x); + let idx : word = Typedef.rep(i); + // Bounds check: idx must be in [0, length). Length lives at the + // slot itself; inlined to avoid an Array(t) dispatch here. + if (idx >= sload(slot)) { out_of_bounds(); } + return storage(hash1(slot) + idx); +} +} + } +} + +// Reading arr[i] yields whatever the element's storage reference loads, rather +// than the element tag type. For word-sized elements that is the element itself; +// for array it is a memory; for a nested array> it +// is the inner array's handle, which push/pop/length then consume. +impl RValueIdxAccess<(storage>, i), v> where storage: CanStore, i: Typedef { + function lookup(xi: (storage>, i)) returns (v) { + return CanStore.load(LValueIdxAccess.lookup(xi)); + } +} + +// Indexed read of a lazily-decoded calldata array: `arr[i]` desugars to +// ridx(arr, i), which dispatches here and decodes element i on demand via +// abiArrayGet. There is deliberately no LValueIdxAccess impl — calldata is +// immutable, so `arr[i] = …` is (correctly) rejected at compile time. +impl RValueIdxAccess<(calldata>, i), t_decoded> where t: ABIAttribs, ABIDecoder: ABIDecode, i: Typedef { + function lookup(xi: (calldata>, i)) returns (t_decoded) { + match (xi) { +case (a, idx) { +return abiArrayGet(a, uint256(Typedef.rep(idx))); +} +} + } +} + +// Memory arrays are read-only through `m[i]`: there is no memory cell reference +// type, so they get an RValue impl but no LValue one. +impl RValueIdxAccess<(memory>, i), t> where t: Typedef, i: Typedef { + function lookup(xi: (memory>, i)) returns (t) { + match (xi) { +case (x, j) { +return IndexAccess.get(x, uint256(Typedef.rep(j))); +} +} + } +} + + +// Mapping reads go through CanStore, matching the write side (Assign -> CanStore.store). +// This lets a mapping hold any value with a CanStore impl — including ADTs whose +// fields are dynamic (memory) — not just the fixed-slot StorageType primitives. +function readStorage(x: storage) returns (a) where storage: CanStore { + return CanStore.load(x); +} +/* +function rval(x: r) returns (a) where a: StorageType, r: RValueIdxAccess { + return RValueIdxAccess.lookup(x); +} + +function lval(x: r) returns (a) where r: LValueIdxAccess { + return LValueIdxAccess.lookup(x); +} +*/ + +// lidx/ridx are the generic indexed-access helpers used by the `arr[i]` +// desugaring. They dispatch through LValueIdxAccess / RValueIdxAccess, so any +// collection (mapping, array, ...) that provides those impls supports the +// `arr[i]` syntax. +function lidx(c: col, i: idx) returns (ref) where (col, idx): LValueIdxAccess { + return LValueIdxAccess.lookup((c, i)); +} + +function ridx(c: col, i: idx) returns (val) where (col, idx): RValueIdxAccess { + return RValueIdxAccess.lookup((c, i)); +} + +// --- Memory Encoding --- + +trait MemorySize { + // The size needed for the value. + function len(v: t) returns (word) ; +} + +// NOTE: this is not implemented for value types. +trait MemoryPointer { + // In-memory location of the given value. + function ptr(v: t) returns (word) ; +} + +trait MemoryEncode { + // Serialize the entire contents at a provided memory area. + function encodeInto(v: t, target: word) ; +} + +// TODO: support variadic arguments +// Allocates new memory and concatenates the inputs into it. +function concat(x: a, y: b) returns (memory) where a: MemorySize, a: MemoryEncode, b: MemorySize, b: MemoryEncode { + let x_len = MemorySize.len(x); + let y_len = MemorySize.len(y); + let res: word = allocate_memory(32 + x_len + y_len); + mstore(res, x_len + y_len); + MemoryEncode.encodeInto(x, res + 32); + MemoryEncode.encodeInto(y, res + 32 + x_len); + return memory(res); +} + +// This is a specialized 1-input version of concat. +function to_bytes(x: a) returns (memory) where a: MemorySize, a: MemoryEncode { + let len = MemorySize.len(x); + let res = allocate_memory(32 + len); + mstore(res, len); + MemoryEncode.encodeInto(x, res + 32); + return memory(res); +} + +impl MemorySize { + function len(v: bytes32) returns (word) { + return 32; + } +} + +impl MemoryEncode { + function encodeInto(v: bytes32, target: word) { + mstore(target, Typedef.rep(v)); + } +} + +impl MemorySize> { + function len(v: memory) returns (word) { + return mload(Typedef.rep(v)); + } +} + +impl MemoryPointer> { + function ptr(v: memory) returns (word) { + return Typedef.rep(v) + 32; + } +} + +impl MemoryEncode> { + function encodeInto(v: memory, target: word) { + let v_ = Typedef.rep(v); + mcopy(target, v_ + 32, mload(v_)); + } +} + +// Placeholder for an empty memory area. +// The value is the size of the area in bytes. The area will be zeroed upon serialization. +// NOTE: not implementing Typedef by design. +enum empty { empty(word) } + +impl MemorySize { + function len(v: empty) returns (word) { + match (v) { +case empty(size) { +return size; +} +} + } +} + +impl MemoryEncode { + function encodeInto(v: empty, target: word) { + let size; + match (v) { +case empty(size_) { +size = size_; +} +} + zeroize_memory(target, size); + } +} + +// --- Memory Slices --- + +// This is a very cheap abstraction over a memory area of [ptr, ptr+len) +// No type information is preserved. +enum memory_ref { memory_ref(word, word) } + +impl MemorySize { + function len(v: memory_ref) returns (word) { + match (v) { +case memory_ref(ptr, len) { +return len; +} +} + } +} + +impl MemoryPointer { + function ptr(v: memory_ref) returns (word) { + match (v) { +case memory_ref(ptr, len) { +return ptr; +} +} + } +} + +impl MemoryEncode { + function encodeInto(v: memory_ref, target: word) { + match (v) { +case memory_ref(ptr, len) { +mcopy(target, ptr, len); +} +} + } +} + +function slice_(input: a, start: word) returns (memory_ref) where a: MemorySize, a: MemoryPointer { + let len = MemorySize.len(input); + // TODO: should this allow (it does now) a zero-length slice? + require(len >= start, Error(0xb4120f14)); // OutOfBounds() + return memory_ref(MemoryPointer.ptr(input) + start, len - start); +} + +function truncate(input: a, end: word) returns (memory_ref) where a: MemorySize, a: MemoryPointer { + let len = MemorySize.len(input); + // TODO: should this allow (it does now) a zero-length slice? + require(len >= end, Error(0xb4120f14)); // OutOfBounds() + return memory_ref(MemoryPointer.ptr(input), end); +} + +// --- Hashing --- + +// NOTE: keccak256 name conflicts with assembly namespace +function keccak256_(input: a) returns (bytes32) where a: MemorySize, a: MemoryPointer { + let len : word = MemorySize.len(input); + let ptr : word = MemoryPointer.ptr(input); + return bytes32(keccak256(ptr, len)); +} + +function sha256(input: a) returns (bytes32) where a: MemorySize, a: MemoryPointer { + let len : word = MemorySize.len(input); + let ptr : word = MemoryPointer.ptr(input); + // We assume the [0, 32] scratch space is reserved. + let ret = staticcall(gas(), 2, ptr, len, 0, 32); + require(ret != 0, Error(0x68c071bb)); // SHA256CallFailed() + return bytes32(mload(0)); +} + +function ripemd160(input: a) returns (bytes32) where a: MemorySize, a: MemoryPointer { + let len : word = MemorySize.len(input); + let ptr : word = MemoryPointer.ptr(input); + // We assume the [0, 32] scratch space is reserved. + let ret = staticcall(gas(), 3, ptr, len, 0, 32); + require(ret != 0, Error(0x31a72d92)); // RIPEMD160CallFailed() + return bytes32(mload(0)); +} + +// --- Precompiles --- + +// Perform an ECDSA signature recovery. It ensures the call has succeeded, +// and that the signature is not malleable (s ≤ secp256k1n/2). Transactions +// were updated to ban this, but the precompile wasn't. If a user relies on that +// feature they can call the precompile via assembly. +// TODO: use uint8 +function ecrecover(hash: bytes32, v: uint256, r: bytes32, s: bytes32) returns (address) { + // MalleableSignatureRejected() + require( + Typedef.rep(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, + Error(0x25260b20) + ); + + let hash_ = Typedef.rep(hash); + let v_ = Typedef.rep(v); + let r_ = Typedef.rep(r); + let s_ = Typedef.rep(s); + let ptr = get_free_memory(); + // We assume the [0, 32] scratch space is reserved. + mstore(ptr, hash_); + mstore(ptr + 32, v_); + mstore(ptr + 64, r_); + mstore(ptr + 96, s_); + // Clear the [0, 32] scratch space that receives the return data. On a + // failed recovery (e.g. v not in {27, 28}, or the generic could-not-recover + // case) the precompile still reports success but returns no data, leaving + // the output area untouched. Without this, a stale non-zero value would + // slip past the `res != 0` check below and yield a bogus address. + mstore(0, 0); + let ret = staticcall(gas(), 1, ptr, 128, 0, 32); + require(ret != 0, Error(0x578763f7)); // ECRecoverCallFailed() + let res = mload(0); + require(res != 0, Error(0x4fbfae63)); // ECRecoverFailed() + return address(res); +} + +// ERC-7201 namespaced storage slot, computed entirely at compile time from a +// string-literal namespace `id`: +// keccak256(abi.encode(uint256(keccak256(bytes(id))) - 1)) & ~bytes32(uint256(0xff)) +function erc7201(comptime id: string) returns (comptime) { + return bytes32(keccakWordLit(keccakLit(id) - 1) & ~0xff); +} + +function raw_call(target: address, value: uint256, payload: a) returns (bool, memory) where a: MemorySize, a: MemoryPointer { + let ret = call( + gas(), + Typedef.rep(target), + Typedef.rep(value), + MemoryPointer.ptr(payload), + MemorySize.len(payload), + 0, + 0 + ); + let retSize = returndatasize(); + let retData = allocate_memory(32 + retSize); + mstore(retData, retSize); + returndatacopy(retData + 32, 0, retSize); + return (tobool(ret), memory(retData)); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/std/std.solc b/crates/parser/tests/fixtures/corpus/ok/std/std.solc deleted file mode 100644 index e095c148..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/std/std.solc +++ /dev/null @@ -1,2822 +0,0 @@ -import std.opcodes.{add, sub, mul, div, mod, addmod as addmod_, mulmod as mulmod_, and as and_, or as or_, xor as xor_, shl, shr, eq, not as not_, gt as gt_, iszero, keccak256, mstore, mload, mcopy, sstore, sload, gas, calldataload, calldatacopy, returndatasize, returndatacopy, log1 as log1_, call, staticcall, revert as revert_, invalid}; - -pragma no-patterson-condition ABIEncode, Num, Array, ArrayPush, Eq, Ord; -pragma no-coverage-condition ABIDecode, MemoryType, Array, ArrayPush, RValueIdxAccess; - -export { - ABIAttribs, - ABIDecode, - ABIDecoder(*), - ABIEncode, - ABITuple(*), - Add, - Array, - ArrayPush, - Assign, - BitAnd, - BitNot, - BitOr, - BitXor, - Bounded, - CalldataWordReader(*), - CanStore, - ContractStorage(*), - Div, - DynArray, - Error(*), - Eq, - HasWordReader, - IndexAccess, - LVA, - LValueIdxAccess, - Length, - MemberAccessProxy(*), - MemoryEncode, - MemoryPointer, - MemorySize, - MemoryType, - MemoryWordReader(*), - Mod, - Mul, - Num, - Ord, - Proxy(*), - RVA, - RValueIdxAccess, - StorageCopy, - StorageSize, - StorageType, - StructField(*), - Sub, - Typedef, - WordReader, - abi_decode, - abi_encode, - absurd, - addWord, - addmod, - allocateDynamicArray, - address(*), - allocate_memory, - allocate_zeroed_memory, - and, - array(*), - arrayLitInit, - arrayLitNew, - assert, - byte(*), - bytes, - bytes4(*), - bytes32(*), - bandWord, - borWord, - bxorWord, - bnotWord, - bshlWord, - bshrWord, - calldata(*), - concat, - concatLit, - ecrecover, - empty(*), - eqWord, - erc7201, - frombool, - ge, - getReader, - get_free_memory, - gt, - gtWord, - hash1, - hash2, - keccak256_, - keccakLit, - keccakWordLit, - le, - lidx, - loadBytesFromStorage, - log1, - lt, - mapping(*), - maxVal, - maxWord, - memberAccessBase, - memory(*), - memory_ref, - minWord, - mulmod, - ne, - not, - or, - out_of_bounds, - raw_call, - readStorage, - returndata(*), - revertLit, - revertEmpty, - revertWithError, - require, - ridx, - ripemd160, - round_up_to_mul_of_32, - rval, - set_free_memory, - sha256, - slice(*), - slice_, - storage(*), - storeArrayLit, - storeBytesFromMemory, - string, - strlen, - strlenLit, - subWord, - truncate, - toWord, - to_bytes, - tobool, - uint256(*), - unimplemented, - zeroize_memory -}; - -/* -- features - - primitive word eq - - include stdlib - - MPTC + optional weak args (MPTC formalization?) - - surface for loops - - better inference for Typedef.rep() calls (have to annotate atm?) - - boolean short circuiting -- sugar - - Proxy (e.g. `@t ==> Proxy : Proxy t` - - IndexAccess reads (e.g. `x[i] ==> IndexAccess.get(x, i)`) - - auto typedef instances -- syntax - - order of type args - - braces for blocks in matches - - trait / impl vs class / instance - - function -> fn? - - assembly vs high level return? -- todo - - abi decoding - - contract desugaring - - mappings - - strings - - full range of uintX / intX / bytesX types - - address types - - statically sized arrays - - tuple field access - - structs - - define numeric tower - - fixed point types - - fixed point numeric routines - - memory vectors -*/ - - -forall t.t:Typedef(word) => -function log1(v:t, topic:word) -> () { - let w : word = Typedef.rep(v); - mstore(0, w); - log1_(0, 32, topic); -} - -function unimplemented() -> () { - let Unimplemented = Error(0x6e128399); - revertWithError(Unimplemented); -} - -function out_of_bounds() -> () { - let OutOfBounds = Error(0xb4120f14); - revertWithError(OutOfBounds); -} - -// ------------------------------------------------------------------ -// High-level revert helper -// ------------------------------------------------------------------ -// EmitHull has special handling for `revertLit("...")` after MastEval has -// constant-folded the argument to a string literal. -function revertLit(comptime s: string) -> () { - unimplemented(); // Sanity check if folding ignores it. - return (); -} - -// Empty revert. -function revertEmpty() -> () { - revert_(0, 0); -} - -// Bottom: a value of any type. absurd never returns, it reverts, so it can -// stand in for a result of any type. Used to derive class instances for empty -// data types (which have no values, so the method bodies are unreachable). The -// recursive tail satisfies the forall a . a return type; execution never -// reaches it because revertEmpty() aborts first. -forall a . function absurd() -> a { - // Despite looking like an infinite loop, this reverts: revertEmpty() - // aborts execution on the first line, so the recursive return absurd() - // is never actually run. The recursion exists only to give the body a - // value of type a, satisfying the forall a . a return type. - revertEmpty(); - return absurd(); -} - -// TODO: use bytes4 -data Error = Error(word) | Empty | Msg(memory(string)); - -// A string literal can be used as an Error: `require(cond, "message")` reverts -// with the message. The literal is materialized into memory(string) here; MastEval -// erases the comptime-only parameter by cloning this method per literal, so -// the materializer sees a literal rather than a parameter. -instance Error : Str { - function fromString(s: string) -> Error { - return Error.Msg(Str.fromString(s)); - } -} - -// Revert with Error selector. -function revertWithError(e:Error) -> () { - match e { - | .Error(selector) => - mstore(0, selector); - // We only care about the BE MSB. - revert_(28, 4); - | .Empty => - revert_(0, 0); - | .Msg(msg) => - let msg_ = Typedef.rep(msg); - revert_(msg_ + 32, mload(msg_)); - } -} - -function assert(cond: bool) -> () { - if (!cond) { - invalid(); - } -} - -function require(cond: bool, e: Error) -> () { - if (!cond) { - revertWithError(e); - } -} - -// --- booleans --- - -// TODO: this should short circuit. probably needs some compiler magic to do so. -function and(x: bool, y: bool) -> bool { - match x, y { - | true, y => return y; - | false, _ => return false; - } -} - -// TODO: this should short circuit. probably needs some compiler magic to do so. -function or(x: bool, y: bool) -> bool { - match x, y { - | true, _ => return true; - | false, y => return y; - } -} - -function not(b:bool) -> bool { - match b { - | false => return true; - | true => return false; - } -} - -function frombool(b : bool) -> word { - match b { - | false => return 0; - | true => return 1; - } -} - -function tobool(x: word) -> bool { - match x { - | 0 => return false; - | _ => return true; - } -} - -// --- Tuple projections --- - -forall a b . function fst(p: (a, b)) -> a { - match p { - | (a, _) => return a; - } -} - -forall a b . function snd(p: (a, b)) -> b { - match p { - | (_, b) => return b; - } -} - -// --- Proxy --- - -// Proxy is a unit type that can be used to pass Types as paramaters at runtime -data Proxy(t) = Proxy; - -// --- Type Abstraction --- - -forall abs rep . class abs:Typedef(rep) { - function abs(x:rep) -> abs; - function rep(x:abs) -> rep; -} - -forall t. -default instance t:Typedef(t) { - function abs(x:t) -> t { return x; } - function rep(x:t) -> t { return x; } -} - -// --- Equality --- -// Note: All these are used by the compiler by name. - -forall a. -class a:Eq { - function eq(x:a, y:a) -> bool; -} - -forall a. a:Eq => -function ne(x:a, y:a) -> bool { - return not(Eq.eq(x,y)); -} - -// --- Ordering --- -// Note: All these are used by the compiler by name. - -forall a. a:Eq => -class a:Ord { - function gt(x:a, y:a) -> bool; -} - -forall a. a:Ord => -function gt(x:a, y:a) -> bool { - return Ord.gt(x,y); -} - -forall a. a:Ord => -function le(x:a, y:a) -> bool { - return not(Ord.gt(x,y)); -} - -forall a. a:Ord => -function ge(x:a, y:a) -> bool { - return le(y,x); -} - -forall a. a:Ord => -function lt(x:a, y:a) -> bool { - return Ord.gt(y,x); -} - -// --- Generic deriving: structural instances over the representation universe --- -// These let `#[derive(Eq)]` / `#[derive(Ord)]` work for any data type through -// its Generic(rep) instance, where rep is built from (), sum(f, g) and (f, g). - -instance () : Eq { - function eq(x : (), y : ()) -> bool { - return true; - } -} - -forall f g . f:Eq, g:Eq => -instance sum(f, g) : Eq { - function eq(x : sum(f, g), y : sum(f, g)) -> bool { - match x { - | inl(a) => - match y { - | inl(b) => return Eq.eq(a, b); - | inr(b) => return false; - } - | inr(a) => - match y { - | inl(b) => return false; - | inr(b) => return Eq.eq(a, b); - } - } - } -} - -forall f g . f:Eq, g:Eq => -instance (f, g) : Eq { - function eq(x : (f, g), y : (f, g)) -> bool { - match x { - | (a1, b1) => - match y { - | (a2, b2) => - match Eq.eq(a1, a2) { - | true => return Eq.eq(b1, b2); - | false => return false; - } - } - } - } -} - -instance () : Ord { - function gt(x : (), y : ()) -> bool { - return false; - } -} - -forall f g . f:Ord, g:Ord => -instance sum(f, g) : Ord { - function gt(x : sum(f, g), y : sum(f, g)) -> bool { - match x { - | inl(a) => - match y { - | inl(b) => return Ord.gt(a, b); - | inr(b) => return false; - } - | inr(a) => - match y { - | inl(b) => return true; - | inr(b) => return Ord.gt(a, b); - } - } - } -} - -forall f g . f:Ord, g:Ord => -instance (f, g) : Ord { - function gt(x : (f, g), y : (f, g)) -> bool { - match x { - | (a1, b1) => - match y { - | (a2, b2) => - match Ord.gt(a1, a2) { - | true => return true; - | false => - match Eq.eq(a1, a2) { - | true => return Ord.gt(b1, b2); - | false => return false; - } - } - } - } - } -} - -// --- Arithmetic --- -// Note: All these are used by the compiler by name. - -forall t . class t:Add { - function add(l: t, r: t) -> t; -} - -forall t . class t:Sub { - function sub(l: t, r: t) -> t; -} - -forall t . class t:Mul { - function mul(l: t, r: t) -> t; -} - -forall t . class t:Div { - function div(l: t, r: t) -> t; -} - -forall t . class t:Mod { - function mod(l: t, r: t) -> t; -} - -forall t . class t:BitAnd { - function band(l: t, r: t) -> t; -} - -forall t . class t:BitOr { - function bor(l: t, r: t) -> t; -} - -forall t . class t:BitXor { - function bxor(l: t, r: t) -> t; -} - -forall t . class t:BitNot { - function bnot(x: t) -> t; -} - -forall t . class t:Bounded { - function minVal() -> t; - function maxVal() -> t; -} - -forall t . t:Bounded => -function maxVal() -> t { return Bounded.maxVal(); } - -// umbrella class -forall a. a:Add, a:Sub, a:Bounded, a:Eq, a:Ord, a:Typedef(word) => -class a:Num { - function maxVal() -> a; - function toWord(x:a) -> word; - function fromWord(x:word) -> a; - function fromInteger(comptime x:integer) -> comptime a; - function add(x:a, y:a) -> a; - function sub(x:a, y:a) -> a; - function gt(x:a, y:a) -> bool; -} - -forall a. a:Add, a:Sub, a:Bounded, a:Eq, a:Ord, a:Typedef(word) => -default instance a:Num { - function maxVal() -> a { return Bounded.maxVal(); } - function toWord(x:a) -> word { return Typedef.rep(x); } - function fromWord(x:word) -> a { return Typedef.abs(x); } - function fromInteger(comptime x:integer) -> comptime a { return Typedef.abs(wordFromInteger(x)); } - function add(x:a, y:a) -> a { return Add.add(x,y); } - function sub(x:a, y:a) -> a { return Sub.sub(x,y); } - function gt(x: a, y: a) -> bool { return Ord.gt(x, y); } -} - -// --- Word Arithmetic & Logic --- -// TODO: make these checked - -// These are intended to be folded by MastEval when their arguments are -// statically known word values. -function eqWord(x:word, y:word) -> bool { - return tobool(eq(x, y)); -} - -function gtWord(x:word, y:word) -> bool { - return tobool(gt_(x, y)); -} - -function maxWord(a : word, b : word) -> word { - match gtWord(a, b) { - | true => return a; - | false => return b; - } -} - -function minWord(a : word, b : word) -> word { - match gtWord(a, b) { - | true => return b; - | false => return a; - } -} - -function addWord(l: word, r: word) -> word { - return add(l, r); -} - -function subWord(l: word, r: word) -> word { - return sub(l, r); -} - -// Bitwise AND -function bandWord(x: word, y: word) -> word { - return and_(x, y); -} - -// Bitwise OR -function borWord(x: word, y: word) -> word { - return or_(x, y); -} - -// Bitwise XOR -function bxorWord(x: word, y: word) -> word { - return xor_(x, y); -} - -// Bitwise NOT -function bnotWord(x: word) -> word { - return not_(x); -} - -// Bitwise SHL -function bshlWord(x: word, y: word) -> word { - return shl(x, y); -} - -// Bitwise SHR -function bshrWord(x: word, y: word) -> word { - return shr(x, y); -} - -instance word:Eq { - function eq(x:word, y:word) -> bool { - return eqWord(x, y); - } -} - -instance word:Ord { - function gt(x:word, y:word) -> bool { - return gtWord(x, y); - } -} - -instance word:Add { - function add(l: word, r: word) -> word { - return addWord(l, r); - } -} - -instance word:Sub { - function sub(l: word, r: word) -> word { - return subWord(l, r); - } -} - -function mulWord(l: word, r: word) -> word { - return mul(l, r); -} - -instance word:Mul { - function mul(l: word, r: word) -> word { - return mulWord(l, r); - } -} - -instance word:Div { - function div(l: word, r: word) -> word { - return div(l, r); - } -} - -instance word:Mod { - function mod (l : word, r : word) -> word { - return mod(l, r); - } -} - -instance word:BitAnd { - function band(l: word, r: word) -> word { - return bandWord(l, r); - } -} - -instance word:BitOr { - function bor(l: word, r: word) -> word { - return borWord(l, r); - } -} - -instance word:BitXor { - function bxor(l: word, r: word) -> word { - return bxorWord(l, r); - } -} - -instance word:BitNot { - function bnot(x: word) -> word { - return bnotWord(x); - } -} - -instance integer : Eq { - function eq(x : integer, y : integer) -> bool { - return integerEq(x, y); - } -} - -instance integer : Ord { - function gt(x : integer, y : integer) -> bool { - return integerLt(y, x); - } -} - -instance integer : Add { - function add(l : integer, r : integer) -> integer { - return integerAdd(l, r); - } -} - -instance integer : Sub { - function sub(l : integer, r : integer) -> integer { - return integerSub(l, r); - } -} - -instance integer : Mul { - function mul(l : integer, r : integer) -> integer { - return integerMul(l, r); - } -} - -instance word:Bounded { - function maxVal() -> word { - return 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - } - function minVal () -> word { - return 0; - } -} - -function hash1(x: word) -> word { - mstore(0, x); - return keccak256(0, 32); -} - -function hash2(x: word, y: word) -> word { - mstore(0, x); - mstore(32, y); - return keccak256(0, 64); -} - -// --- Value Types --- - -forall t. t:Typedef(word) => -function toWord(x:t) -> word { return Typedef.rep(x); } - -data uint256 = uint256(word); -instance uint256:Typedef(word) { - function abs(w: word) -> uint256 { - return uint256(w); - } - - function rep(x: uint256) -> word { - match x { - | uint256(w) => return w; - } - } -} -instance uint256:Add { - function add(x : uint256, y : uint256) -> uint256 { - return Typedef.abs(Add.add(Typedef.rep(x), Typedef.rep(y))); - } -} - -instance uint256:Sub { - function sub(x : uint256, y : uint256) -> uint256 { - return Typedef.abs(Sub.sub(Typedef.rep(x), Typedef.rep(y))); - } -} - -instance uint256:Mul { - function mul(x : uint256, y : uint256) -> uint256 { - return Typedef.abs(Mul.mul(Typedef.rep(x), Typedef.rep(y))); - } -} - -instance uint256:Div { - function div(x : uint256, y : uint256) -> uint256 { - return Typedef.abs(Div.div(Typedef.rep(x), Typedef.rep(y))); - } -} - -instance uint256:Mod { - function mod(x : uint256, y : uint256) -> uint256 { - return Typedef.abs(Mod.mod(Typedef.rep(x), Typedef.rep(y))); - } -} - -instance uint256:BitAnd { - function band(x : uint256, y : uint256) -> uint256 { - return Typedef.abs(BitAnd.band(Typedef.rep(x), Typedef.rep(y))); - } -} - -instance uint256:BitOr { - function bor(x : uint256, y : uint256) -> uint256 { - return Typedef.abs(BitOr.bor(Typedef.rep(x), Typedef.rep(y))); - } -} - -instance uint256:BitXor { - function bxor(x : uint256, y : uint256) -> uint256 { - return Typedef.abs(BitXor.bxor(Typedef.rep(x), Typedef.rep(y))); - } -} - -instance uint256:BitNot { - function bnot(x : uint256) -> uint256 { - return Typedef.abs(BitNot.bnot(Typedef.rep(x))); - } -} - -instance uint256:Eq { - function eq(x : uint256, y : uint256) -> bool { - return Eq.eq(Typedef.rep(x), Typedef.rep(y)); - } -} - -instance uint256:Ord { - function gt(x : uint256, y : uint256) -> bool { - return Ord.gt(Typedef.rep(x), Typedef.rep(y)); - } -} - -instance uint256:Bounded { - function maxVal() -> uint256 { - return uint256(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); - } - function minVal () -> uint256 { - return uint256(0); - } -} - -instance uint256:Int { - function fromInteger(x:integer) -> uint256 { - return uint256(wordFromInteger(x)); - } -} - -function addmod(x: uint256, y: uint256, k: uint256) -> uint256 { - require(k != uint256(0), Error(0x7125cbb9)); // AddModWithZero() - return Typedef.abs(addmod_(Typedef.rep(x), Typedef.rep(y), Typedef.rep(k))); -} - -function mulmod(x: uint256, y: uint256, k: uint256) -> uint256 { - require(k != uint256(0), Error(0xdaea23b9)); // MulModWithZero() - return Typedef.abs(mulmod_(Typedef.rep(x), Typedef.rep(y), Typedef.rep(k))); -} - -data byte = byte(word); -instance byte:Typedef(word) { - function abs(w: word) -> byte { - return byte(w); - } - - function rep(x: byte) -> word { - match x { - | byte(w) => return w; - } - } -} - -// --- Address --- -data address = address(word); - -instance address:Typedef(word) { - function rep(x:address) -> word { - match x { - | address(y) => return y; - } - } - function abs(x:word) -> address { - return address(x); - } -} - -instance address:Eq { - function eq(x : address , y : address) -> bool { - return Eq.eq(Typedef.rep(x), Typedef.rep(y)); - } -} - -// --- Bytes4 --- - -data bytes4 = bytes4(word); - -instance bytes4:Typedef(word) { - function rep(b : bytes4) -> word { - match b { - | bytes4(w) => return w; - } - } - function abs(w : word) -> bytes4 { - return bytes4(w); - } -} - -// --- Bytes32 --- - -data bytes32 = bytes32(word); - -instance bytes32:Typedef(word) { - function rep(b : bytes32) -> word { - match b { - | bytes32(w) => return w; - } - } - function abs(w : word) -> bytes32 { - return bytes32(w); - } -} - -instance bytes32:Eq { - function eq(x : bytes32, y : bytes32) -> bool { - return Eq.eq(Typedef.rep(x), Typedef.rep(y)); - } -} - -instance bytes32:Ord { - function gt(x : bytes32, y : bytes32) -> bool { - return Ord.gt(Typedef.rep(x), Typedef.rep(y)); - } -} - -// --- Pointers --- - -data memory(t) = memory(word); -forall t . instance memory(t) : Typedef(word) { - function abs(x: word) -> memory(t) { - return memory(x); - } - - function rep(x: memory(t)) -> word { - match x { - | memory(w) => return w; - } - } -} - -data storage(t) = storage(word); -forall t . instance storage(t) : Typedef(word) { - function abs(x: word) -> storage(t) { - return storage(x); - } - - function rep(x: storage(t)) -> word { - match x { - | storage(w) => return w; - } - } -} - -data calldata(t) = calldata(word); -forall t . instance calldata(t) : Typedef(word) { - function abs(x: word) -> calldata(t) { - return calldata(x); - } - - function rep(x: calldata(t)) -> word { - match x { - | calldata(w) => return w; - } - } -} - -data returndata(t) = returndata(word); -forall t . instance returndata(t) : Typedef(word) { - function abs(x: word) -> returndata(t) { - return returndata(x); - } - - function rep(x: returndata(t)) -> word { - match x { - | returndata(w) => return w; - } - } -} - -data mapping(member, index) = mapping(word) ; - -data array(member) = array(word) ; - -// --- Low-level memory ops - -function strlen(s:memory(string)) -> word { - match s { | memory(a) => return mload(a); } -} - -// --- Memory Utilities --- - -// Memory in solidity is bump allocated in a single arena -// The word stored in memory at index 0x40 is used to store the start of the currently unused memory region - -// returns the value stored in memory(0x40) -function get_free_memory() -> word { - return mload(0x40); -} - -// set the value stored in memory(0x40) -function set_free_memory(loc : word) -> () { - mstore(0x40, loc); -} - -// Allocate memory and update the memory pointer. -function allocate_memory(size : word) -> word { - let ptr = get_free_memory(); - set_free_memory(ptr + size); - return ptr; -} - -function allocate_zeroed_memory(size: word) -> word { - let ptr = allocate_memory(size); - zeroize_memory(ptr, size); - return ptr; -} - -// Clears a memory area. -function zeroize_memory(ptr: word, len: word) -> () { - let end_ptr = ptr + len; - - // Zero out 32-byte words. - for (let i = 0; i < len / 32; i += 1, ptr += 32) { - mstore(ptr, 0) - } - - // Zero out trailing bytes. We rely on the zero-slot (0x60-0x7f). - mcopy(ptr, 0x60, end_ptr - ptr); -} - -// --- Indexable Types --- - -// types that can be written to and read from at a uint256 index -// TODO: this needs to be split into LValue / RValue variants for `=` desugaring -forall t val . class t:IndexAccess(val) { - function get(c: t, i: uint256) -> val; - function set(c: t, i: uint256, v: val) -> (); -} - -// --- DynArray --- - -// Word arrays with a size known only at runtime -// types with a size smaller than `word` will not be packed, so a `DynArray(byte)` will waste a lot of space -// TODO: storage representation -data DynArray(t); - -// Layout: the length lives at `loc`, so element i lives at `loc + 32 + i*32`. -// An index is in bounds when i < length. -forall t . t:Typedef(word) => instance memory(DynArray(t)):IndexAccess(t) { - function get(ptr : memory(DynArray(t)), i : uint256) -> t { - let i_: word = Typedef.rep(i); - let loc : word = Typedef.rep(ptr); - if (i_ >= mload(loc)) { out_of_bounds(); } - return Typedef.abs(mload(loc + 32 + (i_ * 32))); - } - function set(arr : memory(DynArray(t)), i : uint256, val : t) -> () { - let i_ : word = Typedef.rep(i); - let loc : word = Typedef.rep(arr); - if (i_ >= mload(loc)) { out_of_bounds(); } - mstore(loc + 32 + (i_ * 32), Typedef.rep(val)); - } -} - -// --- Array literals --- -// -// `[e1, ..., en]` is desugared, after type checking, into -// arrayLitInit(... arrayLitInit(arrayLitNew(n), 0, e1) ..., n-1, en) -// The chain is a plain expression: each step returns the array it wrote to. - -forall t . t:Typedef(word) => -function arrayLitNew(n : uint256) -> memory(DynArray(t)) { - let prx : Proxy(t); - return allocateDynamicArray(prx, Typedef.rep(n)); -} - -forall t . t:Typedef(word) => -function arrayLitInit(arr : memory(DynArray(t)), i : uint256, v : t) -> memory(DynArray(t)) { - IndexAccess.set(arr, i, v); - return arr; -} - -forall t . function allocateDynamicArray(prx : Proxy(t), length : word) -> memory(DynArray(t)) { - // size of allocation in bytes - let sz : word = (length + 1) * 32; - - // get start of array & increment free by sz - let free : word = get_free_memory(); - set_free_memory(free + sz); - - // write array length and return - mstore(free, length); - let res : memory(DynArray(t)) = Typedef.abs(free); - return res; -} - -// --- bytes --- - -// tightly packed byte arrays -// bytes does not have a runtime representation since it can only ever exist in -// memory / calldata / storage and serves only as a type tag for pointer types -// TODO: IndexAccess for memory(bytes) -// TODO: IndexAccess for calldata(bytes) -// TODO: IndexAccess for storage(bytes) -data bytes; - -// --- strings --- - -// TODO: should this be a typedef over `bytes`? -data string; - -instance string:Add { - function add(l: string, r: string) -> string { - return concatLit(l, r); - } -} - -// ------------------------------------------------------------------ -// Compile-time string literal builtins -// ------------------------------------------------------------------ -// These are intended to be folded by MastEval when their arguments are -// statically known string literals. - -function concatLit(comptime a: string, comptime b: string) -> string { - unimplemented(); // Sanity check if folding ignores it. - return ""; -} - -function strlenLit(comptime a: string) -> word { - unimplemented(); // Sanity check if folding ignores it. - return 0; -} - -// Keccak-256 hash of the string-literal as UTF-8 bytes. -function keccakLit(comptime a: string) -> word { - unimplemented(); // Sanity check if folding ignores it. - return 0; -} - -// Keccak-256 hash of a word's 32-byte big-endian representation. -// NOTE: this could be deprecated if we have comptime `to_bytes`. -function keccakWordLit(comptime a: word) -> word { - unimplemented(); // Sanity check if folding ignores it. - return 0; -} - -// --- slices (sized pointers) --- - -// A slice is a wrapper around an existing pointer type that extends the -// underlying type with information about the size of the data pointed to by `t` -data slice(ptr) = slice(ptr, word); - -// --- Word Reader --- - -// A WordReader is an abstraction over byte indexed structure that can be read in word sized chunks (e.g. calldata / memory) -// These let us use the same abi decoding routines for calldata / memory -forall ty . class ty:WordReader { - // returns the word currently pointed to by the WordReader - function read(reader:ty) -> word; - // returns a new WordReader that points to a location `offset` bytes further into the array - function advance(reader:ty, offset:word) -> ty; - // copies a block from the underlying source to memory - function copyToMem(reader:ty, dst: word, cnt: word) -> (); -} - -// WordReader for memory -data MemoryWordReader = MemoryWordReader(word); -instance MemoryWordReader:WordReader { - function read(reader:MemoryWordReader) -> word { - match reader { - | MemoryWordReader(ptr) => return mload(ptr); - } - } - function advance(reader:MemoryWordReader, offset:word) -> MemoryWordReader { - match reader { - | MemoryWordReader(ptr) => return MemoryWordReader(ptr + offset); - } - } - function copyToMem(reader:MemoryWordReader, dst:word, cnt: word) -> () { - match reader { - | MemoryWordReader(ptr) => mcopy(dst, ptr, cnt); - } - } -} - -// WordReader for calldata -data CalldataWordReader = CalldataWordReader(word); - -instance CalldataWordReader : Typedef(word) { - function abs(a:word) -> CalldataWordReader { return CalldataWordReader(a); } - function rep(r:CalldataWordReader) -> word { - match r { - | CalldataWordReader(a) => return a; - } - } -} - -instance CalldataWordReader:WordReader { - function read(reader:CalldataWordReader) -> word { - match reader { - | CalldataWordReader(ptr) => return calldataload(ptr); - } - } - function advance(reader:CalldataWordReader, offset:word) -> CalldataWordReader { - match reader { - | CalldataWordReader(ptr) => return CalldataWordReader(ptr + offset); - } - } - function copyToMem(reader:CalldataWordReader, dst:word, cnt: word) -> () { - match reader { - | CalldataWordReader(ptr) => calldatacopy(dst, ptr, cnt); - } - } -} - -// --- HasWordReader --- - -// The HasWordReader class defines the types for which a WordReader can be produced -// We define instances for memory(bytes) and calldata(bytes) -forall self reader . class self:HasWordReader(reader) { - function getWordReader(x:self) -> reader; -} - -instance memory(bytes):HasWordReader(MemoryWordReader) { - function getWordReader(x:memory(bytes)) -> MemoryWordReader { - return MemoryWordReader(Typedef.rep(x)); - } -} - -instance calldata(bytes):HasWordReader(CalldataWordReader) { - function getWordReader(x:calldata(bytes)) -> CalldataWordReader { - return CalldataWordReader(Typedef.rep(x)); - } -} - -// --- MemoryType --- - -// A MemoryType instance abstracts over type specific logic related to memory -// layout, allowing us to write code that is generic over which type is held in memory -forall self loadedType. class self:MemoryType(loadedType) { - // Proxy needed becaused class methods must mention strong type params - // loads an instance of `loadedType` from an instance of `self` located at `loc` in memory - function loadFromMemory(p:Proxy(self), loc:word) -> loadedType; -} - -// A uint256 can be loaded from memory and pushed straight onto the stack -instance uint256:MemoryType(uint256) { - function loadFromMemory(p:Proxy(uint256), loc:word) -> uint256 { - return uint256(mload(loc)); - } -} - -// We load a DynArray into a sized pointer to the first element -/* -forall ty ret . ty:MemoryType(ret) => instance DynArray(ty):MemoryType(slice(memory(ret))) { - function loadFromMemory(p : Proxy (DynArray(ty)), loc:word) -> slice(memory(ret)) { - let length = mload(loc); - return slice(Typedef.abs(loc) : memory(ret), length); - } -} -*/ - -// FAIL: patterson -// FAIL: bound variable -// if we ty is a MemoryType that returns deref and deref is ABIEncode, then we can encode a memory(ty) -// by loading it and then running the ABI encoding for the loaded value -/* -forall ty deref . ty:MemoryType(deref), deref:ABIEncode => instance memory(ty):ABIEncode { - function encodeInto(x:memory(ty), basePtr:word, offset:word, tail:word) -> word { - let prx : Proxy(ty); // FIXED: before was Proxy(deref) - return ABIEncode.encodeInto(MemoryType.loadFromMemory(prx, Typedef.rep(x)) : deref, basePtr, offset, tail); - } -} -*/ -// --- ABI Tuples --- - -// Tuples in Solidity are always desugared to nested pairs (to allow for -// inductive typeclass instance constructions) . -// This is an issue for the ABI routines since the ABI spec differentiates -// between `(1,1,1)` and `(1,(1,1))`, but the language treats both identically. -// The ABITuple type lets us reiintroduce this distinction: -// `ABITuple((1,(1,1))` should be treated as `(1,1,1)` for the purposes of ABI -// encoding / decoding. -data ABITuple(tuple) = ABITuple(tuple); - -forall t . instance ABITuple(t):Typedef(t) { - function abs(t: t) -> ABITuple(t) { - return ABITuple(t); - } - - function rep(x: ABITuple(t)) -> t { - match x { - | ABITuple(v) => return v; - } - } -} - -// --- ABI Metadata --- - -// Statically knowable ABI related metadata about `self` -forall self . class self:ABIAttribs { - // how many bytes should be used for the head portion of the abi encoding of `self` - function headSize(ty:Proxy(self)) -> word; - // whether or not `self` is a fully static type - function isStatic(ty:Proxy(self)) -> bool; -} - -forall t. -default instance t:ABIAttribs { - function headSize(ty : Proxy(t)) -> word { return 32; } - function isStatic(ty : Proxy(t)) -> bool { return true; } -} - -instance ():ABIAttribs { - function headSize(ty : Proxy(())) -> word { return 0; } - function isStatic(ty : Proxy(())) -> bool { return true; } -} -instance uint256:ABIAttribs { - function headSize(ty : Proxy(uint256)) -> word { return 32; } - function isStatic(ty : Proxy(uint256)) -> bool { return true; } -} -instance address:ABIAttribs { - function headSize(ty : Proxy(address)) -> word { return 32; } - function isStatic(ty : Proxy(address)) -> bool { return true; } -} -forall t . instance DynArray(t):ABIAttribs { - function headSize(ty : Proxy(DynArray(t))) -> word { return 32; } - function isStatic(ty : Proxy(DynArray(t))) -> bool { return false; } -} -// A dynamic array is encoded head-first as a 32-byte offset into the tail, so -// its head is one word and it is never static (matching DynArray above). This -// covers `array(t)` under any location qualifier via the `calldata(ty)` / -// `memory(ty)` ABIAttribs bridges. -forall t . instance array(t):ABIAttribs { - function headSize(ty : Proxy(array(t))) -> word { return 32; } - function isStatic(ty : Proxy(array(t))) -> bool { return false; } -} -instance string:ABIAttribs { - function headSize(ty: Proxy(string)) -> word { return 32; } - function isStatic(ty : Proxy(string)) -> bool { return false; } -} -// bytes is dynamic, exactly like string — without this instance it falls to the -// default (isStatic = true), which wrongly marks memory(bytes) (and any ADT -// carrying it) static, so calldata arrays/sums take the inline decode path over -// what is really an offset-referenced value. -instance bytes:ABIAttribs { - function headSize(ty: Proxy(bytes)) -> word { return 32; } - function isStatic(ty : Proxy(bytes)) -> bool { return false; } -} - -// computes the attribs for a pair of two types that implement attribs -forall a b . a:ABIAttribs, b:ABIAttribs => instance (a,b):ABIAttribs { - function headSize(ty : Proxy((a,b))) -> word { - let pa : Proxy(a); - let pb : Proxy(b); - let sza = ABIAttribs.headSize(pa); - let szb = ABIAttribs.headSize(pb); - return sza + szb; - } - function isStatic(ty : Proxy((a,b))) -> bool { - let pa : Proxy(a); - let pb : Proxy(b); - return and(ABIAttribs.isStatic(pa), ABIAttribs.isStatic(pb)); - } -} - -// if an abi tuple contains dynamic elems we store it in the tail, otherwise we -// treat it the same as a series of nested pairs -forall tuple . tuple:ABIAttribs => instance ABITuple(tuple):ABIAttribs { - function headSize(ty : Proxy(ABITuple(tuple))) -> word { - let px : Proxy(tuple); - match ABIAttribs.isStatic(px) { - | true => return ABIAttribs.headSize(px); - | false => return 32; - } - } - function isStatic(ty : Proxy(ABITuple(tuple))) -> bool { - let px : Proxy(tuple); - return ABIAttribs.isStatic(px); - } -} - -// for pointer types we fetch the attribs of the pointed to type, not the pointer itself -forall ty . ty:ABIAttribs => instance memory(ty):ABIAttribs { - function headSize(p : Proxy(memory(ty))) -> word { - let px : Proxy(ty); - return ABIAttribs.headSize(px); - } - function isStatic(p : Proxy(memory(ty))) -> bool { - let px : Proxy(ty); - return ABIAttribs.isStatic(px); - } -} -forall ty . ty:ABIAttribs => instance calldata(ty):ABIAttribs { - function headSize(p : Proxy(calldata(ty))) -> word { - let px : Proxy(ty); - return ABIAttribs.headSize(px); - } - function isStatic(ty : Proxy(calldata(ty))) -> bool { - let px : Proxy(ty); - return ABIAttribs.isStatic(px); - } -} - -// --- ABI Encoding --- -// TODO: make these generic over the location being written to (i.e. memory or returndata) - -// top level encoding function. -// abi encodes an instance of `ty` and returns a pointer to the result -forall ty . ty:ABIAttribs, ty:ABIEncode => function abi_encode(val : ty) -> memory(bytes) { - let ret = get_free_memory(); - let start = ret + 32; - let tail = ABIEncode.encodeInto(val, start, 0, start + ABIAttribs.headSize(Proxy : Proxy(ty))); - mstore(ret, tail - start); - set_free_memory(tail); - return memory(ret); -} - -// types that can be abi encoded -forall self . class self:ABIEncode { - // abi encodes an instance of self into a memory region starting at basePtr - // offset gives the offset in memory from basePtr to the first empty byte of the head - // tail gives the index in memory of the first empty byte of the tail - function encodeInto(x:self, basePtr:word, offset:word, tail:word) -> word /* newTail */; -} - -instance uint256:ABIEncode { - // a unit256 is written directly into the head - function encodeInto(x:uint256, basePtr:word, offset:word, tail:word) -> word { - let repx : word = Typedef.rep(x); - mstore(basePtr + offset, repx); - return tail; - } -} - -instance address:ABIEncode { - // an address is written directly into the head (into a full 32-byte slot) - function encodeInto(x:address, basePtr:word, offset:word, tail:word) -> word { - let repx : word = Typedef.rep(x); - mstore(basePtr + offset, repx); - return tail; - } -} - -instance bytes32:ABIEncode { - // a bytes32 is written directly into the head - function encodeInto(x:bytes32, basePtr:word, offset:word, tail:word) -> word { - let repx : word = Typedef.rep(x); - mstore(basePtr + offset, repx); - return tail; - } -} - -instance bytes4:ABIEncode { - // bytes4's word rep is right-aligned (e.g. `bytes4(shr(224, h))`), - // so it is written directly into the head like bytes32 - function encodeInto(x:bytes4, basePtr:word, offset:word, tail:word) -> word { - let repx : word = Typedef.rep(x); - mstore(basePtr + offset, repx); - return tail; - } -} - -instance bool:ABIEncode { - function encodeInto(x:bool, basePtr:word, offset:word, tail:word) -> word { - let repx : word = frombool(x); - mstore(basePtr + offset, repx); - return tail; - } -} - -function round_up_to_mul_of_32(value:word) -> word { - return (value + 31) & ~31; -} - -function encodeIntoFromBytesLike(srcPtr:word, basePtr:word, offset:word, tail:word) -> word { - let length = mload(srcPtr); - let total = length + 32; - mstore(basePtr + offset, tail - basePtr); - mcopy(tail, srcPtr, total); - let rounded = round_up_to_mul_of_32(total); - zeroize_memory(tail + total, rounded - total); - return tail + rounded; -} - -instance memory(string):ABIEncode { - function encodeInto(x:memory(string), basePtr:word, offset:word, tail:word) -> word { - return encodeIntoFromBytesLike(Typedef.rep(x), basePtr, offset, tail); - } -} - -instance memory(bytes):ABIEncode { - function encodeInto(x:memory(bytes), basePtr:word, offset:word, tail:word) -> word { - return encodeIntoFromBytesLike(Typedef.rep(x), basePtr, offset, tail); - } -} - -// ABI encoding for a memory dynamic array whose elements fit in a single word. -// Assumes memory layout `[ length | elem_0 | elem_1 | ... ]`, which matches the -// on-the-wire tail of `t[]` so the body can be `mcopy`d verbatim. -// `memory(DynArray(t)):ABIAttribs` is already derivable from the generic -// `memory(ty):ABIAttribs` + `DynArray(t):ABIAttribs` instances above. -forall t . t:Typedef(word) => -instance memory(DynArray(t)):ABIEncode { - function encodeInto(x:memory(DynArray(t)), basePtr:word, offset:word, tail:word) -> word { - let srcPtr : word = Typedef.rep(x); - let len : word = mload(srcPtr); - let totalBytes : word = (len + 1) * 32; - - // head slot: relative pointer from basePtr to tail - mstore(basePtr + offset, tail - basePtr); - - // copy length + elements verbatim into the tail - let s : word = srcPtr; - let t_ : word = tail; - let n : word = totalBytes; - mcopy(t_, s, n); - return tail + totalBytes; - } -} - -instance ():ABIEncode { - // a unit256 is written directly into the head - function encodeInto(x:(), basePtr:word, offset:word, tail:word) -> word { - return tail; - } -} - -// abi encoding for a pair of two encodable types -forall a b . a:ABIAttribs, a:ABIEncode, b:ABIEncode => instance (a,b):ABIEncode { - function encodeInto(x: (a,b), basePtr: word, offset: word, tail: word) -> word { - match x { - | (l,r) => - let newTail = ABIEncode.encodeInto(l, basePtr, offset, tail); - let pa : Proxy(a); - let a_sz = ABIAttribs.headSize(pa); - return ABIEncode.encodeInto(r, basePtr, offset + a_sz, newTail); - } - } -} - - -// abi encoding for an ABITuple of encodable types -// TODO: is this correct? -forall tuple . tuple:ABIEncode, tuple:ABIAttribs => instance ABITuple(tuple):ABIEncode { - function encodeInto(x:ABITuple(tuple), basePtr:word, offset:word, tail:word) -> word { - let prx : Proxy(tuple); - match ABIAttribs.isStatic(prx) { - // if the tuple contains only static elements then we encode it in the head - | true => return ABIEncode.encodeInto(Typedef.rep(x), basePtr, offset, tail); - // if the tuple contains dynamically sized elements then we store a - // pointer in the head, and encode the tuple into the tail - | false => - // store the length of the head in basePtr - mstore(basePtr, tail - basePtr); - - // encode the underlying tuple into the tail - let headSize = ABIAttribs.headSize(Proxy : Proxy(tuple)); - basePtr = tail; - tail += headSize; - return ABIEncode.encodeInto(Typedef.rep(x), basePtr, 0, tail); - } - } -} - -// --- ABI Decoding --- - -// Top level decoding function. -// abi decodes an instance of `decodable` into a `ty` -forall decodable reader ty decoded . decodable:HasWordReader(reader), ABIDecoder(ty, reader):ABIDecode(decoded) => -function abi_decode(decodable:decodable, pty:Proxy(ty), prdr:Proxy(reader)) -> decoded { - let decoder : ABIDecoder(ty, reader) = ABIDecoder(HasWordReader.getWordReader(decodable)); - return ABIDecode.decode(decoder, 0); -} - - -forall decoder decoded . class decoder:ABIDecode(decoded) { - function decode(ptr:decoder, currentHeadOffset:word) -> decoded; -} - -// An ABI Decoder for `ty` from `reader` -// This lets us abstract over memory and calldata when decoding -data ABIDecoder(ty, reader) = ABIDecoder(reader); - -// If `reader` is a `WordReader` then so is our `ABIDecoder` -forall ty reader . reader:WordReader => instance ABIDecoder(ty, reader):WordReader { - function read(decoder:ABIDecoder(ty, reader)) -> word { - match decoder { - | ABIDecoder(ptr) => return WordReader.read(ptr); - } - } - function advance(decoder:ABIDecoder(ty, reader), offset:word) -> ABIDecoder(ty, reader) { - match decoder { - | ABIDecoder(ptr) => return ABIDecoder(WordReader.advance(ptr, offset)); - } - } - function copyToMem(decoder:ABIDecoder(ty, reader), dst:word, cnt: word) -> () { - match decoder { - | ABIDecoder(ptr) => WordReader.copyToMem(ptr, dst, cnt); - } - } -} - -// ABI Decoding for uint256 -forall reader . reader:WordReader => instance ABIDecoder(uint256, reader):ABIDecode(uint256) { - function decode(ptr:ABIDecoder(uint256, reader), currentHeadOffset:word) -> uint256 { - return Typedef.abs(WordReader.read(WordReader.advance(ptr, currentHeadOffset))) : uint256; - } -} - -// ABI Decoding for bytes32 -forall reader . reader:WordReader => instance ABIDecoder(bytes32, reader):ABIDecode(bytes32) { - function decode(ptr:ABIDecoder(bytes32, reader), currentHeadOffset:word) -> bytes32 { - return Typedef.abs(WordReader.read(WordReader.advance(ptr, currentHeadOffset))) : bytes32; - } -} - -// ABI Decoding for bytes4 -forall reader . reader:WordReader => instance ABIDecoder(bytes4, reader):ABIDecode(bytes4) { - function decode(ptr:ABIDecoder(bytes4, reader), currentHeadOffset:word) -> bytes4 { - return Typedef.abs(WordReader.read(WordReader.advance(ptr, currentHeadOffset))) : bytes4; - } -} - -// ABI Decoding for bool -// bool is a builtin (not a Typedef(word)), so it round-trips through word via -// tobool, mirroring the bool:ABIEncode instance which uses frombool. -forall reader . reader:WordReader => instance ABIDecoder(bool, reader):ABIDecode(bool) { - function decode(ptr:ABIDecoder(bool, reader), currentHeadOffset:word) -> bool { - let v = WordReader.read(WordReader.advance(ptr, currentHeadOffset)); - require(v <= 1, Error(0x0557dbbf)); // DirtyHigherBitsForBool() - return tobool(v); - } -} - -// ABI Decoding for address -forall reader . reader:WordReader => instance ABIDecoder(address, reader):ABIDecode(address) { - function decode(ptr:ABIDecoder(address, reader), currentHeadOffset:word) -> address { - let raw = WordReader.read(WordReader.advance(ptr, currentHeadOffset)); - require(shr(160, raw) == 0, Error(0x7cc04fa7)); // DirtyHigherBitsForAddress() - return Typedef.abs(raw) : address; - } -} - -forall reader . reader:WordReader => instance ABIDecoder((), reader):ABIDecode(()) { - function decode(ptr:ABIDecoder((), reader), currentHeadOffset:word) -> () { - return (); - } -} - -// ABI decoding for bytes/strings (only in memory) -forall a ptrtype reader. reader:WordReader => -function decodeBytesLike(ptr:ABIDecoder(memory(a), reader), currentHeadOffset:word) -> memory(a) { - let tmp:word; - let headRdr = WordReader.advance(ptr, currentHeadOffset); - let tailPtr : word = WordReader.read(headRdr); - - let src = WordReader.advance(ptr, tailPtr); - let srcRdr = getReader(src); - let length = WordReader.read(src); - let total = length + 32; - let rounded = round_up_to_mul_of_32(total); - let resultPtr : word = allocate_memory(rounded); - WordReader.copyToMem(srcRdr, resultPtr, total); - return memory(resultPtr); -} - -// ABI decoding for strings (only in memory) -forall reader. reader : WordReader => -instance ABIDecoder(memory(string), reader):ABIDecode(memory(string)) -{ - function decode(ptr:ABIDecoder(memory(string), reader), currentHeadOffset:word) -> memory(string) { - return decodeBytesLike(ptr, currentHeadOffset); - } -} - -// ABI decoding for bytes (only in memory) -forall reader. reader : WordReader => -instance ABIDecoder(memory(bytes), reader):ABIDecode(memory(bytes)) -{ - function decode(ptr:ABIDecoder(memory(bytes), reader), currentHeadOffset:word) -> memory(bytes) { - return decodeBytesLike(ptr, currentHeadOffset); - } -} - -// ABI decoding for a pair of decodable values -// FAIL: Coverage -forall a b a_decoded b_decoded reader . reader:WordReader, ABIDecoder(b,reader):ABIDecode(b_decoded), ABIDecoder(a,reader):ABIDecode(a_decoded), a:ABIAttribs => instance ABIDecoder((a,b), reader):ABIDecode((a_decoded,b_decoded)) -{ - function decode(ptr:ABIDecoder((a,b), reader), currentHeadOffset:word) -> (a_decoded, b_decoded) { - match ptr { - | ABIDecoder(rdr) => - let prx : Proxy(a); - let decoder_a : ABIDecoder(a, reader) = ABIDecoder(rdr); - let decoder_b : ABIDecoder(b, reader) = ABIDecoder(rdr); - let a_val : a_decoded = ABIDecode.decode(decoder_a, currentHeadOffset); - let b_val : b_decoded = ABIDecode.decode(decoder_b, currentHeadOffset + ABIAttribs.headSize(prx)); - return (a_val, b_val); - } - } -} - -forall reader tuple tuple_decoded . reader:WordReader, tuple:ABIDecode(tuple_decoded), tuple:ABIAttribs => - instance ABIDecoder(ABITuple(tuple), reader):ABIDecode(tuple_decoded) -{ - function decode(ptr:ABIDecoder(ABITuple(tuple), reader), currentHeadOffset:word) -> tuple_decoded { - let prx : Proxy(tuple); - match ABIAttribs.isStatic(prx) { - | true => return ABIDecode.decode(WordReader.advance(ptr, currentHeadOffset), 0); - | false => - let tailPtr = WordReader.read(ptr); - return ABIDecode.decode(WordReader.advance(ptr, tailPtr), 0); - } - } -} - - -forall reader tuple tuple_decoded . reader:WordReader, tuple:ABIDecode(tuple_decoded), tuple:ABIAttribs => - instance ABIDecoder(memory(ABITuple(tuple)), reader):ABIDecode(memory(tuple_decoded)) -{ - function decode(ptr:ABIDecoder(memory(ABITuple(tuple)), reader), currentHeadOffset:word) -> memory(tuple_decoded) { - let prx : Proxy(tuple); - match ABIAttribs.isStatic(prx) { - | true => return ABIDecode.decode(WordReader.advance(ptr, currentHeadOffset), 0); - | false => - let tailPtr = WordReader.read(ptr); - return ABIDecode.decode(WordReader.advance(ptr, tailPtr), 0); - } - } -} - -forall reader baseType baseType_decoded .baseType : ABIAttribs, reader:WordReader, ABIDecoder(baseType, reader):ABIDecode(baseType_decoded) => - instance ABIDecoder(memory(DynArray(baseType)), reader):ABIDecode(memory(DynArray(baseType_decoded))) -{ - function decode(ptr:ABIDecoder(memory(DynArray(baseType)), reader), currentHeadOffset:word) -> memory(DynArray(baseType_decoded)) { - let arrayPtr = WordReader.advance(ptr, currentHeadOffset); - let length = WordReader.read(arrayPtr); - // this trigger a missing typedef constraint - // let elementPtr:ABIDecoder(baseType, reader) = Typedef.abs(WordReader.advance(arrayPtr, 32)); - arrayPtr = WordReader.advance(arrayPtr, 32); - let prx : Proxy(baseType_decoded); - let result : memory(DynArray(baseType_decoded)) = allocateDynamicArray(prx, length); - let offset : word = 0; - let prx : Proxy(baseType); - let elementHeadSize : word = ABIAttribs.headSize(prx); - - // TODO: surface level loops - // TODO: sugar for assigning to indexAccess types (result[i]) - //for(let i = 0; i < length; i++) { - //result[i] = ABIDecode.decode(elementPtr, offset); - //assembly { offset := add(offset, elementHeadSize) } - //} - - return result; - } -} - -forall ty reader. -function getReader(d:ABIDecoder(ty, reader)) -> reader { - match d { - | ABIDecoder(rdr) => return rdr; - } -} - -forall baseType baseType_decoded . ABIDecoder(baseType, CalldataWordReader):ABIDecode(baseType_decoded), - baseType : WordReader => - instance ABIDecoder(calldata(DynArray(baseType)), CalldataWordReader):ABIDecode(calldata(DynArray(baseType_decoded))) - { - function decode(ptr:ABIDecoder(calldata(DynArray(baseType)), CalldataWordReader), currentHeadOffset:word) -> calldata(DynArray(baseType_decoded)) { - let newptr = WordReader.advance(ptr, currentHeadOffset); - let reader: CalldataWordReader = getReader(newptr); - let addr: word = Typedef.rep(reader); - return Typedef.abs(addr); - } - } - -// ─── Lazy ABI decode of a calldata dynamic array ───────────────────────────── -// The head slot holds the (args-relative) byte offset to the array data; -// following it lands on the length word. The decoded value is a calldata handle -// to that length word, so the elements are left in calldata and decoded on -// demand (abiArrayLength / abiArrayGet). Because nothing is materialised here, -// this works for any decodable element type — including multi-word ADTs such as -// a sum(...) — which the word-per-slot memory(DynArray(...)) path cannot hold. -forall baseType baseType_decoded . - ABIDecoder(baseType, CalldataWordReader):ABIDecode(baseType_decoded) => - instance ABIDecoder(calldata(array(baseType)), CalldataWordReader):ABIDecode(calldata(array(baseType_decoded))) - { - function decode(ptr:ABIDecoder(calldata(array(baseType)), CalldataWordReader), currentHeadOffset:word) -> calldata(array(baseType_decoded)) { - let headRdr = WordReader.advance(ptr, currentHeadOffset); - let dataOffset : word = WordReader.read(headRdr); - let dataRdr = WordReader.advance(ptr, dataOffset); - let rdr : CalldataWordReader = getReader(dataRdr); - let addr : word = Typedef.rep(rdr); - return Typedef.abs(addr); - } - } - -// Length of a decoded calldata array: the handle points at the length word. -forall t . function abiArrayLength(a : calldata(array(t))) -> uint256 { - let rdr : CalldataWordReader = CalldataWordReader(Typedef.rep(a)); - return uint256(WordReader.read(rdr)); -} - -// Decode element `i` of a calldata array on demand. The element region starts -// one word after the handle (past the length word). Two layouts, per the ABI: -// -// * static element type -> elements sit inline, each headSize(t) bytes, so -// element i starts at (handle + 32) + i * headSize(t). The element decoder -// is aimed at the region base and the per-element offset is threaded as the -// head offset. -// -// * dynamic element type -> the region holds a table of 32-byte offsets (one -// per element, relative to the region base), each pointing at that -// element's own encoding (standard-ABI T[] for dynamic T). The element -// decoder is aimed at the region base and given element i's slot as its -// head offset; the element's own dynamic decoder follows that offset. This -// is uniform across element kinds: a dynamic sum follows it and rebases to -// the element start, a bare bytes/string leaf follows it to its length word. -forall t t_decoded . - t : ABIAttribs, - ABIDecoder(t, CalldataWordReader):ABIDecode(t_decoded) => -function abiArrayGet(a : calldata(array(t)), i : uint256) -> t_decoded { - // Bounds check: valid indices are [0, length); i == length is already past - // the last element, so reject i >= length (mirrors the storage-array guard). - require(i < abiArrayLength(a), Error(0x7f52b2bf)); // ArrayOutOfBounds() - let base : word = Typedef.rep(a); - let elemRegion : word = base + 32; - let prx : Proxy(t); - let idx : word = Typedef.rep(i); - match ABIAttribs.isStatic(prx) { - | true => - let elemRdr : CalldataWordReader = CalldataWordReader(elemRegion); - let dec : ABIDecoder(t, CalldataWordReader) = ABIDecoder(elemRdr); - return ABIDecode.decode(dec, idx * ABIAttribs.headSize(prx)); - | false => - // Dynamic elements: the region is a table of 32-byte offsets (relative - // to the region base), one per element. Hand the element decoder the - // region base and element i's slot as its head offset; the element's own - // (dynamic) decoder follows that offset — uniformly for a dynamic sum - // element or a bare bytes/string element (calldata(array(bytes))). - let elemRdr : CalldataWordReader = CalldataWordReader(elemRegion); - let dec : ABIDecoder(t, CalldataWordReader) = ABIDecoder(elemRdr); - return ABIDecode.decode(dec, idx * 32); - } -} - - -// --- Assignment --- - -/* -# Types and classes for assignemnt desugaring -- access proxy types -- LValue and RValue access classes (LVA, RVA) -- Assign class -*/ - - -pragma no-patterson-condition RVA, Assign; -pragma no-coverage-condition MemberAccessProxy, LVA, RVA, CStructField, Assign; -pragma no-bounded-variable-condition LVA, RVA; - -// --- Storage --- - -// Zeroes the storage slots in [start, endSlot). Mirrors solc's -// clear_storage_range, used when a dynamic array shrinks so that regrowing it -// cannot resurrect the old elements. -function clearStorageRange(start: word, endSlot: word) -> () { - for (; start < endSlot; start += 1) { - sstore(start, 0); - } -} - -forall self. -class self:StorageSize { - function size(x:Proxy(self)) -> word; -} - - -forall self. -default instance self:StorageSize { - function size(x:Proxy(self)) -> word { - return 1; - } -} - -instance ():StorageSize { - function size(x:Proxy(())) -> word { - return 0; - } -} - -instance word:StorageSize { - function size(x:Proxy(word)) -> word { - return 1; - } -} -/* -instance uint:StorageSize { - function size(x:Proxy(uint)) -> word { - return 1; - } -} -*/ -instance uint256:StorageSize { - function size(x:Proxy(uint256)) -> word { - return 1; - } -} - -instance bytes32:StorageSize { - function size(x:Proxy(bytes32)) -> word { - return 1; - } -} - -instance address:StorageSize { - function size(x:Proxy(address)) -> word { - return 1; - } -} - -instance string:StorageSize { - function size(x:Proxy(string)) -> word { - return 1; - } -} - -instance memory(string):StorageSize { - function size(x:Proxy(memory(string))) -> word { - return 1; - } -} - -instance bytes:StorageSize { - function size(x:Proxy(bytes)) -> word { - return 1; - } -} - -instance memory(bytes):StorageSize { - function size(x:Proxy(memory(bytes))) -> word { - return 1; - } -} - -forall a b. a:StorageSize, b:StorageSize => instance (a,b):StorageSize { - function size(x:Proxy((a,b))) -> word { - let a_sz:word = StorageSize.size(Proxy:Proxy(a)); - let b_sz:word = StorageSize.size(Proxy:Proxy(b)); - return a_sz + b_sz; - } -} - -forall self. -class self:StorageType { - function load(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -// How to copy one element of type self from one storage slot to another. -// Whole-array assignment (a = b) copies element by element through this class, -// the way solc's copy_array_to_storage calls the element's own copy routine. -// The constraint lives on the *element* type, so it can gate CanStore.store for -// storage(array(self)) without also gating CanStore.load, which must stay -// unconstrained, a field read has to yield the array's storage reference. -// Instances live below, next to the CanStore instances the dynamic ones rely on. -forall self. -class self:StorageCopy { - function copySlot(dst:storage(self), src:storage(self)) -> (); -} - -instance word:StorageType { - function load(ptr:word) -> word { - return sload(ptr); - } - function store(ptr:word, value:word) -> () { - sstore(ptr, value); - } -} - -instance uint256:StorageType { - function load(ptr:word) -> uint256 { return uint256(StorageType.load(ptr):word); } - function store(ptr:word, value:uint256) -> () { StorageType.store(ptr, Typedef.rep(value):word); } -} - -instance bytes32:StorageType { - function load(ptr:word) -> bytes32 { return bytes32(StorageType.load(ptr):word); } - function store(ptr:word, value:bytes32) -> () { StorageType.store(ptr, Typedef.rep(value):word); } -} - -instance address:StorageType { - function load(ptr:word) -> address { return address(StorageType.load(ptr):word); } - function store(ptr:word, value:address) -> () { StorageType.store(ptr, Typedef.rep(value):word); } -} - -// -- structure fields (including contract fields) - -forall self fieldType offsetType. -class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - - -data MemberAccessProxy(a, field, fieldtype, offset) = MemberAccessProxy(a, field); - -forall a field fieldType storageType offset . -function memberAccessBase(x:MemberAccessProxy(a, field, fieldType, offset)) -> a { - match x { - | MemberAccessProxy(y,z) => return y; - } -} - - -// ------------------------------------------------------------------ -// Contract field access -// ------------------------------------------------------------------ - -forall cxt fieldSelector loadType offsetType storageType -. StructField(ContractStorage(cxt), fieldSelector) :CStructField(storage(storageType), offsetType) -, offsetType : StorageSize -, storage(storageType): CanStore(loadType) -=> instance MemberAccessProxy(ContractStorage(cxt), fieldSelector, loadType, offsetType) : LVA (storage(storageType)) { - function acc (x : MemberAccessProxy(ContractStorage(cxt), fieldSelector, loadType, offsetType)) -> storage(storageType) { - let offset : word = StorageSize.size(Proxy : Proxy(offsetType)) ; - return storage(offset):storage(storageType); - } -} - -forall cxt fieldSelector loadType offsetType storageType - . StructField(ContractStorage(cxt), fieldSelector):CStructField(storage(storageType), offsetType) - , storage(storageType):CanStore(loadType) - , offsetType:StorageSize - => instance MemberAccessProxy(ContractStorage(cxt), fieldSelector, loadType, offsetType):RVA(loadType) { - function acc(x:MemberAccessProxy(ContractStorage(cxt), fieldSelector, loadType, offsetType)) -> loadType { - let offset:word = StorageSize.size(Proxy:Proxy(offsetType)); - return CanStore.load(storage(offset):storage(storageType)):loadType; - } -} - -// TODO: structures other than contract context -/* -forall structType fieldSelector fieldType storageType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , offsetType:StorageSize - => instance MemberAccessProxy(storage(structType), fieldSelector, fieldType, offsetType):LVA(storage(fieldType)) { - function acc(x:MemberAccessProxy(storage(structType), fieldSelector, fieldType, offsetType)) -> storage(fieldType) { - let ptr:word = Typedef.rep(memberAccessBase(x)); - let size:word = StorageSize.size(Proxy:Proxy(offsetType)); - return storage(ptr + size); - } -} - -forall structType fieldSelector fieldType storageType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , offsetType:StorageSize - , fieldType:StorageType - => instance MemberAccessProxy(storage(structType), fieldSelector, fieldType, offsetType):RVA(fieldType) { - function acc(x:MemberAccessProxy(storage(structType), fieldSelector, fieldType, offsetType)) -> fieldType { - let ptr:word = Typedef.rep(memberAccessBase(x)); - let size:word = StorageSize.size(Proxy:Proxy(offsetType)); - return CanStore.load(ptr + size); - } -} -*/ - - - -data ContractStorage(cxt) = ContractStorage(cxt); - - -forall member index . instance mapping(index, member):Typedef(word) { - function rep(x:mapping(index, member)) -> word { - match x { - | mapping(y) => return y; - } - } - function abs(x:word) -> mapping(index,member) { - return mapping(x); - } -} - - -// cf https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays -forall index member . -instance mapping(index, member):StorageSize { - function size(x:Proxy(mapping(index, member))) -> word { - return 1; - } -} - -forall member . instance array(member):Typedef(word) { - function rep(x:array(member)) -> word { - match x { - | array(y) => return y; - } - } - function abs(x:word) -> array(member) { - return array(x); - } -} - -// cf https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays -// the slot itself stores the array length; elements live at keccak256(slot) + i -forall member . -instance array(member):StorageSize { - function size(x:Proxy(array(member))) -> word { - return 1; - } -} - -forall self . class self:Length { - function length(arr:self) -> uint256; -} - -// Dynamic storage arrays carry their length at the slot itself (matching the -// Solidity convention) while elements live at keccak256(slot) + i. -forall self . class self:Array { - function setLength(arr:self, n:uint256) -> (); - function pop(arr:self) -> (); -} - -// push is split into its own MPTC so its element type only shows up where it -// actually matters (the value being appended), without forcing `length`/ -// `setLength`/`pop` to drag along an unconstrained `elem` parameter. -forall self elem . class self:ArrayPush(elem) { - function push(arr:self, val:elem) -> (); -} - -forall t . -instance storage(array(t)):Length { - function length(arr:storage(array(t))) -> uint256 { - return uint256(sload(Typedef.rep(arr))); - } -} - -// A lazily-decoded calldata array reports its length from the head length-word -// of its handle (see abiArrayLength), so `arr.length()` resolves through the -// same Length class / UFCS as storage arrays. -forall t . -instance calldata(array(t)):Length { - function length(arr:calldata(array(t))) -> uint256 { - return abiArrayLength(arr); - } -} - -forall t . -instance storage(array(t)):Array { - // Shrinking clears the abandoned slots, matching solc's resize_array. - // For string/bytes elements this zeroes the inline slot, which makes any - // keccak-derived tail unreachable (reads are governed by the length word) but - // does not reclaim it. - function setLength(arr:storage(array(t)), n:uint256) -> () { - let slot : word = Typedef.rep(arr); - let oldLen : word = sload(slot); - let newLen : word = Typedef.rep(n); - if (newLen < oldLen) { - let base : word = hash1(slot); - clearStorageRange(base + newLen, base + oldLen); - } - sstore(slot, newLen); - } - // Zeroes the removed element before decrementing, as solc's array_pop does. - function pop(arr:storage(array(t))) -> () { - let slot : word = Typedef.rep(arr); - let n : word = sload(slot); - if (n == 0) { out_of_bounds(); } - sstore(hash1(slot) + (n - 1), 0); - sstore(slot, n - 1); - } -} - -// The value pushed is whatever the element's storage reference can store, rather -// than the element tag type itself. That is what lets array(string) accept a -// memory(string), via storage(string):CanStore(memory(string)). For word-sized -// elements v collapses to the element type and CanStore.store delegates to -// StorageType.store, so the generated code is unchanged. -forall t v . storage(t):CanStore(v) => -instance storage(array(t)):ArrayPush(v) { - function push(arr:storage(array(t)), val:v) -> () { - let slot : word = Typedef.rep(arr); - let n : word = sload(slot); - CanStore.store(storage(hash1(slot) + n):storage(t), val); - sstore(slot, n + 1); - } -} - -forall self memberRefType. -class self:LVA(memberRefType) { - function acc(x:self) -> memberRefType; -} - - -forall self member. -class self:RVA(member) { - function acc(x:self) -> member; -} - -forall a b. a:RVA(b) => -function rval(x:a) -> b { - return RVA.acc(x); -} - - -// TODO: consider merging CanStore and Assign -forall lhs rhs. -class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - - -// a can store b; e.g. storage(string) : memory(string) -forall a b. -class a:CanStore(b) { - function store(r:a, v:b) -> (); - function load(r:a) -> b; -} - - -forall a b. a:CanStore(b) => -instance a:Assign(b) { - function assign(l:a, r:b) -> () { - CanStore.store(l, r); - } -} - -/* -forall a. a:StorageType => -default instance a:CanStore(a) { - function store(l:storage(a), r:a) -> () { - StorageType.store(Typedef.rep(l), r); - } - function load(l:storage(a)) -> a { - return StorageType.load(Typedef.rep(l)); - } -} -*/ - - instance storage(word):CanStore(word) { - function store(l:storage(word), r:word) -> () { - StorageType.store(Typedef.rep(l), r); - } - function load(l:storage(word)) -> word { - return StorageType.load(Typedef.rep(l)); - } -} - - instance storage(uint256):CanStore(uint256) { - function store(l:storage(uint256), r:uint256) -> () { - StorageType.store(Typedef.rep(l), r); - } - function load(l:storage(uint256)) -> uint256 { - return StorageType.load(Typedef.rep(l)); - } -} - - instance storage(bytes32):CanStore(bytes32) { - function store(l:storage(bytes32), r:bytes32) -> () { - StorageType.store(Typedef.rep(l), r); - } - function load(l:storage(bytes32)) -> bytes32 { - return StorageType.load(Typedef.rep(l)); - } -} - - instance storage(address):CanStore(address) { - function store(l:storage(address), r:address) -> () { - StorageType.store(Typedef.rep(l), r); - } - function load(l:storage(address)) -> address { - return StorageType.load(Typedef.rep(l)); - } -} - -// bool has no StorageType instance (it is a builtin, not a Typedef(word)), but it -// round-trips through word via frombool / tobool, so it can still be stored. -instance storage(bool):CanStore(bool) { - function store(l:storage(bool), r:bool) -> () { - StorageType.store(Typedef.rep(l), frombool(r)); - } - function load(l:storage(bool)) -> bool { - return tobool(StorageType.load(Typedef.rep(l))); - } -} - -forall k v. - instance storage(mapping(k,v)):CanStore(storage(mapping(k,v))) { - function store(l:storage(mapping(k,v)), r:storage(mapping(k,v))) -> () { - // StorageType.store(Typedef.rep(l), r); - unimplemented(); - } - function load(l:storage(mapping(k,v))) -> storage(mapping(k,v)) { - // "Loading" a storage mapping field yields its storage reference (the - // slot); indexed access / method calls consume that reference directly. - return l; - } -} - -forall v. v:StorageCopy => - instance storage(array(v)):CanStore(storage(array(v))) { - // Whole-array assignment is a deep copy, as in Solidity: a = b resizes a - // to b's length and then copies every - // element. Assigning an array to itself is a no-op. A *local* bound to an - // array field stays an alias, because a let is not an Assign.assign. - function store(l:storage(array(v)), r:storage(array(v))) -> () { - let dst : word = Typedef.rep(l); - let src : word = Typedef.rep(r); - if (dst != src) { - let oldLen : word = sload(dst); - let newLen : word = sload(src); - let dstBase : word = hash1(dst); - if (newLen < oldLen) { - clearStorageRange(dstBase + newLen, dstBase + oldLen); - } - sstore(dst, newLen); - let srcBase : word = hash1(src); - for (let i = 0; i < newLen; i += 1) { - StorageCopy.copySlot(storage(dstBase + i):storage(v), storage(srcBase + i):storage(v)); - } - } - } - function load(l:storage(array(v))) -> storage(array(v)) { - // "Loading" a storage array field yields its storage reference (the - // slot). push / pop / length / arr[i] all consume that reference, so a - // field read like `ArrayPush.push(members, x)` must return the slot, - // not a copy. - return l; - } -} - -// Assigning an array literal to a storage array field: `xs = [1,2,3]`. -// -// This is Solidity's memory -> storage array copy. It is a plain function, not -// a CanStore instance, on purpose: instance overlap is decided by the main type -// alone, so a second CanStore instance for storage(array(t)) would clash with -// the deep-copy one above. FieldAccess routes `field = ` here -// instead of through Assign.assign. -// -// Array.setLength resizes and clears the abandoned tail, so old elements never -// resurrect. The element types differ: `t` is the storage element tag and `v` -// what a value of it looks like in memory (they coincide for word-sized -// elements; for array(string), t = string and v = memory(string)). -forall t v . storage(t):CanStore(v), v:Typedef(word) => -function storeArrayLit(dst : storage(array(t)), src : memory(DynArray(v))) -> () { - let n : word = mload(Typedef.rep(src)); - Array.setLength(dst, uint256(n)); - let base : word = hash1(Typedef.rep(dst)); - let i : word = 0; - for (; i < n; i += 1) { - CanStore.store(storage(base + i) : storage(t), IndexAccess.get(src, uint256(i))); - } -} - -instance storage(string):CanStore(memory(string)) { - function store(dst:storage(string), src:memory(string)) -> () { - let srcPtr : word = Typedef.rep(src); - let slot = Typedef.rep(dst); - storeBytesFromMemory(slot, srcPtr); - } - - function load(src:storage(string)) -> memory(string) { - let srcPtr : word = Typedef.rep(src); - let dstPtr : word = get_free_memory(); - let endPtr = loadBytesFromStorage(srcPtr, dstPtr); - set_free_memory(endPtr); - return memory(dstPtr); - } -} - -// bytes share the same storage layout as string, so the same -// storeBytesFromMemory / loadBytesFromStorage helpers apply. -instance storage(bytes):CanStore(memory(bytes)) { - function store(dst:storage(bytes), src:memory(bytes)) -> () { - let srcPtr : word = Typedef.rep(src); - let slot = Typedef.rep(dst); - storeBytesFromMemory(slot, srcPtr); - } - - function load(src:storage(bytes)) -> memory(bytes) { - let srcPtr : word = Typedef.rep(src); - let dstPtr : word = get_free_memory(); - let endPtr = loadBytesFromStorage(srcPtr, dstPtr); - set_free_memory(endPtr); - return memory(dstPtr); - } -} - -// --- StorageCopy: per-element copy used by whole-array assignment --- - -// Word-sized elements are self-contained: the slot is the value. -instance word:StorageCopy { - function copySlot(dst:storage(word), src:storage(word)) -> () { - sstore(Typedef.rep(dst), sload(Typedef.rep(src))); - } -} -instance uint256:StorageCopy { - function copySlot(dst:storage(uint256), src:storage(uint256)) -> () { - sstore(Typedef.rep(dst), sload(Typedef.rep(src))); - } -} -instance bytes32:StorageCopy { - function copySlot(dst:storage(bytes32), src:storage(bytes32)) -> () { - sstore(Typedef.rep(dst), sload(Typedef.rep(src))); - } -} -instance address:StorageCopy { - function copySlot(dst:storage(address), src:storage(address)) -> () { - sstore(Typedef.rep(dst), sload(Typedef.rep(src))); - } -} - -// Dynamic elements keep their payload at keccak256(elementSlot), so copying the -// inline slot alone would leave the destination pointing at the *source's* tail. -// Round-tripping through memory copies the payload too. -instance string:StorageCopy { - function copySlot(dst:storage(string), src:storage(string)) -> () { - CanStore.store(dst, CanStore.load(src):memory(string)); - } -} -instance bytes:StorageCopy { - function copySlot(dst:storage(bytes), src:storage(bytes)) -> () { - CanStore.store(dst, CanStore.load(src):memory(bytes)); - } -} - -// Nested arrays recurse into the array CanStore instance above. The recursion is -// on the element type, so it terminates with the type's structure. -forall t . t:StorageCopy => -instance array(t):StorageCopy { - function copySlot(dst:storage(array(t)), src:storage(array(t))) -> () { - CanStore.store(dst, src); - } -} - -// Shamelessly stolen from function copy_byte_array_to_storage_from_t_bytes_memory_ptr_to_t_bytes_storage -// TODO: consider wrapping behaviour at end of storage -function storeBytesFromMemory(slot: word, src: word) -> () { - assembly { - let newLen := mload(src) - // TODO: check old len, cleanup etc - let srcOffset := 32 - switch gt(newLen, 31) - case 1 { - mstore(0,slot) - let dstPtr := keccak256(0,32) - let loopEnd := and(newLen, not(0x1f)) - let i := 0 - for { } lt(i, loopEnd) { i := add(i, 0x20) } { - sstore(dstPtr, mload(add(src, srcOffset))) - dstPtr := add(dstPtr, 1) - srcOffset := add(srcOffset, 32) - } - if lt(loopEnd, newLen) { - let lastValue := mload(add(src, srcOffset)) - let lastLen := and(newLen, 0x1f) - let mask := not(shr(mul(8, lastLen), not(0))) - let nudata := and(lastValue, mask) // a Yul variable cannot be called "data". Go figure. - sstore(dstPtr, nudata) - } - sstore(slot, add(mul(newLen, 2), 1)) - } - default { - let value := 0 - if newLen { - value := mload(add(src, srcOffset)) - } - let mask := not(shr(mul(8, newLen), not(0))) - let nudata := and(value, mask) - let used := or(nudata, mul(2, newLen)) - sstore(slot,used) - } - } -} - - -// shamelessly stolen from abi_encode_t_string_storage_to_t_string_memory_ptr -function loadBytesFromStorage(slot:word, memPtr:word) -> word { - let pos = memPtr; - let slotValue = sload(slot); - let length = slotValue / 2; - let outOfPlaceEncoding = tobool(slotValue & 1); - if (!outOfPlaceEncoding) { - length &= 0x7f; - } - mstore(pos, length); - pos += 32; - match outOfPlaceEncoding { - | false => - // Short byte array - mstore(pos, slotValue & ~0xff); - let empty = iszero(length); - let notzero = iszero(empty); - return pos + (notzero * 32); - | true => - // Long byte array - let dataPos = hash1(slot); - let i = 0; - for (; i < length; i += 32, dataPos += 1) { - mstore(pos + i, sload(dataPos)); - } - return pos + i; - } -} - - -// -- Tuple-based indexed access: - -forall col_idx val . class col_idx:RValueIdxAccess(val) { - function lookup(ci : col_idx) -> val; -} - -forall col_idx ref . class col_idx:LValueIdxAccess(ref) { - function lookup(ci : col_idx) -> ref; -} - -forall i a . i:Typedef(word) => -instance (storage(mapping(i,a)), i): LValueIdxAccess(storage(a)) { - function lookup(xi : (storage(mapping(i,a)), i)) -> storage(a) { - match(xi) { - | (x, i) => return storage(hash2(Typedef.rep(x), Typedef.rep(i))); - } - } -} - -forall i a . storage(a):CanStore(a), i:Typedef(word) => -instance (storage(mapping(i,a)), i): RValueIdxAccess(a) { - function lookup(xi : (storage(mapping(i,a)), i)) -> a { - /* - match(xi) { - | (x, i) => return StorageType.load(hash2(Typedef.rep(x), Typedef.rep(i))); - } - */ - return readStorage(LValueIdxAccess.lookup(xi)); - } -} - -forall a i . i:Typedef(word) => -instance (storage(array(a)), i): LValueIdxAccess(storage(a)) { - function lookup(xi : (storage(array(a)), i)) -> storage(a) { - match(xi) { - | (x, i) => - let slot : word = Typedef.rep(x); - let idx : word = Typedef.rep(i); - // Bounds check: idx must be in [0, length). Length lives at the - // slot itself; inlined to avoid an Array(t) dispatch here. - if (idx >= sload(slot)) { out_of_bounds(); } - return storage(hash1(slot) + idx); - } - } -} - -// Reading arr[i] yields whatever the element's storage reference loads, rather -// than the element tag type. For word-sized elements that is the element itself; -// for array(string) it is a memory(string); for a nested array(array(t)) it -// is the inner array's handle, which push/pop/length then consume. -forall a v i . storage(a):CanStore(v), i:Typedef(word) => -instance (storage(array(a)), i): RValueIdxAccess(v) { - function lookup(xi : (storage(array(a)), i)) -> v { - return CanStore.load(LValueIdxAccess.lookup(xi)); - } -} - -// Indexed read of a lazily-decoded calldata array: `arr[i]` desugars to -// ridx(arr, i), which dispatches here and decodes element i on demand via -// abiArrayGet. There is deliberately no LValueIdxAccess instance — calldata is -// immutable, so `arr[i] = …` is (correctly) rejected at compile time. -forall t t_decoded i . - t : ABIAttribs, - ABIDecoder(t, CalldataWordReader):ABIDecode(t_decoded), - i : Typedef(word) => -instance (calldata(array(t)), i): RValueIdxAccess(t_decoded) { - function lookup(xi : (calldata(array(t)), i)) -> t_decoded { - match(xi) { - | (a, idx) => return abiArrayGet(a, uint256(Typedef.rep(idx))); - } - } -} - -// Memory arrays are read-only through `m[i]`: there is no memory cell reference -// type, so they get an RValue instance but no LValue one. -forall t i . t:Typedef(word), i:Typedef(word) => -instance (memory(DynArray(t)), i): RValueIdxAccess(t) { - function lookup(xi : (memory(DynArray(t)), i)) -> t { - match xi { - | (x, j) => return IndexAccess.get(x, uint256(Typedef.rep(j))); - } - } -} - - -// Mapping reads go through CanStore, matching the write side (Assign -> CanStore.store). -// This lets a mapping hold any value with a CanStore instance — including ADTs whose -// fields are dynamic (memory(bytes)) — not just the fixed-slot StorageType primitives. -forall a. storage(a):CanStore(a) => -function readStorage(x:storage(a)) -> a { - return CanStore.load(x); -} -/* -forall r a. a:StorageType, r: RValueIdxAccess(a) => -function rval(x:r) -> a { - return RValueIdxAccess.lookup(x); -} - -forall r a. r: LValueIdxAccess(a) => -function lval(x:r) -> a { - return LValueIdxAccess.lookup(x); -} -*/ - -// lidx/ridx are the generic indexed-access helpers used by the `arr[i]` -// desugaring. They dispatch through LValueIdxAccess / RValueIdxAccess, so any -// collection (mapping, array, ...) that provides those instances supports the -// `arr[i]` syntax. -forall col idx ref . (col, idx):LValueIdxAccess(ref) => -function lidx(c: col, i: idx) -> ref { - return LValueIdxAccess.lookup((c, i)); -} - -forall col idx val . (col, idx):RValueIdxAccess(val) => -function ridx(c: col, i: idx) -> val { - return RValueIdxAccess.lookup((c, i)); -} - -// --- Memory Encoding --- - -forall t . class t:MemorySize { - // The size needed for the value. - function len(v: t) -> word; -} - -// NOTE: this is not implemented for value types. -forall t . class t:MemoryPointer { - // In-memory location of the given value. - function ptr(v: t) -> word; -} - -forall t . class t:MemoryEncode { - // Serialize the entire contents at a provided memory area. - function encodeInto(v: t, target: word) -> (); -} - -// TODO: support variadic arguments -// Allocates new memory and concatenates the inputs into it. -forall a b . a:MemorySize, a:MemoryEncode, b:MemorySize, b:MemoryEncode => function concat(x: a, y: b) -> memory(bytes) { - let x_len = MemorySize.len(x); - let y_len = MemorySize.len(y); - let res: word = allocate_memory(32 + x_len + y_len); - mstore(res, x_len + y_len); - MemoryEncode.encodeInto(x, res + 32); - MemoryEncode.encodeInto(y, res + 32 + x_len); - return memory(res); -} - -// This is a specialized 1-input version of concat. -forall a . a:MemorySize, a:MemoryEncode => function to_bytes(x: a) -> memory(bytes) { - let len = MemorySize.len(x); - let res = allocate_memory(32 + len); - mstore(res, len); - MemoryEncode.encodeInto(x, res + 32); - return memory(res); -} - -instance bytes32:MemorySize { - function len(v: bytes32) -> word { - return 32; - } -} - -instance bytes32:MemoryEncode { - function encodeInto(v: bytes32, target: word) -> () { - mstore(target, Typedef.rep(v)); - } -} - -instance memory(bytes):MemorySize { - function len(v: memory(bytes)) -> word { - return mload(Typedef.rep(v)); - } -} - -instance memory(bytes):MemoryPointer { - function ptr(v: memory(bytes)) -> word { - return Typedef.rep(v) + 32; - } -} - -instance memory(bytes):MemoryEncode { - function encodeInto(v: memory(bytes), target: word) -> () { - let v_ = Typedef.rep(v); - mcopy(target, v_ + 32, mload(v_)); - } -} - -// Placeholder for an empty memory area. -// The value is the size of the area in bytes. The area will be zeroed upon serialization. -// NOTE: not implementing Typedef by design. -data empty = empty(word); - -instance empty:MemorySize { - function len(v: empty) -> word { - match v { - | empty(size) => return size; - } - } -} - -instance empty:MemoryEncode { - function encodeInto(v: empty, target: word) -> () { - let size; - match v { - | empty(size_) => size = size_; - } - zeroize_memory(target, size); - } -} - -// --- Memory Slices --- - -// This is a very cheap abstraction over a memory area of [ptr, ptr+len) -// No type information is preserved. -data memory_ref = memory_ref(word, word); - -instance memory_ref:MemorySize { - function len(v: memory_ref) -> word { - match v { - | memory_ref(ptr, len) => return len; - } - } -} - -instance memory_ref:MemoryPointer { - function ptr(v: memory_ref) -> word { - match v { - | memory_ref(ptr, len) => return ptr; - } - } -} - -instance memory_ref:MemoryEncode { - function encodeInto(v: memory_ref, target: word) -> () { - match v { - | memory_ref(ptr, len) => mcopy(target, ptr, len); - } - } -} - -forall a . a:MemorySize, a:MemoryPointer => -function slice_(input: a, start: word) -> memory_ref { - let len = MemorySize.len(input); - // TODO: should this allow (it does now) a zero-length slice? - require(len >= start, Error(0xb4120f14)); // OutOfBounds() - return memory_ref(MemoryPointer.ptr(input) + start, len - start); -} - -forall a . a:MemorySize, a:MemoryPointer => -function truncate(input: a, end: word) -> memory_ref { - let len = MemorySize.len(input); - // TODO: should this allow (it does now) a zero-length slice? - require(len >= end, Error(0xb4120f14)); // OutOfBounds() - return memory_ref(MemoryPointer.ptr(input), end); -} - -// --- Hashing --- - -// NOTE: keccak256 name conflicts with assembly namespace -forall a . a:MemorySize, a:MemoryPointer => function keccak256_(input: a) -> bytes32 { - let len : word = MemorySize.len(input); - let ptr : word = MemoryPointer.ptr(input); - return bytes32(keccak256(ptr, len)); -} - -forall a . a:MemorySize, a:MemoryPointer => function sha256(input: a) -> bytes32 { - let len : word = MemorySize.len(input); - let ptr : word = MemoryPointer.ptr(input); - // We assume the [0, 32] scratch space is reserved. - let ret = staticcall(gas(), 2, ptr, len, 0, 32); - require(ret != 0, Error(0x68c071bb)); // SHA256CallFailed() - return bytes32(mload(0)); -} - -forall a . a:MemorySize, a:MemoryPointer => function ripemd160(input: a) -> bytes32 { - let len : word = MemorySize.len(input); - let ptr : word = MemoryPointer.ptr(input); - // We assume the [0, 32] scratch space is reserved. - let ret = staticcall(gas(), 3, ptr, len, 0, 32); - require(ret != 0, Error(0x31a72d92)); // RIPEMD160CallFailed() - return bytes32(mload(0)); -} - -// --- Precompiles --- - -// Perform an ECDSA signature recovery. It ensures the call has succeeded, -// and that the signature is not malleable (s ≤ secp256k1n/2). Transactions -// were updated to ban this, but the precompile wasn't. If a user relies on that -// feature they can call the precompile via assembly. -// TODO: use uint8 -function ecrecover(hash: bytes32, v: uint256, r: bytes32, s: bytes32) -> address { - // MalleableSignatureRejected() - require( - Typedef.rep(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, - Error(0x25260b20) - ); - - let hash_ = Typedef.rep(hash); - let v_ = Typedef.rep(v); - let r_ = Typedef.rep(r); - let s_ = Typedef.rep(s); - let ptr = get_free_memory(); - // We assume the [0, 32] scratch space is reserved. - mstore(ptr, hash_); - mstore(ptr + 32, v_); - mstore(ptr + 64, r_); - mstore(ptr + 96, s_); - // Clear the [0, 32] scratch space that receives the return data. On a - // failed recovery (e.g. v not in {27, 28}, or the generic could-not-recover - // case) the precompile still reports success but returns no data, leaving - // the output area untouched. Without this, a stale non-zero value would - // slip past the `res != 0` check below and yield a bogus address. - mstore(0, 0); - let ret = staticcall(gas(), 1, ptr, 128, 0, 32); - require(ret != 0, Error(0x578763f7)); // ECRecoverCallFailed() - let res = mload(0); - require(res != 0, Error(0x4fbfae63)); // ECRecoverFailed() - return address(res); -} - -// ERC-7201 namespaced storage slot, computed entirely at compile time from a -// string-literal namespace `id`: -// keccak256(abi.encode(uint256(keccak256(bytes(id))) - 1)) & ~bytes32(uint256(0xff)) -function erc7201(comptime id: string) -> comptime bytes32 { - return bytes32(keccakWordLit(keccakLit(id) - 1) & ~0xff); -} - -forall a . a:MemorySize, a:MemoryPointer => function raw_call(target: address, value: uint256, payload: a) -> (bool, memory(bytes)) { - let ret = call( - gas(), - Typedef.rep(target), - Typedef.rep(value), - MemoryPointer.ptr(payload), - MemorySize.len(payload), - 0, - 0 - ); - let retSize = returndatasize(); - let retData = allocate_memory(32 + retSize); - mstore(retData, retSize); - returndatacopy(retData + 32, 0, retSize); - return (tobool(ret), memory(retData)); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Ackermann.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Ackermann.sol new file mode 100644 index 00000000..94279f63 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Ackermann.sol @@ -0,0 +1,16 @@ +enum Nat { Zero, Succ(Nat) } + +function foo(x: Nat, y: Nat) returns (word) { + match (y, x) { +case (y1, Nat.Zero) { +return 1 ; +} +case (Nat.Zero, Nat.Succ(x2)) { +return 2; +} +case (Nat.Succ(y3), Nat.Succ(x3)) { +return 3; +} +} +} + diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Ackermann.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Ackermann.solc deleted file mode 100644 index c2181cc0..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Ackermann.solc +++ /dev/null @@ -1,10 +0,0 @@ -data Nat = Zero | Succ(Nat) ; - -function foo (x : Nat, y : Nat) -> word { - match y, x { - | y1, Nat.Zero => return 1 ; - | Nat.Zero, Nat.Succ(x2) => return 2; - | Nat.Succ(y3), Nat.Succ(x3) => return 3; - } -} - diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Add1.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Add1.sol new file mode 100644 index 00000000..72baa53a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Add1.sol @@ -0,0 +1,9 @@ +contract Add1 { + function main() public returns (word) { + let res: word; + assembly { + res := add(40, 2) + } + return res; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Add1.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Add1.solc deleted file mode 100644 index 8c47763d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Add1.solc +++ /dev/null @@ -1,9 +0,0 @@ -contract Add1 { - public function main() -> word { - let res: word; - assembly { - res := add(40, 2) - } - return res; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/BoolNot.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/BoolNot.sol new file mode 100644 index 00000000..a4de0a14 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/BoolNot.sol @@ -0,0 +1,12 @@ +enum Bool { False, True } + +function not(b: Bool) returns (Bool) { + match (b) { +case Bool.False { +return Bool.True ; +} +case Bool.True { +return Bool.False ; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/BoolNot.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/BoolNot.solc deleted file mode 100644 index 37969845..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/BoolNot.solc +++ /dev/null @@ -1,8 +0,0 @@ -data Bool = False | True; - -function not (b : Bool) -> Bool { - match b { - | Bool.False => return Bool.True ; - | Bool.True => return Bool.False ; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Compose.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Compose.sol new file mode 100644 index 00000000..4d465a89 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Compose.sol @@ -0,0 +1,7 @@ +contract Compose { + function id(x: word) public returns (word) { return x; } + + function main() public returns (word) { + return id(id(42)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Compose.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Compose.solc deleted file mode 100644 index 8b25bc25..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Compose.solc +++ /dev/null @@ -1,7 +0,0 @@ -contract Compose { - public function id(x : word) -> word { return x; } - - public function main() -> word { - return id(id(42)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Compose3.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Compose3.sol new file mode 100644 index 00000000..4da95232 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Compose3.sol @@ -0,0 +1,11 @@ +contract Compose { + function id(x: a) public returns (a) { return x; } + + function apply1(f: function(word) returns (word), a: word) public returns (word) { return f(a); } + + function idThenId(x: word) public returns (word) { return id(id(x)); } + + function main() public returns (word) { + return apply1(idThenId, 42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Compose3.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Compose3.solc deleted file mode 100644 index d04847e8..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Compose3.solc +++ /dev/null @@ -1,11 +0,0 @@ -contract Compose { - forall a . public function id(x : a) -> a { return x; } - - public function apply1(f : (word) -> word, a : word) -> word { return f(a); } - - public function idThenId(x : word) -> word { return id(id(x)); } - - public function main() -> word { - return apply1(idThenId, 42); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/CondExp.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/CondExp.sol new file mode 100644 index 00000000..8eb68845 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/CondExp.sol @@ -0,0 +1,8 @@ +contract CondExp { + function main() public returns (word) { + return + ( true ? false : true + ) ? false ? 1 : 2 + : true ? 42 : 56; + } +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/CondExp.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/CondExp.solc deleted file mode 100644 index 4c5a236d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/CondExp.solc +++ /dev/null @@ -1,8 +0,0 @@ -contract CondExp { - public function main() -> word { - return - if if true then false else true - then if false then 1 else 2 - else if true then 42 else 56; - } -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/DuplicateFun.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/DuplicateFun.sol new file mode 100644 index 00000000..e6e9202b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/DuplicateFun.sol @@ -0,0 +1,21 @@ + +trait A { + function foo(p: self) returns (word) ; +} + +trait B { + function foo(p: self) returns (word) ; +} + +impl B { + function foo(x: word) returns (word) { + return x; + } +} + +// error: Constraint for A not found in type of foo +impl A { + function foo(x: word) returns (word) { + return x; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/DuplicateFun.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/DuplicateFun.solc deleted file mode 100644 index 5ef3bb2c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/DuplicateFun.solc +++ /dev/null @@ -1,21 +0,0 @@ - -forall self . class self:A { - function foo(p : self) -> word; -} - -forall self . class self:B { - function foo(p : self) -> word; -} - -instance word:B { - function foo(x : word) -> word { - return x; - } -} - -// error: Constraint for A not found in type of foo -instance word:A { - function foo(x : word) -> word { - return x; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EitherModule.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EitherModule.sol new file mode 100644 index 00000000..93da7d7e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EitherModule.sol @@ -0,0 +1,24 @@ +contract EitherModule { + enum Either { Left(a), Right(b) } + enum List { Nil, Cons(a, List) } + + function lefts(xs: List>) public returns (List) { + match (xs) { +case List.Nil { +return List.Nil ; +} +case List.Cons(y,ys) { +match (y) { +case Either.Left(z) { +return List.Cons(z,lefts(ys)) ; +} +case Either.Right(z) { +return lefts(ys) ; +} +} +} +} + } + + function main() public returns (word) { return 0; } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EitherModule.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EitherModule.solc deleted file mode 100644 index abf8b393..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EitherModule.solc +++ /dev/null @@ -1,17 +0,0 @@ -contract EitherModule { - data Either(a,b) = Left(a) | Right(b); - data List(a) = Nil | Cons(a,List(a)); - - public function lefts(xs : List(Either(word,word))) -> List(word) { - match xs { - | List.Nil => return List.Nil ; - | List.Cons(y,ys) => - match y { - | Either.Left(z) => return List.Cons(z,lefts(ys)) ; - | Either.Right(z) => return lefts(ys) ; - } - } - } - - public function main() -> word { return 0; } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EqQual.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EqQual.sol new file mode 100644 index 00000000..51141ecc --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EqQual.sol @@ -0,0 +1,26 @@ +enum Bool { True, False } + +trait Eq { + function eq(x: a, y: a) returns (Bool) ; +} + +trait Ord where a: Eq { + function lt(x: a, y: a) returns (Bool) ; +} + +impl Eq { + function eq(x: word, y: word) returns (Bool) { + match (primEqWord(x,y)) { +case 0 { +return Bool.False; +} +default { +return Bool.True ; +} +} + } +} + +function foo(x: word) returns (Bool) { + return Eq.eq (x, 0); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EqQual.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EqQual.solc deleted file mode 100644 index 874acf93..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EqQual.solc +++ /dev/null @@ -1,24 +0,0 @@ -data Bool = True | False; - -forall a . class a : Eq { - function eq (x : a, y : a) -> Bool; -} - -forall a . a : Eq => class a : Ord { - function lt (x : a, y : a) -> Bool ; -} - -instance word : Eq { - function eq (x : word, y : word) -> Bool { - match primEqWord(x,y) { - | 0 => - return Bool.False; - | _ => - return Bool.True ; - } - } -} - -function foo (x : word) -> Bool { - return Eq.eq (x, 0); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EvenOdd.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EvenOdd.sol new file mode 100644 index 00000000..8d8e1663 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EvenOdd.sol @@ -0,0 +1,28 @@ +contract EvenOdd { + enum Nat { Zero, Succ(Nat) } + enum Bool { False, True } + + function even(n: Nat) public returns (Bool) { + match (n) { +case Nat.Zero { +return Bool.True; +} +case Nat.Succ(m) { +return odd(m); +} +} + } + + function odd(n: Nat) public returns (Bool) { + match (n) { +case Nat.Zero { +return Bool.False; +} +case Nat.Succ(m) { +return even(m); +} +} + } + + function main() public returns (word) { return 0; } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EvenOdd.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EvenOdd.solc deleted file mode 100644 index 96da4173..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/EvenOdd.solc +++ /dev/null @@ -1,20 +0,0 @@ -contract EvenOdd { - data Nat = Zero | Succ(Nat); - data Bool = False | True; - - public function even (n : Nat) -> Bool { - match n { - | Nat.Zero => return Bool.True; - | Nat.Succ(m) => return odd(m); - } - } - - public function odd(n : Nat) -> Bool { - match n { - | Nat.Zero => return Bool.False; - | Nat.Succ(m) => return even(m); - } - } - - public function main() -> word { return 0; } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Foo.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Foo.sol new file mode 100644 index 00000000..5adbcda0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Foo.sol @@ -0,0 +1,9 @@ + function one() returns (word) { + return primAddWord(1, zero()) ; + } + + function zero() returns (word) { + return 0; + } + + diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Foo.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Foo.solc deleted file mode 100644 index c416cd9f..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Foo.solc +++ /dev/null @@ -1,9 +0,0 @@ - function one() -> word { - return primAddWord(1, zero()) ; - } - - function zero () -> word { - return 0; - } - - diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Id.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Id.sol new file mode 100644 index 00000000..d00cde77 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Id.sol @@ -0,0 +1,10 @@ +function id(x: word) returns (word) { + return x; +} + +contract Id { + function main() public returns (word) { + return id(0); + } +} + diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Id.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Id.solc deleted file mode 100644 index 1594f7cb..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Id.solc +++ /dev/null @@ -1,10 +0,0 @@ -function id (x : word) -> word { - return x; -} - -contract Id { - public function main () -> word { - return id(0); - } -} - diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ListModule.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ListModule.sol new file mode 100644 index 00000000..e2e06e14 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ListModule.sol @@ -0,0 +1,34 @@ +contract ListModule { + enum List { Nil, Cons(a, List) } + enum Bool { True, False } + + + function zipWith(f: function(a, b) returns (c), xs: List, ys: List) public returns (List) { + match (xs, ys) { +case (List.Nil, List.Nil) { +return List.Nil ; +} +case (List.Cons(x1,xs1), List.Cons(y1,ys1)) { +return List.Cons(f(x1,y1), zipWith(f,xs1,ys1)) ; +} +default { +return List.Nil; +} +} + } + + function foldr(f: function(a, b) returns (b), v: b, xs: List) public returns (b) { + match (xs) { +case List.Nil { +return v; +} +case List.Cons(y,ys) { +return f(y, foldr(f,v,ys)) ; +} +} + } + + function main() public returns (word) { + return 0; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ListModule.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ListModule.solc deleted file mode 100644 index ec5343fa..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ListModule.solc +++ /dev/null @@ -1,26 +0,0 @@ -contract ListModule { - data List(a) = Nil | Cons(a,List(a)); - data Bool = True | False; - - - forall a b c . public function zipWith (f : (a,b) -> c,xs : List(a),ys : List(b)) -> List(c) { - match xs, ys { - | List.Nil, List.Nil => return List.Nil ; - | List.Cons(x1,xs1), List.Cons(y1,ys1) => - return List.Cons(f(x1,y1), zipWith(f,xs1,ys1)) ; - | _, _ => return List.Nil; - } - } - - forall a b . public function foldr(f : (a,b) -> b, v : b, xs : List(a)) -> b { - match xs { - | List.Nil => return v; - | List.Cons(y,ys) => - return f(y, foldr(f,v,ys)) ; - } - } - - public function main () -> word { - return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Logic.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Logic.sol new file mode 100644 index 00000000..3cea62b4 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Logic.sol @@ -0,0 +1,55 @@ +contract Logic { + enum Bool { True, False } + + function not(x: Bool) public returns (Bool) { + match (x) { +case Bool.True { +return Bool.False ; +} +case Bool.False { +return Bool.True ; +} +} + } + + function and(x: Bool, y: Bool) public returns (Bool) { + match (x, y) { +case (Bool.False, _) { +return Bool.False ; +} +case (Bool.True , _) { +return y ; +} +} + } + + function and1(x: Bool, y: Bool) public returns (Bool) { + match (x, y) { +case (Bool.False, Bool.False) { +return Bool.False ; +} +case (Bool.True , Bool.False) { +return Bool.False; +} +case (Bool.False ,Bool.True) { +return Bool.False; +} +case (Bool.True, Bool.True) { +return Bool.True; +} +} + } + + function elim(f: word, g: word, x: Bool) public returns (word) { + match (x) { +case Bool.True { +return f; +} +case Bool.False { +return g; +} +} + } + + function main() public returns (word) { return 0; } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Logic.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Logic.solc deleted file mode 100644 index e5463613..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Logic.solc +++ /dev/null @@ -1,35 +0,0 @@ -contract Logic { - data Bool = True | False; - - public function not (x : Bool) -> Bool { - match x { - | Bool.True => return Bool.False ; - | Bool.False => return Bool.True ; - } - } - - public function and(x : Bool, y : Bool) -> Bool { - match x, y { - | Bool.False, _ => return Bool.False ; - | Bool.True , _ => return y ; - } - } - - public function and1 (x : Bool, y : Bool) -> Bool { - match x, y { - | Bool.False, Bool.False => return Bool.False ; - | Bool.True , Bool.False => return Bool.False; - | Bool.False ,Bool.True => return Bool.False; - | Bool.True, Bool.True => return Bool.True; - } - } - - public function elim (f : word, g : word, x : Bool) -> word { - match x { - | Bool.True => return f; - | Bool.False => return g; - } - } - - public function main() -> word { return 0; } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/MatchCall.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/MatchCall.sol new file mode 100644 index 00000000..aa5cbdac --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/MatchCall.sol @@ -0,0 +1,18 @@ +enum Bool { False, True } + +contract MatchCall { + function f() public returns (Bool) { + return Bool.True; + } + + function main() public returns (word) { + match (f()) { +case Bool.True { +return 42; +} +case Bool.False { +return 0; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/MatchCall.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/MatchCall.solc deleted file mode 100644 index c4c4be10..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/MatchCall.solc +++ /dev/null @@ -1,14 +0,0 @@ -data Bool = False | True; - -contract MatchCall { - public function f() -> Bool { - return Bool.True; - } - - public function main() -> word { - match f() { - | Bool.True => return 42; - | Bool.False => return 0; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Memory1.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Memory1.sol new file mode 100644 index 00000000..eed86675 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Memory1.sol @@ -0,0 +1,7 @@ +enum memory { memory(word) } + +function g() { + let x : memory>; + let y : memory = memory(1); + x = memory(0); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Memory1.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Memory1.solc deleted file mode 100644 index af3a5ecd..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Memory1.solc +++ /dev/null @@ -1,7 +0,0 @@ -data memory(a) = memory(word); - -function g() -> () { - let x : memory(memory(word)); - let y : memory(word) = memory(1); - x = memory(0); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Memory2.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Memory2.sol new file mode 100644 index 00000000..b7083cf8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Memory2.sol @@ -0,0 +1,5 @@ +enum Memory { Memory(word) } + +function g() { + let x : Memory> = Memory(0); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Memory2.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Memory2.solc deleted file mode 100644 index 64fb9d95..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Memory2.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Memory(a) = Memory(word); - -function g() -> () { - let x : Memory(Memory(word)) = Memory(0); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Mutuals.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Mutuals.sol new file mode 100644 index 00000000..09ac8593 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Mutuals.sol @@ -0,0 +1,8 @@ +contract Mutual { + function main() public returns (word) { + return f(); + } + function f() public returns (word) { + return 42; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Mutuals.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Mutuals.solc deleted file mode 100644 index aa2d70e4..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Mutuals.solc +++ /dev/null @@ -1,8 +0,0 @@ -contract Mutual { - public function main () -> word { - return f(); - } - public function f () -> word { - return 42; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/NegPair.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/NegPair.sol new file mode 100644 index 00000000..895624bb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/NegPair.sol @@ -0,0 +1,69 @@ + +trait Neg { + function neg(x: a) returns (a) ; +} + +enum B { F, T } + +impl Neg { + function neg(x: B) returns (B) { + match (x) { +case B.F { +return B.T; +} +case B.T { +return B.F; +} +} + } +} + +function fst(p: (a, b)) returns (a) { + match (p) { +case (x,y) { +return x; +} +} +} + +function snd(p: (a, b)) returns (b) { + match (p) { +case (x,y) { +return y; +} +} +} + + +impl Neg<(a, b)> where a: Neg, b: Neg { + function neg(p: (a, b)) returns (a, b) { + return (Neg.neg (fst(p)), Neg.neg(snd (p))); + } +} + +contract NegPair { + + function bnot(x: B) public returns (B) { + match (x) { +case B.T { +return B.F; +} +case B.F { +return B.T; +} +} +} + + function fromB(b: B) public returns (word) { + match (b) { +case B.F { +return 0; +} +case B.T { +return 1; +} +} +} + + function main() public returns (word) { return fromB(fst(Neg.neg((B.F,B.T)))); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/NegPair.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/NegPair.solc deleted file mode 100644 index d3d9da62..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/NegPair.solc +++ /dev/null @@ -1,53 +0,0 @@ - -forall a . class a : Neg { - function neg(x:a) -> a; -} - -data B = F | T; - -instance B : Neg { - function neg (x : B) -> B { - match x { - | B.F => return B.T; - | B.T => return B.F; - } - } -} - -forall a b . function fst (p : (a,b)) -> a { - match p { - | (x,y) => return x; - } -} - -forall a b . function snd(p : (a,b)) -> b { - match p { - | (x,y) => return y; - } -} - - -forall a b . a : Neg, b : Neg => instance (a,b):Neg { - function neg(p : (a,b)) -> (a,b) { - return (Neg.neg (fst(p)), Neg.neg(snd (p))); - } -} - -contract NegPair { - - public function bnot(x : B) -> B { - match x { - | B.T => return B.F; - | B.F => return B.T; - } -} - - public function fromB(b : B) -> word { - match b { - | B.F => return 0; - | B.T => return 1; - } -} - - public function main() -> word { return fromB(fst(Neg.neg((B.F,B.T)))); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Option.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Option.sol new file mode 100644 index 00000000..e84e01f2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Option.sol @@ -0,0 +1,19 @@ +contract Option { + enum Option { None, Some(a) } + + function join(mmx: Option>) public returns (Option) { + match (mmx) { +case Option.None { +return Option.None; +} +case Option.Some(Option.Some(x)) { +return Option.Some(x); +} +case Option.Some(Option.None) { +return Option.None; +} +} + } + + function main() public returns (word) { return 0; } + } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Option.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Option.solc deleted file mode 100644 index 5176d111..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Option.solc +++ /dev/null @@ -1,13 +0,0 @@ -contract Option { - data Option(a) = None | Some(a); - - public function join(mmx : Option(Option(word))) -> Option(word) { - match mmx { - | Option.None => return Option.None; - | Option.Some(Option.Some(x)) => return Option.Some(x); - | Option.Some(Option.None) => return Option.None; - } - } - - public function main() -> word { return 0; } - } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Pair.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Pair.sol new file mode 100644 index 00000000..8ad2e11d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Pair.sol @@ -0,0 +1,35 @@ + function fst(x: (a, b)) returns (a) { + match (x) { +case (a,_) { +return a; +} +} + } + + function snd(x: (a, b)) returns (b) { + match (x) { +case (_,b) { +return b; +} +} + } + + function uncurry(f: function(word, word) returns (word), x: (word, word)) returns (word) { + match (x) { +case (a,b) { +return f(a,b); +} +} + } + + function snds(p1: (word, word), p2: (word, word)) returns (word, word) { + match (p1, p2) { +case ((a,b) , (c,d)) { +return (b,d); +} +} + } + + function curry(f: function((word, word)) returns (word), x: word, y: word) returns (word) { + return f((x,y)) ; + } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Pair.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Pair.solc deleted file mode 100644 index 5e698e45..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Pair.solc +++ /dev/null @@ -1,27 +0,0 @@ - forall a b . function fst (x : (a,b)) -> a { - match x { - | (a,_) => return a; - } - } - - forall a b . function snd(x : (a,b)) -> b { - match x { - | (_,b) => return b; - } - } - - function uncurry(f : (word, word) -> word, x : (word,word)) -> word { - match x { - | (a,b) => return f(a,b); - } - } - - function snds (p1 : (word,word), p2 : (word,word)) -> (word,word) { - match p1, p2 { - | (a,b) , (c,d) => return (b,d); - } - } - - function curry(f : ((word,word)) -> word, x : word, y : word) -> word { - return f((x,y)) ; - } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Peano.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Peano.sol new file mode 100644 index 00000000..1bcfc428 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Peano.sol @@ -0,0 +1,16 @@ +enum Nat { Zero, Succ(Nat) } + +function natInd(step: function(Nat, Nat) returns (Nat), v: Nat, n: Nat) returns (Nat) { + match (n) { +case Nat.Zero { +return v ; +} +case Nat.Succ(m) { +return step(m, natInd(step,v,m)); +} +} +} + +function add(n: Nat, m: Nat) returns (Nat) { + return natInd (lam (x, acc) {return Nat.Succ(acc) ; }, m, n); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Peano.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Peano.solc deleted file mode 100644 index 4deac861..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Peano.solc +++ /dev/null @@ -1,12 +0,0 @@ -data Nat = Zero | Succ(Nat); - -function natInd (step : (Nat, Nat) -> Nat, v : Nat, n : Nat) -> Nat { - match n { - | Nat.Zero => return v ; - | Nat.Succ(m) => return step(m, natInd(step,v,m)); - } -} - -function add(n : Nat, m : Nat) -> Nat { - return natInd (lam (x, acc) {return Nat.Succ(acc) ; }, m, n); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/PeanoMatch.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/PeanoMatch.sol new file mode 100644 index 00000000..252e91ad --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/PeanoMatch.sol @@ -0,0 +1,15 @@ +enum Nat { Zero, Succ(Nat) } + +function foo(n: Nat) returns (Nat) { + match (n) { +case Nat.Zero { +return Nat.Succ(Nat.Zero) ; +} +case Nat.Succ(Nat.Succ(x)) { +return x; +} +case x { +return Nat.Zero; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/PeanoMatch.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/PeanoMatch.solc deleted file mode 100644 index 696c5136..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/PeanoMatch.solc +++ /dev/null @@ -1,9 +0,0 @@ -data Nat = Zero | Succ(Nat); - -function foo(n : Nat) -> Nat { - match n { - | Nat.Zero => return Nat.Succ(Nat.Zero) ; - | Nat.Succ(Nat.Succ(x)) => return x; - | x => return Nat.Zero; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/RefDeref.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/RefDeref.sol new file mode 100644 index 00000000..2f2be66c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/RefDeref.sol @@ -0,0 +1,10 @@ +trait Loadable { + function load(r: ref) returns (deref) ; +} + +trait Storable { + function store(r: ref, d: deref) ; +} + +// haskell style class constraints +trait Ref where ref: Loadable, ref: Storable {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/RefDeref.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/RefDeref.solc deleted file mode 100644 index f096ffb5..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/RefDeref.solc +++ /dev/null @@ -1,12 +0,0 @@ -forall ref deref . class ref:Loadable (deref) { - function load (r : ref) -> deref; -} - -forall ref deref . class ref:Storable (deref) { - function store (r : ref, d : deref) -> (); -} - -// haskell style class constraints -forall ref deref . - ref : Loadable(deref) - , ref : Storable(ref) => class ref:Ref (deref) {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SimpleLambda.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SimpleLambda.sol new file mode 100644 index 00000000..b1bc3033 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SimpleLambda.sol @@ -0,0 +1,22 @@ +function addWord(x: word, y: word) returns (word) { + let res: word; + assembly { + res := add(x, y) + } + return res; +} + +contract SimpleLambda{ + function f(z: word) public returns (word) { + let n = lam (x : word, y : word) { + return addWord(x,addWord(y,1)); + } ; + let m = lam (x : word) { + return addWord (z,x) ; + } ; + return m(n(1,0)); + } + function main() public returns (word) { + return f(40); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SimpleLambda.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SimpleLambda.solc deleted file mode 100644 index 1a68797e..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SimpleLambda.solc +++ /dev/null @@ -1,22 +0,0 @@ -function addWord(x : word, y : word) -> word { - let res: word; - assembly { - res := add(x, y) - } - return res; -} - -contract SimpleLambda{ - public function f (z : word) -> word { - let n = lam (x : word, y : word) { - return addWord(x,addWord(y,1)); - } ; - let m = lam (x : word) { - return addWord (z,x) ; - } ; - return m(n(1,0)); - } - public function main() -> word { - return f(40); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SingleFun.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SingleFun.sol new file mode 100644 index 00000000..e922f202 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SingleFun.sol @@ -0,0 +1,3 @@ +function id(x: word) returns (word) { + return x ; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SingleFun.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SingleFun.solc deleted file mode 100644 index 0f93d869..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SingleFun.solc +++ /dev/null @@ -1,3 +0,0 @@ -function id (x : word) -> word { - return x ; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Uncurry.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Uncurry.sol new file mode 100644 index 00000000..ba9be2f9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Uncurry.sol @@ -0,0 +1,7 @@ +function uncurry(f: word, p: (word, word)) returns (word) { + match (p) { +case (x,y) { +return f(x,y); +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Uncurry.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Uncurry.solc deleted file mode 100644 index bde18537..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Uncurry.solc +++ /dev/null @@ -1,5 +0,0 @@ -function uncurry (f : word, p : (word, word)) -> word { - match p { - | (x,y) => return f(x,y); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/abigeneric.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/abigeneric.sol new file mode 100644 index 00000000..ddd5893e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/abigeneric.sol @@ -0,0 +1,113 @@ +pragma no-patterson-condition ABIAttribs, ABIEncode, ABIDecode; +pragma no-bounded-variable-condition ABIAttribs, ABIEncode, ABIDecode; +pragma no-coverage-condition ABIDecode; + +export { + encode, + decode +}; + +import * from std; +import {mstore} from std.opcodes; +import * from std.Generic; + +// ─── ABIAttribs for the primitive sum(f, g) type ───────────────────────── +// headSize = 32 (tag word) + max(headSize(f), headSize(g)) + +impl ABIAttribs> where f: ABIAttribs, g: ABIAttribs { + function headSize(ty: Proxy>) returns (word) { + let pf : Proxy; + let pg : Proxy; + return 32 + maxWord(ABIAttribs.headSize(pf), ABIAttribs.headSize(pg)); + } + function isStatic(ty: Proxy>) returns (bool) { + let pf : Proxy; + let pg : Proxy; + return and(ABIAttribs.isStatic(pf), ABIAttribs.isStatic(pg)); + } +} + +// ─── ABIEncode for sum(f, g) ───────────────────────────────────────────── +// Wire layout (static sums only): +// [offset + 0 .. offset + 31] : tag word (0 = inl, 1 = inr) +// [offset + 32 .. ] : encoded branch payload + +impl ABIEncode> where f: ABIAttribs, f: ABIEncode, g: ABIAttribs, g: ABIEncode { + function encodeInto(x: sum, basePtr: word, offset: word, tail: word) returns (word) { + match (x) { +case inl(v) { +mstore(basePtr + offset, 0); + return ABIEncode.encodeInto(v, basePtr, offset + 32, tail); +} +case inr(v) { +mstore(basePtr + offset, 1); + return ABIEncode.encodeInto(v, basePtr, offset + 32, tail); +} +} + } +} + +// ─── ABIDecode for sum(f, g) ───────────────────────────────────────────── +// Reads the tag word at headOffset; dispatches to f or g decoder at headOffset + 32. + +impl ABIDecode, reader>, sum> where reader: WordReader, f: ABIAttribs, ABIDecoder: ABIDecode, ABIDecoder: ABIDecode { + function decode(ptr: ABIDecoder, reader>, headOffset: word) returns (sum) { + match (ptr) { +case ABIDecoder(rdr) { +let tag = WordReader.read(WordReader.advance(rdr, headOffset)); + match (tag) { +case 0 { +let dec_f : ABIDecoder = ABIDecoder(rdr); + return inl(ABIDecode.decode(dec_f, headOffset + 32)); +} +default { +let dec_g : ABIDecoder = ABIDecoder(rdr); + return inr(ABIDecode.decode(dec_g, headOffset + 32)); +} +} +} +} + } +} + +// ─── Default bridges: ABIAttribs and ABIEncode via Generic ─────────────── +// Any type 'a' with Generic(rep) inherits its ABI layout from rep. + +default impl ABIAttribs where a: Generic, rep: ABIAttribs { + function headSize(ty: Proxy) returns (word) { + let prx : Proxy; + return ABIAttribs.headSize(prx); + } + function isStatic(ty: Proxy) returns (bool) { + let prx : Proxy; + return ABIAttribs.isStatic(prx); + } +} + +default impl ABIEncode where a: Generic, rep: ABIAttribs, rep: ABIEncode { + function encodeInto(x: a, basePtr: word, offset: word, tail: word) returns (word) { + return ABIEncode.encodeInto(Generic.from(x), basePtr, offset, tail); + } +} + +// ─── Top-level generic encode function ─────────────────────────────────── +// Serialises any 'a' that has a Generic(rep) instance. +// Only the Generic instance is required — ABIEncode is resolved via the bridge. + +function encode(x: a, basePtr: word, offset: word, tail: word) returns (word) where a: Generic, rep: ABIAttribs, rep: ABIEncode { + let xrep : rep = Generic.from(x); + return ABIEncode.encodeInto(xrep, basePtr, offset, tail); +} + +// ─── Top-level generic decode function ─────────────────────────────────── +// Deserialises any 'a' that has a Generic(rep) instance. +// Only the Generic instance is required — ABIDecode is resolved via the bridge. + +function decode(ptr: ABIDecoder, headOffset: word) returns (a) where a: Generic, reader: WordReader, ABIDecoder: ABIDecode { + match (ptr) { +case ABIDecoder(rdr) { +let rep_ptr : ABIDecoder = ABIDecoder(rdr); + return Generic.to(ABIDecode.decode(rep_ptr, headOffset)); +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/abigeneric.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/abigeneric.solc deleted file mode 100644 index d357a803..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/abigeneric.solc +++ /dev/null @@ -1,121 +0,0 @@ -pragma no-patterson-condition ABIAttribs, ABIEncode, ABIDecode; -pragma no-bounded-variable-condition ABIAttribs, ABIEncode, ABIDecode; -pragma no-coverage-condition ABIDecode; - -export { - encode, - decode -}; - -import std.{*}; -import std.opcodes.{mstore}; -import std.Generic.{*}; - -// ─── ABIAttribs for the primitive sum(f, g) type ───────────────────────── -// headSize = 32 (tag word) + max(headSize(f), headSize(g)) - -forall f g . f:ABIAttribs, g:ABIAttribs => -instance sum(f, g) : ABIAttribs { - function headSize(ty : Proxy(sum(f, g))) -> word { - let pf : Proxy(f); - let pg : Proxy(g); - return 32 + maxWord(ABIAttribs.headSize(pf), ABIAttribs.headSize(pg)); - } - function isStatic(ty : Proxy(sum(f, g))) -> bool { - let pf : Proxy(f); - let pg : Proxy(g); - return and(ABIAttribs.isStatic(pf), ABIAttribs.isStatic(pg)); - } -} - -// ─── ABIEncode for sum(f, g) ───────────────────────────────────────────── -// Wire layout (static sums only): -// [offset + 0 .. offset + 31] : tag word (0 = inl, 1 = inr) -// [offset + 32 .. ] : encoded branch payload - -forall f g . f:ABIAttribs, f:ABIEncode, g:ABIAttribs, g:ABIEncode => -instance sum(f, g) : ABIEncode { - function encodeInto(x : sum(f, g), basePtr : word, offset : word, tail : word) -> word { - match x { - | inl(v) => - mstore(basePtr + offset, 0); - return ABIEncode.encodeInto(v, basePtr, offset + 32, tail); - | inr(v) => - mstore(basePtr + offset, 1); - return ABIEncode.encodeInto(v, basePtr, offset + 32, tail); - } - } -} - -// ─── ABIDecode for sum(f, g) ───────────────────────────────────────────── -// Reads the tag word at headOffset; dispatches to f or g decoder at headOffset + 32. - -forall f g reader . - reader : WordReader, - f : ABIAttribs, - ABIDecoder(f, reader) : ABIDecode(f), - ABIDecoder(g, reader) : ABIDecode(g) => -instance ABIDecoder(sum(f, g), reader) : ABIDecode(sum(f, g)) { - function decode(ptr : ABIDecoder(sum(f, g), reader), headOffset : word) -> sum(f, g) { - match ptr { - | ABIDecoder(rdr) => - let tag = WordReader.read(WordReader.advance(rdr, headOffset)); - match tag { - | 0 => - let dec_f : ABIDecoder(f, reader) = ABIDecoder(rdr); - return inl(ABIDecode.decode(dec_f, headOffset + 32)); - | _ => - let dec_g : ABIDecoder(g, reader) = ABIDecoder(rdr); - return inr(ABIDecode.decode(dec_g, headOffset + 32)); - } - } - } -} - -// ─── Default bridges: ABIAttribs and ABIEncode via Generic ─────────────── -// Any type 'a' with Generic(rep) inherits its ABI layout from rep. - -forall a rep . a:Generic(rep), rep:ABIAttribs => -default instance a : ABIAttribs { - function headSize(ty : Proxy(a)) -> word { - let prx : Proxy(rep); - return ABIAttribs.headSize(prx); - } - function isStatic(ty : Proxy(a)) -> bool { - let prx : Proxy(rep); - return ABIAttribs.isStatic(prx); - } -} - -forall a rep . a:Generic(rep), rep:ABIAttribs, rep:ABIEncode => -default instance a : ABIEncode { - function encodeInto(x : a, basePtr : word, offset : word, tail : word) -> word { - return ABIEncode.encodeInto(Generic.from(x), basePtr, offset, tail); - } -} - -// ─── Top-level generic encode function ─────────────────────────────────── -// Serialises any 'a' that has a Generic(rep) instance. -// Only the Generic instance is required — ABIEncode is resolved via the bridge. - -forall a rep . a:Generic(rep), rep:ABIAttribs, rep:ABIEncode => -function encode(x : a, basePtr : word, offset : word, tail : word) -> word { - let xrep : rep = Generic.from(x); - return ABIEncode.encodeInto(xrep, basePtr, offset, tail); -} - -// ─── Top-level generic decode function ─────────────────────────────────── -// Deserialises any 'a' that has a Generic(rep) instance. -// Only the Generic instance is required — ABIDecode is resolved via the bridge. - -forall a rep reader . - a : Generic(rep), - reader : WordReader, - ABIDecoder(rep, reader) : ABIDecode(rep) => -function decode(ptr : ABIDecoder(a, reader), headOffset : word) -> a { - match ptr { - | ABIDecoder(rdr) => - let rep_ptr : ABIDecoder(rep, reader) = ABIDecoder(rdr); - return Generic.to(ABIDecode.decode(rep_ptr, headOffset)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/another-subst.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/another-subst.sol new file mode 100644 index 00000000..82ec0e03 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/another-subst.sol @@ -0,0 +1,11 @@ +trait Foo {function foo(x: a) ; } + +impl Foo<(a, b)> where a: Foo, b: Foo { + function foo(p: (a, b)) { + match (p) { +case (pa, pb) { +Foo.foo(pa); Foo.foo(pb); +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/another-subst.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/another-subst.solc deleted file mode 100644 index 37840ccd..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/another-subst.solc +++ /dev/null @@ -1,9 +0,0 @@ -forall a . class a: Foo {function foo(x:a) -> (); } - -forall a b . a : Foo, b : Foo => instance (a,b) : Foo { - function foo( p : (a,b) ) -> () { - match p { - | (pa, pb) => Foo.foo(pa); Foo.foo(pb); - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/app.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/app.sol new file mode 100644 index 00000000..53bed945 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/app.sol @@ -0,0 +1,21 @@ +function app(f: c, x: a) returns (b) where c: invokable { + return invokable.invoke(f, x); +} + +enum t_id { t_id } + +impl invokable { + function invoke(self: t_id, x: word) returns (word) { + return x; + } +} + +function foo() returns (word) { + return app(t_id, 0); +} + +contract C { + function main() public returns (word) { + return foo(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/app.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/app.solc deleted file mode 100644 index 60f4b573..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/app.solc +++ /dev/null @@ -1,21 +0,0 @@ -forall a b c . c : invokable(a, b) => function app (f : c, x : a) -> b { - return invokable.invoke(f, x); -} - -data t_id = t_id; - -instance t_id : invokable(word, word) { - function invoke(self : t_id, x : word) -> word { - return x; - } -} - -function foo() -> word { - return app(t_id, 0); -} - -contract C { - public function main () -> word { - return foo(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/array.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/array.sol new file mode 100644 index 00000000..d5b659e9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/array.sol @@ -0,0 +1,119 @@ +pragma no-coverage-condition TAdd; + +enum Zero {} +enum Succ {} + +trait TAdd {} +impl TAdd<(Zero, a), a> {} +impl TAdd<(Succ, a), Succ> where (b, a): TAdd {} + +trait Eq {} +impl Eq {} + +// this should work but doesnt: forall sizel sizer elem sizeout . (sizel, sizer):TAdd(sizeout) +// TODO: this panics during specialization +/* +forall sizel sizer elem sizeout pairSizelSizer . pairSizelSizer:Eq((sizel, sizer)), pairSizelSizer:TAdd(sizeout) => function concat(lhs:memory(array(sizel, elem)), rhs:memory(array(sizer, elem))) -> memory(array(sizeout, elem)) { + return memory(0) : memory(array(sizeout, elem)); // :D +} +*/ +enum Itself { ItselfRuntimeTag } + +enum array { array } +enum memory { memory(word) } + +trait IndexAccessible { + function set(self: self, ix: indexType, val: elementType) ; + function at(self: self, ix: indexType) returns (elementType) ; +} + +trait ToWord { + function toWord(self: Itself) returns (word) ; +} + +impl ToWord { + function toWord(zero: Itself) returns (word) { return 0; } +} + +impl ToWord> where prev: ToWord { + function toWord(self: Itself>) returns (word) { + let prevTag : Itself = Itself.ItselfRuntimeTag; + let returnVal : word = ToWord.toWord(prevTag); + assembly { + returnVal := add(1, returnVal) + } + return returnVal; + } +} + +trait MemoryType { + function load(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +impl MemoryType { + function load(ptr: word) returns (word) { + let val : word; + assembly { val := mload(ptr) } + return val; + } + function store(ptr: word, value: word) { + assembly { mstore(ptr, value) } + } +} + +impl IndexAccessible>, word, elem> where size: ToWord, elem: MemoryType { + function at(self: memory>, index: word) returns (elem) { + let sizeTag : Itself = Itself.ItselfRuntimeTag; + let sizeValue = ToWord.toWord(sizeTag); + + assembly { + if iszero(lt(index, sizeValue)) { + revert(0, 0) + } + } + + match (self) { +case memory(offset) { +let x = offset; // can't use this inside the assembly block :-( + assembly { + index := add(x, mul(32, index)) + } + return MemoryType.load(index); +} +} + } + + function set(self: memory>, index: word, val: elem) { + let sizeTag : Itself = Itself.ItselfRuntimeTag; + let sizeValue = ToWord.toWord(sizeTag); + + assembly { + if iszero(lt(index, sizeValue)) { + revert(0, 0) + } + } + + match (self) { +case memory(offset) { +let x = offset; // can't use this inside the assembly block :-( + assembly { + index := add(x, mul(32, index)) + } + MemoryType.store(index, val); +} +} + } +} + + + +contract Array { + + function main() public returns (word) { + let arr : memory>>>, word>> = memory(42); // = (1,2,3,4,5,6,7,8,9,10); + IndexAccessible.set(arr, 3, 33); + + return IndexAccessible.at(arr, 3); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/array.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/array.solc deleted file mode 100644 index b587bb4b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/array.solc +++ /dev/null @@ -1,114 +0,0 @@ -pragma no-coverage-condition TAdd; - -data Zero; -data Succ(a); - -forall self res . class self:TAdd(res) {} -forall a . instance (Zero, a):TAdd(a) {} -forall a b c . (b, a):TAdd(c) => instance (Succ(b), a):TAdd(Succ(c)) {} - -forall lhs rhs . class lhs:Eq(rhs) {} -forall a . instance a:Eq(a) {} - -// this should work but doesnt: forall sizel sizer elem sizeout . (sizel, sizer):TAdd(sizeout) -// TODO: this panics during specialization -/* -forall sizel sizer elem sizeout pairSizelSizer . pairSizelSizer:Eq((sizel, sizer)), pairSizelSizer:TAdd(sizeout) => function concat(lhs:memory(array(sizel, elem)), rhs:memory(array(sizer, elem))) -> memory(array(sizeout, elem)) { - return memory(0) : memory(array(sizeout, elem)); // :D -} -*/ -data Itself(a) = ItselfRuntimeTag; - -data array(size, elem) = array; -data memory(a) = memory(word); - -forall self indexType elementType . class self:IndexAccessible (indexType, elementType){ - function set(self:self, ix:indexType, val:elementType) -> (); - function at(self:self, ix:indexType) -> elementType; -} - -forall self . class self:ToWord{ - function toWord(self:Itself(self)) -> word; -} - -instance Zero : ToWord { - function toWord(zero : Itself(Zero)) -> word { return 0; } -} - -forall prev . prev:ToWord => instance Succ(prev) : ToWord { - function toWord(self: Itself(Succ(prev))) -> word { - let returnVal : word = ToWord.toWord(Itself.ItselfRuntimeTag:Itself(prev)); - assembly { - returnVal := add(1, returnVal) - } - return returnVal; - } -} - -forall self . class self:MemoryType { - function load(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -instance word:MemoryType { - function load(ptr:word) -> word { - let val : word; - assembly { val := mload(ptr) } - return val; - } - function store(ptr:word, value:word) -> () { - assembly { mstore(ptr, value) } - } -} - -forall size elem . size : ToWord, elem:MemoryType => instance memory(array(size, elem)) : IndexAccessible(word, elem) { - function at(self : memory(array(size,elem)), index : word) -> elem { - let sizeValue = ToWord.toWord(Itself.ItselfRuntimeTag:Itself(size)); - - assembly { - if iszero(lt(index, sizeValue)) { - revert(0, 0) - } - } - - match self { - | memory(offset) => - let x = offset; // can't use this inside the assembly block :-( - assembly { - index := add(x, mul(32, index)) - } - return MemoryType.load(index); - } - } - - function set(self : memory(array(size,elem)), index : word, val : elem) -> () { - let sizeValue = ToWord.toWord(Itself.ItselfRuntimeTag:Itself(size)); - - assembly { - if iszero(lt(index, sizeValue)) { - revert(0, 0) - } - } - - match self { - | memory(offset) => - let x = offset; // can't use this inside the assembly block :-( - assembly { - index := add(x, mul(32, index)) - } - MemoryType.store(index, val); - } - } -} - - - -contract Array { - - public function main() -> word { - let arr : memory(array(Succ(Succ(Succ(Succ(Zero)))), word)) = memory(42); // = (1,2,3,4,5,6,7,8,9,10); - IndexAccessible.set(arr, 3, 33); - - return IndexAccessible.at(arr, 3); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-let-bool-lit.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-let-bool-lit.sol new file mode 100644 index 00000000..8cb30c46 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-let-bool-lit.sol @@ -0,0 +1,13 @@ +// Yul has no boolean type: `true`/`false` are word literals (1/0). A literal +// `true` in an assembly block must type-check as `word`. Before the fix +// `tcYLit YulTrue/YulFalse` called `notImplemented`, crashing the compiler. +contract Test { + function main() public returns (word) { + let r : word = 0; + assembly { + let x := true + r := x + } + return r; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-let-bool-lit.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-let-bool-lit.solc deleted file mode 100644 index 4af0a717..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-let-bool-lit.solc +++ /dev/null @@ -1,13 +0,0 @@ -// Yul has no boolean type: `true`/`false` are word literals (1/0). A literal -// `true` in an assembly block must type-check as `word`. Before the fix -// `tcYLit YulTrue/YulFalse` called `notImplemented`, crashing the compiler. -contract Test { - public function main() -> word { - let r : word = 0; - assembly { - let x := true - r := x - } - return r; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-let-uninit.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-let-uninit.sol new file mode 100644 index 00000000..8f35b9ac --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-let-uninit.sol @@ -0,0 +1,15 @@ +// An uninitialized Yul `let x` must introduce the binding so that later +// assignments and reads of `x` resolve and are type-checked as `word`. +// Before the fix `tcYulStmt` dropped `YLet ns Nothing`, so `x` never entered +// the env and the read `r := x` failed to resolve. +contract Test { + function main() public returns (word) { + let r : word = 0; + assembly { + let x + x := add(1, 1) + r := x + } + return r; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-let-uninit.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-let-uninit.solc deleted file mode 100644 index 0229db02..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-let-uninit.solc +++ /dev/null @@ -1,15 +0,0 @@ -// An uninitialized Yul `let x` must introduce the binding so that later -// assignments and reads of `x` resolve and are type-checked as `word`. -// Before the fix `tcYulStmt` dropped `YLet ns Nothing`, so `x` never entered -// the env and the read `r := x` failed to resolve. -contract Test { - public function main() -> word { - let r : word = 0; - assembly { - let x - x := add(1, 1) - r := x - } - return r; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-match-tuple-read.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-match-tuple-read.sol new file mode 100644 index 00000000..ca2f92f6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-match-tuple-read.sol @@ -0,0 +1,12 @@ +contract C { + function main() returns (word) { + let res : word; + let foo : (word, word) = (1, 42); + match (foo) { +case (v0, v1) { +assembly { res := v1 } +} +} + return res; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-match-tuple-read.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-match-tuple-read.solc deleted file mode 100644 index 359a0249..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-match-tuple-read.solc +++ /dev/null @@ -1,10 +0,0 @@ -contract C { - function main() -> word { - let res : word; - let foo : (word,word) = (1, 42); - match foo { - | (v0, v1) => assembly { res := v1 } - } - return res; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-match-tuple-write-read.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-match-tuple-write-read.sol new file mode 100644 index 00000000..230911c2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-match-tuple-write-read.sol @@ -0,0 +1,20 @@ +// After an assembly block writes to a pattern variable, subsequent code in the +// same match arm should read the written value (not the original tuple component). +// Runtime correctness of the write->read depends on ecSubst being updated after +// the assembly block (EmitHull.hs: emitStmt MastAsm, modify ecSubst). +contract C { + function main() returns (word) { + let res : word; + let foo : (word, word) = (0, 0); + match (foo) { +case (v0, v1) { +{ + assembly { v1 := 42 } + let x : word = v1; + assembly { res := x } + } +} +} + return res; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-match-tuple-write-read.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-match-tuple-write-read.solc deleted file mode 100644 index 1816e0fb..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/asm-match-tuple-write-read.solc +++ /dev/null @@ -1,18 +0,0 @@ -// After an assembly block writes to a pattern variable, subsequent code in the -// same match arm should read the written value (not the original tuple component). -// Runtime correctness of the write->read depends on ecSubst being updated after -// the assembly block (EmitHull.hs: emitStmt MastAsm, modify ecSubst). -contract C { - function main() -> word { - let res : word; - let foo : (word,word) = (0, 0); - match foo { - | (v0, v1) => { - assembly { v1 := 42 } - let x : word = v1; - assembly { res := x } - } - } - return res; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/assembly.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/assembly.sol new file mode 100644 index 00000000..5ed3d22c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/assembly.sol @@ -0,0 +1,18 @@ +trait Mem { + function size(x: a) returns (word) ; +} + +impl Mem { + function size(x: word) returns (word) { + return 32; + } +} + +function foo() { + let ptr : word; + let arg : word = 0; + let size = Mem.size(arg); + assembly { + ptr := add(32, size) + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/assembly.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/assembly.solc deleted file mode 100644 index 5850f0ca..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/assembly.solc +++ /dev/null @@ -1,18 +0,0 @@ -forall a . class a : Mem { - function size(x : a) -> word; -} - -instance word : Mem { - function size(x : word) -> word { - return 32; - } -} - -function foo () -> () { - let ptr : word; - let arg : word = 0; - let size = Mem.size(arg); - assembly { - ptr := add(32, size) - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bal.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bal.sol new file mode 100644 index 00000000..1becf7a2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bal.sol @@ -0,0 +1,57 @@ +enum Proxy { Proxy } +enum dict { dict(word, Proxy, Proxy) } +enum address { address(word) } +enum storage { storage(word) } + +enum IndexAP { IndexAP(m, idx, Proxy) } + +function wal(ref: storage>, src: address, amt: word) { + let ip = IndexAP(ref, src, @word); + Assign.assign(LVA.acc(ip), amt); +} + + +/* Expected: + +ip : IndexAP(storage(dict(address, word)) , address, ?1) + +LVA.acc : forall self memberRefType. self:LVA(memberRefType) => self -> memberRefType + +instance IndexAP(storage(dict(index,member)), index, member):LVA(storage(member)) + + |- instance IndexAP(storage(map(address, word)), address, ?1) : LVA(storage(word))) where ?1 ~ word + +*/ + +/* Actual + +> Enter reduce() |- (?l4 : Assign (word), IndexAP(storage(dict(address, word)), address, ?e4) : LVA (?l4)) +> Reducing wanted constraints:(?l4 : Assign (word), IndexAP(storage(dict(address, word)), address, ?e4) : LVA (?l4)) using () +> After entailment:(?l4 : Assign (word), IndexAP(storage(dict(address, word)), address, ?e4) : LVA (?l4)) - () +>> Before eliminating equalities (?l4 : Assign (word), IndexAP(storage(dict(address, word)), address, ?e4) : LVA (?l4)) +>> After eliminating equalities:(IndexAP(storage(dict(address, word)), address, ?e4) : LVA (?l4), ?l4 : Assign (word)) +>>> Found instance for:IndexAP(storage(dict(address, word)), address, ?e4) : LVA (?l4) + +>>>Instance:?a5 ~ storage(?b5) => IndexAP(storage(dict(?c5, ?b5)), ?c5, ?b5) : LVA (?a5) !!! +>>>Subst:{?c5 +-> address, ?b5 +-> word, ?b5 +-> ?e4, ?l4 +-> ?a5} ??? + +b5 +-> e4 should really be b5 ~ e4 + +*/ +trait LVA { + function acc(x: self) returns (memberRefType) ; +} + +impl LVA>, index, member>, storage> { + function acc(x: IndexAP>, index, member>) returns (storage) { + return storage(30); + } +} + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +impl Assign, a> { + function assign(l: storage, y: a) {} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bal.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bal.solc deleted file mode 100644 index c2f51f0c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bal.solc +++ /dev/null @@ -1,60 +0,0 @@ -data Proxy (a) = Proxy ; -data dict(member, index) = dict(word, Proxy(member), Proxy(index)) ; -data address = address(word) ; -data storage(a) = storage(word) ; - -data IndexAP (m, idx, member) = IndexAP(m, idx, Proxy(member)) ; - -function wal(ref: storage(dict(address, word)) , src : address, amt: word) -> () { - let ip = IndexAP(ref, src, Proxy : Proxy(word)); - Assign.assign(LVA.acc(ip), amt); -} - - -/* Expected: - -ip : IndexAP(storage(dict(address, word)) , address, ?1) - -LVA.acc : forall self memberRefType. self:LVA(memberRefType) => self -> memberRefType - -instance IndexAP(storage(dict(index,member)), index, member):LVA(storage(member)) - - |- instance IndexAP(storage(map(address, word)), address, ?1) : LVA(storage(word))) where ?1 ~ word - -*/ - -/* Actual - -> Enter reduce() |- (?l4 : Assign (word), IndexAP(storage(dict(address, word)), address, ?e4) : LVA (?l4)) -> Reducing wanted constraints:(?l4 : Assign (word), IndexAP(storage(dict(address, word)), address, ?e4) : LVA (?l4)) using () -> After entailment:(?l4 : Assign (word), IndexAP(storage(dict(address, word)), address, ?e4) : LVA (?l4)) - () ->> Before eliminating equalities (?l4 : Assign (word), IndexAP(storage(dict(address, word)), address, ?e4) : LVA (?l4)) ->> After eliminating equalities:(IndexAP(storage(dict(address, word)), address, ?e4) : LVA (?l4), ?l4 : Assign (word)) ->>> Found instance for:IndexAP(storage(dict(address, word)), address, ?e4) : LVA (?l4) - ->>>Instance:?a5 ~ storage(?b5) => IndexAP(storage(dict(?c5, ?b5)), ?c5, ?b5) : LVA (?a5) !!! ->>>Subst:{?c5 +-> address, ?b5 +-> word, ?b5 +-> ?e4, ?l4 +-> ?a5} ??? - -b5 +-> e4 should really be b5 ~ e4 - -*/ -forall self memberRefType. -class self:LVA(memberRefType) { - function acc(x:self) -> memberRefType; -} - -forall index member. - instance IndexAP(storage(dict(index,member)), index, member):LVA(storage(member)) { - function acc(x:IndexAP(storage(dict(index,member)), index, member)) -> storage(member) { - return storage(30); - } -} - -forall lhs rhs. -class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -forall a . instance storage(a):Assign(a) { - function assign(l:storage(a), y:a) -> () {} -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bar.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bar.sol new file mode 100644 index 00000000..c522a5cd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bar.sol @@ -0,0 +1,18 @@ +pragma no-coverage-condition Bar; + +enum Wrap { Wrap(a) } + +trait Foo {} + +trait Bar {} + +impl Bar, b> where a: Foo {} + +function need_bar(x: Wrap) where Wrap: Bar { + return (); +} + +function use_bar(x: Wrap) where a: Foo { + need_bar(x); + return (); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bar.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bar.solc deleted file mode 100644 index 24e215e1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bar.solc +++ /dev/null @@ -1,20 +0,0 @@ -pragma no-coverage-condition Bar; - -data Wrap(a) = Wrap(a); - -forall self rep . class self : Foo(rep) {} - -forall self rep . class self : Bar(rep) {} - -forall a b . a : Foo(b) => instance Wrap(a) : Bar(b) {} - -forall a rep . Wrap(a) : Bar(rep) => -function need_bar(x : Wrap(a)) -> () { - return (); -} - -forall a . a : Foo(word) => -function use_bar(x : Wrap(a)) -> () { - need_bar(x); - return (); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bitwise.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bitwise.sol new file mode 100644 index 00000000..480b72df --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bitwise.sol @@ -0,0 +1,35 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +// Exercises the `^` / `&` / `|` binary operators, the unary `~`, the +// `^=` / `&=` / `|=` compound assignments and the unary `~=` in-place +// complement, plus the bxorWord / bandWord / borWord / bnotWord constant +// folding (mirrors gtWord). +function fxor(x: word, y: word) returns (word) { + let acc : word = x ^ y; + acc ^= x; // acc = (x ^ y) ^ x == y + return acc ^ 0; // identity: a ^ 0 == a +} + +function fbitwise(x: word, y: word) returns (word) { + let acc : word = x & y; + acc |= x; // acc = (x & y) | x == x + acc &= y; // acc = x & y + return acc | 0; // identity: a | 0 == a +} + +// `~x` complements every bit, so `~(~x) == x` and `x & ~0 == x` (`~0` is +// all ones, the AND identity). +function fnot(x: word) returns (word) { + let acc : word = ~x; // acc = ~x + acc ~=; // acc = ~(~x) == x (in-place `~=`) + return acc & ~0; // identity: a & ~0 == a +} + +contract Bitwise { + // fxor(5, 3) == 3, fbitwise(6, 3) == 2, fnot(4) == 4; + // 3 ^ 2 ^ 4 == 5 — folded at compile time. + function main() public returns (word) { return fxor(5, 3) ^ fbitwise(6, 3) ^ fnot(4); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bitwise.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bitwise.solc deleted file mode 100644 index a68ad730..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bitwise.solc +++ /dev/null @@ -1,35 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -// Exercises the `^` / `&` / `|` binary operators, the unary `~`, the -// `^=` / `&=` / `|=` compound assignments and the unary `~=` in-place -// complement, plus the bxorWord / bandWord / borWord / bnotWord constant -// folding (mirrors gtWord). -function fxor(x: word, y: word) -> word { - let acc : word = x ^ y; - acc ^= x; // acc = (x ^ y) ^ x == y - return acc ^ 0; // identity: a ^ 0 == a -} - -function fbitwise(x: word, y: word) -> word { - let acc : word = x & y; - acc |= x; // acc = (x & y) | x == x - acc &= y; // acc = x & y - return acc | 0; // identity: a | 0 == a -} - -// `~x` complements every bit, so `~(~x) == x` and `x & ~0 == x` (`~0` is -// all ones, the AND identity). -function fnot(x: word) -> word { - let acc : word = ~x; // acc = ~x - acc ~=; // acc = ~(~x) == x (in-place `~=`) - return acc & ~0; // identity: a & ~0 == a -} - -contract Bitwise { - // fxor(5, 3) == 3, fbitwise(6, 3) == 2, fnot(4) == 4; - // 3 ^ 2 ^ 4 == 5 — folded at compile time. - public function main() -> word { return fxor(5, 3) ^ fbitwise(6, 3) ^ fnot(4); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bool-elim.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bool-elim.sol new file mode 100644 index 00000000..5332ab74 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bool-elim.sol @@ -0,0 +1,18 @@ +enum Bool { False, True } + + function second(x: Bool, y: word) returns (word) { + match (x, y) { +case (Bool.True, z) { +return z; +} +case (Bool.False, z) { +return z; +} +} + } + +contract Second { + function main() public returns (word) { + second(Bool.True, 42) + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bool-elim.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bool-elim.solc deleted file mode 100644 index c1236689..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bool-elim.solc +++ /dev/null @@ -1,14 +0,0 @@ -data Bool = False | True; - - function second(x : Bool, y : word) -> word { - match x, y { - | Bool.True, z => return z; - | Bool.False, z => return z; - } - } - -contract Second { - public function main() -> word { - second(Bool.True, 42) - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bound-merge-case.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bound-merge-case.sol new file mode 100644 index 00000000..9b491034 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bound-merge-case.sol @@ -0,0 +1,5 @@ +// Pragmas to disable checks for specific classes +//pragma no-bounded-variable-condition TestClassB1; + +// === Test Classes === +trait TestClassP1 {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bound-merge-case.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bound-merge-case.solc deleted file mode 100644 index 661e8588..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bound-merge-case.solc +++ /dev/null @@ -1,5 +0,0 @@ -// Pragmas to disable checks for specific classes -//pragma no-bounded-variable-condition TestClassB1; - -// === Test Classes === -forall a . class a:TestClassP1 {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bound-with-pragma.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bound-with-pragma.sol new file mode 100644 index 00000000..0ff81f0d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bound-with-pragma.sol @@ -0,0 +1,14 @@ +// Same test but with pragma to disable bound variable check +// This SHOULD PASS + +pragma no-bounded-variable-condition TestBound; +pragma no-patterson-condition TestBound; // Also disable Patterson to avoid that error + +trait TestBound {} +trait TestHelper {} + +enum TestType { TestType } + +// Variable 'bad' appears in context but not in instance head +// But pragma disables the check, so should pass +impl TestBound> where bad: TestHelper {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bound-with-pragma.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bound-with-pragma.solc deleted file mode 100644 index 3f668786..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bound-with-pragma.solc +++ /dev/null @@ -1,14 +0,0 @@ -// Same test but with pragma to disable bound variable check -// This SHOULD PASS - -pragma no-bounded-variable-condition TestBound; -pragma no-patterson-condition TestBound; // Also disable Patterson to avoid that error - -forall a . class a:TestBound {} -forall a b . class a:TestHelper(b) {} - -data TestType(x) = TestType; - -// Variable 'bad' appears in context but not in instance head -// But pragma disables the check, so should pass -forall x bad . bad:TestHelper(x) => instance TestType(x):TestBound {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-call-expected-nontail-return.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-call-expected-nontail-return.sol new file mode 100644 index 00000000..ef8d7fce --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-call-expected-nontail-return.sol @@ -0,0 +1,39 @@ +// A class-method call in a `return` that is not the last statement of the +// function must take its result type from the declared return type. +// +// The method is resolved by its *result* type alone, so nothing else can pin +// it down. In tail position this always worked, because the body's type flows +// up to the function level and is unified there; in a non-tail position that +// type is discarded, so the expected type has to reach the call itself. +// Without that, `pick` fails to compile with an ambiguous `a:FromWord`. + +enum Box { Box(a) } + +trait FromWord { + function fromWord(x: word) returns (a) ; +} + +impl FromWord> { + function fromWord(x: word) returns (Box) { + return Box(x); + } +} + +function pick(cond: bool, w: word) returns (Box) { + if (cond) { return FromWord.fromWord(w); } + return Box(w); +} + +function unbox(b: Box) returns (word) { + match (b) { +case Box(x) { +return x; +} +} +} + +contract CallExpectedNonTailReturn { + function main() returns (word) { + return unbox(pick(true, 42)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-call-expected-nontail-return.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-call-expected-nontail-return.solc deleted file mode 100644 index f6736f77..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-call-expected-nontail-return.solc +++ /dev/null @@ -1,38 +0,0 @@ -// A class-method call in a `return` that is not the last statement of the -// function must take its result type from the declared return type. -// -// The method is resolved by its *result* type alone, so nothing else can pin -// it down. In tail position this always worked, because the body's type flows -// up to the function level and is unified there; in a non-tail position that -// type is discarded, so the expected type has to reach the call itself. -// Without that, `pick` fails to compile with an ambiguous `a:FromWord`. - -data Box(a) = Box(a); - -forall a. -class a:FromWord { - function fromWord(x: word) -> a; -} - -instance Box(word):FromWord { - function fromWord(x: word) -> Box(word) { - return Box(x); - } -} - -function pick(cond: bool, w: word) -> Box(word) { - if (cond) { return FromWord.fromWord(w); } - return Box(w); -} - -function unbox(b: Box(word)) -> word { - match b { - | Box(x) => return x; - } -} - -contract CallExpectedNonTailReturn { - function main() -> word { - return unbox(pick(true, 42)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-import-default-inst-shadow.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-import-default-inst-shadow.sol new file mode 100644 index 00000000..5925aad6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-import-default-inst-shadow.sol @@ -0,0 +1,31 @@ +pragma no-patterson-condition ABIAttribs, ABIEncode; +pragma no-bounded-variable-condition ABIAttribs, ABIEncode; + +import * from std; +import * from std.Generic; + +// Minimal reproducer for the "imported-default-instance-stub mis-tagged" bug. +// +// std/Generic.sol exports: +// forall a rep . a:Generic(rep), rep:ABIAttribs, rep:ABIEncode => +// default instance a : ABIEncode { function encodeInto ... } +// +// This file redefines the exact same default instance locally. +// The instance head (True, "ABIEncode", [], TyVar "a") is shared. +// +// Bug path: +// 1. filterImportedInstanceConflicts uses topDeclClassNames, which returns [] +// because this file defines no class -- only instances. The imported stub +// is NOT filtered. +// 2. moduleInferenceDeclSegmentByKey maps the shared key to ModuleLocalDecl +// (the local definition arrives first in the ordered list). +// 3. retagModuleInferenceDecls retags the imported stub with the same key, +// giving it ModuleLocalDecl / CheckTopDeclBody mode. +// 4. tcTopDeclWithVisibility calls tcTopDecl' on the stub (funs = []). +// 5. tcInstance' -> checkCompleteInstDef -> "Incomplete definition for ABIEncode". + +default impl ABIEncode where a: Generic, rep: ABIAttribs, rep: ABIEncode { + function encodeInto(x: a, basePtr: word, offset: word, tail: word) returns (word) { + return ABIEncode.encodeInto(Generic.from(x), basePtr, offset, tail); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-import-default-inst-shadow.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-import-default-inst-shadow.solc deleted file mode 100644 index 8425be2d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-import-default-inst-shadow.solc +++ /dev/null @@ -1,32 +0,0 @@ -pragma no-patterson-condition ABIAttribs, ABIEncode; -pragma no-bounded-variable-condition ABIAttribs, ABIEncode; - -import std.{*}; -import std.Generic.{*}; - -// Minimal reproducer for the "imported-default-instance-stub mis-tagged" bug. -// -// std/Generic.solc exports: -// forall a rep . a:Generic(rep), rep:ABIAttribs, rep:ABIEncode => -// default instance a : ABIEncode { function encodeInto ... } -// -// This file redefines the exact same default instance locally. -// The instance head (True, "ABIEncode", [], TyVar "a") is shared. -// -// Bug path: -// 1. filterImportedInstanceConflicts uses topDeclClassNames, which returns [] -// because this file defines no class -- only instances. The imported stub -// is NOT filtered. -// 2. moduleInferenceDeclSegmentByKey maps the shared key to ModuleLocalDecl -// (the local definition arrives first in the ordered list). -// 3. retagModuleInferenceDecls retags the imported stub with the same key, -// giving it ModuleLocalDecl / CheckTopDeclBody mode. -// 4. tcTopDeclWithVisibility calls tcTopDecl' on the stub (funs = []). -// 5. tcInstance' -> checkCompleteInstDef -> "Incomplete definition for ABIEncode". - -forall a rep . a:Generic(rep), rep:ABIAttribs, rep:ABIEncode => -default instance a : ABIEncode { - function encodeInto(x : a, basePtr : word, offset : word, tail : word) -> word { - return ABIEncode.encodeInto(Generic.from(x), basePtr, offset, tail); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-rep-name-capture.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-rep-name-capture.sol new file mode 100644 index 00000000..13736195 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-rep-name-capture.sol @@ -0,0 +1,24 @@ +// Bug: local variable named `rep` causes name capture with the type variable `rep` +// from `class abs : Typedef(rep)`. In NameResolution.hs, the S.ExpVar and +// S.ExpName cases used a wildcard `_` for the qualifier in patterns like +// `(_, Just TLocalVar)`, so a qualified call `Typedef.rep(a)` resolved to the +// local variable `rep` instead of the class method. +// +// Expected: compiles successfully; `Typedef.rep` resolves to the class method. +// Actual (before fix): PANIC: no resolution found for invokable.invoke + +import * from std; +import * from std.dispatch; +pragma no-patterson-condition; +pragma no-coverage-condition; +pragma no-bounded-variable-condition; + +contract Bug { + constructor() {} + + function f(a: uint256) returns (uint256) { + let rep : uint256 = a; + let w : word = Typedef.rep(a); + return Typedef.abs(w); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-rep-name-capture.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-rep-name-capture.solc deleted file mode 100644 index 953fdb33..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/bug-rep-name-capture.solc +++ /dev/null @@ -1,24 +0,0 @@ -// Bug: local variable named `rep` causes name capture with the type variable `rep` -// from `class abs : Typedef(rep)`. In NameResolution.hs, the S.ExpVar and -// S.ExpName cases used a wildcard `_` for the qualifier in patterns like -// `(_, Just TLocalVar)`, so a qualified call `Typedef.rep(a)` resolved to the -// local variable `rep` instead of the class method. -// -// Expected: compiles successfully; `Typedef.rep` resolves to the class method. -// Actual (before fix): PANIC: no resolution found for invokable.invoke - -import std.{*}; -import std.dispatch.{*}; -pragma no-patterson-condition; -pragma no-coverage-condition; -pragma no-bounded-variable-condition; - -contract Bug { - constructor() {} - - function f(a : uint256) -> uint256 { - let rep : uint256 = a; - let w : word = Typedef.rep(a); - return Typedef.abs(w); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/catch-all.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/catch-all.sol new file mode 100644 index 00000000..c78083ce --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/catch-all.sol @@ -0,0 +1,18 @@ +enum Bool { False, True } + +contract CatchAll { + function catchAll(x: Bool, y: Bool) public returns (Bool) { + match (x, y) { +case (Bool.True, Bool.True) { +return Bool.True; +} +case (z, w) { +return z; +} +} + } + + function main() public returns (Bool) { + catchAll(Bool.True, Bool.False) + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/catch-all.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/catch-all.solc deleted file mode 100644 index a3fd9f8b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/catch-all.solc +++ /dev/null @@ -1,14 +0,0 @@ -data Bool = False | True; - -contract CatchAll { - public function catchAll(x : Bool, y : Bool) -> Bool{ - match x, y { - | Bool.True, Bool.True => return Bool.True; - | z, w => return z; - } - } - - public function main() -> Bool { - catchAll(Bool.True, Bool.False) - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/class-context.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/class-context.sol new file mode 100644 index 00000000..85444e7e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/class-context.sol @@ -0,0 +1,3 @@ +trait CStructField { + function offsetSize(s: self) returns (word) ; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/class-context.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/class-context.solc deleted file mode 100644 index 8a2477c7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/class-context.solc +++ /dev/null @@ -1,4 +0,0 @@ -forall self fieldType offsetType -. class self:CStructField(fieldType, offsetType) { - function offsetSize(s: self) -> word; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/clone-deriving.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/clone-deriving.sol new file mode 100644 index 00000000..ffbf9a18 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/clone-deriving.sol @@ -0,0 +1,20 @@ +import * from std; +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +trait Clone { + function clone(x: a) returns (a) ; +} + +impl Clone { + function clone(x: word) returns (word) { return x; } +} + +#[derive(Clone)] +enum Box { Box(word) } + +function cloneBox(x: Box) returns (Box) { + return Clone.clone(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/clone-deriving.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/clone-deriving.solc deleted file mode 100644 index 0d5faedd..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/clone-deriving.solc +++ /dev/null @@ -1,21 +0,0 @@ -import std.{*}; -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -forall a. -class a : Clone { - function clone(x : a) -> a; -} - -instance word : Clone { - function clone(x : word) -> word { return x; } -} - -#[derive(Clone)] -data Box = Box(word); - -function cloneBox(x : Box) -> Box { - return Clone.clone(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-capture-only.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-capture-only.sol new file mode 100644 index 00000000..199845cb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-capture-only.sol @@ -0,0 +1,7 @@ +function testApplied(x: word) returns (word) { + return x; +} + +function main() returns (word) { + return testApplied(1); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-capture-only.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-capture-only.solc deleted file mode 100644 index 96228cd1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-capture-only.solc +++ /dev/null @@ -1,7 +0,0 @@ -function testApplied(x: word) -> word { - return x; -} - -function main() -> word { - return testApplied(1); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-bound-test.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-bound-test.sol new file mode 100644 index 00000000..434fdc31 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-bound-test.sol @@ -0,0 +1,7 @@ +function foo(b: bool) { + let y:word; + let f = lam(x : word) { + if (b) { let z : word = 7; y = z; } else {x = 1;} + }; + f(44); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-bound-test.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-bound-test.solc deleted file mode 100644 index 6ffc20ca..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-bound-test.solc +++ /dev/null @@ -1,7 +0,0 @@ -function foo (b : bool) -> () { - let y:word; - let f = lam(x : word) { - if (b) { let z : word = 7; y = z; } else {x = 1;} - }; - f(44); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var-local.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var-local.sol new file mode 100644 index 00000000..398a2cd6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var-local.sol @@ -0,0 +1,13 @@ +function test() returns (word) { + let f = lam (x: word) -> word { + let y : word = 42; + return y; + }; + return f(1); +} + +contract C { + function main() public returns (word) { + return test(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var-local.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var-local.solc deleted file mode 100644 index a396740c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var-local.solc +++ /dev/null @@ -1,13 +0,0 @@ -function test() -> word { - let f = lam (x: word) -> word { - let y : word = 42; - return y; - }; - return f(1); -} - -contract C { - public function main() -> word { - return test(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var-std.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var-std.sol new file mode 100644 index 00000000..352e4a08 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var-std.sol @@ -0,0 +1,17 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract Bug { + function main() public returns (word) { + return makeClosure(42); + } + + function makeClosure(e: word) public returns (word) { + let f = lam (x : word) { + return e + x; // Uses Add.add typeclass method + }; + return f(1); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var-std.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var-std.solc deleted file mode 100644 index 8ce806b8..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var-std.solc +++ /dev/null @@ -1,17 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract Bug { - public function main() -> word { - return makeClosure(42); - } - - public function makeClosure(e : word) -> word { - let f = lam (x : word) { - return e + x; // Uses Add.add typeclass method - }; - return f(1); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var.sol new file mode 100644 index 00000000..b866c6c6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var.sol @@ -0,0 +1,29 @@ +function addW(l: word, r: word) returns (word) { + let rw : word; + assembly { + rw := add(l,r) + } + return rw; +} + +trait Add { + function add(l: t, r: t) returns (t) ; +} + +impl Add { + function add(l: word, r: word) returns (word) { return addW(l,r); } +} + +contract Bug { + function main() public returns (word) { + return makeClosure(42); + } + + function makeClosure(e: word) public returns (word) { + let f = lam (x : word) { + return Add.add(x,e); // this crashes + // return addW(e,x); // this works + }; + return f(1); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var.solc deleted file mode 100644 index dd29195b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure-free-var.solc +++ /dev/null @@ -1,29 +0,0 @@ -function addW (l: word, r: word) -> word { - let rw : word; - assembly { - rw := add(l,r) - } - return rw; -} - -forall t . class t:Add { - function add(l: t, r: t) -> t; -} - -instance word:Add { - function add(l: word, r: word) -> word { return addW(l,r); } -} - -contract Bug { - public function main() -> word { - return makeClosure(42); - } - - public function makeClosure(e : word) -> word { - let f = lam (x : word) { - return Add.add(x,e); // this crashes - // return addW(e,x); // this works - }; - return f(1); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure.sol new file mode 100644 index 00000000..9c5441d8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure.sol @@ -0,0 +1,7 @@ + function foo(z: word, k: (), a: word) returns (word) { + let f = lam (x : word, y : word) { + k; + return primAddWord(a,primAddWord(y,z)); + }; + return f(0,1); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure.solc deleted file mode 100644 index 497d5acc..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/closure.solc +++ /dev/null @@ -1,7 +0,0 @@ - function foo (z : word, k : (), a : word) -> word { - let f = lam (x : word, y : word) { - k; - return primAddWord(a,primAddWord(y,z)); - }; - return f(0,1); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/comparisons.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/comparisons.sol new file mode 100644 index 00000000..c524956f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/comparisons.sol @@ -0,0 +1,17 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; +function f(x: word, y: word) returns (bool) { + return (!((x == y) + && (x != y) + && (x >= y) + && (x <= y) + || (x > y) + && (x < y) + )); +} + +contract Comparisons { + function main() public returns (bool) { return f(0,1); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/comparisons.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/comparisons.solc deleted file mode 100644 index 98608204..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/comparisons.solc +++ /dev/null @@ -1,17 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; -function f(x: word, y:word) -> bool { - return (!((x == y) - && (x != y) - && (x >= y) - && (x <= y) - || (x > y) - && (x < y) - )); -} - -contract Comparisons { - public function main() -> bool { return f(0,1); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/compose0.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/compose0.sol new file mode 100644 index 00000000..485e35fa --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/compose0.sol @@ -0,0 +1,5 @@ +function compose(f: function(b) returns (c), g: function(a) returns (b)) returns (function(a) returns (c)) { + return lam (x) { + return f(g(x)); + }; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/compose0.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/compose0.solc deleted file mode 100644 index 10a70385..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/compose0.solc +++ /dev/null @@ -1,5 +0,0 @@ -forall a b c . function compose (f : (b) -> c,g : (a) -> b) -> ((a) -> c) { - return lam (x) { - return f(g(x)); - }; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/compound-operators.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/compound-operators.sol new file mode 100644 index 00000000..54196425 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/compound-operators.sol @@ -0,0 +1,31 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +// Exercises every compound assignment operator statement, keeping the AST +// statements in sync with the binary operators: +// += -= *= /= (arithmetic: Add / Sub / Mul / Div) +// %= (Mod) +// ^= &= |= (bitwise: BitXor / BitAnd / BitOr) +// ~= (unary bitwise NOT: BitNot, `acc ~=` -> `acc := ~acc`) +// each binary `lhs op= rhs` desugars to `lhs := lhs op rhs`. +function f(x: word) returns (word) { + let acc : word = x; // 6 + acc += 4; // 10 + acc -= 3; // 7 + acc *= 6; // 42 + acc /= 2; // 21 + acc %= 8; // 5 (21 % 8) + acc ^= 3; // 6 (5 ^ 3) + acc |= 9; // 15 (6 | 9) + acc &= 12; // 12 (15 & 12) + acc ~=; // ~12 (in-place bitwise NOT) + acc &= 15; // 3 (~12 & 0xf == 0b0011) + return acc; +} + +contract CompoundOperators { + // f(6) == 3 — folded at compile time. + function main() public returns (word) { return f(6); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/compound-operators.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/compound-operators.solc deleted file mode 100644 index 44f4a082..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/compound-operators.solc +++ /dev/null @@ -1,31 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -// Exercises every compound assignment operator statement, keeping the AST -// statements in sync with the binary operators: -// += -= *= /= (arithmetic: Add / Sub / Mul / Div) -// %= (Mod) -// ^= &= |= (bitwise: BitXor / BitAnd / BitOr) -// ~= (unary bitwise NOT: BitNot, `acc ~=` -> `acc := ~acc`) -// each binary `lhs op= rhs` desugars to `lhs := lhs op rhs`. -function f(x: word) -> word { - let acc : word = x; // 6 - acc += 4; // 10 - acc -= 3; // 7 - acc *= 6; // 42 - acc /= 2; // 21 - acc %= 8; // 5 (21 % 8) - acc ^= 3; // 6 (5 ^ 3) - acc |= 9; // 15 (6 | 9) - acc &= 12; // 12 (15 & 12) - acc ~=; // ~12 (in-place bitwise NOT) - acc &= 15; // 3 (~12 & 0xf == 0b0011) - return acc; -} - -contract CompoundOperators { - // f(6) == 3 — folded at compile time. - public function main() -> word { return f(6); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/const.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/const.sol new file mode 100644 index 00000000..a36e76d0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/const.sol @@ -0,0 +1,9 @@ +function constApplied(x: word, y: word) returns (word) { + return y; +} + +contract Foo { + function main() public returns (word) { + return constApplied(0,1); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/const.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/const.solc deleted file mode 100644 index 0871138b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/const.solc +++ /dev/null @@ -1,9 +0,0 @@ -function constApplied(x : word, y : word) -> word { - return y; -} - -contract Foo { - public function main () -> word { - return constApplied(0,1); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constrained-instance-context.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constrained-instance-context.sol new file mode 100644 index 00000000..fc96451e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constrained-instance-context.sol @@ -0,0 +1,28 @@ + + +enum memory { memory(word) } + +trait ValueTy { + function rep(x: t) returns (word) ; +} + +impl ValueTy> { + function rep(x: memory) returns (word) { + match (x) { +case memory(w) { +return w; +} +} + } +} + +trait Ref { + function store(loc: ref, value: deref) ; +} + +impl Ref, t> where t: ValueTy { + function store(loc: memory, value: t) { + // We don't have a `ValueTy` bound on `t` anywhere, so this should raise a type error... + let vw = ValueTy.rep(value); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constrained-instance-context.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constrained-instance-context.solc deleted file mode 100644 index 8a6781cb..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constrained-instance-context.solc +++ /dev/null @@ -1,26 +0,0 @@ - - -data memory(t) = memory(word); - -forall t . class t:ValueTy { - function rep(x:t) -> word; -} - -forall t . instance memory(t) : ValueTy { - function rep(x: memory(t)) -> word { - match x { - | memory(w) => return w; - } - } -} - -forall ref deref . class ref:Ref(deref) { - function store(loc: ref, value: deref) -> (); -} - -forall t . t : ValueTy => instance memory(t) : Ref(t) { - function store(loc: memory(t), value: t) -> () { - // We don't have a `ValueTy` bound on `t` anywhere, so this should raise a type error... - let vw = ValueTy.rep(value); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constrained-instance.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constrained-instance.sol new file mode 100644 index 00000000..49f85904 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constrained-instance.sol @@ -0,0 +1,27 @@ + +enum memory { memory(word) } + +trait ValueTy { + function rep(x: t) returns (word) ; +} + +impl ValueTy> { + function rep(x: memory) returns (word) { + match (x) { +case memory(w) { +return w; +} +} + } +} + +trait Ref { + function store(loc: ref, value: deref) ; +} + +impl Ref, t> where t: ValueTy { + function store(loc: memory, value: t) { + // We don't have a `ValueTy` bound on `t` anywhere, so this should raise a type error... + let vw = ValueTy.rep(value); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constrained-instance.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constrained-instance.solc deleted file mode 100644 index 9ae0ccb4..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constrained-instance.solc +++ /dev/null @@ -1,25 +0,0 @@ - -data memory(t) = memory(word); - -forall t . class t:ValueTy { - function rep(x:t) -> word; -} - -forall t . instance memory(t) : ValueTy { - function rep(x: memory(t)) -> word { - match x { - | memory(w) => return w; - } - } -} - -forall ref deref . class ref:Ref(deref) { - function store(loc: ref, value: deref) -> (); -} - -forall t . t : ValueTy => instance memory(t) : Ref(t) { - function store(loc: memory(t), value: t) -> () { - // We don't have a `ValueTy` bound on `t` anywhere, so this should raise a type error... - let vw = ValueTy.rep(value); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constructor-weak-args.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constructor-weak-args.sol new file mode 100644 index 00000000..0ba6be42 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constructor-weak-args.sol @@ -0,0 +1,7 @@ +trait Loadable { + function load(r: ref) returns (deref) ; +} + +function foo(v: t) returns (word) where t: Loadable { + return Loadable.load(v); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constructor-weak-args.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constructor-weak-args.solc deleted file mode 100644 index 4f2ee34b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/constructor-weak-args.solc +++ /dev/null @@ -1,7 +0,0 @@ -forall ref deref . class ref:Loadable (deref) { - function load (r : ref) -> deref; -} - -forall t . t : Loadable(word) => function foo(v : t) -> word { - return Loadable.load(v); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/contract-local-derive.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/contract-local-derive.sol new file mode 100644 index 00000000..ac98da5b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/contract-local-derive.sol @@ -0,0 +1,14 @@ +import * from std; +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +contract ContractLocalDerive { + #[derive(Eq)] + enum Color { Red, Green } + + function same() public returns (bool) { + return Eq.eq(Color.Red, Color.Red); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/contract-local-derive.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/contract-local-derive.solc deleted file mode 100644 index bb6c24f3..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/contract-local-derive.solc +++ /dev/null @@ -1,14 +0,0 @@ -import std.{*}; -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -contract ContractLocalDerive { - #[derive(Eq)] - data Color = Red | Green; - - public function same() -> bool { - return Eq.eq(Color.Red, Color.Red); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/contract-local-type-same-name.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/contract-local-type-same-name.sol new file mode 100644 index 00000000..b66419d0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/contract-local-type-same-name.sol @@ -0,0 +1,36 @@ +// Two contracts each declare a type named `T`, with DIFFERENT constructors. +// Contract-local types are qualified by their contract (A.T vs B.T), so the two +// declarations are distinct and neither the type names nor the constructor +// names collide. If they aliased to a single `T`, one contract's `match` would +// fail to find its constructors. +import * from std; + +contract A { + enum T { Foo, Bar } + + function pickA() public returns (word) { + match (T.Foo) { +case T.Foo { +return 1; +} +case T.Bar { +return 2; +} +} + } +} + +contract B { + enum T { Baz, Qux } + + function pickB() public returns (word) { + match (T.Qux) { +case T.Baz { +return 3; +} +case T.Qux { +return 4; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/contract-local-type-same-name.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/contract-local-type-same-name.solc deleted file mode 100644 index 4473b0ba..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/contract-local-type-same-name.solc +++ /dev/null @@ -1,28 +0,0 @@ -// Two contracts each declare a type named `T`, with DIFFERENT constructors. -// Contract-local types are qualified by their contract (A.T vs B.T), so the two -// declarations are distinct and neither the type names nor the constructor -// names collide. If they aliased to a single `T`, one contract's `match` would -// fail to find its constructors. -import std.{*}; - -contract A { - data T = Foo | Bar; - - public function pickA() -> word { - match T.Foo { - | T.Foo => return 1; - | T.Bar => return 2; - } - } -} - -contract B { - data T = Baz | Qux; - - public function pickB() -> word { - match T.Qux { - | T.Baz => return 3; - | T.Qux => return 4; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/copytomem.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/copytomem.sol new file mode 100644 index 00000000..68ae5d88 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/copytomem.sol @@ -0,0 +1,16 @@ +enum MemoryWordReader { MemoryWordReader(word) } + +function copyToMem(reader: MemoryWordReader, dst: word, cnt: word) { + match (reader) { +case MemoryWordReader(ptr) { +assembly { mcopy(dst, ptr, cnt) } +} +} +} + +contract Main { + function main() public { + let r : MemoryWordReader = MemoryWordReader(42); + copyToMem(r, 0, 32); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/copytomem.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/copytomem.solc deleted file mode 100644 index b37fb5b8..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/copytomem.solc +++ /dev/null @@ -1,14 +0,0 @@ -data MemoryWordReader = MemoryWordReader(word); - -function copyToMem(reader:MemoryWordReader, dst:word, cnt: word) -> () { - match reader { - | MemoryWordReader(ptr) => assembly { mcopy(dst, ptr, cnt) } - } -} - -contract Main { - public function main() -> () { - let r : MemoryWordReader = MemoryWordReader(42); - copyToMem(r, 0, 32); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/cyclical-defs-inferred.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/cyclical-defs-inferred.sol new file mode 100644 index 00000000..3c54829e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/cyclical-defs-inferred.sol @@ -0,0 +1,12 @@ +function foo(x: word) returns (word) { + return bar(x); +} +function bar(x: word) returns (word) { + return foo(x); +} + +contract C { + function main() public returns (word) { + return foo(1); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/cyclical-defs-inferred.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/cyclical-defs-inferred.solc deleted file mode 100644 index 4304a96b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/cyclical-defs-inferred.solc +++ /dev/null @@ -1,12 +0,0 @@ -function foo(x : word) -> word { - return bar(x); -} -function bar(x : word) -> word { - return foo(x); -} - -contract C { - public function main() -> word { - return foo(1); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/cyclical-defs.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/cyclical-defs.sol new file mode 100644 index 00000000..ce57434e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/cyclical-defs.sol @@ -0,0 +1,18 @@ +function foo(x: word) returns (word) { + return bar(x); +} +function bar(x: word) returns (word) { + return foo(x); +} + +contract C { + function m(x: word) public returns (word) { + return n(x); + } + function n(x: word) public returns (word) { + return m(x); + } + function main() public returns (word) { + return m(1); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/cyclical-defs.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/cyclical-defs.solc deleted file mode 100644 index 9c31ed61..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/cyclical-defs.solc +++ /dev/null @@ -1,18 +0,0 @@ -function foo(x : word) -> word { - return bar(x); -} -function bar(x : word) -> word { - return foo(x); -} - -contract C { - public function m(x : word) -> word { - return n(x); - } - public function n(x : word) -> word { - return m(x); - } - public function main() -> word { - return m(1); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-custom-hash.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-custom-hash.sol new file mode 100644 index 00000000..0ac6127a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-custom-hash.sol @@ -0,0 +1,55 @@ +import * from std; +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +trait Hash { + function hash(x: a) returns (word) ; +} + +impl Hash { + function hash(x: word) returns (word) { return x; } +} + +impl Hash<()> { + function hash(x: ()) returns (word) { return 0; } +} + +impl Hash> where f: Hash, g: Hash { + function hash(x: sum) returns (word) { + match (x) { +case inl(u) { +return Hash.hash(u); +} +case inr(v) { +return Hash.hash(v) + 1; +} +} + } +} + +impl Hash<(f, g)> where f: Hash, g: Hash { + function hash(x: (f, g)) returns (word) { + match (x) { +case (u, v) { +return Hash.hash(u) * 31 + Hash.hash(v); +} +} + } +} + +#[derive(Hash)] +enum Color { Red, Green, Blue } + +#[derive(Hash)] +enum Pair { Pair(a, b) } + +function hashRed() returns (word) { + return Hash.hash(Color.Red); +} + +function hashPair() returns (word) { + let p : Pair = Pair(3, 7); + return Hash.hash(p); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-custom-hash.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-custom-hash.solc deleted file mode 100644 index d3a7c440..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-custom-hash.solc +++ /dev/null @@ -1,52 +0,0 @@ -import std.{*}; -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -forall a. -class a : Hash { - function hash(x : a) -> word; -} - -instance word : Hash { - function hash(x : word) -> word { return x; } -} - -instance () : Hash { - function hash(x : ()) -> word { return 0; } -} - -forall f g . f:Hash, g:Hash => -instance sum(f, g) : Hash { - function hash(x : sum(f, g)) -> word { - match x { - | inl(u) => return Hash.hash(u); - | inr(v) => return Hash.hash(v) + 1; - } - } -} - -forall f g . f:Hash, g:Hash => -instance (f, g) : Hash { - function hash(x : (f, g)) -> word { - match x { - | (u, v) => return Hash.hash(u) * 31 + Hash.hash(v); - } - } -} - -#[derive(Hash)] -data Color = Red | Green | Blue; - -#[derive(Hash)] -data Pair(a, b) = Pair(a, b); - -function hashRed() -> word { - return Hash.hash(Color.Red); -} - -function hashPair() -> word { - let p : Pair(word, word) = Pair(3, 7); - return Hash.hash(p); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-action.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-action.sol new file mode 100644 index 00000000..b972a5b5 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-action.sol @@ -0,0 +1,21 @@ +import * from std; +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +#[derive(Eq, Ord)] +enum Action { Transfer(word, word), Approve(word) } + +function sameTransfer() returns (bool) { + return Eq.eq(Action.Transfer(1, 100), Action.Transfer(1, 100)); +} + +function transferLtApprove() returns (bool) { + return Ord.gt(Action.Approve(1), Action.Transfer(1, 100)); +} + +// Within the same constructor fields compare left to right. +function amountsCompare() returns (bool) { + return Ord.gt(Action.Transfer(1, 100), Action.Transfer(1, 50)); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-action.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-action.solc deleted file mode 100644 index 0b47064b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-action.solc +++ /dev/null @@ -1,21 +0,0 @@ -import std.{*}; -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -#[derive(Eq, Ord)] -data Action = Transfer(word, word) | Approve(word); - -function sameTransfer() -> bool { - return Eq.eq(Action.Transfer(1, 100), Action.Transfer(1, 100)); -} - -function transferLtApprove() -> bool { - return Ord.gt(Action.Approve(1), Action.Transfer(1, 100)); -} - -// Within the same constructor fields compare left to right. -function amountsCompare() -> bool { - return Ord.gt(Action.Transfer(1, 100), Action.Transfer(1, 50)); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-enum.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-enum.sol new file mode 100644 index 00000000..85f2fcd3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-enum.sol @@ -0,0 +1,27 @@ +import * from std; +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +#[derive(Eq, Ord)] +enum Color { Red, Green, Blue } + +function sameColor() returns (bool) { + return Eq.eq(Color.Red, Color.Red); +} + +function diffColor() returns (bool) { + return ne(Color.Red, Color.Blue); +} + +function ordering() returns (bool) { + match (Ord.gt(Color.Green, Color.Red)) { +case true { +return not(Ord.gt(Color.Red, Color.Green)); +} +case false { +return false; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-enum.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-enum.solc deleted file mode 100644 index fec8ff02..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-enum.solc +++ /dev/null @@ -1,23 +0,0 @@ -import std.{*}; -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -#[derive(Eq, Ord)] -data Color = Red | Green | Blue; - -function sameColor() -> bool { - return Eq.eq(Color.Red, Color.Red); -} - -function diffColor() -> bool { - return ne(Color.Red, Color.Blue); -} - -function ordering() -> bool { - match Ord.gt(Color.Green, Color.Red) { - | true => return not(Ord.gt(Color.Red, Color.Green)); - | false => return false; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-pair.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-pair.sol new file mode 100644 index 00000000..28ae5b3e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-pair.sol @@ -0,0 +1,20 @@ +import * from std; +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +#[derive(Eq)] +enum Pair { Pair(a, b) } + +function samePair() returns (bool) { + let p : Pair = Pair(1, 2); + let q : Pair = Pair(1, 2); + return Eq.eq(p, q); +} + +function diffPair() returns (bool) { + let p : Pair = Pair(1, 2); + let q : Pair = Pair(1, 3); + return ne(p, q); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-pair.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-pair.solc deleted file mode 100644 index 9a812a30..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-eq-pair.solc +++ /dev/null @@ -1,20 +0,0 @@ -import std.{*}; -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -#[derive(Eq)] -data Pair(a, b) = Pair(a, b); - -function samePair() -> bool { - let p : Pair(word, word) = Pair(1, 2); - let q : Pair(word, word) = Pair(1, 2); - return Eq.eq(p, q); -} - -function diffPair() -> bool { - let p : Pair(word, word) = Pair(1, 2); - let q : Pair(word, word) = Pair(1, 3); - return ne(p, q); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-generic-excluded.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-generic-excluded.sol new file mode 100644 index 00000000..5d5d922e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-generic-excluded.sol @@ -0,0 +1,44 @@ +// Test: pragma no-generic-instance-for suppresses auto-derivation for the +// listed types. Pair has its instance suppressed and provided manually; +// Box gets its instance generated automatically. + +import * from std; +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; +pragma no-generic-instance-for Pair; + +enum Pair { MkPair(a, b) } + +enum Box { MkBox(a) } + +// Manual instance for Pair (suppressed from auto-derivation). +impl Generic, (a, b)> { + function from(p: Pair) returns (a, b) { + match (p) { +case Pair.MkPair(x, y) { +return (x, y); +} +} + } + function to(t: (a, b)) returns (Pair) { + match (t) { +case (x, y) { +return Pair.MkPair(x, y); +} +} + } +} + +// Box gets its Generic instance auto-derived (not excluded). +function boxRoundtrip(v: word) returns (bool) { + let b : Box = Box.MkBox(v); + let r : word = Generic.from(b); + let b2 : Box = Generic.to(r); + match (b2) { +case Box.MkBox(v2) { +return eqWord(v, v2); +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-generic-excluded.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-generic-excluded.solc deleted file mode 100644 index 784f244c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-generic-excluded.solc +++ /dev/null @@ -1,39 +0,0 @@ -// Test: pragma no-generic-instance-for suppresses auto-derivation for the -// listed types. Pair has its instance suppressed and provided manually; -// Box gets its instance generated automatically. - -import std.{*}; -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; -pragma no-generic-instance-for Pair; - -data Pair(a, b) = MkPair(a, b); - -data Box(a) = MkBox(a); - -// Manual instance for Pair (suppressed from auto-derivation). -forall a b. -instance Pair(a, b) : Generic((a, b)) { - function from(p : Pair(a, b)) -> (a, b) { - match p { - | Pair.MkPair(x, y) => return (x, y); - } - } - function to(t : (a, b)) -> Pair(a, b) { - match t { - | (x, y) => return Pair.MkPair(x, y); - } - } -} - -// Box gets its Generic instance auto-derived (not excluded). -function boxRoundtrip(v : word) -> bool { - let b : Box(word) = Box.MkBox(v); - let r : word = Generic.from(b); - let b2 : Box(word) = Generic.to(r); - match b2 { - | Box.MkBox(v2) => return eqWord(v, v2); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-generic-sum.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-generic-sum.sol new file mode 100644 index 00000000..35842af3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-generic-sum.sol @@ -0,0 +1,42 @@ +// Test: Generic instances are auto-derived for sum types. +// Neither Option nor Tree has an explicit Generic instance; both should be +// generated automatically by DeriveGeneric. + +import * from std; +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +enum Option { None, Some(a) } + +enum Tree { Leaf, Node(Tree, a, Tree) } + +// Use the auto-derived instances to check that from/to round-trip. +function roundtripNone() returns (bool) { + let x : Option = Option.None; + let r : sum<(), word> = Generic.from(x); + let x2 : Option = Generic.to(r); + match (x2) { +case Option.None { +return true; +} +case Option.Some(_) { +return false; +} +} +} + +function roundtripSome(v: word) returns (bool) { + let x : Option = Option.Some(v); + let r : sum<(), word> = Generic.from(x); + let x2 : Option = Generic.to(r); + match (x2) { +case Option.None { +return false; +} +case Option.Some(v2) { +return eqWord(v, v2); +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-generic-sum.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-generic-sum.solc deleted file mode 100644 index aa93b560..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-generic-sum.solc +++ /dev/null @@ -1,34 +0,0 @@ -// Test: Generic instances are auto-derived for sum types. -// Neither Option nor Tree has an explicit Generic instance; both should be -// generated automatically by DeriveGeneric. - -import std.{*}; -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -data Option(a) = None | Some(a); - -data Tree(a) = Leaf | Node(Tree(a), a, Tree(a)); - -// Use the auto-derived instances to check that from/to round-trip. -function roundtripNone() -> bool { - let x : Option(word) = Option.None; - let r : sum((), word) = Generic.from(x); - let x2 : Option(word) = Generic.to(r); - match x2 { - | Option.None => return true; - | Option.Some(_) => return false; - } -} - -function roundtripSome(v : word) -> bool { - let x : Option(word) = Option.Some(v); - let r : sum((), word) = Generic.from(x); - let x2 : Option(word) = Generic.to(r); - match x2 { - | Option.None => return false; - | Option.Some(v2) => return eqWord(v, v2); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-universe-instances.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-universe-instances.sol new file mode 100644 index 00000000..94a1b8bb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-universe-instances.sol @@ -0,0 +1,40 @@ +import * from std; +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +function eqUnit() returns (bool) { + let u : () = (); + return Eq.eq(u, u); +} + +function eqInl() returns (bool) { + let x : sum = inl(1); + let y : sum = inl(1); + return Eq.eq(x, y); +} + +function neqTags() returns (bool) { + let x : sum = inl(1); + let y : sum = inr(1); + return ne(x, y); +} + +function ordInlLtInr() returns (bool) { + let x : sum = inl(1); + let y : sum = inr(1); + return not(Ord.gt(x, y)); +} + +function eqPair() returns (bool) { + let p : (word, word) = (1, 2); + let q : (word, word) = (1, 2); + return Eq.eq(p, q); +} + +function ordPairLex() returns (bool) { + let p : (word, word) = (1, 100); + let q : (word, word) = (1, 50); + return Ord.gt(p, q); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-universe-instances.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-universe-instances.solc deleted file mode 100644 index 12a9795a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/derive-universe-instances.solc +++ /dev/null @@ -1,40 +0,0 @@ -import std.{*}; -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -function eqUnit() -> bool { - let u : () = (); - return Eq.eq(u, u); -} - -function eqInl() -> bool { - let x : sum(word, word) = inl(1); - let y : sum(word, word) = inl(1); - return Eq.eq(x, y); -} - -function neqTags() -> bool { - let x : sum(word, word) = inl(1); - let y : sum(word, word) = inr(1); - return ne(x, y); -} - -function ordInlLtInr() -> bool { - let x : sum(word, word) = inl(1); - let y : sum(word, word) = inr(1); - return not(Ord.gt(x, y)); -} - -function eqPair() -> bool { - let p : (word, word) = (1, 2); - let q : (word, word) = (1, 2); - return Eq.eq(p, q); -} - -function ordPairLex() -> bool { - let p : (word, word) = (1, 100); - let q : (word, word) = (1, 50); - return Ord.gt(p, q); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/deriving-empty-type.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/deriving-empty-type.sol new file mode 100644 index 00000000..79517d6d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/deriving-empty-type.sol @@ -0,0 +1,8 @@ +import * from std; +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +#[derive(Eq)] +enum Void {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/deriving-empty-type.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/deriving-empty-type.solc deleted file mode 100644 index d9f993ac..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/deriving-empty-type.solc +++ /dev/null @@ -1,8 +0,0 @@ -import std.{*}; -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -#[derive(Eq)] -data Void; diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-assignment-context.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-assignment-context.sol new file mode 100644 index 00000000..724fdc67 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-assignment-context.sol @@ -0,0 +1,7 @@ +enum Option { Some(a), None } + +function main() returns (Option) { + let x : Option; + x = .None; + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-assignment-context.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-assignment-context.solc deleted file mode 100644 index 6037f00a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-assignment-context.solc +++ /dev/null @@ -1,7 +0,0 @@ -data Option(a) = Some(a) | None; - -function main() -> Option(word) { - let x : Option(word); - x = .None; - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-call-arg-context.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-call-arg-context.sol new file mode 100644 index 00000000..43925fe1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-call-arg-context.sol @@ -0,0 +1,16 @@ +enum Option { None, Some(word) } + +function use(x: Option) returns (word) { + match (x) { +case Option.Some(v) { +return v; +} +case Option.None { +return 0; +} +} +} + +function main() returns (word) { + return use(.Some(7)); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-call-arg-context.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-call-arg-context.solc deleted file mode 100644 index ba4781ed..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-call-arg-context.solc +++ /dev/null @@ -1,12 +0,0 @@ -data Option = None | Some(word); - -function use(x: Option) -> word { - match x { - | Option.Some(v) => return v; - | Option.None => return 0; - } -} - -function main() -> word { - return use(.Some(7)); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-constructor.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-constructor.sol new file mode 100644 index 00000000..192427e5 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-constructor.sol @@ -0,0 +1,16 @@ +enum Option { None, Some(word) } + +function mkSome(x: word) returns (Option) { + return .Some(x); +} + +function main() returns (word) { + match (mkSome(7)) { +case Option.Some(v) { +return v; +} +case Option.None { +return 0; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-constructor.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-constructor.solc deleted file mode 100644 index 5163ac1c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-constructor.solc +++ /dev/null @@ -1,12 +0,0 @@ -data Option = None | Some(word); - -function mkSome(x: word) -> Option { - return .Some(x); -} - -function main() -> word { - match mkSome(7) { - | Option.Some(v) => return v; - | Option.None => return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-match-return.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-match-return.sol new file mode 100644 index 00000000..0eabf9ee --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-match-return.sol @@ -0,0 +1,17 @@ +enum Bar { Foo(word) } + +function x(x: Bar) returns (Bar) { + match (x) { +case .Foo(w) { +return .Foo(w); +} +} +} + +function main() returns (word) { + match (x(Bar.Foo(7))) { +case Bar.Foo(w) { +return w; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-match-return.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-match-return.solc deleted file mode 100644 index 26f6c946..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-match-return.solc +++ /dev/null @@ -1,13 +0,0 @@ -data Bar = Foo(word); - -function x(x: Bar) -> Bar { - match x { - | .Foo(w) => return .Foo(w); - } -} - -function main() -> word { - match x(Bar.Foo(7)) { - | Bar.Foo(w) => return w; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-nested-context.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-nested-context.sol new file mode 100644 index 00000000..effe5742 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-nested-context.sol @@ -0,0 +1,5 @@ +enum Option { Some(a), None } + +function main() returns (Option>) { + return .Some(.None); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-nested-context.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-nested-context.solc deleted file mode 100644 index 97d6f177..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-expression-nested-context.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Option(a) = Some(a) | None; - -function main() -> Option(Option(word)) { - return .Some(.None); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-pattern-constructor.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-pattern-constructor.sol new file mode 100644 index 00000000..94b372b1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-pattern-constructor.sol @@ -0,0 +1,16 @@ +enum Option { None, Some(word) } + +function fromOption(x: Option) returns (word) { + match (x) { +case .Some(v) { +return v; +} +case .None { +return 0; +} +} +} + +function main() returns (word) { + return fromOption(Option.Some(3)); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-pattern-constructor.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-pattern-constructor.solc deleted file mode 100644 index 0f204633..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-pattern-constructor.solc +++ /dev/null @@ -1,12 +0,0 @@ -data Option = None | Some(word); - -function fromOption(x: Option) -> word { - match x { - | .Some(v) => return v; - | .None => return 0; - } -} - -function main() -> word { - return fromOption(Option.Some(3)); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-pattern-nested-constructor.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-pattern-nested-constructor.sol new file mode 100644 index 00000000..6258de0c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-pattern-nested-constructor.sol @@ -0,0 +1,23 @@ +enum Option { None, Some(a) } + +function join(mmx: Option>) returns (Option) { + match (mmx) { +case .Some(.Some(x)) { +return .Some(x); +} +default { +return .None; +} +} +} + +function main() returns (word) { + match (join(.Some(.Some(9)))) { +case .Some(v) { +return v; +} +case .None { +return 0; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-pattern-nested-constructor.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-pattern-nested-constructor.solc deleted file mode 100644 index 10cb4a89..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-pattern-nested-constructor.solc +++ /dev/null @@ -1,15 +0,0 @@ -data Option(a) = None | Some(a); - -function join(mmx: Option(Option(word))) -> Option(word) { - match mmx { - | .Some(.Some(x)) => return .Some(x); - | _ => return .None; - } -} - -function main() -> word { - match join(.Some(.Some(9))) { - | .Some(v) => return v; - | .None => return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-primitive-constructor.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-primitive-constructor.sol new file mode 100644 index 00000000..a3ba9b4b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-primitive-constructor.sol @@ -0,0 +1,11 @@ +function main() returns (word) { + let b: bool = .true; + match (b) { +case .true { +return 1; +} +case .false { +return 0; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-primitive-constructor.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-primitive-constructor.solc deleted file mode 100644 index fb935c67..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/dot-primitive-constructor.solc +++ /dev/null @@ -1,7 +0,0 @@ -function main() -> word { - let b: bool = .true; - match b { - | .true => return 1; - | .false => return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/empty-asm.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/empty-asm.sol new file mode 100644 index 00000000..13a60f78 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/empty-asm.sol @@ -0,0 +1,12 @@ +function f(x: word) returns (word) { + match (x) { +case 0 { +let ret : word; + assembly {} + return ret; +} +default { +return 0; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/empty-asm.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/empty-asm.solc deleted file mode 100644 index 7c288305..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/empty-asm.solc +++ /dev/null @@ -1,9 +0,0 @@ -function f(x : word) -> word { - match x { - | 0 => - let ret : word; - assembly {} - return ret; - | _ => return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/encoder.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/encoder.sol new file mode 100644 index 00000000..8bc7bff1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/encoder.sol @@ -0,0 +1,45 @@ +enum TagA { TagA(word) } +enum TagB { TagB(word) } + +trait Tag { + function getTag(x: self) returns (rep) ; +} + +enum TypeA { TypeA(word) } +impl Tag { + function getTag(x: TypeA) returns (TagA) { + match (x) { +case TypeA(w) { +return TagA(w); +} +} + } +} + +enum TypeB { TypeB(word) } +impl Tag { + function getTag(x: TypeB) returns (TagB) { + match (x) { +case TypeB(w) { +return TagB(w); +} +} + } +} + +function tagFirst(x: a, y: b) returns (rep1) where a: Tag, b: Tag { + return Tag.getTag(x); +} + +contract C { + constructor() {} + + function main() public returns (word) { + let r : TagA = tagFirst(TypeA(42), TypeB(7)); + match (r) { +case TagA(w) { +return w; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/encoder.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/encoder.solc deleted file mode 100644 index bc470ddd..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/encoder.solc +++ /dev/null @@ -1,35 +0,0 @@ -data TagA = TagA(word); -data TagB = TagB(word); - -forall self rep. -class self:Tag(rep) { - function getTag(x:self) -> rep; -} - -data TypeA = TypeA(word); -instance TypeA:Tag(TagA) { - function getTag(x:TypeA) -> TagA { - match x { | TypeA(w) => return TagA(w); } - } -} - -data TypeB = TypeB(word); -instance TypeB:Tag(TagB) { - function getTag(x:TypeB) -> TagB { - match x { | TypeB(w) => return TagB(w); } - } -} - -forall a b rep1 rep2 . a:Tag(rep1), b:Tag(rep2) => -function tagFirst(x:a, y:b) -> rep1 { - return Tag.getTag(x); -} - -contract C { - constructor() {} - - public function main() -> word { - let r : TagA = tagFirst(TypeA(42), TypeB(7)); - match r { | TagA(w) => return w; } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/encoder1.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/encoder1.sol new file mode 100644 index 00000000..f2418c53 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/encoder1.sol @@ -0,0 +1,28 @@ +import * from std; + +trait Encoder { + function encode(x: self, hint: word) returns (rep) ; +} + +enum Foo { Foo(word) } +impl Encoder { + function encode(x: Foo, hint: word) returns (word) { + match (x) { +case Foo(w) { +return w; +} +} + } +} + +function encodeAndDiscard(x: a) where a: Encoder { + let enc : rep = Encoder.encode(x, 0); + return (); +} + +contract C { + function main() public returns (word) { + encodeAndDiscard(Foo(42)); + return 0; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/encoder1.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/encoder1.solc deleted file mode 100644 index ac418562..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/encoder1.solc +++ /dev/null @@ -1,26 +0,0 @@ -import std.{*}; - -forall self rep. -class self:Encoder(rep) { - function encode(x:self, hint:word) -> rep; -} - -data Foo = Foo(word); -instance Foo:Encoder(word) { - function encode(x:Foo, hint:word) -> word { - match x { | Foo(w) => return w; } - } -} - -forall a rep . a:Encoder(rep) => -function encodeAndDiscard(x:a) -> () { - let enc : rep = Encoder.encode(x, 0); - return (); -} - -contract C { - public function main() -> word { - encodeAndDiscard(Foo(42)); - return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/false-redundant-warning.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/false-redundant-warning.sol new file mode 100644 index 00000000..4cf0da15 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/false-redundant-warning.sol @@ -0,0 +1,21 @@ +enum Bool { False, True } + +function test(x: Bool, y: Bool) returns (Bool) { + match (x, y) { +case (Bool.True, z) { +return z; +} +case (w, Bool.True) { +return w; +} +case (a, b) { +return b; +} +} +} + +contract FalseRedundantWarning { + function main() public returns (Bool) { + test(Bool.False, Bool.True) + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/false-redundant-warning.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/false-redundant-warning.solc deleted file mode 100644 index 88f95679..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/false-redundant-warning.solc +++ /dev/null @@ -1,15 +0,0 @@ -data Bool = False | True; - -function test(x : Bool, y : Bool) -> Bool { - match x, y { - | Bool.True, z => return z; - | w, Bool.True => return w; - | a, b => return b; - } -} - -contract FalseRedundantWarning { - public function main() -> Bool { - test(Bool.False, Bool.True) - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/field-helper-cxt-collision.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/field-helper-cxt-collision.sol new file mode 100644 index 00000000..06e40161 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/field-helper-cxt-collision.sol @@ -0,0 +1,14 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +enum FooCxt { FooCxt } + +contract Foo { + x: word; + + function get() public returns (word) { + return x; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/field-helper-cxt-collision.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/field-helper-cxt-collision.solc deleted file mode 100644 index 994f6568..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/field-helper-cxt-collision.solc +++ /dev/null @@ -1,14 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -data FooCxt = FooCxt; - -contract Foo { - x: word; - - public function get() -> word { - return x; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/field-name-error.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/field-name-error.sol new file mode 100644 index 00000000..5cdbf428 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/field-name-error.sol @@ -0,0 +1,12 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract PoC { + x : word; + + function main() public returns (word) { + return 0; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/field-name-error.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/field-name-error.solc deleted file mode 100644 index fd1bc3c5..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/field-name-error.solc +++ /dev/null @@ -1,12 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract PoC { - x : word; - - public function main () -> word { - return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/foo-class.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/foo-class.sol new file mode 100644 index 00000000..7b67326d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/foo-class.sol @@ -0,0 +1,3 @@ +trait Foo { + function foo(x: self) returns (b) ; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/foo-class.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/foo-class.solc deleted file mode 100644 index bb78a2ef..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/foo-class.solc +++ /dev/null @@ -1,4 +0,0 @@ -forall b self . -class self:Foo(b) { - function foo(x:self) -> b; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-body-shadow.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-body-shadow.sol new file mode 100644 index 00000000..a1f60bb2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-body-shadow.sol @@ -0,0 +1,11 @@ +import {Num,Add,Sub,Eq,Ord,Bounded,Typedef,le} from std; + +contract C { + function main() public returns (word) { + let x : word = 100; + let i : word = 0; + let s : word = 0; + for(i=0;i<=0;i=i+1) { let x : word = 1; s = x; } + return s; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-body-shadow.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-body-shadow.solc deleted file mode 100644 index 94fc9fc8..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-body-shadow.solc +++ /dev/null @@ -1,11 +0,0 @@ -import std.{Num,Add,Sub,Eq,Ord,Bounded,Typedef,le}; - -contract C { - public function main() -> word { - let x : word = 100; - let i : word = 0; - let s : word = 0; - for(i=0;i<=0;i=i+1) { let x : word = 1; s = x; } - return s; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-break.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-break.sol new file mode 100644 index 00000000..a4ef2ad9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-break.sol @@ -0,0 +1,13 @@ +import {lt,Ord,Add,Sub,Bounded,Num,Eq,Typedef} from std; +contract BreakTest { + function main() public returns (word) { + let result : word = 0; + for (let i : word = 0; i < 10; i = i + 1) { + if (i == 5) { + break; + } else {} + result = result + 1; + } + return result; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-break.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-break.solc deleted file mode 100644 index 79e827d9..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-break.solc +++ /dev/null @@ -1,13 +0,0 @@ -import std.{lt,Ord,Add,Sub,Bounded,Num,Eq,Typedef}; -contract BreakTest { - public function main() -> word { - let result : word = 0; - for (let i : word = 0; i < 10; i = i + 1) { - if (i == 5) { - break; - } else {} - result = result + 1; - } - return result; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-continue.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-continue.sol new file mode 100644 index 00000000..acf93627 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-continue.sol @@ -0,0 +1,13 @@ +import {lt,Ord,Add,Sub,Bounded,Num,Eq,Typedef} from std; +contract ContinueTest { + function main() public returns (word) { + let result : word = 0; + for (let i : word = 0; i < 10; i = i + 1) { + if (i < 5) { + continue; + } else {} + result = result + 1; + } + return result; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-continue.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-continue.solc deleted file mode 100644 index 03c68ed3..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-continue.solc +++ /dev/null @@ -1,13 +0,0 @@ -import std.{lt,Ord,Add,Sub,Bounded,Num,Eq,Typedef}; -contract ContinueTest { - public function main() -> word { - let result : word = 0; - for (let i : word = 0; i < 10; i = i + 1) { - if (i < 5) { - continue; - } else {} - result = result + 1; - } - return result; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-empty-init.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-empty-init.sol new file mode 100644 index 00000000..008a0e99 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-empty-init.sol @@ -0,0 +1,10 @@ +import {Num,Add,Sub,Eq,Ord,Bounded,Typedef,le} from std; + +contract ForEmptyInit { + function main() returns (word) { + let i : word = 1; + let s = 0; + for(; i <= 10; i = i + 1) { s = s + i; } + return s; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-empty-init.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-empty-init.solc deleted file mode 100644 index 5bbaa539..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-empty-init.solc +++ /dev/null @@ -1,10 +0,0 @@ -import std.{Num,Add,Sub,Eq,Ord,Bounded,Typedef,le}; - -contract ForEmptyInit { - function main() -> word { - let i : word = 1; - let s = 0; - for(; i <= 10; i = i + 1) { s = s + i; } - return s; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-init-shadow.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-init-shadow.sol new file mode 100644 index 00000000..e79dc931 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-init-shadow.sol @@ -0,0 +1,10 @@ +import {Num,Add,Sub,Eq,Ord,Bounded,Typedef,le} from std; + +contract Prefor { + function main() public returns (word) { + let i : word = 100; + let s : word = 0; + for(let i=1;i<=10;i=i+1) { s = s + i; } + return s; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-init-shadow.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-init-shadow.solc deleted file mode 100644 index d6ceaf8b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-init-shadow.solc +++ /dev/null @@ -1,10 +0,0 @@ -import std.{Num,Add,Sub,Eq,Ord,Bounded,Typedef,le}; - -contract Prefor { - public function main() -> word { - let i : word = 100; - let s : word = 0; - for(let i=1;i<=10;i=i+1) { s = s + i; } - return s; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-inner-block.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-inner-block.sol new file mode 100644 index 00000000..e251eeb2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-inner-block.sol @@ -0,0 +1,10 @@ +import {lt,Ord,Add,Sub,Bounded,Num,Eq,Typedef} from std; +contract ForInner { + function main() public returns (word) { + let result : word = 0; + for (let height : word = 0; height < 7; height = height + 1) { + if (true) { result = height; } else {} + } + return result; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-inner-block.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-inner-block.solc deleted file mode 100644 index 30790370..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-inner-block.solc +++ /dev/null @@ -1,10 +0,0 @@ -import std.{lt,Ord,Add,Sub,Bounded,Num,Eq,Typedef}; -contract ForInner { - public function main() -> word { - let result : word = 0; - for (let height : word = 0; height < 7; height = height + 1) { - if (true) { result = height; } else {} - } - return result; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-let.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-let.sol new file mode 100644 index 00000000..807cf86c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-let.sol @@ -0,0 +1,10 @@ +import {Num,Add,Sub,Eq,Ord,Bounded,Typedef,le} from std; + +contract Prefor { + function main() public returns (word) { + let s : word = 0; + for(let i=1;i<=10;i=i+1) { s = s + i;} + + return s; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-let.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-let.solc deleted file mode 100644 index b5900f17..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-let.solc +++ /dev/null @@ -1,10 +0,0 @@ -import std.{Num,Add,Sub,Eq,Ord,Bounded,Typedef,le}; - -contract Prefor { - public function main() -> word { - let s : word = 0; - for(let i=1;i<=10;i=i+1) { s = s + i;} - - return s; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-loop.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-loop.sol new file mode 100644 index 00000000..12511915 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-loop.sol @@ -0,0 +1,11 @@ +import {Num,Add,Sub,Eq,Ord,Bounded,Typedef,le} from std; + +contract Prefor { + function main() public returns (word) { + let i:word; + let s : word = 0; + for(i=1;i<=10;i=i+1) { s = s + i;} + + return s; + } +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-loop.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-loop.solc deleted file mode 100644 index d910c943..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-loop.solc +++ /dev/null @@ -1,11 +0,0 @@ -import std.{Num,Add,Sub,Eq,Ord,Bounded,Typedef,le}; - -contract Prefor { - public function main() -> word { - let i:word; - let s : word = 0; - for(i=1;i<=10;i=i+1) { s = s + i;} - - return s; - } -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-multi-init.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-multi-init.sol new file mode 100644 index 00000000..46c1763b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-multi-init.sol @@ -0,0 +1,12 @@ +import {Num,Add,Sub,Eq,Ord,Bounded,Typedef,le} from std; + +contract ForMultiInit { + function main() returns (word) { + let i = 0; + let j = 0; + for (i = 1, j = 10; i <= 3; i = i + 1) { + j = j + i; + } + return j; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-multi-init.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-multi-init.solc deleted file mode 100644 index 5f134c4a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-multi-init.solc +++ /dev/null @@ -1,12 +0,0 @@ -import std.{Num,Add,Sub,Eq,Ord,Bounded,Typedef,le}; - -contract ForMultiInit { - function main() -> word { - let i = 0; - let j = 0; - for (i = 1, j = 10; i <= 3; i = i + 1) { - j = j + i; - } - return j; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-multi-post.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-multi-post.sol new file mode 100644 index 00000000..e4559026 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-multi-post.sol @@ -0,0 +1,11 @@ +import {Num,Add,Sub,Eq,Ord,Bounded,Typedef,le} from std; + +contract ForMultiPost { + function main() returns (word) { + let j = 0; + for (let i = 0; i <= 3; i = i + 1, j = j + 2) { + j = j + i; + } + return j; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-multi-post.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-multi-post.solc deleted file mode 100644 index b0183e9a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/for-multi-post.solc +++ /dev/null @@ -1,11 +0,0 @@ -import std.{Num,Add,Sub,Eq,Ord,Bounded,Typedef,le}; - -contract ForMultiPost { - function main() -> word { - let j = 0; - for (let i = 0; i <= 3; i = i + 1, j = j + 2) { - j = j + i; - } - return j; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-pat-arg-synonym.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-pat-arg-synonym.sol new file mode 100644 index 00000000..c24a6442 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-pat-arg-synonym.sol @@ -0,0 +1,10 @@ +type W = word; + +function f(x: W) returns (W) { x } + +contract C { + + function main() public returns (word) { + return f(42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-pat-arg-synonym.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-pat-arg-synonym.solc deleted file mode 100644 index 876e5bda..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-pat-arg-synonym.solc +++ /dev/null @@ -1,10 +0,0 @@ -type W = word; - -function f(x:W) -> W { x } - -contract C { - - public function main () -> word { - return f(42); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-pat-arg.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-pat-arg.sol new file mode 100644 index 00000000..ccc92693 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-pat-arg.sol @@ -0,0 +1,7 @@ +function g(x: word) returns (word) { x } + +function h(x: a) returns (a) { x } + +contract C { + function main() public returns (word) { g(h(42)) } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-pat-arg.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-pat-arg.solc deleted file mode 100644 index b7e0958b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-pat-arg.solc +++ /dev/null @@ -1,7 +0,0 @@ -function g(x:word) -> word { x } - -forall a. function h(x:a) -> a { x } - -contract C { - public function main() -> word { g(h(42)) } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-variable-shadowing.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-variable-shadowing.sol new file mode 100644 index 00000000..f8ab5e51 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-variable-shadowing.sol @@ -0,0 +1,18 @@ +enum Bool { False, True } + +function test(v0: Bool, p: Bool) returns (Bool) { + match (p) { +case Bool.True { +return Bool.False; +} +case z { +return v0; +} +} +} + +contract FreshVariableShadowing { + function main() public returns (Bool) { + test(Bool.True, Bool.False) + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-variable-shadowing.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-variable-shadowing.solc deleted file mode 100644 index 930f81ba..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/fresh-variable-shadowing.solc +++ /dev/null @@ -1,14 +0,0 @@ -data Bool = False | True; - -function test(v0 : Bool, p : Bool) -> Bool { - match p { - | Bool.True => return Bool.False; - | z => return v0; - } -} - -contract FreshVariableShadowing { - public function main() -> Bool { - test(Bool.True, Bool.False) - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/if-examples.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/if-examples.sol new file mode 100644 index 00000000..f2e9b678 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/if-examples.sol @@ -0,0 +1,47 @@ +function toBool(x: word) returns (bool) { + match (x) { +case 0 { +return false; +} +default { +return true; +} +} +} + +function gt(x: word, y: word) returns (bool) { + let res : word; + assembly { + res := gt(x,y) + } + return toBool(res); +} + +function max(x: word, y: word) returns (word) { + let res : word; + if (gt(x,y)) { + res = x; + } else { + res = y; + } + return res; +} + +function not(x: bool) returns (bool) { + if (x) { return false; } else { return true; } +} + +function foo(x: word) returns (bool) { + if (gt(x,0)) { + return true; + } else { + return false; + } +} + + +contract IfExamples { + function main() public returns (word) { + return ( not(foo(42)) ? 0 : 1); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/if-examples.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/if-examples.solc deleted file mode 100644 index a440c275..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/if-examples.solc +++ /dev/null @@ -1,43 +0,0 @@ -function toBool(x : word) -> bool { - match x { - | 0 => return false; - | _ => return true; - } -} - -function gt(x : word, y : word) -> bool { - let res : word; - assembly { - res := gt(x,y) - } - return toBool(res); -} - -function max(x : word, y : word) -> word { - let res : word; - if (gt(x,y)) { - res = x; - } else { - res = y; - } - return res; -} - -function not(x:bool) -> bool { - if (x) { return false; } else { return true; } -} - -function foo(x : word) -> bool { - if (gt(x,0)) { - return true; - } else { - return false; - } -} - - -contract IfExamples { - public function main() -> word { - return (if not(foo(42)) then 0 else 1); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/import-std.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/import-std.sol new file mode 100644 index 00000000..0dede110 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/import-std.sol @@ -0,0 +1,10 @@ +import std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract Test { + function main() public returns (word) { + return std.addWord(21, 21); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/import-std.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/import-std.solc deleted file mode 100644 index cbb62e40..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/import-std.solc +++ /dev/null @@ -1,10 +0,0 @@ -import std; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract Test { - public function main() -> word { - return std.addWord(21, 21); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/inc-closure.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/inc-closure.sol new file mode 100644 index 00000000..bf4c784f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/inc-closure.sol @@ -0,0 +1,17 @@ +function inc(x: word) returns (word) { + let f = lam () { + let res : word ; + assembly { + res := add(x,1) + } + return res; + } ; + return f(); +} + +contract Foo { + + function main() public returns (word) { + return inc(0); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/inc-closure.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/inc-closure.solc deleted file mode 100644 index 210cf69b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/inc-closure.solc +++ /dev/null @@ -1,17 +0,0 @@ -function inc(x : word) -> word { - let f = lam () { - let res : word ; - assembly { - res := add(x,1) - } - return res; - } ; - return f(); -} - -contract Foo { - - public function main () -> word { - return inc(0); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-closure-error.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-closure-error.sol new file mode 100644 index 00000000..3ebe396a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-closure-error.sol @@ -0,0 +1,11 @@ +trait CtFun { + function ct(x: t) returns (function(t) returns (t)) ; +} + +impl CtFun { + function ct(x: word) returns (function(word) returns (word)) { + return lam(y : word) { + return x; + }; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-closure-error.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-closure-error.solc deleted file mode 100644 index 0d6d22fc..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-closure-error.solc +++ /dev/null @@ -1,11 +0,0 @@ -forall t . class t:CtFun { - function ct(x : t) -> ((t) -> t); -} - -instance word:CtFun { - function ct(x : word) -> ((word) -> word) { - return lam(y : word) { - return x; - }; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-synonym-int.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-synonym-int.sol new file mode 100644 index 00000000..45cd4ff7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-synonym-int.sol @@ -0,0 +1,17 @@ +type W = word; + +trait FromWord { + function fromWord(x: word) returns (i) ; +} + +impl FromWord { + function fromWord(x: word) returns (word) { x } +} + +contract C { + + function main() public returns (W) { + let r : W = FromWord.fromWord(42); + return r; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-synonym-int.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-synonym-int.solc deleted file mode 100644 index e705196d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-synonym-int.solc +++ /dev/null @@ -1,18 +0,0 @@ -type W = word; - -forall i. -class i : FromWord { - function fromWord(x:word) -> i; -} - -instance word : FromWord { - function fromWord(x:word) -> word { x } -} - -contract C { - - public function main () -> W { - let r : W = FromWord.fromWord(42); - return r; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-synonym.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-synonym.sol new file mode 100644 index 00000000..cefe95d1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-synonym.sol @@ -0,0 +1,17 @@ +type W = word; + +trait IdTy { + function id(x: self) returns (self) ; +} + +impl IdTy { + function id(x: W) returns (W) { + return x; + } +} + +contract C { + function main() public returns (word) { + return IdTy.id(42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-synonym.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-synonym.solc deleted file mode 100644 index 17d1520d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/instance-synonym.solc +++ /dev/null @@ -1,17 +0,0 @@ -type W = word; - -forall self . class self:IdTy { - function id(x:self) -> self; -} - -instance W:IdTy { - function id(x:W) -> W { - return x; - } -} - -contract C { - public function main() -> word { - return IdTy.id(42); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/invokable-issue.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/invokable-issue.sol new file mode 100644 index 00000000..3e2ac6ef --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/invokable-issue.sol @@ -0,0 +1,11 @@ +trait Typedef { + function abs(x: rep) returns (abs) ; + function rep(x: abs) returns (rep) ; +} + +impl Typedef { + function abs(x: t) returns (t) { return x; } + function rep(x: t) returns (t) { return x; } +} + +function lift1ac(f: function(rep) returns (res), x: rep) returns (res) where abs: Typedef { f(Typedef.rep(x)) } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/invokable-issue.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/invokable-issue.solc deleted file mode 100644 index a282f233..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/invokable-issue.solc +++ /dev/null @@ -1,13 +0,0 @@ -forall abs rep . class abs:Typedef(rep) { - function abs(x:rep) -> abs; - function rep(x:abs) -> rep; -} - -forall t. -/* default */ instance t:Typedef(t) { - function abs(x:t) -> t { return x; } - function rep(x:t) -> t { return x; } -} - -forall abs rep res. abs:Typedef(rep) => -function lift1ac(f:(rep) -> res, x:rep) -> res { f(Typedef.rep(x)) } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ixa.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ixa.sol new file mode 100644 index 00000000..27e5df86 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ixa.sol @@ -0,0 +1,161 @@ +// --- preamble / duplicated std defs --- + +enum Proxy { Proxy } + +// dynamic arrays with a runtime size. cannot exist on stack so no data constructor (i.e. should be used in combination with memory / storage pointers). +enum array {} + +// a typed pointer to a location in memory +enum memory { memory(word) } + +// word arithmetc +trait Add { function add(l: t, r: t) returns (t) ; } +trait Mul { function mul(l: t, r: t) returns (t) ; } +impl Add { + function add(l: word, r: word) returns (word) { + let rw : word; + assembly { + rw := add(l,r) + } + return rw; + } +} +impl Mul { + function mul(l: word, r: word) returns (word) { + let rw : word; + assembly { + rw := mul(l,r) + } + return rw; + } +} + +// --- MemoryType --- + +trait MemoryType { + function load(loc: word) returns (a) ; + function store(loc: word, val: a) ; + function size(prx: Proxy) returns (word) ; +} + +impl MemoryType { + function load(loc: word) returns (word) { + let ret : word; + assembly { ret := mload(loc) } + return ret; + } + + function store(loc: word, val: word) { + assembly { mstore(loc,val) } + } + + function size(prx: Proxy) returns (word) { + return 32; + } +} + +impl MemoryType>> { + function load(loc: word) returns (memory>) { + let ret : word; + assembly { ret := mload(loc) } + return memory(ret); + } + + function store(loc: word, val: memory>) { + match (val) { +case memory(ptr) { +assembly { mstore(loc,ptr) } +} +} + } + + function size(prx: Proxy>) returns (word) { + return 32; + } +} + +// --- Assignment --- + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +impl Assign, word> { + function assign(ptr: memory, val: word) { + match (ptr) { +case memory(loc) { +assembly { + mstore(loc, val) + } +} +} + } +} + +// --- Index Access --- + +trait RValueIdxAccess { + function lookup(ci: col_idx) returns (val) ; +} + +trait LValueIdxAccess { + function lookup(ci: col_idx) returns (val) ; +} + +impl RValueIdxAccess<(memory>, word), a> where a: MemoryType { + function lookup(col_idx: (memory>, word)) returns (a) { + let sz = MemoryType.size(@a); + match (col_idx) { +case (col, idx) { +match (col) { +case memory(loc) { +return MemoryType.load(Add.add(loc, Mul.mul(idx, sz))); +} +} +} +} + } +} + +impl LValueIdxAccess<(memory>, word), memory> where a: MemoryType { + function lookup(col_idx: (memory>, word)) returns (memory) { + let sz = MemoryType.size(@a); + match (col_idx) { +case (col, idx) { +match (col) { +case memory(loc) { +return memory(Add.add(loc, Mul.mul(idx, sz))); +} +} +} +} + } +} + +// --- Examples --- + +function main() { + let x : memory>>> = memory(0); + let y : word = 0; + let z : memory> = memory(0); + + let i0 : word = 0; + let i1 : word = 1; + let i2 : word = 2; + let i3 : word = 3; + let i4 : word = 4; + let i5 : word = 5; + + // y = z[0] + y = RValueIdxAccess.lookup((z, i0)); + + //y = x[0][1] + y = RValueIdxAccess.lookup((RValueIdxAccess.lookup((x, i0)), i1)); + + //x[2][3] = x[5][4] + Assign.assign( + // TODO: R or L for the x[2] lookup? + LValueIdxAccess.lookup((RValueIdxAccess.lookup((x, i2)), i3)), + RValueIdxAccess.lookup((RValueIdxAccess.lookup((x, i5)), i4)) + ); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ixa.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ixa.solc deleted file mode 100644 index 1cddc66c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ixa.solc +++ /dev/null @@ -1,150 +0,0 @@ -// --- preamble / duplicated std defs --- - -data Proxy(a) = Proxy; - -// dynamic arrays with a runtime size. cannot exist on stack so no data constructor (i.e. should be used in combination with memory / storage pointers). -data array(a); - -// a typed pointer to a location in memory -data memory(a) = memory(word); - -// word arithmetc -forall t . class t:Add { function add(l: t, r: t) -> t; } -forall t . class t:Mul { function mul(l: t, r: t) -> t; } -instance word:Add { - function add(l: word, r: word) -> word { - let rw : word; - assembly { - rw := add(l,r) - } - return rw; - } -} -instance word:Mul { - function mul(l: word, r: word) -> word { - let rw : word; - assembly { - rw := mul(l,r) - } - return rw; - } -} - -// --- MemoryType --- - -forall a . class a:MemoryType { - function load(loc : word) -> a; - function store(loc: word, val : a) -> (); - function size(prx : Proxy(a)) -> word; -} - -instance word:MemoryType { - function load(loc : word) -> word { - let ret : word; - assembly { ret := mload(loc) } - return ret; - } - - function store(loc : word, val : word) -> () { - assembly { mstore(loc,val) } - } - - function size(prx : Proxy(word)) -> word { - return 32; - } -} - -forall a . instance memory(array(a)):MemoryType { - function load(loc: word) -> memory(array(a)) { - let ret : word; - assembly { ret := mload(loc) } - return memory(ret); - } - - function store(loc : word, val : memory(array(a))) -> () { - match val { - | memory(ptr) => assembly { mstore(loc,ptr) } - } - } - - function size(prx : Proxy(memory(a))) -> word { - return 32; - } -} - -// --- Assignment --- - -forall lhs rhs . class lhs:Assign(rhs) { - function assign(l : lhs, r : rhs) -> (); -} - -instance memory(word):Assign(word) { - function assign(ptr : memory(word), val : word) -> () { - match ptr { - | memory(loc) => assembly { - mstore(loc, val) - } - } - } -} - -// --- Index Access --- - -forall col_idx val . class col_idx:RValueIdxAccess(val) { - function lookup(ci : col_idx) -> val; -} - -forall col_idx val . class col_idx:LValueIdxAccess(val) { - function lookup(ci : col_idx) -> val; -} - -forall a . a:MemoryType => instance (memory(array(a)), word):RValueIdxAccess(a) { - function lookup(col_idx : (memory(array(a)), word)) -> a { - let sz = MemoryType.size(Proxy : Proxy(a)); - match col_idx { - | (col, idx) => match col { - | memory(loc) => - return MemoryType.load(Add.add(loc, Mul.mul(idx, sz))); - } - } - } -} - -forall a . a:MemoryType => instance (memory(array(a)), word):LValueIdxAccess(memory(a)) { - function lookup(col_idx : (memory(array(a)), word)) -> memory(a) { - let sz = MemoryType.size(Proxy : Proxy(a)); - match col_idx { - | (col, idx) => match col { - | memory(loc) => return memory(Add.add(loc, Mul.mul(idx, sz))); - } - } - } -} - -// --- Examples --- - -function main() -> () { - let x : memory(array(memory(array(word)))) = memory(0); - let y : word = 0; - let z : memory(array(word)) = memory(0); - - let i0 : word = 0; - let i1 : word = 1; - let i2 : word = 2; - let i3 : word = 3; - let i4 : word = 4; - let i5 : word = 5; - - // y = z[0] - y = RValueIdxAccess.lookup((z, i0)); - - //y = x[0][1] - y = RValueIdxAccess.lookup((RValueIdxAccess.lookup((x, i0)), i1)); - - //x[2][3] = x[5][4] - Assign.assign( - // TODO: R or L for the x[2] lookup? - LValueIdxAccess.lookup((RValueIdxAccess.lookup((x, i2)), i3)), - RValueIdxAccess.lookup((RValueIdxAccess.lookup((x, i5)), i4)) - ); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/join.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/join.sol new file mode 100644 index 00000000..25d39a58 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/join.sol @@ -0,0 +1,38 @@ +contract Option { + enum Option { None, Some(a) } + enum Bool { False, True } + + function maybe(n: word, o: Option) public returns (word) { + match (o) { +case Option.None { +return n; +} +case Option.Some(x) { +return x; +} +} + } + + function join(mmx: Option>) public returns (Option) { + let result = Option.None; + match (mmx) { +case Option.Some(Option.Some(x)) { +result = Option.Some(x); +} +case Option.None { +result = Option.None; +} +case Option.Some(Option.None) { +result = Option.None; +} +default { +result = Option.None; +} +} + return result; + } + + function main() public returns (word) { + return maybe(0, join(Option.Some(Option.Some(0)))); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/join.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/join.solc deleted file mode 100644 index e320eece..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/join.solc +++ /dev/null @@ -1,26 +0,0 @@ -contract Option { - data Option(a) = None | Some(a); - data Bool = False | True; - - public function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.None => return n; - | Option.Some(x) => return x; - } - } - - public function join(mmx : Option(Option(word))) -> Option(word) { - let result = Option.None; - match mmx { - | Option.Some(Option.Some(x)) => result = Option.Some(x); - | Option.None => result = Option.None; - | Option.Some(Option.None) => result = Option.None; - | _ => result = Option.None; - } - return result; - } - - public function main() -> word { - return maybe(0, join(Option.Some(Option.Some(0)))); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/listid.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/listid.sol new file mode 100644 index 00000000..3d219646 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/listid.sol @@ -0,0 +1,16 @@ +enum List { Nil, Cons(a, List) } + +function id(x: a) returns (a) { + return x; +} + +function listid(xs: List) returns (List) { + match (xs) { +case List.Nil { +return List.Nil ; +} +case List.Cons(x,xs) { +return List.Cons(id(x), listid(xs)); +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/listid.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/listid.solc deleted file mode 100644 index b483fa4f..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/listid.solc +++ /dev/null @@ -1,12 +0,0 @@ -data List(a) = Nil | Cons(a, List(a)); - -forall a . function id(x : a) -> a { - return x; -} - -function listid(xs : List(word)) -> List(word) { - match xs { - | List.Nil => return List.Nil ; - | List.Cons(x,xs) => return List.Cons(id(x), listid(xs)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ltimp.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ltimp.sol new file mode 100644 index 00000000..866c9b5e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ltimp.sol @@ -0,0 +1,5 @@ +import {ltproxy} from ltproxy; + +contract LtImp { + function main() public returns (bool) { ltproxy() } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ltimp.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ltimp.solc deleted file mode 100644 index c31fc5f3..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ltimp.solc +++ /dev/null @@ -1,5 +0,0 @@ -import ltproxy.{ltproxy}; - -contract LtImp { - public function main() -> bool { ltproxy() } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ltproxy.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ltproxy.sol new file mode 100644 index 00000000..493118bf --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ltproxy.sol @@ -0,0 +1,7 @@ +import {lt} from std; +export { ltproxy }; + +function ltproxy() returns (bool) { + let zero : word = 0; + return (zero < 42); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ltproxy.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ltproxy.solc deleted file mode 100644 index 15e88c87..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ltproxy.solc +++ /dev/null @@ -1,7 +0,0 @@ -import std.{lt}; -export { ltproxy }; - -function ltproxy() -> bool { - let zero : word = 0; - return (zero < 42); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/match-bitwise.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/match-bitwise.sol new file mode 100644 index 00000000..37e266f6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/match-bitwise.sol @@ -0,0 +1,30 @@ +import * from std; +import {mstore} from std.opcodes; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +// Regression for parsing the bitwise-or operator inside a match case block. +// The `|` inside the call is an expression operator; `case` starts the next +// arm, and each expression statement uses the canonical trailing semicolon. +function emit(x: word) { + match (x) { +case 0 { +mstore(0, x | 1); +} +case 1 { +mstore(0, x & 1); +} +default { +mstore(0, x); +} +} +} + +contract MatchBitwise { + // `0 | 1` still folds to 1 at the top level. + function main() public returns (word) { + emit(0); + return 0 | 1; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/match-bitwise.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/match-bitwise.solc deleted file mode 100644 index 509088f9..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/match-bitwise.solc +++ /dev/null @@ -1,27 +0,0 @@ -import std.{*}; -import std.opcodes.{mstore}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -// Regression for the `|` ambiguity between the bitwise-or operator and the -// match-arm separator. Each arm below ends in a *bare* expression statement -// (no trailing `;`), which is exactly the shape that previously made the -// parser read `mstore(...) | => ...` as a single bitwise-or -// expression and break the `match`. The `|` *inside* the parentheses is a -// genuine bitwise-or; the `|` that starts each arm is a separator. -function emit(x: word) -> () { - match x { - | 0 => mstore(0, x | 1) - | 1 => mstore(0, x & 1) - | _ => mstore(0, x) - } -} - -contract MatchBitwise { - // `0 | 1` still folds to 1 at the top level. - public function main() -> word { - emit(0); - return 0 | 1; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/match-yul.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/match-yul.sol new file mode 100644 index 00000000..a4d3af7a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/match-yul.sol @@ -0,0 +1,16 @@ +enum Wrapper { Wrapper(word) } +contract C { + function main() public returns (word) { + return foo(Wrapper(1)); + } + function foo(w: Wrapper) public returns (word) { + let result : word; + match (w) { +case Wrapper(ptr) { +//let ptr2 : word = ptr; + assembly { result := calldataload(ptr) } +} +} + return result; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/match-yul.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/match-yul.solc deleted file mode 100644 index a9fe458b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/match-yul.solc +++ /dev/null @@ -1,15 +0,0 @@ -data Wrapper = Wrapper(word); -contract C { - public function main() -> word { - return foo(Wrapper(1)); - } - public function foo(w:Wrapper) -> word { - let result : word; - match w { - | Wrapper(ptr) => - //let ptr2 : word = ptr; - assembly { result := calldataload(ptr) } - } - return result; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/memory.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/memory.sol new file mode 100644 index 00000000..360a2b9a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/memory.sol @@ -0,0 +1,10 @@ +enum Memory { Memory(word) } +enum Bytes { Bytes } + +function get_bytes() returns (Memory) { + let ptr : word; + assembly { + ptr := mload(0x40) + } + return Memory(ptr); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/memory.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/memory.solc deleted file mode 100644 index 9ed30b4b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/memory.solc +++ /dev/null @@ -1,10 +0,0 @@ -data Memory(t) = Memory(word); -data Bytes = Bytes; - -function get_bytes() -> Memory(Bytes) { - let ptr : word; - assembly { - ptr := mload(0x40) - } - return Memory(ptr); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mod-example.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mod-example.sol new file mode 100644 index 00000000..15b19327 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mod-example.sol @@ -0,0 +1,7 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; +function foo(x: word, y: word) returns (word) { + return x % y; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mod-example.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mod-example.solc deleted file mode 100644 index c69f188e..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mod-example.solc +++ /dev/null @@ -1,7 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; -function foo(x: word, y: word) -> word { - return x % y; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/modifier.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/modifier.sol new file mode 100644 index 00000000..b425f38b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/modifier.sol @@ -0,0 +1,21 @@ +contract C { + function add(x: word, y: word) public returns (word) { + let r : word; + assembly { + r := add(x, y) + } + return r; + } + + // modifier pattern: wrap add with before/after code + function modifiedAdd(x: word, y: word) public returns (word) { + // before solidity placeholder + let result = add(x, y); // Solidity's placeholder: _; + // after solidity placeholder + return result; + } + + function main() public returns (word) { + return modifiedAdd(2, 1); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/modifier.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/modifier.solc deleted file mode 100644 index ad6c5009..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/modifier.solc +++ /dev/null @@ -1,21 +0,0 @@ -contract C { - public function add(x: word, y:word) -> word { - let r : word; - assembly { - r := add(x, y) - } - return r; - } - - // modifier pattern: wrap add with before/after code - public function modifiedAdd(x : word, y : word) -> word { - // before solidity placeholder - let result = add(x, y); // Solidity's placeholder: _; - // after solidity placeholder - return result; - } - - public function main() -> word { - return modifiedAdd(2, 1); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/modulo.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/modulo.sol new file mode 100644 index 00000000..ead52317 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/modulo.sol @@ -0,0 +1,17 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +// Exercises the `%` operator and the `%=` compound assignment +// (the Mod class), plus the mod constant folding. +function f(x: word, y: word) returns (word) { + let acc : word = x % y; + acc %= y; // (x % y) % y == x % y once reduced + return acc; +} + +contract Modulo { + // 17 % 5 == 2, 2 % 5 == 2 — folded at compile time. + function main() public returns (word) { return f(17, 5); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/modulo.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/modulo.solc deleted file mode 100644 index 0c05ad3d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/modulo.solc +++ /dev/null @@ -1,17 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -// Exercises the `%` operator and the `%=` compound assignment -// (the Mod class), plus the mod constant folding. -function f(x: word, y: word) -> word { - let acc : word = x % y; - acc %= y; // (x % y) % y == x % y once reduced - return acc; -} - -contract Modulo { - // 17 % 5 == 2, 2 % 5 == 2 — folded at compile time. - public function main() -> word { return f(17, 5); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/monomorphic-require.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/monomorphic-require.sol new file mode 100644 index 00000000..0754b24e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/monomorphic-require.sol @@ -0,0 +1,35 @@ +// This should trigger a warning and an error in the specialiser +// due to unability to resolve result type of require +import {uint256,lt,not,Eq,ne,Proxy,bytes4,string} from std; +import * from std.dispatch; + +function myrevert(offset: word, length: word) returns (a) { + assembly { + revert(offset, length) + } + +} +function require(cond: bool) { + if (!cond) { + myrevert(0,0); + } +} + +function callvalue() returns (uint256) { + let res : word; + assembly { + res := callvalue() + } + return uint256(res); +} + +contract Deposit { +function deposit() public { + require(callvalue() != uint256(0)); + return (); + } + +function main() public { + deposit(); +} +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/monomorphic-require.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/monomorphic-require.solc deleted file mode 100644 index df7b1a2a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/monomorphic-require.solc +++ /dev/null @@ -1,36 +0,0 @@ -// This should trigger a warning and an error in the specialiser -// due to unability to resolve result type of require -import std.{uint256,lt,not,Eq,ne,Proxy,bytes4,string}; -import std.dispatch.{*}; - -forall a. -function myrevert(offset:word, length:word) -> a { - assembly { - revert(offset, length) - } - -} -function require(cond: bool) -> () { - if (!cond) { - myrevert(0,0):(); - } -} - -function callvalue() -> uint256 { - let res : word; - assembly { - res := callvalue() - } - return uint256(res); -} - -contract Deposit { -public function deposit() -> () { - require(callvalue() != uint256(0)); - return (); - } - -public function main() -> () { - deposit(); -} -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/morefun.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/morefun.sol new file mode 100644 index 00000000..31efb0d2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/morefun.sol @@ -0,0 +1,9 @@ +enum Proxy { Proxy } + +trait C { + function fun(p: Proxy) returns (word) ; +} + +function morefun(p: Proxy) returns (word) where t: C { + return C.fun(@t); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/morefun.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/morefun.solc deleted file mode 100644 index fbcd6058..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/morefun.solc +++ /dev/null @@ -1,9 +0,0 @@ -data Proxy(a) = Proxy; - -forall a . class a:C { - function fun(p:Proxy(a)) -> word; -} - -forall t . t : C => function morefun(p:Proxy(t)) -> word { - return C.fun(Proxy:Proxy(t)); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-both-templates.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-both-templates.sol new file mode 100644 index 00000000..be3aa13f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-both-templates.sol @@ -0,0 +1,40 @@ +// Tests that both Template A and Template B fire when the class has methods in +// both directions. Both should discover the same binding rep=word; the second +// application is idempotent (extSpSubst with the same binding is a no-op). + +enum Box { Box(word) } + +trait Convert { + function toRep(x: self) returns (rep) ; + function fromRep(x: rep) returns (self) ; +} + +impl Convert { + function toRep(x: Box) returns (word) { + match (x) { +case Box(w) { +return w; +} +} + } + function fromRep(x: word) returns (Box) { + return Box(x); + } +} + +function roundtrip(x: a) returns (a) where a: Convert { + let r : rep = Convert.toRep(x); + return Convert.fromRep(r); +} + +contract C { + constructor() {} + function main() public returns (word) { + let b : Box = roundtrip(Box(99)); + match (b) { +case Box(w) { +return w; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-both-templates.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-both-templates.solc deleted file mode 100644 index 222c102a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-both-templates.solc +++ /dev/null @@ -1,34 +0,0 @@ -// Tests that both Template A and Template B fire when the class has methods in -// both directions. Both should discover the same binding rep=word; the second -// application is idempotent (extSpSubst with the same binding is a no-op). - -data Box = Box(word); - -forall self rep. -class self:Convert(rep) { - function toRep(x:self) -> rep; - function fromRep(x:rep) -> self; -} - -instance Box:Convert(word) { - function toRep(x:Box) -> word { - match x { | Box(w) => return w; } - } - function fromRep(x:word) -> Box { - return Box(x); - } -} - -forall a rep . a:Convert(rep) => -function roundtrip(x:a) -> a { - let r : rep = Convert.toRep(x); - return Convert.fromRep(r); -} - -contract C { - constructor() {} - public function main() -> word { - let b : Box = roundtrip(Box(99)); - match b { | Box(w) => return w; } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-chain-phantom.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-chain-phantom.sol new file mode 100644 index 00000000..d03e83d8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-chain-phantom.sol @@ -0,0 +1,52 @@ +// Tests resolveMPTCsFromPreds in a "chain" scenario: +// - f has phantom rep in its monotype (Foo -> ()) +// - inside f, encode returns a value of type rep +// - that value is passed to sink whose monotype is rep -> () +// +// Without resolveMPTCsFromPreds the SM substitution lacks rep=word when +// sink's specialisation name is being built, which would produce sink$rep +// (wrong) instead of sink$word (correct). + +enum Foo { Foo(word) } + +trait Encoder { + function encode(x: self, hint: word) returns (rep) ; +} + +trait Sink { + function sink(x: rep) ; +} + +impl Encoder { + function encode(x: Foo, hint: word) returns (word) { + match (x) { +case Foo(v) { +return v; +} +} + } +} + +impl Sink { + function sink(x: word) { + return (); + } +} + +// phantom rep: rep does not appear in f's argument or return type. +// Inside the body, encode returns rep and sink consumes rep. +// resolveMPTCsFromPreds must bind rep=word so that sink specialises +// to sink$word (not sink$rep). +function f(x: a) where a: Encoder, rep: Sink { + let r : rep = Encoder.encode(x, 0); + Sink.sink(r); + return (); +} + +contract C { + constructor() {} + function main() public returns (word) { + f(Foo(42)); + return 0; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-chain-phantom.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-chain-phantom.solc deleted file mode 100644 index f5822c36..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-chain-phantom.solc +++ /dev/null @@ -1,51 +0,0 @@ -// Tests resolveMPTCsFromPreds in a "chain" scenario: -// - f has phantom rep in its monotype (Foo -> ()) -// - inside f, encode returns a value of type rep -// - that value is passed to sink whose monotype is rep -> () -// -// Without resolveMPTCsFromPreds the SM substitution lacks rep=word when -// sink's specialisation name is being built, which would produce sink$rep -// (wrong) instead of sink$word (correct). - -data Foo = Foo(word); - -forall self rep. -class self:Encoder(rep) { - function encode(x:self, hint:word) -> rep; -} - -forall rep r. -class rep:Sink(r) { - function sink(x:rep) -> (); -} - -instance Foo:Encoder(word) { - function encode(x:Foo, hint:word) -> word { - match x { | Foo(v) => return v; } - } -} - -instance word:Sink(word) { - function sink(x:word) -> () { - return (); - } -} - -// phantom rep: rep does not appear in f's argument or return type. -// Inside the body, encode returns rep and sink consumes rep. -// resolveMPTCsFromPreds must bind rep=word so that sink specialises -// to sink$word (not sink$rep). -forall a rep . a:Encoder(rep), rep:Sink(word) => -function f(x:a) -> () { - let r : rep = Encoder.encode(x, 0); - Sink.sink(r); - return (); -} - -contract C { - constructor() {} - public function main() -> word { - f(Foo(42)); - return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-guard-extras-concrete.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-guard-extras-concrete.sol new file mode 100644 index 00000000..84782a26 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-guard-extras-concrete.sol @@ -0,0 +1,31 @@ +// Tests the guard in resolveMPTCFromPreds that skips tryResolveMPTC when all +// extras are already fully concrete. Here rep is written as the concrete type +// `word` directly in the constraint, so freetv extras = [] and the function +// compiles through normal type inference without phantom variable discovery. + +enum Box { Box(word) } + +trait Unbox { + function unbox(x: self) returns (rep) ; +} + +impl Unbox { + function unbox(x: Box) returns (word) { + match (x) { +case Box(w) { +return w; +} +} + } +} + +function extractWord(x: a) returns (word) where a: Unbox { + return Unbox.unbox(x); +} + +contract C { + constructor() {} + function main() public returns (word) { + return extractWord(Box(42)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-guard-extras-concrete.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-guard-extras-concrete.solc deleted file mode 100644 index 588af17f..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-guard-extras-concrete.solc +++ /dev/null @@ -1,29 +0,0 @@ -// Tests the guard in resolveMPTCFromPreds that skips tryResolveMPTC when all -// extras are already fully concrete. Here rep is written as the concrete type -// `word` directly in the constraint, so freetv extras = [] and the function -// compiles through normal type inference without phantom variable discovery. - -data Box = Box(word); - -forall self rep. -class self:Unbox(rep) { - function unbox(x:self) -> rep; -} - -instance Box:Unbox(word) { - function unbox(x:Box) -> word { - match x { | Box(w) => return w; } - } -} - -forall a . a:Unbox(word) => -function extractWord(x:a) -> word { - return Unbox.unbox(x); -} - -contract C { - constructor() {} - public function main() -> word { - return extractWord(Box(42)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-multi-instance.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-multi-instance.sol new file mode 100644 index 00000000..5f47e3a0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-multi-instance.sol @@ -0,0 +1,51 @@ +// Tests that tryResolveMPTC selects the correct instance when multiple instances +// of the same class are registered in the resolution table. +// For getTag(Foo(1)): specmgu (Bar -> RepBar) (Foo -> freshV) fails (Bar != Foo), +// so only the Foo entry fires and rep is resolved to RepFoo. +// Similarly for getTag(Bar(2)) rep resolves to RepBar. + +enum Foo { Foo(word) } +enum Bar { Bar(word) } +enum RepFoo { RepFoo(word) } +enum RepBar { RepBar(word) } + +trait Tagged { + function tag(x: self) returns (rep) ; +} + +impl Tagged { + function tag(x: Foo) returns (RepFoo) { + match (x) { +case Foo(w) { +return RepFoo(w); +} +} + } +} + +impl Tagged { + function tag(x: Bar) returns (RepBar) { + match (x) { +case Bar(w) { +return RepBar(w); +} +} + } +} + +function getTag(x: a) returns (rep) where a: Tagged { + return Tagged.tag(x); +} + +contract C { + constructor() {} + function main() public returns (word) { + let rf : RepFoo = getTag(Foo(1)); + let rb : RepBar = getTag(Bar(2)); + match (rf) { +case RepFoo(w) { +return w; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-multi-instance.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-multi-instance.solc deleted file mode 100644 index 20d74d23..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-multi-instance.solc +++ /dev/null @@ -1,41 +0,0 @@ -// Tests that tryResolveMPTC selects the correct instance when multiple instances -// of the same class are registered in the resolution table. -// For getTag(Foo(1)): specmgu (Bar -> RepBar) (Foo -> freshV) fails (Bar != Foo), -// so only the Foo entry fires and rep is resolved to RepFoo. -// Similarly for getTag(Bar(2)) rep resolves to RepBar. - -data Foo = Foo(word); -data Bar = Bar(word); -data RepFoo = RepFoo(word); -data RepBar = RepBar(word); - -forall self rep. -class self:Tagged(rep) { - function tag(x:self) -> rep; -} - -instance Foo:Tagged(RepFoo) { - function tag(x:Foo) -> RepFoo { - match x { | Foo(w) => return RepFoo(w); } - } -} - -instance Bar:Tagged(RepBar) { - function tag(x:Bar) -> RepBar { - match x { | Bar(w) => return RepBar(w); } - } -} - -forall a rep . a:Tagged(rep) => -function getTag(x:a) -> rep { - return Tagged.tag(x); -} - -contract C { - constructor() {} - public function main() -> word { - let rf : RepFoo = getTag(Foo(1)); - let rb : RepBar = getTag(Bar(2)); - match rf { | RepFoo(w) => return w; } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-nop-mainty-free.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-nop-mainty-free.sol new file mode 100644 index 00000000..9dfd101b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-nop-mainty-free.sol @@ -0,0 +1,41 @@ +// Documents the NOP-A guard in resolveMPTCsFromPreds. +// +// The guard `null (freetv mainTy')` is false when the main type variable +// is not yet bound in the SM substitution. This happens for higher-order +// polymorphic functions that are specialised from the outside. +// +// Here `mapEncode` is only ever called with a concrete `a=Foo`, so at every +// call site the SM substitution has a=Foo before the body is processed. +// However, if `mapEncode` were called with an unresolved type the guard +// would fire and tryResolveMPTC would be skipped. +// +// This is a compile-only test: it verifies that the NOP-A guard does NOT +// interfere with the normal specialisation of `mapEncode` when called +// from a concrete call site. + +enum Foo { Foo(word) } + +trait Encoder { + function encode(x: self, hint: word) returns (rep) ; +} + +impl Encoder { + function encode(x: Foo, hint: word) returns (word) { + match (x) { +case Foo(v) { +return v; +} +} + } +} + +function extractVal(x: a) returns (rep) where a: Encoder { + return Encoder.encode(x, 0); +} + +contract C { + constructor() {} + function main() public returns (word) { + return extractVal(Foo(7)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-nop-mainty-free.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-nop-mainty-free.solc deleted file mode 100644 index 0ddc08e2..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-nop-mainty-free.solc +++ /dev/null @@ -1,39 +0,0 @@ -// Documents the NOP-A guard in resolveMPTCsFromPreds. -// -// The guard `null (freetv mainTy')` is false when the main type variable -// is not yet bound in the SM substitution. This happens for higher-order -// polymorphic functions that are specialised from the outside. -// -// Here `mapEncode` is only ever called with a concrete `a=Foo`, so at every -// call site the SM substitution has a=Foo before the body is processed. -// However, if `mapEncode` were called with an unresolved type the guard -// would fire and tryResolveMPTC would be skipped. -// -// This is a compile-only test: it verifies that the NOP-A guard does NOT -// interfere with the normal specialisation of `mapEncode` when called -// from a concrete call site. - -data Foo = Foo(word); - -forall self rep. -class self:Encoder(rep) { - function encode(x:self, hint:word) -> rep; -} - -instance Foo:Encoder(word) { - function encode(x:Foo, hint:word) -> word { - match x { | Foo(v) => return v; } - } -} - -forall a rep. a:Encoder(rep) => -function extractVal(x:a) -> rep { - return Encoder.encode(x, 0); -} - -contract C { - constructor() {} - public function main() -> word { - return extractVal(Foo(7)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-partial-instance.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-partial-instance.sol new file mode 100644 index 00000000..9883e3db --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-partial-instance.sol @@ -0,0 +1,45 @@ +// Exercises the PARTIAL guard in tryResolveMPTC. +// +// The instance forall a b. instance Zero:Nth((a,b), a) has free type variables +// in its extras even after successfully matching Zero against the concrete main type. +// resolveMPTCsFromPreds detects this (concreteExtras still has free vars) and skips +// the instance, letting normal type inference determine the extra type instead. + +pragma no-coverage-condition Nth; + +enum Zero {} +enum Succ {} +enum Proxy { Proxy } + +trait Nth { + function nth(x: Proxy, y: b) returns (c) ; +} + +impl Nth { + function nth(x: Proxy, y: (a, b)) returns (a) { + match (y) { +case (a, b) { +return a; +} +} + } +} + +impl Nth, (a, b), c> where n: Nth { + function nth(x: Proxy>, y: (a, b)) returns (c) { + match (y) { +case (a, b) { +return Nth.nth(@n, b); +} +} + } +} + +contract C { + constructor() {} + function main() public returns (word) { + let p : (word, word, word) = (1, 2, 3); + let x : word = Nth.nth(@Zero, p); + return x; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-partial-instance.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-partial-instance.solc deleted file mode 100644 index ac1f8743..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-partial-instance.solc +++ /dev/null @@ -1,37 +0,0 @@ -// Exercises the PARTIAL guard in tryResolveMPTC. -// -// The instance forall a b. instance Zero:Nth((a,b), a) has free type variables -// in its extras even after successfully matching Zero against the concrete main type. -// resolveMPTCsFromPreds detects this (concreteExtras still has free vars) and skips -// the instance, letting normal type inference determine the extra type instead. - -pragma no-coverage-condition Nth; - -data Zero; -data Succ(a); -data Proxy(a) = Proxy; - -forall a b c. class a:Nth(b, c) { - function nth(x:Proxy(a), y:b) -> c; -} - -forall a b. instance Zero:Nth((a,b), a) { - function nth(x:Proxy(Zero), y:(a,b)) -> a { - match y { | (a, b) => return a; } - } -} - -forall n a b c. n:Nth(b,c) => instance Succ(n):Nth((a,b), c) { - function nth(x:Proxy(Succ(n)), y:(a,b)) -> c { - match y { | (a, b) => return Nth.nth(Proxy : Proxy(n), b); } - } -} - -contract C { - constructor() {} - public function main() -> word { - let p : (word, word, word) = (1, 2, 3); - let x : word = Nth.nth(Proxy : Proxy(Zero), p); - return x; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-template-a-only.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-template-a-only.sol new file mode 100644 index 00000000..f292d99f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-template-a-only.sol @@ -0,0 +1,31 @@ +// Tests tryResolveMPTC Template A path. +// The class has only a method of the form (self -> rep), so Template B cannot +// fire. The specialiser must discover rep=word solely via Template A: +// specmgu (Box -> word) (Box -> freshV) => freshV = word => rep = word + +enum Box { Box(word) } + +trait Unbox { + function unbox(x: self) returns (rep) ; +} + +impl Unbox { + function unbox(x: Box) returns (word) { + match (x) { +case Box(w) { +return w; +} +} + } +} + +function extract(x: a) returns (rep) where a: Unbox { + return Unbox.unbox(x); +} + +contract C { + constructor() {} + function main() public returns (word) { + return extract(Box(42)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-template-a-only.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-template-a-only.solc deleted file mode 100644 index c42458eb..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-template-a-only.solc +++ /dev/null @@ -1,29 +0,0 @@ -// Tests tryResolveMPTC Template A path. -// The class has only a method of the form (self -> rep), so Template B cannot -// fire. The specialiser must discover rep=word solely via Template A: -// specmgu (Box -> word) (Box -> freshV) => freshV = word => rep = word - -data Box = Box(word); - -forall self rep. -class self:Unbox(rep) { - function unbox(x:self) -> rep; -} - -instance Box:Unbox(word) { - function unbox(x:Box) -> word { - match x { | Box(w) => return w; } - } -} - -forall a rep . a:Unbox(rep) => -function extract(x:a) -> rep { - return Unbox.unbox(x); -} - -contract C { - constructor() {} - public function main() -> word { - return extract(Box(42)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-template-b-only.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-template-b-only.sol new file mode 100644 index 00000000..2437d413 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-template-b-only.sol @@ -0,0 +1,33 @@ +// Tests tryResolveMPTC Template B path. +// The class has only a method of the form (rep -> self), so Template A cannot +// fire. The specialiser must discover rep=word solely via Template B: +// specmgu (word -> Box) (freshV -> Box) => freshV = word => rep = word +// The `hint:a` argument makes a=Box concrete at the call site. + +enum Box { Box(word) } + +trait Rebox { + function rebox(x: rep) returns (self) ; +} + +impl Rebox { + function rebox(x: word) returns (Box) { + return Box(x); + } +} + +function rewrap(val: rep, hint: a) returns (a) where a: Rebox { + return Rebox.rebox(val); +} + +contract C { + constructor() {} + function main() public returns (word) { + let b : Box = rewrap(7, Box(0)); + match (b) { +case Box(w) { +return w; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-template-b-only.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-template-b-only.solc deleted file mode 100644 index 07ac91c8..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/mptc-template-b-only.solc +++ /dev/null @@ -1,31 +0,0 @@ -// Tests tryResolveMPTC Template B path. -// The class has only a method of the form (rep -> self), so Template A cannot -// fire. The specialiser must discover rep=word solely via Template B: -// specmgu (word -> Box) (freshV -> Box) => freshV = word => rep = word -// The `hint:a` argument makes a=Box concrete at the call site. - -data Box = Box(word); - -forall self rep. -class self:Rebox(rep) { - function rebox(x:rep) -> self; -} - -instance Box:Rebox(word) { - function rebox(x:word) -> Box { - return Box(x); - } -} - -forall a rep . a:Rebox(rep) => -function rewrap(val:rep, hint:a) -> a { - return Rebox.rebox(val); -} - -contract C { - constructor() {} - public function main() -> word { - let b : Box = rewrap(7, Box(0)); - match b { | Box(w) => return w; } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/multi-stmt-var-leaf.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/multi-stmt-var-leaf.sol new file mode 100644 index 00000000..2ccc9626 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/multi-stmt-var-leaf.sol @@ -0,0 +1,12 @@ +enum Bool { False, True } + +contract MultiStmtVarLeaf { + function main(x: Bool) public returns (Bool) { + match (x) { +case y { +let z = y; + return z; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/multi-stmt-var-leaf.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/multi-stmt-var-leaf.solc deleted file mode 100644 index aff2196a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/multi-stmt-var-leaf.solc +++ /dev/null @@ -1,11 +0,0 @@ -data Bool = False | True; - -contract MultiStmtVarLeaf { - public function main(x:Bool) -> Bool { - match x { - | y => - let z = y; - return z; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/nid.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/nid.sol new file mode 100644 index 00000000..4c06795e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/nid.sol @@ -0,0 +1,8 @@ +function id(x: word) returns (word) { + return x; +} + +function nid(x: word) returns (word) { + return id(x); +} + diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/nid.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/nid.solc deleted file mode 100644 index 24d8a88c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/nid.solc +++ /dev/null @@ -1,8 +0,0 @@ -function id (x : word) -> word { - return x; -} - -function nid (x : word) -> word { - return id(x); -} - diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/noclosure.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/noclosure.sol new file mode 100644 index 00000000..163ca306 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/noclosure.sol @@ -0,0 +1,6 @@ +function foo(z: word) returns (word) { + let f = lam (x : word, y : word) { + return primAddWord(x,primAddWord(y,1)); + }; + return primAddWord(f(0,1),z); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/noclosure.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/noclosure.solc deleted file mode 100644 index f961cdae..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/noclosure.solc +++ /dev/null @@ -1,6 +0,0 @@ -function foo (z : word) -> word { - let f = lam (x : word, y : word) { - return primAddWord(x,primAddWord(y,1)); - }; - return primAddWord(f(0,1),z); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/notif.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/notif.sol new file mode 100644 index 00000000..2a114790 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/notif.sol @@ -0,0 +1,14 @@ +function not(x: bool) returns (bool) { + if (x) { + return false ; + } else { + return true ; + } +} + +function not2(x: bool) returns (bool) { + if (x) { + return false ; + } + return true; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/notif.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/notif.solc deleted file mode 100644 index ee4e9244..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/notif.solc +++ /dev/null @@ -1,14 +0,0 @@ -function not(x : bool) -> bool { - if (x) { - return false ; - } else { - return true ; - } -} - -function not2(x : bool) -> bool { - if (x) { - return false ; - } - return true; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/option2.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/option2.sol new file mode 100644 index 00000000..1a470118 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/option2.sol @@ -0,0 +1,53 @@ +contract Option { + enum Option { None, Some(a) } + + function just(x: word) public returns (Option) { return Option.Some(x); } + + function maybe(n: word, o: Option) public returns (word) { + match (o) { +case Option.None { +return n; +} +case Option.Some(x) { +return x; +} +} + } + + function join(mmx: Option>) public returns (Option) { + match (mmx) { +case Option.None { +return Option.None; +} +case Option.Some(Option.None) { +return Option.None; +} +case Option.Some(Option.Some(x)) { +return Option.Some(x); +} +} + } + + function join2(mmx: Option>) public returns (Option) { + match (mmx) { +case Option.Some(m) { +match (m) { +case Option.None { +return Option.None; +} +case Option.Some(x) { +return Option.Some(x); +} +} +} +default { +return Option.None; +} +} + } + + function main() public returns (word) { + // return maybe(0, join(Option.Some(Option.Some(42)))); + return 42; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/option2.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/option2.solc deleted file mode 100644 index b60d551d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/option2.solc +++ /dev/null @@ -1,35 +0,0 @@ -contract Option { - data Option(a) = None | Some(a); - - public function just(x : word) -> Option(word) { return Option.Some(x); } - - public function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.None => return n; - | Option.Some(x) => return x; - } - } - - public function join(mmx : Option(Option(word))) -> Option(word) { - match mmx { - | Option.None => return Option.None; - | Option.Some(Option.None) => return Option.None; - | Option.Some(Option.Some(x)) => return Option.Some(x); - } - } - - public function join2(mmx : Option(Option(word))) -> Option(word) { - match mmx { - | Option.Some(m) => match m { - | Option.None => return Option.None; - | Option.Some(x) => return Option.Some(x); - } - | _ => return Option.None; - } - } - - public function main() -> word { - // return maybe(0, join(Option.Some(Option.Some(42)))); - return 42; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pair-bug.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pair-bug.sol new file mode 100644 index 00000000..f5a83b45 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pair-bug.sol @@ -0,0 +1,9 @@ +import * from std; + +contract TupleRet { + constructor() {} + + function pair() returns (uint256, uint256) { + return (uint256(7), uint256(11)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pair-bug.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pair-bug.solc deleted file mode 100644 index 3006338f..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pair-bug.solc +++ /dev/null @@ -1,9 +0,0 @@ -import std.{*}; - -contract TupleRet { - constructor() {} - - function pair() -> (uint256, uint256) { - return (uint256(7), uint256(11)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pars.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pars.sol new file mode 100644 index 00000000..70417bee --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pars.sol @@ -0,0 +1,3 @@ +contract Pars { + function main() public { let f: word; let ignored: word = 42; (); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pars.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pars.solc deleted file mode 100644 index d25d89f6..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pars.solc +++ /dev/null @@ -1,3 +0,0 @@ -contract Pars { - public function main() -> (){ let f:word; 42:word; (); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/phantom-type-return-con.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/phantom-type-return-con.sol new file mode 100644 index 00000000..137321aa --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/phantom-type-return-con.sol @@ -0,0 +1,18 @@ +enum Foo { Foo(word) } + function wrap(x: word) returns (Foo) { + return Foo(x); + } + + function unwrap() returns (word) { + match (wrap(42)) { +case Foo(w) { +return w; +} +} + } + + contract C { + function main() public returns (word) { + return unwrap(); + } + } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/phantom-type-return-con.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/phantom-type-return-con.solc deleted file mode 100644 index 87ae1364..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/phantom-type-return-con.solc +++ /dev/null @@ -1,16 +0,0 @@ -data Foo(a) = Foo(word); - forall a . function wrap(x : word) -> Foo(a) { - return Foo(x); - } - - function unwrap() -> word { - match(wrap(42)) { - | Foo(w) => return w; - } - } - - contract C { - public function main() -> word { - return unwrap(); - } - } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/polymatch-error.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/polymatch-error.sol new file mode 100644 index 00000000..7fcbac6f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/polymatch-error.sol @@ -0,0 +1,16 @@ +function fst(p: (a, b)) returns (a) { + match (p) { +case (a, _) { +return a; +} +} +} +contract TestUnitMatch { + function main() public { + match (((), ())) { +case x { +return fst(x); +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/polymatch-error.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/polymatch-error.solc deleted file mode 100644 index 96462fd7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/polymatch-error.solc +++ /dev/null @@ -1,12 +0,0 @@ -forall a b . function fst(p: (a, b)) -> a { - match p { - | (a, _) => return a; - } -} -contract TestUnitMatch { - public function main() -> () { - match ((), ()) { - | x => return fst(x); - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/polymorphic-require.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/polymorphic-require.sol new file mode 100644 index 00000000..fc89944b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/polymorphic-require.sol @@ -0,0 +1,31 @@ +// This should trigger a warning and an error in the specialiser +// due to unability to resolve result type of require +import {uint256,lt,not,Eq,ne,Proxy,bytes4,string} from std; +import * from std.dispatch; + +function require(cond: bool) returns (a) { + if (!cond) { + assembly { + revert(0, 0) + } + } +} + +function callvalue() returns (uint256) { + let res : word; + assembly { + res := callvalue() + } + return uint256(res); +} + +contract Deposit { +function deposit() public { + require(callvalue() != uint256(0)); + return (); + } + +function main() public { + deposit(); +} +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/polymorphic-require.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/polymorphic-require.solc deleted file mode 100644 index dbab329e..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/polymorphic-require.solc +++ /dev/null @@ -1,32 +0,0 @@ -// This should trigger a warning and an error in the specialiser -// due to unability to resolve result type of require -import std.{uint256,lt,not,Eq,ne,Proxy,bytes4,string}; -import std.dispatch.{*}; - -forall a. -function require(cond: bool) -> a { - if (!cond) { - assembly { - revert(0, 0) - } - } -} - -function callvalue() -> uint256 { - let res : word; - assembly { - res := callvalue() - } - return uint256(res); -} - -contract Deposit { -public function deposit() -> () { - require(callvalue() != uint256(0)); - return (); - } - -public function main() -> () { - deposit(); -} -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pragma_merge_base.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pragma_merge_base.sol new file mode 100644 index 00000000..334a200f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pragma_merge_base.sol @@ -0,0 +1,47 @@ +// Test base file for pragma merging functionality +// This file contains violations of all three condition types with pragmas to disable checks + +// Pragmas to disable checks for specific classes +pragma no-patterson-condition TestClassP1, TestClassB1, TestClassP3, TestClassB3; +pragma no-coverage-condition TestClassC1, TestClassP3; +pragma no-bounded-variable-condition TestClassB1, TestClassB3; + +// --- Test Classes --- + +trait TestClassP1 {} +trait TestClassP2 {} +trait TestClassP3 {} + +trait TestClassC1 {} +trait TestClassC2 {} + +trait TestClassB1 {} +trait TestClassB2 {} +trait TestClassB3 {} + +// --- Data Types --- + +enum TestType1 { TestType1 } +enum TestType2 { TestType2 } + +// Fails Patterson: context constraint not smaller then head +impl TestClassP1 where (U, word): TestClassP1 {} + +// Patterson OK: No context predicates +impl TestClassP2 {} + +// --- Coverage Condition --- + +// Fails Coverage: Variable 'a' only appears in weak position (parameter to TestClassC1) +impl TestClassC1, a> {} + +// Coverage OK: All variables in strong positions +impl TestClassC2 {} + +// === Bound Variable Violations === + +// Fails Bound Variable & Patterson: Variable 'c' appears in context but not in instance head +impl TestClassB1, a> where c: TestClassB2 {} + +// Bound Variable OK: Simple instance without context +impl TestClassB2, TestType2> {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pragma_merge_base.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pragma_merge_base.solc deleted file mode 100644 index 98d66cfb..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pragma_merge_base.solc +++ /dev/null @@ -1,47 +0,0 @@ -// Test base file for pragma merging functionality -// This file contains violations of all three condition types with pragmas to disable checks - -// Pragmas to disable checks for specific classes -pragma no-patterson-condition TestClassP1, TestClassB1, TestClassP3, TestClassB3; -pragma no-coverage-condition TestClassC1, TestClassP3; -pragma no-bounded-variable-condition TestClassB1, TestClassB3; - -// --- Test Classes --- - -forall a . class a:TestClassP1 {} -forall a . class a:TestClassP2 {} -forall a b . class a:TestClassP3(b) {} - -forall a b . class a:TestClassC1(b) {} -forall a b c . class a:TestClassC2(b,c) {} - -forall a b . class a:TestClassB1(b) {} -forall a b . class a:TestClassB2(b) {} -forall a . class a:TestClassB3 {} - -// --- Data Types --- - -data TestType1(x) = TestType1; -data TestType2 = TestType2; - -// Fails Patterson: context constraint not smaller then head -forall U . (U,word):TestClassP1 => instance U:TestClassP1 {} - -// Patterson OK: No context predicates -instance TestType2:TestClassP2 {} - -// --- Coverage Condition --- - -// Fails Coverage: Variable 'a' only appears in weak position (parameter to TestClassC1) -forall a b . instance TestType1(b):TestClassC1(a) {} - -// Coverage OK: All variables in strong positions -instance TestType2:TestClassC2(TestType2, TestType2) {} - -// === Bound Variable Violations === - -// Fails Bound Variable & Patterson: Variable 'c' appears in context but not in instance head -forall a c . c:TestClassB2(a) => instance TestType1(a):TestClassB1(a) {} - -// Bound Variable OK: Simple instance without context -instance TestType1(TestType2):TestClassB2(TestType2) {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pragma_test_patterson.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pragma_test_patterson.sol new file mode 100644 index 00000000..91062406 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pragma_test_patterson.sol @@ -0,0 +1,9 @@ +// Simple Patterson test - should fail without pragma + +trait C1 {} +trait C2 {} + +enum T { T } + +// This violates Patterson: context measure (2) >= conclusion measure (2) +impl C1> where U: C1, U: C2 {} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pragma_test_patterson.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pragma_test_patterson.solc deleted file mode 100644 index fd7be0ed..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/pragma_test_patterson.solc +++ /dev/null @@ -1,9 +0,0 @@ -// Simple Patterson test - should fail without pragma - -forall a . class a:C1 {} -forall a . class a:C2 {} - -data T(x) = T; - -// This violates Patterson: context measure (2) >= conclusion measure (2) -forall U . U:C1, U:C2 => instance T(U):C1 {} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/proxy-desugar.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/proxy-desugar.sol new file mode 100644 index 00000000..04229265 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/proxy-desugar.sol @@ -0,0 +1,12 @@ +import * from std; +pragma no-patterson-condition; +pragma no-coverage-condition; +pragma no-bounded-variable-condition; + +function foo(x: @word) returns (word) { + return 0; +} + +function fuz(y: word) returns (word) { + return y + foo(@word); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/proxy-desugar.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/proxy-desugar.solc deleted file mode 100644 index 82be4dea..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/proxy-desugar.solc +++ /dev/null @@ -1,12 +0,0 @@ -import std.{*}; -pragma no-patterson-condition; -pragma no-coverage-condition; -pragma no-bounded-variable-condition; - -function foo(x : @word) -> word { - return 0; -} - -function fuz(y : word) -> word { - return y + foo(@word); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/proxy.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/proxy.sol new file mode 100644 index 00000000..7f883c93 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/proxy.sol @@ -0,0 +1,10 @@ +enum Proxy { Proxy } + +trait BaseMemoryType { + function memorySize(x: Proxy) returns (word) ; +} + + +function morefun(p: Proxy) returns (word) where t: BaseMemoryType { + return BaseMemoryType.memorySize(@t); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/proxy.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/proxy.solc deleted file mode 100644 index a3e30424..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/proxy.solc +++ /dev/null @@ -1,11 +0,0 @@ -data Proxy(a) = Proxy; - -forall self . class self:BaseMemoryType { - function memorySize(x:Proxy(self)) -> word; -} - - -forall t . t : BaseMemoryType => -function morefun(p:Proxy(t)) -> word { - return BaseMemoryType.memorySize(Proxy:Proxy(t)); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/rec.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/rec.sol new file mode 100644 index 00000000..333925dd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/rec.sol @@ -0,0 +1,10 @@ +function rec(n: word, b: word, f: word) returns (word) { + match (n) { +case 0 { +return b; +} +case m { +return f(primAddWord(m,1), rec(m, b, f)); +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/rec.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/rec.solc deleted file mode 100644 index 53aec28d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/rec.solc +++ /dev/null @@ -1,6 +0,0 @@ -function rec (n : word, b : word, f : word) -> word { - match n { - | 0 => return b; - | m => return f(primAddWord(m,1), rec(m, b, f)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/redundant-match.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/redundant-match.sol new file mode 100644 index 00000000..34a24b1d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/redundant-match.sol @@ -0,0 +1,19 @@ +enum Bool { False, True } + + function f(x: Bool) returns (Bool) { + match (x) { +case z { +return z; +} +case Bool.True { +return Bool.True; +} +case Bool.False { +return Bool.False; +} +} + } + + contract Test { + function main() public returns (Bool) { f(Bool.True) } + } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/redundant-match.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/redundant-match.solc deleted file mode 100644 index f7913c2b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/redundant-match.solc +++ /dev/null @@ -1,13 +0,0 @@ -data Bool = False | True; - - function f(x : Bool) -> Bool { - match x { - | z => return z; - | Bool.True => return Bool.True; - | Bool.False => return Bool.False; - } - } - - contract Test { - public function main() -> Bool { f(Bool.True) } - } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/reference-encoding-good.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/reference-encoding-good.sol new file mode 100644 index 00000000..64588046 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/reference-encoding-good.sol @@ -0,0 +1,241 @@ + +/////// Construction +trait Typedef { + function rep(x: abs) returns (rep) ; + function abs(x: rep) returns (abs) ; +} + +impl Typedef { + function rep(x: word) returns (word) { return x; } + function abs(x: word) returns (word) { return x; } +} + +enum uint { uint(word) } + +impl Typedef { + function rep(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + function abs(x: word) returns (uint) { + return uint(x); + } +} + +enum memory { memory(word) } +enum memoryRef { memoryRef(word) } +enum Proxy { Proxy } + +impl Typedef, word> { + function rep(x: memory) returns (word) { + match (x) { +case memory(y) { +return y; +} +} + } + function abs(x: word) returns (memory) { + return memory(x); + } +} +impl Typedef, word> { + function rep(x: memoryRef) returns (word) { + match (x) { +case memoryRef(y) { +return y; +} +} + } + function abs(x: word) returns (memoryRef) { + return memoryRef(x); + } +} + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +enum ref { ref(a) } + +impl Assign, a> { + function assign(l: ref, r: a) { + // builtin "stack store" + return (); + } +} + +trait MemoryType { + function load(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +trait MemorySize { + function size(x: Proxy) returns (word) ; +} + +impl MemoryType { + function load(ptr: word) returns (word) { + let r:word; + assembly { + r := mload(ptr) + } + return r; + } + function store(ptr: word, value: word) { + assembly { + mstore(ptr, value) + } + } +} + +impl MemoryType { + function load(ptr: word) returns (uint) { + return Typedef.abs(MemoryType.load(ptr)); + } + function store(ptr: word, value: uint) { + return MemoryType.store(ptr, Typedef.rep(value)); + } +} + +impl Assign, a> where a: MemoryType { + function assign(l: memoryRef, y: a) { + MemoryType.store(Typedef.rep(l), y); + } +} + + + +enum MemberAccessProxy { MemberAccessProxy(a, field) } + +function memberAccessD1(x: MemberAccessProxy) returns (a) { + match (x) { +case MemberAccessProxy(y,z) { +return y; +} +} +} + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +trait RValueMemberAccess { + function memberAccess(x: self) returns (memberValueType) ; +} + +// This is *a lot* of pragmas... +pragma no-coverage-condition CStructField, LValueMemberAccess, RValueMemberAccess; +pragma no-patterson-condition LValueMemberAccess, RValueMemberAccess; +pragma no-bounded-variable-condition LValueMemberAccess, RValueMemberAccess; +trait CStructField {} +enum StructField { StructField(structType) } + +impl LValueMemberAccess, fieldSelector>, memoryRef> where StructField: CStructField, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector>) returns (memoryRef) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return memoryRef(Typedef.abs(ptr)); + } +} + +impl MemorySize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + +impl MemorySize<(a, b)> where a: MemorySize, b: MemorySize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz:word = MemorySize.size(@a); + let b_sz:word = MemorySize.size(@b); + assembly { + a_sz := add(a_sz, b_sz) + } + return a_sz; + } +} + +impl RValueMemberAccess, fieldSelector>, fieldType> where StructField: CStructField, fieldType: MemoryType, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector>) returns (fieldType) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + // BUG: Something wrong here? Complains about ptr not being word... + assembly { + ptr := add(ptr, size) + } + return MemoryType.load(Typedef.abs(ptr)); + } +} + +////// Testing + +// struct S { x:word; y:uint; z:word; } +enum S { S(word, uint, word) } +enum x_sel { x_sel } +enum y_sel { y_sel } +enum z_sel { z_sel } + +impl CStructField, word, ()> {} +impl CStructField, uint, word> {} +// BUG: This next one should really be the following, but that breaks weirdly: +// (I get a patterson condition violation on an invoke instance for g) +// instance StructField(S, z_sel):CStructField(word, (word,uint)) {} +// So instead I use: +impl CStructField, word, word> {} + + +function f() { + let x:memory; + let y:memory; + // x = y + Assign.assign(ref(x), y); + /* + * Idea in the above: to avoid overlapping instances, + * we can desugar a simple identifier referring to a local variable on the lhs of an assignment to ref(x), + * to be able to choose a disjoint assign instance. + * Of course this needs special treatment during code generation, + * on the other hand, stack assignments generally do... + * Actually, even simpler might be just *not* to desugar assignments at all, if the lhs is just an identifier referring to a local variable and just directly take care of it when translating to core. + */ +} + +function g() { + let s:memory = Typedef.abs(0x80); + let y:word = 42; + let z:uint = uint(42); + // s.x = y + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel)), y); + // s.y = 21 + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, y_sel)), z); + // s.z = y; + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, z_sel)), y); + // y = s.x + Assign.assign(ref(y), RValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel))); + // s.z = s.x + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, z_sel)), RValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel))); +} +contract C { + function main() public { + f(); + g(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/reference-encoding-good.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/reference-encoding-good.solc deleted file mode 100644 index c7c7dd10..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/reference-encoding-good.solc +++ /dev/null @@ -1,234 +0,0 @@ - -/////// Construction -forall abs rep . class abs:Typedef(rep) { - function rep(x:abs) -> rep; - function abs(x:rep) -> abs; -} - -instance word:Typedef(word) { - function rep(x:word) -> word { return x; } - function abs(x:word) -> word { return x; } -} - -data uint = uint(word); - -instance uint:Typedef(word) { - function rep(x:uint) -> word { - match x { - | uint(y) => return y; - } - } - function abs(x:word) -> uint { - return uint(x); - } -} - -data memory(a) = memory(word); -data memoryRef(a) = memoryRef(word); -data Proxy(a) = Proxy; - -forall a . instance memory(a):Typedef(word) { - function rep(x:memory(a)) -> word { - match x { - | memory(y) => return y; - } - } - function abs(x:word) -> memory(a) { - return memory(x); - } -} -forall a . instance memoryRef(a):Typedef(word) { - function rep(x:memoryRef(a)) -> word { - match x { - | memoryRef(y) => return y; - } - } - function abs(x:word) -> memoryRef(a) { - return memoryRef(x); - } -} - -forall lhs rhs . class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -data ref(a) = ref(a); - -forall a . instance ref(a):Assign(a) { - function assign(l:ref(a), r:a) -> () { - // builtin "stack store" - return (); - } -} - -forall self . class self:MemoryType { - function load(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -forall self . class self:MemorySize { - function size(x:Proxy(self)) -> word; -} - -instance word:MemoryType { - function load(ptr:word) -> word { - let r:word; - assembly { - r := mload(ptr) - } - return r; - } - function store(ptr:word, value:word) -> () { - assembly { - mstore(ptr, value) - } - } -} - -instance uint:MemoryType { - function load(ptr:word) -> uint { - return Typedef.abs(MemoryType.load(ptr)); - } - function store(ptr:word, value:uint) -> () { - return MemoryType.store(ptr, Typedef.rep(value)); - } -} - -forall a . a : MemoryType => instance memoryRef(a):Assign(a) { - function assign(l:memoryRef(a), y:a) -> () { - MemoryType.store(Typedef.rep(l), y); - } -} - - - -data MemberAccessProxy(a, field) = MemberAccessProxy(a, field); - -forall a field . -function memberAccessD1(x:MemberAccessProxy(a, field)) -> a { - match x { - | MemberAccessProxy(y,z) => return y; - } -} - -forall self memberRefType . class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -forall self memberValueType . class self:RValueMemberAccess(memberValueType) { - function memberAccess(x:self) -> memberValueType; -} - -// This is *a lot* of pragmas... -pragma no-coverage-condition CStructField, LValueMemberAccess, RValueMemberAccess; -pragma no-patterson-condition LValueMemberAccess, RValueMemberAccess; -pragma no-bounded-variable-condition LValueMemberAccess, RValueMemberAccess; -forall self fieldType offsetType . class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - -forall structType fieldSelector fieldType offsetType . StructField(structType, fieldSelector):CStructField(fieldType, offsetType), offsetType:MemorySize => instance MemberAccessProxy(memory(structType), fieldSelector):LValueMemberAccess(memoryRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector)) -> memoryRef(fieldType) { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return memoryRef(Typedef.abs(ptr)); - } -} - -instance ():MemorySize { - function size(x:Proxy(())) -> word { - return 0; - } -} - -instance word:MemorySize { - function size(x:Proxy(word)) -> word { - return 32; - } -} - - -instance uint:MemorySize { - function size(x:Proxy(uint)) -> word { - return 32; - } -} - -forall a b . a:MemorySize, b:MemorySize => instance (a,b):MemorySize { - function size(x:Proxy((a,b))) -> word { - let a_sz:word = MemorySize.size(Proxy:Proxy(a)); - let b_sz:word = MemorySize.size(Proxy:Proxy(b)); - assembly { - a_sz := add(a_sz, b_sz) - } - return a_sz; - } -} - -forall structType fieldSelector fieldType offsetType . StructField(structType, fieldSelector):CStructField(fieldType, offsetType), fieldType:MemoryType, offsetType:MemorySize => instance MemberAccessProxy(memory(structType), fieldSelector):RValueMemberAccess(fieldType) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector)) -> fieldType { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - // BUG: Something wrong here? Complains about ptr not being word... - assembly { - ptr := add(ptr, size) - } - return MemoryType.load(Typedef.abs(ptr)):fieldType; - } -} - -////// Testing - -// struct S { x:word; y:uint; z:word; } -data S = S(word, uint, word); -data x_sel = x_sel; -data y_sel = y_sel; -data z_sel = z_sel; - -instance StructField(S, x_sel):CStructField(word, ()) {} -instance StructField(S, y_sel):CStructField(uint, word) {} -// BUG: This next one should really be the following, but that breaks weirdly: -// (I get a patterson condition violation on an invoke instance for g) -// instance StructField(S, z_sel):CStructField(word, (word,uint)) {} -// So instead I use: -instance StructField(S, z_sel):CStructField(word, word) {} - - -function f() -> () { - let x:memory(word); - let y:memory(word); - // x = y - Assign.assign(ref(x), y); - /* - * Idea in the above: to avoid overlapping instances, - * we can desugar a simple identifier referring to a local variable on the lhs of an assignment to ref(x), - * to be able to choose a disjoint assign instance. - * Of course this needs special treatment during code generation, - * on the other hand, stack assignments generally do... - * Actually, even simpler might be just *not* to desugar assignments at all, if the lhs is just an identifier referring to a local variable and just directly take care of it when translating to core. - */ -} - -function g() -> () { - let s:memory(S) = Typedef.abs(0x80); - let y:word = 42; - let z:uint = uint(42); - // s.x = y - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel)), y); - // s.y = 21 - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, y_sel)), z); - // s.z = y; - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, z_sel)), y); - // y = s.x - Assign.assign(ref(y), RValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel))); - // s.z = s.x - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, z_sel)), RValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel))); -} -contract C { - public function main() -> () { - f(); - g(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/reference-encoding-good1.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/reference-encoding-good1.sol new file mode 100644 index 00000000..8ff5eab1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/reference-encoding-good1.sol @@ -0,0 +1,242 @@ + +/////// Construction +trait Typedef { + function rep(x: abs) returns (rep) ; + function abs(x: rep) returns (abs) ; +} + + +enum uint { uint(word) } + +impl Typedef { + function rep(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + function abs(x: word) returns (uint) { + return uint(x); + } +} + +enum memory { memory(word) } +enum memoryRef { memoryRef(word) } +enum Proxy { Proxy } + +impl Typedef, word> { + function rep(x: memory) returns (word) { + match (x) { +case memory(y) { +return y; +} +} + } + function abs(x: word) returns (memory) { + return memory(x); + } +} +impl Typedef, word> { + function rep(x: memoryRef) returns (word) { + match (x) { +case memoryRef(y) { +return y; +} +} + } + function abs(x: word) returns (memoryRef) { + return memoryRef(x); + } +} + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +enum ref { ref(a) } + +impl Assign, a> { + function assign(l: ref, r: a) { + // builtin "stack store" + return (); + } +} + +trait MemoryType { + function load(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +trait MemorySize { + function size(x: Proxy) returns (word) ; +} + +impl MemoryType { + function load(ptr: word) returns (word) { + let r:word; + assembly { + r := mload(ptr) + } + return r; + } + function store(ptr: word, value: word) { + assembly { + mstore(ptr, value) + } + } +} + +impl MemoryType { + function load(ptr: word) returns (uint) { + return Typedef.abs(MemoryType.load(ptr)); + } + function store(ptr: word, value: uint) { + return MemoryType.store(ptr, Typedef.rep(value)); + } +} + +impl Assign, a> where a: MemoryType { + function assign(l: memoryRef, y: a) { + MemoryType.store(Typedef.rep(l), y); + } +} + +impl Typedef { + function rep(x: word) returns (word) { return x; } + function abs(x: word) returns (word) { return x; } +} + + + +enum MemberAccessProxy { MemberAccessProxy(a, field) } + +function memberAccessD1(x: MemberAccessProxy) returns (a) { + match (x) { +case MemberAccessProxy(y,z) { +return y; +} +} +} + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +trait RValueMemberAccess { + function memberAccess(x: self) returns (memberValueType) ; +} + +// This is *a lot* of pragmas... +pragma no-coverage-condition CStructField, LValueMemberAccess, RValueMemberAccess; +pragma no-patterson-condition LValueMemberAccess, RValueMemberAccess; +pragma no-bounded-variable-condition LValueMemberAccess, RValueMemberAccess; +trait CStructField {} +enum StructField { StructField(structType) } + +impl LValueMemberAccess, fieldSelector>, memoryRef> where StructField: CStructField, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector>) returns (memoryRef) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return memoryRef(Typedef.abs(ptr)); + } +} + +impl MemorySize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + + +impl MemorySize { + function size(x: Proxy) returns (word) { + return 32; + } +} + +impl MemorySize<(a, b)> where a: MemorySize, b: MemorySize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz:word = MemorySize.size(@a); + let b_sz:word = MemorySize.size(@b); + assembly { + a_sz := add(a_sz, b_sz) + } + return a_sz; + } +} + +impl RValueMemberAccess, fieldSelector>, fieldType> where StructField: CStructField, fieldType: MemoryType, offsetType: MemorySize { + function memberAccess(x: MemberAccessProxy, fieldSelector>) returns (fieldType) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = MemorySize.size(@offsetType); + // BUG: Something wrong here? Complains about ptr not being word... + assembly { + ptr := add(ptr, size) + } + return MemoryType.load(Typedef.abs(ptr)); + } +} + +////// Testing + +// struct S { x:word; y:uint; z:word; } +enum S { S(word, uint, word) } +enum x_sel { x_sel } +enum y_sel { y_sel } +enum z_sel { z_sel } + +impl CStructField, word, ()> {} +impl CStructField, uint, word> {} +// BUG: This next one should really be the following, but that breaks weirdly: +// (I get a patterson condition violation on an invoke instance for g) +// instance StructField(S, z_sel):CStructField(word, (word,uint)) {} +// So instead I use: +impl CStructField, word, word> {} + + +function f() { + let x:memory; + let y:memory; + // x = y + Assign.assign(ref(x), y); + /* + * Idea in the above: to avoid overlapping instances, + * we can desugar a simple identifier referring to a local variable on the lhs of an assignment to ref(x), + * to be able to choose a disjoint assign instance. + * Of course this needs special treatment during code generation, + * on the other hand, stack assignments generally do... + * Actually, even simpler might be just *not* to desugar assignments at all, if the lhs is just an identifier referring to a local variable and just directly take care of it when translating to core. + */ +} + +function g() { + let s:memory = Typedef.abs(0x80); + let y:word = 42; + let z:uint = uint(42); + // s.x = y + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel)), y); + // s.y = 21 + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, y_sel)), z); + // s.z = y; + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, z_sel)), y); + // y = s.x + Assign.assign(ref(y), RValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel))); + // s.z = s.x + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, z_sel)), RValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel))); +} +contract C { + function main() public { + f(); + g(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/reference-encoding-good1.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/reference-encoding-good1.solc deleted file mode 100644 index 42f1d4af..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/reference-encoding-good1.solc +++ /dev/null @@ -1,235 +0,0 @@ - -/////// Construction -forall abs rep . class abs:Typedef(rep) { - function rep(x:abs) -> rep; - function abs(x:rep) -> abs; -} - - -data uint = uint(word); - -instance uint:Typedef(word) { - function rep(x:uint) -> word { - match x { - | uint(y) => return y; - } - } - function abs(x:word) -> uint { - return uint(x); - } -} - -data memory(a) = memory(word); -data memoryRef(a) = memoryRef(word); -data Proxy(a) = Proxy; - -forall a . instance memory(a):Typedef(word) { - function rep(x:memory(a)) -> word { - match x { - | memory(y) => return y; - } - } - function abs(x:word) -> memory(a) { - return memory(x); - } -} -forall a . instance memoryRef(a):Typedef(word) { - function rep(x:memoryRef(a)) -> word { - match x { - | memoryRef(y) => return y; - } - } - function abs(x:word) -> memoryRef(a) { - return memoryRef(x); - } -} - -forall lhs rhs . class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -data ref(a) = ref(a); - -forall a . instance ref(a):Assign(a) { - function assign(l:ref(a), r:a) -> () { - // builtin "stack store" - return (); - } -} - -forall self . class self:MemoryType { - function load(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -forall self . class self:MemorySize { - function size(x:Proxy(self)) -> word; -} - -instance word:MemoryType { - function load(ptr:word) -> word { - let r:word; - assembly { - r := mload(ptr) - } - return r; - } - function store(ptr:word, value:word) -> () { - assembly { - mstore(ptr, value) - } - } -} - -instance uint:MemoryType { - function load(ptr:word) -> uint { - return Typedef.abs(MemoryType.load(ptr)); - } - function store(ptr:word, value:uint) -> () { - return MemoryType.store(ptr, Typedef.rep(value)); - } -} - -forall a . a : MemoryType => instance memoryRef(a):Assign(a) { - function assign(l:memoryRef(a), y:a) -> () { - MemoryType.store(Typedef.rep(l), y); - } -} - -instance word:Typedef(word) { - function rep(x:word) -> word { return x; } - function abs(x:word) -> word { return x; } -} - - - -data MemberAccessProxy(a, field) = MemberAccessProxy(a, field); - -forall a field . -function memberAccessD1(x:MemberAccessProxy(a, field)) -> a { - match x { - | MemberAccessProxy(y,z) => return y; - } -} - -forall self memberRefType . class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -forall self memberValueType . class self:RValueMemberAccess(memberValueType) { - function memberAccess(x:self) -> memberValueType; -} - -// This is *a lot* of pragmas... -pragma no-coverage-condition CStructField, LValueMemberAccess, RValueMemberAccess; -pragma no-patterson-condition LValueMemberAccess, RValueMemberAccess; -pragma no-bounded-variable-condition LValueMemberAccess, RValueMemberAccess; -forall self fieldType offsetType . class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - -forall structType fieldSelector fieldType offsetType . StructField(structType, fieldSelector):CStructField(fieldType, offsetType), offsetType:MemorySize => instance MemberAccessProxy(memory(structType), fieldSelector):LValueMemberAccess(memoryRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector)) -> memoryRef(fieldType) { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return memoryRef(Typedef.abs(ptr)); - } -} - -instance ():MemorySize { - function size(x:Proxy(())) -> word { - return 0; - } -} - -instance word:MemorySize { - function size(x:Proxy(word)) -> word { - return 32; - } -} - - -instance uint:MemorySize { - function size(x:Proxy(uint)) -> word { - return 32; - } -} - -forall a b . a:MemorySize, b:MemorySize => instance (a,b):MemorySize { - function size(x:Proxy((a,b))) -> word { - let a_sz:word = MemorySize.size(Proxy:Proxy(a)); - let b_sz:word = MemorySize.size(Proxy:Proxy(b)); - assembly { - a_sz := add(a_sz, b_sz) - } - return a_sz; - } -} - -forall structType fieldSelector fieldType offsetType . StructField(structType, fieldSelector):CStructField(fieldType, offsetType), fieldType:MemoryType, offsetType:MemorySize => instance MemberAccessProxy(memory(structType), fieldSelector):RValueMemberAccess(fieldType) { - function memberAccess(x:MemberAccessProxy(memory(structType), fieldSelector)) -> fieldType { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = MemorySize.size(Proxy:Proxy(offsetType)); - // BUG: Something wrong here? Complains about ptr not being word... - assembly { - ptr := add(ptr, size) - } - return MemoryType.load(Typedef.abs(ptr)):fieldType; - } -} - -////// Testing - -// struct S { x:word; y:uint; z:word; } -data S = S(word, uint, word); -data x_sel = x_sel; -data y_sel = y_sel; -data z_sel = z_sel; - -instance StructField(S, x_sel):CStructField(word, ()) {} -instance StructField(S, y_sel):CStructField(uint, word) {} -// BUG: This next one should really be the following, but that breaks weirdly: -// (I get a patterson condition violation on an invoke instance for g) -// instance StructField(S, z_sel):CStructField(word, (word,uint)) {} -// So instead I use: -instance StructField(S, z_sel):CStructField(word, word) {} - - -function f() -> () { - let x:memory(word); - let y:memory(word); - // x = y - Assign.assign(ref(x), y); - /* - * Idea in the above: to avoid overlapping instances, - * we can desugar a simple identifier referring to a local variable on the lhs of an assignment to ref(x), - * to be able to choose a disjoint assign instance. - * Of course this needs special treatment during code generation, - * on the other hand, stack assignments generally do... - * Actually, even simpler might be just *not* to desugar assignments at all, if the lhs is just an identifier referring to a local variable and just directly take care of it when translating to core. - */ -} - -function g() -> () { - let s:memory(S) = Typedef.abs(0x80); - let y:word = 42; - let z:uint = uint(42); - // s.x = y - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel)), y); - // s.y = 21 - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, y_sel)), z); - // s.z = y; - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, z_sel)), y); - // y = s.x - Assign.assign(ref(y), RValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel))); - // s.z = s.x - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(s, z_sel)), RValueMemberAccess.memberAccess(MemberAccessProxy(s, x_sel))); -} -contract C { - public function main() -> () { - f(); - g(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-adder.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-adder.sol new file mode 100644 index 00000000..9b3b7cf7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-adder.sol @@ -0,0 +1,20 @@ +// Returns a function with CORRECT type annotations. +// Validates the single-pass type checker: closure conversion must not hide +// that the returned lambda really has type (word) -> word. +// Uses an assembly block instead of primAddWord so it lowers end-to-end. +function makeAdder(x: word) returns (function(word) returns (word)) { + return lam (y : word) -> word { + let res : word; + assembly { + res := add(x, y) + } + return res; + }; +} + +contract C { + function main() public returns (word) { + let f = makeAdder(10); + return f(5); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-adder.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-adder.solc deleted file mode 100644 index 552fda13..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-adder.solc +++ /dev/null @@ -1,20 +0,0 @@ -// Returns a function with CORRECT type annotations. -// Validates the single-pass type checker: closure conversion must not hide -// that the returned lambda really has type (word) -> word. -// Uses an assembly block instead of primAddWord so it lowers end-to-end. -function makeAdder(x : word) -> ((word) -> word) { - return lam (y : word) -> word { - let res : word; - assembly { - res := add(x, y) - } - return res; - }; -} - -contract C { - public function main() -> word { - let f = makeAdder(10); - return f(5); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-const.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-const.sol new file mode 100644 index 00000000..b77bd7df --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-const.sol @@ -0,0 +1,7 @@ +// Returns a constant function that closes over its argument. +// Correct annotations: (word) -> word, body returns the captured word. +function constFn(x: word) returns (function(word) returns (word)) { + return lam (y : word) -> word { + return x; + }; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-const.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-const.solc deleted file mode 100644 index b2709271..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-const.solc +++ /dev/null @@ -1,7 +0,0 @@ -// Returns a constant function that closes over its argument. -// Correct annotations: (word) -> word, body returns the captured word. -function constFn(x : word) -> ((word) -> word) { - return lam (y : word) -> word { - return x; - }; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-eq.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-eq.sol new file mode 100644 index 00000000..85892d3e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-eq.sol @@ -0,0 +1,18 @@ +// Returns a function comparing against a captured word, CORRECT annotations. +// Uses an assembly `eq` instead of primEqWord so it lowers end-to-end. +function makeEq(x: word) returns (function(word) returns (word)) { + return lam (y : word) -> word { + let res : word; + assembly { + res := eq(x, y) + } + return res; + }; +} + +contract C { + function main() public returns (word) { + let f = makeEq(7); + return f(7); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-eq.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-eq.solc deleted file mode 100644 index 6f148fc8..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-eq.solc +++ /dev/null @@ -1,18 +0,0 @@ -// Returns a function comparing against a captured word, CORRECT annotations. -// Uses an assembly `eq` instead of primEqWord so it lowers end-to-end. -function makeEq(x : word) -> ((word) -> word) { - return lam (y : word) -> word { - let res : word; - assembly { - res := eq(x, y) - } - return res; - }; -} - -contract C { - public function main() -> word { - let f = makeEq(7); - return f(7); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-instance.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-instance.sol new file mode 100644 index 00000000..88699204 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-instance.sol @@ -0,0 +1,13 @@ +// Instance member returning a function with CORRECT annotations. +// The compiled-away validation pass used to check this; the single pass must too. +trait CtFun { + function ct(x: t) returns (function(t) returns (t)) ; +} + +impl CtFun { + function ct(x: word) returns (function(word) returns (word)) { + return lam (y : word) -> word { + return x; + }; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-instance.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-instance.solc deleted file mode 100644 index 067da6c6..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/return-fun-instance.solc +++ /dev/null @@ -1,13 +0,0 @@ -// Instance member returning a function with CORRECT annotations. -// The compiled-away validation pass used to check this; the single pass must too. -forall t . class t:CtFun { - function ct(x : t) -> ((t) -> t); -} - -instance word:CtFun { - function ct(x : word) -> ((word) -> word) { - return lam (y : word) -> word { - return x; - }; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/same-name-constructor-qualifier.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/same-name-constructor-qualifier.sol new file mode 100644 index 00000000..53613291 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/same-name-constructor-qualifier.sol @@ -0,0 +1,29 @@ +// Qualifier access (T.C) must work even when T has a same-name constructor. +// Regression test for: `Error.Empty` reporting "Unqualified constructor: Empty". +enum Err { Err(word), Empty, Msg(word) } + +function pickEmpty() returns (Err) { + return Err.Empty; +} + +function pickMsg(x: word) returns (Err) { + return Err.Msg(x); +} + +function pickErr(x: word) returns (Err) { + return Err.Err(x); +} + +function main() returns (word) { + match (pickEmpty()) { +case Err.Empty { +return 1; +} +case Err.Err(_) { +return 2; +} +case Err.Msg(_) { +return 3; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/same-name-constructor-qualifier.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/same-name-constructor-qualifier.solc deleted file mode 100644 index 6850b084..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/same-name-constructor-qualifier.solc +++ /dev/null @@ -1,23 +0,0 @@ -// Qualifier access (T.C) must work even when T has a same-name constructor. -// Regression test for: `Error.Empty` reporting "Unqualified constructor: Empty". -data Err = Err(word) | Empty | Msg(word); - -function pickEmpty() -> Err { - return Err.Empty; -} - -function pickMsg(x: word) -> Err { - return Err.Msg(x); -} - -function pickErr(x: word) -> Err { - return Err.Err(x); -} - -function main() -> word { - match pickEmpty() { - | Err.Empty => return 1; - | Err.Err(_) => return 2; - | Err.Msg(_) => return 3; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/simpleDiscount.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/simpleDiscount.sol new file mode 100644 index 00000000..fba9820f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/simpleDiscount.sol @@ -0,0 +1,28 @@ +// test complex match example from the blog post +// simplified to use word instead of uint256 + +import {address, Num, Add, Sub, Div, Bounded, Eq, Ord, Typedef} from std; + +enum AuctionState { NotStarted(word), Active(word, address), Ended(word, address), Cancelled(word, address) } + +enum Phase { Early, Late } + +function discount(state: AuctionState, phase: Phase) returns (word) { + match (state, phase) { +case (.Active(bid, _), .Early) { +return bid / 10; +} +case (.Active(bid, _), .Late) { +return bid / 20; +} +default { +return 0; +} +} +} + +contract Discount { + function main() public returns (word) { + discount(.Active(420,.address(0)), .Early) + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/simpleDiscount.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/simpleDiscount.solc deleted file mode 100644 index ebafe1b0..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/simpleDiscount.solc +++ /dev/null @@ -1,26 +0,0 @@ -// test complex match example from the blog post -// simplified to use word instead of uint256 - -import std.{address, Num, Add, Sub, Div, Bounded, Eq, Ord, Typedef}; - -data AuctionState = - NotStarted(word) - | Active(word, address) - | Ended(word, address) - | Cancelled(word, address); - -data Phase = Early | Late; - -function discount(state : AuctionState, phase : Phase) -> word { - match state, phase { - | .Active(bid, _), .Early => return bid / 10; - | .Active(bid, _), .Late => return bid / 20; - | _, _ => return 0; - } -} - -contract Discount { - public function main() -> word { - discount(.Active(420,.address(0)), .Early) - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/simpleid.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/simpleid.sol new file mode 100644 index 00000000..f344c110 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/simpleid.sol @@ -0,0 +1,3 @@ +function id(x: a) returns (a) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/simpleid.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/simpleid.solc deleted file mode 100644 index a85da975..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/simpleid.solc +++ /dev/null @@ -1,3 +0,0 @@ -forall a . function id(x : a) -> a { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/single-lambda.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/single-lambda.sol new file mode 100644 index 00000000..b9397c2a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/single-lambda.sol @@ -0,0 +1,3 @@ +function foo() returns (function(word) returns (bool)) { + return lam (x:word) -> bool { return true; }; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/single-lambda.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/single-lambda.solc deleted file mode 100644 index 7c6a1729..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/single-lambda.solc +++ /dev/null @@ -1,3 +0,0 @@ -function foo () -> (word) -> bool { - return lam (x:word) -> bool { return true; }; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/snds.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/snds.sol new file mode 100644 index 00000000..e9d9c050 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/snds.sol @@ -0,0 +1,9 @@ + function snds(p1: (word, word), p2: (word, word)) returns (word, word) { + match (p1, p2) { +case ((a,b) , (c,d)) { +return (b,d); +} +} + } + + diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/snds.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/snds.solc deleted file mode 100644 index 44b7bdb1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/snds.solc +++ /dev/null @@ -1,7 +0,0 @@ - function snds (p1 : (word, word), p2 : (word, word)) -> (word, word) { - match p1, p2 { - | (a,b) , (c,d) => return (b,d); - } - } - - diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/spec-fail-ungrounded.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/spec-fail-ungrounded.sol new file mode 100644 index 00000000..8a168910 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/spec-fail-ungrounded.sol @@ -0,0 +1,27 @@ +// Specialiser rejects this program even though the type checker accepts it. +// +// abort_ : word -> a has a polymorphic return type (it diverges). +// sink_ : b -> word accepts any argument and discards it. +// +// At the call sink_(abort_(0)) the intermediate type 'a' (= 'b') is never +// pinned to a concrete type: +// - The type checker is satisfied because a type 'a' EXISTS that makes the +// program consistent (any type works); the overall expression has type word. +// - The specialiser needs a CONCRETE 'a' to emit code for abort_. It finds +// no constraint, no instance, and no return-type context to fix 'a', so +// ensureClosed reports a free type variable and aborts. + +function abort_(x: word) returns (a) { + return abort_(x); +} + +function sink_(y: b) returns (word) { + return 0; +} + +contract C { + constructor() {} + function main() public returns (word) { + return sink_(abort_(0)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/spec-fail-ungrounded.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/spec-fail-ungrounded.solc deleted file mode 100644 index dde3745a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/spec-fail-ungrounded.solc +++ /dev/null @@ -1,29 +0,0 @@ -// Specialiser rejects this program even though the type checker accepts it. -// -// abort_ : word -> a has a polymorphic return type (it diverges). -// sink_ : b -> word accepts any argument and discards it. -// -// At the call sink_(abort_(0)) the intermediate type 'a' (= 'b') is never -// pinned to a concrete type: -// - The type checker is satisfied because a type 'a' EXISTS that makes the -// program consistent (any type works); the overall expression has type word. -// - The specialiser needs a CONCRETE 'a' to emit code for abort_. It finds -// no constraint, no instance, and no return-type context to fix 'a', so -// ensureClosed reports a free type variable and aborts. - -forall a. -function abort_(x:word) -> a { - return abort_(x); -} - -forall b. -function sink_(y:b) -> word { - return 0; -} - -contract C { - constructor() {} - public function main() -> word { - return sink_(abort_(0)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/storage-adt-recursive-fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/storage-adt-recursive-fail.sol new file mode 100644 index 00000000..0895dd9a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/storage-adt-recursive-fail.sol @@ -0,0 +1,22 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.StorageGeneric; + +// A recursive data type has no bounded slot footprint, so DeriveGeneric +// (isRecursiveData) deliberately skips deriving StorageSize and +// storage(T):CanStore(T) for it. Using one as a contract field must therefore +// fail: the field's CanStore obligation has no instance. +// +// The failure surfaces at the use site (the field assignment), not at +// derivation time, which is the design stated in DeriveGeneric. + +enum IntList { Nil, Cons(uint256, IntList) } + +contract C { + xs : IntList; + + constructor() { + xs = IntList.Nil; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/storage-adt-recursive-fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/storage-adt-recursive-fail.solc deleted file mode 100644 index d7739a57..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/storage-adt-recursive-fail.solc +++ /dev/null @@ -1,22 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -// A recursive data type has no bounded slot footprint, so DeriveGeneric -// (isRecursiveData) deliberately skips deriving StorageSize and -// storage(T):CanStore(T) for it. Using one as a contract field must therefore -// fail: the field's CanStore obligation has no instance. -// -// The failure surfaces at the use site (the field assignment), not at -// derivation time, which is the design stated in DeriveGeneric. - -data IntList = Nil | Cons(uint256, IntList); - -contract C { - xs : IntList; - - constructor() { - xs = IntList.Nil; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/storage-adt-recursive-ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/storage-adt-recursive-ok.sol new file mode 100644 index 00000000..cacf5e80 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/storage-adt-recursive-ok.sol @@ -0,0 +1,34 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.StorageGeneric; + +// The counterpart of storage-adt-recursive-fail.sol: skipping storage +// derivation for a recursive type is a SKIP, not a hard error. The type still +// gets its Generic instance and remains usable everywhere except storage. + +enum IntList { Nil, Cons(uint256, IntList) } + +function len(xs: IntList) returns (uint256) { + match (xs) { +case IntList.Nil { +return uint256(0); +} +case IntList.Cons(_, r) { +return uint256(1) + len(r); +} +} +} + +// A non-recursive neighbour in the same module still gets its storage +// instances, so the skip is per-type rather than per-module. +enum Point { Point(uint256, uint256) } + +contract C { + p : Point; + + constructor() { + p = Point(uint256(1), uint256(2)); + assert(StorageSize.size(@Point) == 2); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/storage-adt-recursive-ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/storage-adt-recursive-ok.solc deleted file mode 100644 index e28974fc..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/storage-adt-recursive-ok.solc +++ /dev/null @@ -1,30 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -// The counterpart of storage-adt-recursive-fail.solc: skipping storage -// derivation for a recursive type is a SKIP, not a hard error. The type still -// gets its Generic instance and remains usable everywhere except storage. - -data IntList = Nil | Cons(uint256, IntList); - -function len(xs : IntList) -> uint256 { - match xs { - | IntList.Nil => return uint256(0); - | IntList.Cons(_, r) => return uint256(1) + len(r); - } -} - -// A non-recursive neighbour in the same module still gets its storage -// instances, so the skip is per-type rather than per-module. -data Point = Point(uint256, uint256); - -contract C { - p : Point; - - constructor() { - p = Point(uint256(1), uint256(2)); - assert(StorageSize.size(Proxy : Proxy(Point)) == 2); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/strange-unbound.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/strange-unbound.sol new file mode 100644 index 00000000..2fa817cb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/strange-unbound.sol @@ -0,0 +1,3 @@ +trait IsA { + function ais(p: (a, b)) returns (a) ; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/strange-unbound.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/strange-unbound.solc deleted file mode 100644 index 230f6ae5..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/strange-unbound.solc +++ /dev/null @@ -1,5 +0,0 @@ -forall b. -class b:IsA { - forall a. - function ais(p : (a,b)) -> a; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/sum-match-default.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/sum-match-default.sol new file mode 100644 index 00000000..ded6f663 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/sum-match-default.sol @@ -0,0 +1,19 @@ +contract SumMatchDefault { + enum Option { None, Some(a) } + + function g(s: Option) public returns (Option) { + match (s) { +case Option.None { +return Option.None; +} +case x { +return x; +} +} + } + + function main() public returns (word) { + g(Option.None); + return 42; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/sum-match-default.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/sum-match-default.solc deleted file mode 100644 index fb90bf70..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/sum-match-default.solc +++ /dev/null @@ -1,15 +0,0 @@ -contract SumMatchDefault { - data Option(a) = None | Some(a); - - public function g(s : Option(word)) -> Option(word) { - match s { - | Option.None => return Option.None; - | x => return x; - } - } - - public function main() -> word { - g(Option.None); - return 42; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class-cycle.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class-cycle.sol new file mode 100644 index 00000000..c21df6ca --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class-cycle.sol @@ -0,0 +1,14 @@ +trait A where a: B {} +trait B where a: A {} + +function needsB(x: a) where a: B { + return (); +} + +function usesSuperCycle(x: a) where a: A { + return needsB(x); +} + +function main() { + return (); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class-cycle.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class-cycle.solc deleted file mode 100644 index 04a42f71..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class-cycle.solc +++ /dev/null @@ -1,14 +0,0 @@ -forall a . a:B => class a:A {} -forall a . a:A => class a:B {} - -forall a . a:B => function needsB(x:a) -> () { - return (); -} - -forall a . a:A => function usesSuperCycle(x:a) -> () { - return needsB(x); -} - -function main() -> () { - return (); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class-num.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class-num.sol new file mode 100644 index 00000000..fcc615bb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class-num.sol @@ -0,0 +1,81 @@ +enum Bool { False, True } + +function fromBool(b: Bool) returns (word) { + match (b) { +case Bool.False { +return 0; +} +case Bool.True { +return 1; +} +} +} + +function toBool(x: word) returns (Bool) { + match (x) { +case 0 { +return Bool.False; +} +default { +return Bool.True; +} +} +} + +trait Eq { + function eq(x: a, y: a) returns (Bool) ; +} + +impl Eq { + function eq(x: word, y: word) returns (Bool) { + let res : word; + assembly { + res := eq(x, y) + } + return toBool(res); + } +} + +function not(b: Bool) returns (Bool) { + match (b) { +case Bool.True { +return Bool.False ; +} +case Bool.False { +return Bool.True ; +} +} +} + +function ne(x: a, y: a) returns (Bool) where a: Eq { + return not(Eq.eq(x,y)); +} + +trait Num where a: Eq { + function toWord(x: a) returns (word) ; + function fromWord(x: word) returns (a) ; +} + +impl Num { + function toWord(x: word) returns (word) { return x; } + function fromWord(x: word) returns (word) { return x; } +} + + +enum uint { uint(word) } + +impl Eq { + function eq(x: uint, y: uint) returns (Bool) { return Eq.eq(Num.toWord(x), Num.toWord(y)); } +} + + +impl Num { + function toWord(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + function fromWord(x: word) returns (uint) { return uint(x); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class-num.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class-num.solc deleted file mode 100644 index 920a0b45..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class-num.solc +++ /dev/null @@ -1,70 +0,0 @@ -data Bool = False | True; - -function fromBool(b:Bool) -> word { - match b { - | Bool.False => return 0; - | Bool.True => return 1; - } -} - -function toBool(x: word) -> Bool { - match x { - | 0 => return Bool.False; - | _ => return Bool.True; - } -} - -forall a. -class a:Eq { - function eq(x:a, y:a) -> Bool; -} - -instance word:Eq { - function eq(x:word, y:word) -> Bool { - let res : word; - assembly { - res := eq(x, y) - } - return toBool(res); - } -} - -function not (b : Bool) -> Bool { - match b { - | Bool.True => return Bool.False ; - | Bool.False => return Bool.True ; - } -} - -forall a . a:Eq => function ne(x : a, y : a) -> Bool { - return not(Eq.eq(x,y)); -} - -forall a. a:Eq => -class a:Num { - function toWord(x:a) -> word; - function fromWord(x:word) -> a; -} - -instance word:Num { - function toWord(x:word) -> word { return x; } - function fromWord(x:word) -> word { return x; } -} - - -data uint = uint(word); - -instance uint:Eq { - function eq(x:uint, y:uint) -> Bool { return Eq.eq(Num.toWord(x), Num.toWord(y)); } -} - - -instance uint:Num { - function toWord(x:uint) -> word - { - match x { - | uint(y) => return y; - } - } - function fromWord(x:word) -> uint { return uint(x); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class.sol new file mode 100644 index 00000000..6189e9b1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class.sol @@ -0,0 +1,53 @@ +enum List { Nil, Cons(a, List) } +enum Bool { False, True } + +function and(x: Bool, y: Bool) returns (Bool) { + match (x,y) { +case (Bool.False, _) { +return Bool.False; +} +case (Bool.True, y) { +return y; +} +} +} + +trait Eq { + function eq(x: a, y: a) returns (Bool) ; +} + +impl Eq { + function eq(x: Bool, y: Bool) returns (Bool) { + match (x, y) { +case (Bool.False, Bool.False) { +return Bool.True; +} +case (Bool.True, Bool.True) { +return Bool.True; +} +default { +return Bool.False; +} +} + } +} + +impl Eq<(List)> where a: Eq { + function eq(xs: List, ys: List) returns (Bool) { + match (xs, ys) { +case (List.Nil, List.Nil) { +return Bool.True; +} +case (List.Cons(x,xs), List.Cons(y,ys)) { +return and(Eq.eq(x,y),Eq.eq(xs,ys)); +} +default { +return Bool.False; +} +} + } +} + +function foo() { + let x = Eq.eq(List.Cons(Bool.True,List.Nil), List.Nil); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class.solc deleted file mode 100644 index e413219a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/super-class.solc +++ /dev/null @@ -1,38 +0,0 @@ -data List(a) = Nil | Cons(a,List(a)); -data Bool = False | True; - -function and (x : Bool, y : Bool) -> Bool { - match x,y { - | Bool.False, _ => return Bool.False; - | Bool.True, y => return y; - } -} - -forall a . class a : Eq { - function eq(x : a, y : a) -> Bool; -} - -instance Bool : Eq { - function eq (x : Bool, y : Bool) -> Bool { - match x, y { - | Bool.False, Bool.False => return Bool.True; - | Bool.True, Bool.True => return Bool.True; - | _, _ => return Bool.False; - } - } -} - -forall a . a : Eq => instance (List(a)) : Eq { - function eq (xs : List(a), ys : List(a)) -> Bool { - match xs, ys { - | List.Nil, List.Nil => return Bool.True; - | List.Cons(x,xs), List.Cons(y,ys) => - return and(Eq.eq(x,y),Eq.eq(xs,ys)); - | _ , _ => return Bool.False; - } - } -} - -function foo() -> () { - let x = Eq.eq(List.Cons(Bool.True,List.Nil), List.Nil); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-basic.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-basic.sol new file mode 100644 index 00000000..5ed70679 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-basic.sol @@ -0,0 +1,23 @@ +type Uint = word; +type Point = pair; + +function useUint(x: Uint) returns (word) { + return x; +} + +function makePoint(x: word, y: word) returns (Point) { + return pair(x, y); +} + +function getX(p: Point) returns (word) { + match (p) { +case pair(x, _) { +return x; +} +} +} + +function main() returns (word) { + let p: Point = makePoint(10, 20); + return getX(p); +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-basic.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-basic.solc deleted file mode 100644 index 2f521980..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-basic.solc +++ /dev/null @@ -1,21 +0,0 @@ -type Uint = word; -type Point = pair(word, word); - -function useUint(x: Uint) -> word { - return x; -} - -function makePoint(x: word, y: word) -> Point { - return pair(x, y); -} - -function getX(p: Point) -> word { - match p { - | pair(x, _) => return x; - } -} - -function main() -> word { - let p: Point = makePoint(10, 20); - return getX(p); -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-in-function.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-in-function.sol new file mode 100644 index 00000000..c776227c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-in-function.sol @@ -0,0 +1,34 @@ +// Synonyms in function parameter and return types +type Int = word; +type Point = pair; + +function add(a: Int, b: Int) returns (Int) { + return a; +} + +function makePoint(x: Int, y: Int) returns (Point) { + return pair(x, y); +} + +function getX(p: Point) returns (Int) { + match (p) { +case pair(x, _) { +return x; +} +} +} + +function getY(p: Point) returns (Int) { + match (p) { +case pair(_, y) { +return y; +} +} +} + +function main() returns (word) { + let a: Int = 10; + let b: Int = 20; + let p: Point = makePoint(a, b); + return getX(p); +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-in-function.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-in-function.solc deleted file mode 100644 index a71676b0..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-in-function.solc +++ /dev/null @@ -1,30 +0,0 @@ -// Synonyms in function parameter and return types -type Int = word; -type Point = pair(Int, Int); - -function add(a: Int, b: Int) -> Int { - return a; -} - -function makePoint(x: Int, y: Int) -> Point { - return pair(x, y); -} - -function getX(p: Point) -> Int { - match p { - | pair(x, _) => return x; - } -} - -function getY(p: Point) -> Int { - match p { - | pair(_, y) => return y; - } -} - -function main() -> word { - let a: Int = 10; - let b: Int = 20; - let p: Point = makePoint(a, b); - return getX(p); -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-nested.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-nested.sol new file mode 100644 index 00000000..fed660be --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-nested.sol @@ -0,0 +1,26 @@ +// Deeply nested synonyms (synonym of synonym of synonym) +type Word1 = word; +type Word2 = Word1; +type Word3 = Word2; + +type Pair1 = pair; +type Pair2 = Pair1; +type Pair3 = Pair2; + +function useWord3(x: Word3) returns (word) { + return x; +} + +function usePair3(p: Pair3) returns (word) { + match (p) { +case pair(x, _) { +return x; +} +} +} + +function main() returns (word) { + let x: Word3 = 42; + let p: Pair3 = pair(1, 2); + return useWord3(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-nested.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-nested.solc deleted file mode 100644 index 912cc705..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-nested.solc +++ /dev/null @@ -1,24 +0,0 @@ -// Deeply nested synonyms (synonym of synonym of synonym) -type Word1 = word; -type Word2 = Word1; -type Word3 = Word2; - -type Pair1 = pair(word, word); -type Pair2 = Pair1; -type Pair3 = Pair2; - -function useWord3(x: Word3) -> word { - return x; -} - -function usePair3(p: Pair3) -> word { - match p { - | pair(x, _) => return x; - } -} - -function main() -> word { - let x: Word3 = 42; - let p: Pair3 = pair(1, 2); - return useWord3(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-param.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-param.sol new file mode 100644 index 00000000..0d27f46e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-param.sol @@ -0,0 +1,15 @@ +type MyPair(a, b) = pair; +type IntPair = MyPair; + +function makePair(x: word, y: word) returns (MyPair) { + return pair(x, y); +} + +function main() returns (word) { + let p: IntPair = makePair(42, 100); + match (p) { +case pair(x, _) { +return x; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-param.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-param.solc deleted file mode 100644 index 1ed3f566..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/synonym-param.solc +++ /dev/null @@ -1,13 +0,0 @@ -type MyPair(a, b) = pair(a, b); -type IntPair = MyPair(word, word); - -function makePair(x: word, y: word) -> MyPair(word, word) { - return pair(x, y); -} - -function main() -> word { - let p: IntPair = makePair(42, 100); - match p { - | pair(x, _) => return x; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-default-instance.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-default-instance.sol new file mode 100644 index 00000000..c2b8dedd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-default-instance.sol @@ -0,0 +1,14 @@ +trait Fallback { + function tag(x: a) returns (word) ; +} + +default impl Fallback { + function tag(x: a) returns (word) { + return 7; + } +} + +function main() returns (word) { + let value: word = 0; + return Fallback.tag(value); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-default-instance.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-default-instance.solc deleted file mode 100644 index 8bc39371..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-default-instance.solc +++ /dev/null @@ -1,13 +0,0 @@ -forall a . class a:Fallback { - function tag(x:a) -> word; -} - -forall a . default instance a:Fallback { - function tag(x:a) -> word { - return 7; - } -} - -function main() -> word { - return Fallback.tag(0:word); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-given-order.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-given-order.sol new file mode 100644 index 00000000..4d688c8f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-given-order.sol @@ -0,0 +1,23 @@ +pragma no-patterson-condition C; + +trait A {} +trait B {} +trait C {} + +impl C where a: A, a: B {} + +function needsC(x: a) where a: C { + return (); +} + +function fromAB(x: a) where a: A, a: B { + return needsC(x); +} + +function fromBA(x: a) where a: B, a: A { + return needsC(x); +} + +function main() { + return (); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-given-order.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-given-order.solc deleted file mode 100644 index 689dee14..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-given-order.solc +++ /dev/null @@ -1,23 +0,0 @@ -pragma no-patterson-condition C; - -forall a . class a:A {} -forall a . class a:B {} -forall a . class a:C {} - -forall a . a:A, a:B => instance a:C {} - -forall a . a:C => function needsC(x:a) -> () { - return (); -} - -forall a . a:A, a:B => function fromAB(x:a) -> () { - return needsC(x); -} - -forall a . a:B, a:A => function fromBA(x:a) -> () { - return needsC(x); -} - -function main() -> () { - return (); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-residual-given.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-residual-given.sol new file mode 100644 index 00000000..02beed75 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-residual-given.sol @@ -0,0 +1,18 @@ +pragma no-patterson-condition Wanted; + +trait Known {} +trait Wanted {} + +impl Wanted where a: Known {} + +function needsWanted(x: a) where a: Wanted { + return (); +} + +function passKnown(x: a) where a: Known { + return needsWanted(x); +} + +function main() { + return (); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-residual-given.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-residual-given.solc deleted file mode 100644 index 29daa886..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tabled-residual-given.solc +++ /dev/null @@ -1,18 +0,0 @@ -pragma no-patterson-condition Wanted; - -forall a . class a:Known {} -forall a . class a:Wanted {} - -forall a . a:Known => instance a:Wanted {} - -forall a . a:Wanted => function needsWanted(x:a) -> () { - return (); -} - -forall a . a:Known => function passKnown(x:a) -> () { - return needsWanted(x); -} - -function main() -> () { - return (); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/td.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/td.sol new file mode 100644 index 00000000..f456e1fa --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/td.sol @@ -0,0 +1,17 @@ +trait Typedef { + function abs(x: rep) returns (abs) ; + function rep(x: abs) returns (rep) ; +} + +impl Typedef { + function abs(x: t) returns (t) { return x; } + function rep(x: t) returns (t) { return x; } +} + +function lift1ac(f: function(rep) returns (res), x: abs) returns (res) where abs: Typedef { f(Typedef.rep(x)) } + + +function id(x: a) returns (a) {x} +contract TD { + function main() public returns (word) { lift1ac(id, 42) } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/td.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/td.solc deleted file mode 100644 index 8b922c9d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/td.solc +++ /dev/null @@ -1,19 +0,0 @@ -forall abs rep . class abs:Typedef(rep) { - function abs(x:rep) -> abs; - function rep(x:abs) -> rep; -} - -forall t. -/* default */ instance t:Typedef(t) { - function abs(x:t) -> t { return x; } - function rep(x:t) -> t { return x; } -} - -forall abs rep res. abs:Typedef(rep) => -function lift1ac(f:(rep) -> res, x:abs) -> res { f(Typedef.rep(x)) } - - -forall a. function id(x:a) -> a {x} -contract TD { - public function main() -> word { lift1ac(id, 42) } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tiamat.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tiamat.sol new file mode 100644 index 00000000..6a48f806 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tiamat.sol @@ -0,0 +1,129 @@ +enum Proxy { Proxy } +enum dict { dict(word, Proxy, Proxy) } +enum address { address(word) } +enum storage { storage(word) } + +function saddr(s: storage) returns (word) { + match (s) { +case storage(a) { +return a; +} +} +} + + +// Untyped Index (access) Proxy +enum UIP { UIP(m, idx) } +// Typed Index (access) Proxy +enum TIP { TIP(m, idx, Proxy) } + +function setbal(ref: storage>, src: address, amt: word) { + /* Based on inference: + ref : storage(dict(address, word)) + => ref[src] : storage(word) assuming src is of the right type + */ + let tip = TIP(ref, src, @word); + Assign.assign(LVA.acc(tip), amt); +} + +function setAllowance(ref: storage>>, owner: address, spender: address, amt: word) { + + let tip1 : TIP>>, address, dict> + = TIP(ref, owner, @dict); + let ref2 : storage> = LVA.acc(tip1); + let tip2 : TIP>, address, word> + = TIP(ref2, spender, @word); + let ref3 : storage = LVA.acc(tip2); + Assign.assign(ref3, amt); +} + +function getAllowance(ref: storage>>, owner: address, spender: address) returns (word) { +/* + let tip : TIP(storage(dict(address, dict(address, word))), address, dict(address, word)) + = TIP(ref, owner, Proxy:Proxy(dict(address, word) )); + let ref2 : storage(dict(address,word)) = LVA.acc(tip); + let tip2 : TIP(storage(dict(address, word)), address, word) + = TIP(ref2, spender, Proxy:Proxy(word)); +*/ + return RVA.acc( + TIP + ( LVA.acc( + TIP + (ref + , owner + , @dict + ) /* tip : TIP(storage(dict(address, dict(address, word))), address, dict(address, word)) */ + ) /* ref2 : storage(dict(address,word)) */ + , spender + , @word + ) /* tip2 : TIP(storage(dict(address, word)), address, word) */ + ); +} + +trait LVA { + function acc(x: self) returns (memberRefType) ; +} + + +trait RVA { + function acc(x: self) returns (member) ; +} + +impl LVA>, index, member>, storage> { + function acc(x: TIP>, index, member>) returns (storage) { + return storage(42); + } +} + +impl LVA>, index, member>, storage> { + function acc(x: UIP>, index, member>) returns (storage) { + return storage(42); + } +} + +trait StorageType { + function sload(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +impl StorageType { + function sload(ptr: word) returns (word) { + let r:word; + assembly { + r := sload(ptr) + } + return r; + } + function store(ptr: word, value: word) { + assembly { + sstore(ptr, value) + } + } +} + +impl RVA>, index, member>, member> where member: StorageType { + function acc(x: TIP>, index, member>) returns (member) { + let addr = saddr(LVA.acc(x)); + return StorageType.sload(addr); + } +} + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + + +impl Assign, a> where a: StorageType { + function assign(l: storage, r: a) { + StorageType.store(saddr(l), r); + } +} + +contract Tiamat { + function main() public returns (word) { + let allowances : storage>>; + let src = address(17); + setAllowance(allowances, address(1),address(2), 666); + return getAllowance(allowances, address(1),address(2)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tiamat.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tiamat.solc deleted file mode 100644 index f51124d8..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tiamat.solc +++ /dev/null @@ -1,136 +0,0 @@ -data Proxy (a) = Proxy ; -data dict(member, index) = dict(word, Proxy(member), Proxy(index)) ; -data address = address(word) ; -data storage(a) = storage(word) ; - -forall a. -function saddr(s: storage(a)) -> word { - match s { - | storage(a) => return a; - } -} - - -// Untyped Index (access) Proxy -data UIP (m, idx, member) = UIP(m ,idx); -// Typed Index (access) Proxy -data TIP (m, idx, member) = TIP(m ,idx, Proxy(member)); - -function setbal(ref: storage(dict(address, word)) , src : address, amt: word) -> () { - /* Based on inference: - ref : storage(dict(address, word)) - => ref[src] : storage(word) assuming src is of the right type - */ - let tip = TIP(ref, src, Proxy:Proxy(word)); - Assign.assign(LVA.acc(tip), amt); -} - -function setAllowance(ref: storage(dict(address, dict(address, word))), owner : address, spender : address, amt : word) -> () { - - let tip1 : TIP(storage(dict(address, dict(address, word))), address, dict(address, word)) - = TIP(ref, owner, Proxy:Proxy(dict(address, word) )); - let ref2 : storage(dict(address,word)) = LVA.acc(tip1); - let tip2 : TIP(storage(dict(address, word)), address, word) - = TIP(ref2, spender, Proxy:Proxy(word)); - let ref3 : storage(word) = LVA.acc(tip2); - Assign.assign(ref3, amt); -} - -function getAllowance(ref: storage(dict(address, dict(address, word))), owner : address, spender : address) -> word { -/* - let tip : TIP(storage(dict(address, dict(address, word))), address, dict(address, word)) - = TIP(ref, owner, Proxy:Proxy(dict(address, word) )); - let ref2 : storage(dict(address,word)) = LVA.acc(tip); - let tip2 : TIP(storage(dict(address, word)), address, word) - = TIP(ref2, spender, Proxy:Proxy(word)); -*/ - return RVA.acc( - TIP - ( LVA.acc( - TIP - (ref - , owner - , Proxy:Proxy(dict(address, word) ) - ) /* tip : TIP(storage(dict(address, dict(address, word))), address, dict(address, word)) */ - ) /* ref2 : storage(dict(address,word)) */ - , spender - , Proxy:Proxy(word) - ) /* tip2 : TIP(storage(dict(address, word)), address, word) */ - ); -} - -forall self memberRefType. -class self:LVA(memberRefType) { - function acc(x:self) -> memberRefType; -} - - -forall self member. -class self:RVA(member) { - function acc(x:self) -> member; -} - -forall index member. - instance TIP(storage(dict(index,member)), index, member):LVA(storage(member)) { - function acc(x:TIP(storage(dict(index,member)), index, member)) -> storage(member) { - return storage(42); - } -} - -forall index member. - instance UIP(storage(dict(index,member)), index, member):LVA(storage(member)) { - function acc(x:UIP(storage(dict(index,member)), index, member)) -> storage(member) { - return storage(42); - } -} - -forall self. -class self:StorageType { - function sload(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -instance word:StorageType { - function sload(ptr:word) -> word { - let r:word; - assembly { - r := sload(ptr) - } - return r; - } - function store(ptr:word, value:word) -> () { - assembly { - sstore(ptr, value) - } - } -} - -forall index member. member:StorageType => - instance TIP(storage(dict(index,member)), index, member):RVA(member) { - function acc(x:TIP(storage(dict(index,member)), index, member)) -> member { - let addr = saddr(LVA.acc(x)); - return StorageType.sload(addr); - } -} - -forall lhs rhs. -class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - - -forall a. a:StorageType => -instance storage(a):Assign(a) { - function assign(l:storage(a), r:a) -> () { - StorageType.store(saddr(l), r); - } -} - -contract Tiamat { - public function main() -> word { - let allowances : storage(dict(address, dict(address, word))); - let src = address(17); - setAllowance(allowances, address(1),address(2), 666); - return getAllowance(allowances, address(1),address(2)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tuple-trick.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tuple-trick.sol new file mode 100644 index 00000000..46b620b9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tuple-trick.sol @@ -0,0 +1,45 @@ +pragma no-coverage-condition Nth; + + +enum Zero {} +enum Succ {} + +enum Proxy { Proxy } + +trait Nth { + function nth(x: Proxy, y: b) returns (c) ; +} + +impl Nth { + function nth(x: Proxy, y: (a, b)) returns (a) { + match (y) { +case (a, b) { +return a ; +} +} + } +} + +impl Nth, (a, b), c> where n: Nth { + function nth(x: Proxy>, y: (a, b)) returns (c) { + match (y) { +case (a,b) { +return Nth.nth(@n, b); +} +} + } +} + +contract C { + function id(x: word) public returns (word) { + return x; + } + function main() public { + let p : (word, word, word, ()); + let x : word = Nth.nth(@Zero, p); + let y : word = Nth.nth(@Succ, p); + let z : word = Nth.nth(@Succ>, p); + id(z); + } +} + diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tuple-trick.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tuple-trick.solc deleted file mode 100644 index 0de688ce..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tuple-trick.solc +++ /dev/null @@ -1,41 +0,0 @@ -pragma no-coverage-condition Nth; - - -data Zero; -data Succ(a); - -data Proxy(a) = Proxy; - -forall a b c . class a : Nth(b,c) { - function nth (x : Proxy(a), y : b) -> c; -} - -forall a b . instance Zero : Nth((a,b), a) { - function nth (x : Proxy(Zero), y : (a,b)) -> a { - match y { - | (a, b) => return a ; - } - } -} - -forall n a b c . n : Nth (b,c) => instance Succ(n) : Nth ((a,b), c) { - function nth (x : Proxy(Succ(n)), y : (a,b)) -> c { - match y { - | (a,b) => return Nth.nth(Proxy : Proxy(n), b); - } - } -} - -contract C { - public function id (x : word) -> word { - return x; - } - public function main () -> () { - let p : (word, word, word, ()); - let x : word = Nth.nth(Proxy : Proxy(Zero), p); - let y : word = Nth.nth(Proxy : Proxy(Succ(Zero)), p); - let z : word = Nth.nth(Proxy : Proxy(Succ(Succ(Zero))), p); - id(z); - } -} - diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tuva.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tuva.sol new file mode 100644 index 00000000..001fb9f3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tuva.sol @@ -0,0 +1,78 @@ +// TUVA: TUple-based Value Access +/* +# Types and classes for assignemnt desugaring using +- access proxy types +- LValue and RValue access classes (LVA, RVA) +- StorageType class +- Assign class +*/ + +import * from std hiding {LValueIdxAccess, RValueIdxAccess, readStorage}; +import {Typedef, storage, mapping, address, hash2, StorageType, Assign} from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + + +trait RValueIdxAccess { + function lookup(ci: col_idx) returns (val) ; +} + +trait LValueIdxAccess { + function lookup(ci: col_idx) returns (ref) ; +} + +impl LValueIdxAccess<(storage a)>, i), storage> where i: Typedef { + function lookup(xi: (storage a)>, i)) returns (storage) { + match (xi) { +case (x, i) { +return storage(hash2(Typedef.rep(x), Typedef.rep(i))); +} +} + + // return storage(42); // FIXME: hash2(x,i); + } +} + +impl RValueIdxAccess<(storage a)>, i), a> where a: StorageType, i: Typedef { + function lookup(xi: (storage a)>, i)) returns (a) { + /* + match(xi) { + | (x, i) => return StorageType.load(hash2(Typedef.rep(x), Typedef.rep(i))); + } + */ + return readStorage(LValueIdxAccess.lookup(xi)); + } +} + +function readStorage(x: storage) returns (a) where a: StorageType { + return StorageType.load(Typedef.rep(x)); +} + +function idx_rval(x: r) returns (a) where r: RValueIdxAccess { + return RValueIdxAccess.lookup(x); +} + +function idx_lval(x: r) returns (a) where r: LValueIdxAccess { + return LValueIdxAccess.lookup(x); +} + +contract TestTuva { + function main() public returns (word) { + let balances : storage word)>; + let allowances : storage mapping(address => word))>; + let ref1 : storage = idx_lval( (balances, address(17)) ); + Assign.assign(idx_lval( (balances, address(1)) ), 1337); + + let ref2a // : storage( mapping(address, word) ) // omitting this type makes instance resolution fail + = idx_lval ( (allowances, address(1)) ); + + let ref2b // : storage( word ) + = idx_lval ( (ref2a, address(2)) ); + + Assign.assign( ref2b, 777 ); + +// return idx_rval( (balances, address(1)) ); + return idx_rval ( (ref2a, address(2)) ); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tuva.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tuva.solc deleted file mode 100644 index 31bb144b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tuva.solc +++ /dev/null @@ -1,81 +0,0 @@ -// TUVA: TUple-based Value Access -/* -# Types and classes for assignemnt desugaring using -- access proxy types -- LValue and RValue access classes (LVA, RVA) -- StorageType class -- Assign class -*/ - -import std.{*} hiding {LValueIdxAccess, RValueIdxAccess, readStorage}; -import std.{Typedef, storage, mapping, address, hash2, StorageType, Assign}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - - -forall col_idx val . class col_idx:RValueIdxAccess(val) { - function lookup(ci : col_idx) -> val; -} - -forall col_idx ref . class col_idx:LValueIdxAccess(ref) { - function lookup(ci : col_idx) -> ref; -} - -forall i a . i:Typedef(word) => -instance (storage(mapping(i,a)), i): LValueIdxAccess(storage(a)) { - function lookup(xi : (storage(mapping(i,a)), i)) -> storage(a) { - match(xi) { - | (x, i) => return storage(hash2(Typedef.rep(x), Typedef.rep(i))); - } - - // return storage(42); // FIXME: hash2(x,i); - } -} - -forall i a . a:StorageType, i:Typedef(word) => -instance (storage(mapping(i,a)), i): RValueIdxAccess(a) { - function lookup(xi : (storage(mapping(i,a)), i)) -> a { - /* - match(xi) { - | (x, i) => return StorageType.load(hash2(Typedef.rep(x), Typedef.rep(i))); - } - */ - return readStorage(LValueIdxAccess.lookup(xi)); - } -} - -forall a. a:StorageType => -function readStorage(x:storage(a)) -> a { - return StorageType.load(Typedef.rep(x)); -} - -forall r a. r: RValueIdxAccess(a) => -function idx_rval(x:r) -> a { - return RValueIdxAccess.lookup(x); -} - -forall r a. r: LValueIdxAccess(a) => -function idx_lval(x:r) -> a { - return LValueIdxAccess.lookup(x); -} - -contract TestTuva { - public function main() -> word { - let balances : storage(mapping(address, word)); - let allowances : storage(mapping(address, mapping(address, word) )); - let ref1 : storage(word) = idx_lval( (balances, address(17)) ); - Assign.assign(idx_lval( (balances, address(1)) ), 1337); - - let ref2a // : storage( mapping(address, word) ) // omitting this type makes instance resolution fail - = idx_lval ( (allowances, address(1)) ); - - let ref2b // : storage( word ) - = idx_lval ( (ref2a, address(2)) ); - - Assign.assign( ref2b, 777 ); - -// return idx_rval( (balances, address(1)) ); - return idx_rval ( (ref2a, address(2)) ); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tyexp.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tyexp.sol new file mode 100644 index 00000000..c6b5f9d0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tyexp.sol @@ -0,0 +1,4 @@ +function main() returns (word) { + let y = 0 ; + return y; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tyexp.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tyexp.solc deleted file mode 100644 index c3fec25c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/tyexp.solc +++ /dev/null @@ -1,4 +0,0 @@ -function main () -> word { - let y = 0 : word ; - return y; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/type-synonym-arg.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/type-synonym-arg.sol new file mode 100644 index 00000000..c24a6442 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/type-synonym-arg.sol @@ -0,0 +1,10 @@ +type W = word; + +function f(x: W) returns (W) { x } + +contract C { + + function main() public returns (word) { + return f(42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/type-synonym-arg.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/type-synonym-arg.solc deleted file mode 100644 index 876e5bda..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/type-synonym-arg.solc +++ /dev/null @@ -1,10 +0,0 @@ -type W = word; - -function f(x:W) -> W { x } - -contract C { - - public function main () -> word { - return f(42); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/typedef.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/typedef.sol new file mode 100644 index 00000000..754ddce9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/typedef.sol @@ -0,0 +1,8 @@ +trait Typedef { + function rep(x: self) returns (underlyingType) ; + function abs(x: underlyingType) returns (self) ; +} + +function tripleFun(x: t) returns (word, (word, word)) where t: Typedef<(word, (word, word))> { + return Typedef.rep(x); + } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/typedef.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/typedef.solc deleted file mode 100644 index 1421e691..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/typedef.solc +++ /dev/null @@ -1,9 +0,0 @@ -forall self underlyingType . class self:Typedef(underlyingType) { - function rep(x:self) -> underlyingType; - function abs(x:underlyingType) -> self; -} - -forall t . t : Typedef((word,(word,word))) => - function tripleFun(x:t) -> (word, (word, word)) { - return Typedef.rep(x); - } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ufcs-no-conflict.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ufcs-no-conflict.sol new file mode 100644 index 00000000..59473364 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ufcs-no-conflict.sol @@ -0,0 +1,51 @@ +import * from std; + +// Regression test: the UFCS (receiver-style) method-call rewriting in +// NameResolution must coexist with the other uses of dot syntax without +// hijacking any of them. All of the following appear in one contract: +// +// * UFCS call val.combine(z) ==> Combiner.combine(val, z) +// * qualified class call Combiner.combine(val, z) (must stay as-is) +// * qualified constructor Color.Red (dotted constructor) +// * plain field read val +// +// UFCS only fires when the receiver is an (unqualified) contract field, so a +// receiver that resolves to a class/module name (`Combiner.combine(...)`) or a +// type name (`Color.Red`) is handled by the earlier qualified-name cases and +// never reaches the UFCS rule. + +trait Combiner { + function combine(x: a, y: word) returns (word) ; +} + +impl Combiner { + function combine(x: word, y: word) returns (word) { + return y; + } +} + +enum Color { Red, Green } + +contract UfcsNoConflict { + val : word; + + constructor() {} + + // UFCS receiver call on a contract field. + function viaUfcs(z: word) public returns (word) { + return val.combine(z); + } + + // The explicit qualified class call for the same method: NOT rewritten by + // UFCS (receiver is the class name `Combiner`, not a field). + function viaQualified(z: word) public returns (word) { + return Combiner.combine(val, z); + } + + // A dotted constructor and a bare field read still resolve normally + // alongside the UFCS rule. + function dottedConstructorAndFieldRead() public returns (word) { + let c : Color = Color.Red; + return val; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ufcs-no-conflict.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ufcs-no-conflict.solc deleted file mode 100644 index 78990e05..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/ufcs-no-conflict.solc +++ /dev/null @@ -1,52 +0,0 @@ -import std.{*}; - -// Regression test: the UFCS (receiver-style) method-call rewriting in -// NameResolution must coexist with the other uses of dot syntax without -// hijacking any of them. All of the following appear in one contract: -// -// * UFCS call val.combine(z) ==> Combiner.combine(val, z) -// * qualified class call Combiner.combine(val, z) (must stay as-is) -// * qualified constructor Color.Red (dotted constructor) -// * plain field read val -// -// UFCS only fires when the receiver is an (unqualified) contract field, so a -// receiver that resolves to a class/module name (`Combiner.combine(...)`) or a -// type name (`Color.Red`) is handled by the earlier qualified-name cases and -// never reaches the UFCS rule. - -forall a. -class a : Combiner { - function combine(x : a, y : word) -> word; -} - -instance word : Combiner { - function combine(x : word, y : word) -> word { - return y; - } -} - -data Color = Red | Green; - -contract UfcsNoConflict { - val : word; - - constructor() {} - - // UFCS receiver call on a contract field. - public function viaUfcs(z : word) -> word { - return val.combine(z); - } - - // The explicit qualified class call for the same method: NOT rewritten by - // UFCS (receiver is the class name `Combiner`, not a field). - public function viaQualified(z : word) -> word { - return Combiner.combine(val, z); - } - - // A dotted constructor and a bare field read still resolve normally - // alongside the UFCS rule. - public function dottedConstructorAndFieldRead() -> word { - let c : Color = Color.Red; - return val; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/uintdesugared.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/uintdesugared.sol new file mode 100644 index 00000000..249a9a5a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/uintdesugared.sol @@ -0,0 +1,498 @@ +// Before desugaring: +/* +import IndexLib; + +contract Uint { + reserved : word; + owner : address; + decimals : uint; + totalSupply : uint; + balances : mapping(address,uint); + + function mint(amount:uint) { + balances[owner] = Num.add(balances[owner], amount); + totalSupply = Num.add(totalSupply, amount); + } + + function init() { + owner = address(0x123456789abcdef); + decimals = Num.fromWord(18); + } + function main() -> uint { + init(); + mint(uint(1000)); + mint(uint(1000)); + return balances[owner] : uint; + } +} +*/ + + +function addW(x: word, y: word) returns (word) { + let res: word; + assembly { + res := add(x, y) + } + return res; +} + +function subW(x: word, y: word) returns (word) { + let res: word; + assembly { + res := sub(x, y) + } + return res; +} + +function addU(x: uint, y: uint) returns (uint) { + let res: word; + let xw : word = Num.toWord(x); + let yw : word = Num.toWord(y); + assembly { + res := add(xw, yw) + } + return uint(res); +} + +function hash1(x: word) returns (word) { + let result: word = 0; + assembly { + mstore(0, x) + result := keccak256(0,32) + } + return result; +} + +function hash2(x: word, y: word) returns (word) { + let result: word = 0; + assembly { + mstore(0, x) + mstore(32, y) + result := keccak256(0,64) + } + return result; +} + +trait Num { + function toWord(x: a) returns (word) ; + function fromWord(x: word) returns (a) ; + function add(x: a, y: a) returns (a) ; + function sub(x: a, y: a) returns (a) ; +} + +impl Num { + function toWord(x: word) returns (word) { return x; } + function fromWord(x: word) returns (word) { return x; } + function add(x: word, y: word) returns (word) { return addW(x, y); } + function sub(x: word, y: word) returns (word) { return addW(x, y); } +} + +enum uint { uint(word) } + +impl Num { + function toWord(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + + function fromWord(x: word) returns (uint) { return uint(x); } + function add(x: uint, y: uint) returns (uint) { return uint(addW(Num.toWord(x), Num.toWord(y))); } + function sub(x: uint, y: uint) returns (uint) { return uint(subW(Num.toWord(x), Num.toWord(y))); } +} + +/* // this breaks the Paterson condition +forall a. a:Typedef(word) => +instance a:Num { + function toWord(x:a) -> word { return Typedef.rep(x); } + function fromWord(x:word) { return Typedef.abs(x); } + function add(x:a, y:a) -> a { return Typedef.abs(addW(Typedef.rep(x), Typedef.rep(y))); } +} +*/ + +// Storage slots and mapping access + + +/////// Construction +trait Typedef { + function rep(x: abs) returns (rep) ; + function abs(x: rep) returns (abs) ; +} + + +// this does not work :( +/* +forall a +. default instance a:Typedef(a) { + function rep(x:a) -> word { return a; } + function abs(x:a) -> word { return a;} +} +*/ + +impl Typedef { + function rep(x: word) returns (word) { return x; } + function abs(x: word) returns (word) { return x; } +} + +impl Typedef { + function rep(x: uint) returns (word) { + match (x) { +case uint(y) { +return y; +} +} + } + function abs(x: word) returns (uint) { + return uint(x); + } +} + +enum address { address(word) } + +impl Typedef { + function rep(x: address) returns (word) { + match (x) { +case address(y) { +return y; +} +} + } + function abs(x: word) returns (address) { + return address(x); + } +} + +enum storage { storage(word) } +enum ContractStorage { ContractStorage(cxt) } + +enum storageRef { storageRef(word) } +enum Proxy { Proxy } + +enum mapRef { mapRef(word) } //ref to a map elem + +// data memoryRef(a) = memoryRef(word); + +impl Typedef, word> { + function rep(x: storage) returns (word) { + match (x) { +case storage(y) { +return y; +} +} + } + function abs(x: word) returns (storage) { + return storage(x); + } +} + +impl Typedef, word> { + function rep(x: storageRef) returns (word) { + match (x) { +case storageRef(y) { +return y; +} +} + } + function abs(x: word) returns (storageRef) { + return storageRef(x); + } +} + +trait Assign { + function assign(l: lhs, r: rhs) ; +} + +enum ref { ref(a) } + +impl Assign, a> { + function assign(l: ref, r: a) { + // builtin "stack store" + return (); + } +} + +trait StorageType { + function sload(ptr: word) returns (self) ; + function store(ptr: word, value: self) ; +} + +trait StorageSize { + function size(x: Proxy) returns (word) ; +} + + +function sload_(x: word) returns (word) { + let res: word; + assembly { + res := sload(x) + } + return res; + } + +function sstore_(a: word, v: word) { + assembly { sstore(a,v) } +} + +impl StorageType { + function sload(ptr: word) returns (word) { + let r:word; + assembly { + r := sload(ptr) + } + return r; + } + function store(ptr: word, value: word) { + assembly { + sstore(ptr, value) + } + } +} + +impl StorageType { + function sload(ptr: word) returns (uint) { + return Typedef.abs(sload_(ptr)); // type annotation needed due to a typechecker bug + } + function store(ptr: word, value: uint) { + return sstore_(ptr, Typedef.rep(value)); + } +} + +impl StorageType
{ + function sload(ptr: word) returns (address) { + return Typedef.abs(sload_(ptr)); // type annotation needed due to a typechecker bug + } + function store(ptr: word, value: address) { + return sstore_(ptr, Typedef.rep(value)); + } +} + +impl Assign, a> where a: StorageType { + function assign(l: storageRef, y: a) { + StorageType.store(Typedef.rep(l), y); + } +} + +trait CStructField {} +enum StructField { StructField(structType) } + + +enum MemberAccessProxy { MemberAccessProxy(a, field) } + +function memberAccessD1(x: MemberAccessProxy) returns (a) { + match (x) { +case MemberAccessProxy(y,z) { +return y; +} +} +} + +trait LValueMemberAccess { + function memberAccess(x: self) returns (memberRefType) ; +} + +trait RValueMemberAccess { + function memberAccess(x: self) returns (memberValueType) ; +} + +impl LValueMemberAccess, fieldSelector, offsetType>, storageRef> where StructField: CStructField, offsetType: StorageSize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (storageRef) { + let ptr:word = Typedef.rep(memberAccessD1(x)); + let size:word = StorageSize.size(@offsetType); + assembly { + ptr := add(ptr, size) + } + return storageRef(ptr); + } +} + +impl StorageSize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} + +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + +impl StorageSize
{ + function size(x: Proxy
) returns (word) { + return 1; + } +} + + +/* +// fails Patterson cond +forall a b . a:Typedef(b), b:StorageSize +=> instance a:StorageSize { + function size(x:Proxy(a)) -> word { + return StorageSize.size(Proxy(b)); + } +} +*/ + +impl StorageSize<(a, b)> where a: StorageSize, b: StorageSize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz:word = StorageSize.size(@a); + let b_sz:word = StorageSize.size(@b); + assembly { + a_sz := add(a_sz, b_sz) + } + return a_sz; + } +} + +pragma no-patterson-condition RValueMemberAccess; // this is due to ContractStorage(cxt); probably not needed once we have local instances +pragma no-coverage-condition MemberAccessProxy, LValueMemberAccess, RValueMemberAccess; + +// ------------------------------------------------------------------ +// Contract field access +// ------------------------------------------------------------------ + +impl LValueMemberAccess, fieldSelector, offsetType>, storageRef> where StructField, fieldSelector>: CStructField, offsetType: StorageSize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (storageRef) { + let ptr:word = 0x100; // forge uses at least 1 storage slot + let offsetSize:word = StorageSize.size(@offsetType); + + assembly { + ptr := add(ptr, offsetSize) + } + return storageRef(ptr); // contract storage starts at 0 + } +} + +impl RValueMemberAccess, fieldSelector, offsetType>, fieldType> where StructField, fieldSelector>: CStructField, fieldType: StorageType, offsetType: StorageSize { + function memberAccess(x: MemberAccessProxy, fieldSelector, offsetType>) returns (fieldType) { + let ptr:word = 0x100; + let offsetSize:word = StorageSize.size(@offsetType); + return StorageType.sload(addW(ptr, offsetSize)); + } +} + +/* +forall cxt fieldSelector fieldType offsetType + . StructField(ContractStorage(cxt), fieldSelector):CStructField(fieldType, offsetType) + , fieldType:StorageType + , offsetType:StorageSize + => instance MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType):RValueMemberAccess(fieldType) { + function memberAccess(x:MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType)) -> fieldType { + let ptr:word = 0x100; + let offsetSize:word = StorageSize.size(Proxy:Proxy(offsetType)); + return StorageType.sload(addW(ptr, offsetSize)):fieldType; + } +} +*/ +// ------------------------------------------------------------------ +// Indexed access +// ------------------------------------------------------------------ + +enum mapping { mapping(word) } + +impl Typedef member), word> { + function rep(x: mapping(index => member)) returns (word) { + match (x) { +case mapping(y) { +return y; +} +} + } + function abs(x: word) returns (mapping(index => member)) { + return mapping(x); + } +} + + +// cf https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays +impl StorageSize member)> { + function size(x: Proxy member)>) returns (word) { + return 1; + } +} + +enum IndexAccessProxy { IndexAccessProxy(map, index) } + +impl LValueMemberAccess, storageRef> where index: Typedef, map: Typedef { + function memberAccess(x: IndexAccessProxy) returns (storageRef) { + return storageRef(indexStorageSlot(x)); + } +} + +impl RValueMemberAccess, member> where index: Typedef, member: StorageType, map: Typedef { + function memberAccess(x: IndexAccessProxy) returns (member) { + let slot:word = indexStorageSlot(x); + return StorageType.sload(slot); + } +} + +function indexStorageSlot(x: IndexAccessProxy) returns (word) where map: Typedef, index: Typedef { + match (x) { +case IndexAccessProxy(map, i) { +let mapptr:word = Typedef.rep(map); + let rawidx:word = Typedef.rep(i); + let loc:word = hash2(mapptr, rawidx); + return loc; +} +} +} + +/* +forall index map member. map:Typedef(word), index:Typedef(word) +=> function indexedSlot(mapref : storageRef(mapping(index, member)), i: index) -> word +{ + match mapref { + | storageRef(mapptr) => + let rawidx:word = Typedef.rep(i); + let loc:word = hash2(mapptr, rawidx); + return loc; + } +} +*/ + +function rval(x: a) returns (b) where a: RValueMemberAccess { + return RValueMemberAccess.memberAccess(x); +} + +enum UintCxt { UintCxt } +enum reserved_sel { reserved_sel } +impl CStructField, reserved_sel>, word, ()> { +} +enum owner_sel { owner_sel } +impl CStructField, owner_sel>, address, (word, ())> { +} +enum decimals_sel { decimals_sel } +impl CStructField, decimals_sel>, uint, (word, (address, ()))> { +} +enum totalSupply_sel { totalSupply_sel } +impl CStructField, totalSupply_sel>, uint, (word, (address, (uint, ())))> { +} +enum balances_sel { balances_sel } +impl CStructField, balances_sel>, mapping(address => uint), (word, (address, (uint, (uint, ()))))> { +} +contract Uint { + function mint(amount: uint) public { + Assign.assign(LValueMemberAccess.memberAccess(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)))), Num.add(rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)))), amount)); + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), totalSupply_sel)), Num.add(rval(MemberAccessProxy(ContractStorage(UintCxt), totalSupply_sel)), amount)); + } + function init() public { + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)), address(81985529216486895)); + Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), decimals_sel)), Num.fromWord(18)); + } + function main() public returns (uint) { + init(); + mint(uint(1000)); + mint(uint(1000)); + return rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)))) ; + } +} + diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/uintdesugared.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/uintdesugared.solc deleted file mode 100644 index 47365ee2..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/uintdesugared.solc +++ /dev/null @@ -1,512 +0,0 @@ -// Before desugaring: -/* -import IndexLib; - -contract Uint { - reserved : word; - owner : address; - decimals : uint; - totalSupply : uint; - balances : mapping(address,uint); - - function mint(amount:uint) { - balances[owner] = Num.add(balances[owner], amount); - totalSupply = Num.add(totalSupply, amount); - } - - function init() { - owner = address(0x123456789abcdef); - decimals = Num.fromWord(18); - } - function main() -> uint { - init(); - mint(uint(1000)); - mint(uint(1000)); - return balances[owner] : uint; - } -} -*/ - - -function addW(x : word, y : word) -> word { - let res: word; - assembly { - res := add(x, y) - } - return res; -} - -function subW(x : word, y : word) -> word { - let res: word; - assembly { - res := sub(x, y) - } - return res; -} - -function addU(x : uint, y : uint) -> uint { - let res: word; - let xw : word = Num.toWord(x); - let yw : word = Num.toWord(y); - assembly { - res := add(xw, yw) - } - return uint(res); -} - -function hash1(x: word) -> word { - let result: word = 0; - assembly { - mstore(0, x) - result := keccak256(0,32) - } - return result; -} - -function hash2(x: word, y: word) -> word { - let result: word = 0; - assembly { - mstore(0, x) - mstore(32, y) - result := keccak256(0,64) - } - return result; -} - -forall a. -class a:Num { - function toWord(x:a) -> word; - function fromWord(x:word) -> a; - function add(x:a, y:a) -> a; - function sub(x:a, y:a) -> a; -} - -instance word:Num { - function toWord(x:word) -> word { return x; } - function fromWord(x:word) -> word { return x; } - function add(x:word, y:word) -> word { return addW(x, y); } - function sub(x:word, y:word) -> word { return addW(x, y); } -} - -data uint = uint(word); - -instance uint:Num { - function toWord(x:uint) -> word - { - match x { - | uint(y) => return y; - } - } - - function fromWord(x:word) -> uint { return uint(x); } - function add(x:uint, y:uint) -> uint { return uint(addW(Num.toWord(x), Num.toWord(y))); } - function sub(x:uint, y:uint) -> uint { return uint(subW(Num.toWord(x), Num.toWord(y))); } -} - -/* // this breaks the Paterson condition -forall a. a:Typedef(word) => -instance a:Num { - function toWord(x:a) -> word { return Typedef.rep(x); } - function fromWord(x:word) { return Typedef.abs(x); } - function add(x:a, y:a) -> a { return Typedef.abs(addW(Typedef.rep(x), Typedef.rep(y))); } -} -*/ - -// Storage slots and mapping access - - -/////// Construction -forall abs rep. -class abs:Typedef(rep) { - function rep(x:abs) -> rep; - function abs(x:rep) -> abs; -} - - -// this does not work :( -/* -forall a -. default instance a:Typedef(a) { - function rep(x:a) -> word { return a; } - function abs(x:a) -> word { return a;} -} -*/ - -instance word:Typedef(word) { - function rep(x:word) -> word { return x; } - function abs(x:word) -> word { return x; } -} - -instance uint:Typedef(word) { - function rep(x:uint) -> word { - match x { - | uint(y) => return y; - } - } - function abs(x:word) -> uint { - return uint(x); - } -} - -data address = address(word); - -instance address:Typedef(word) { - function rep(x:address) -> word { - match x { - | address(y) => return y; - } - } - function abs(x:word) -> address { - return address(x); - } -} - -data storage(a) = storage(word); -data ContractStorage(cxt) = ContractStorage(cxt); - -data storageRef(a) = storageRef(word); -data Proxy(a) = Proxy; - -data mapRef(a) = mapRef(word); //ref to a map elem - -// data memoryRef(a) = memoryRef(word); - -forall a. -instance storage(a):Typedef(word) { - function rep(x:storage(a)) -> word { - match x { - | storage(y) => return y; - } - } - function abs(x:word) -> storage(a) { - return storage(x); - } -} - -forall a. -instance storageRef(a):Typedef(word) { - function rep(x:storageRef(a)) -> word { - match x { - | storageRef(y) => return y; - } - } - function abs(x:word) -> storageRef(a) { - return storageRef(x); - } -} - -forall lhs rhs. -class lhs:Assign(rhs) { - function assign(l:lhs, r:rhs) -> (); -} - -data ref(a) = ref(a); - -forall a. -instance ref(a):Assign(a) { - function assign(l:ref(a), r:a) -> () { - // builtin "stack store" - return (); - } -} - -forall self. -class self:StorageType { - function sload(ptr:word) -> self; - function store(ptr:word, value:self) -> (); -} - -forall self. -class self:StorageSize { - function size(x:Proxy(self)) -> word; -} - - -function sload_(x:word) -> word { - let res: word; - assembly { - res := sload(x) - } - return res; - } - -function sstore_(a:word, v:word) -> () { - assembly { sstore(a,v) } -} - -instance word:StorageType { - function sload(ptr:word) -> word { - let r:word; - assembly { - r := sload(ptr) - } - return r; - } - function store(ptr:word, value:word) -> () { - assembly { - sstore(ptr, value) - } - } -} - -instance uint:StorageType { - function sload(ptr:word) -> uint { - return Typedef.abs(sload_(ptr)):uint; // type annotation needed due to a typechecker bug - } - function store(ptr:word, value:uint) -> () { - return sstore_(ptr, Typedef.rep(value)); - } -} - -instance address:StorageType { - function sload(ptr:word) -> address { - return Typedef.abs(sload_(ptr)):address; // type annotation needed due to a typechecker bug - } - function store(ptr:word, value:address) -> () { - return sstore_(ptr, Typedef.rep(value)); - } -} - -forall a . a : StorageType => instance storageRef(a):Assign(a) { - function assign(l:storageRef(a), y:a) -> () { - StorageType.store(Typedef.rep(l), y); - } -} - -forall self fieldType offsetType. -class self:CStructField(fieldType, offsetType) {} -data StructField(structType, fieldSelector) = StructField(structType); - - -data MemberAccessProxy(a, field, offset) = MemberAccessProxy(a, field); - -forall a field offset . -function memberAccessD1(x:MemberAccessProxy(a, field, offset)) -> a { - match x { - | MemberAccessProxy(y,z) => return y; - } -} - -forall self memberRefType. -class self:LValueMemberAccess(memberRefType) { - function memberAccess(x:self) -> memberRefType; -} - -forall self memberValueType . -class self:RValueMemberAccess(memberValueType) { - function memberAccess(x:self) -> memberValueType; -} - -forall structType fieldSelector fieldType offsetType - . StructField(structType, fieldSelector):CStructField(fieldType, offsetType) - , offsetType:StorageSize - => instance MemberAccessProxy(storage(structType), fieldSelector, offsetType):LValueMemberAccess(storageRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(storage(structType), fieldSelector, offsetType)) -> storageRef(fieldType) { - let ptr:word = Typedef.rep(memberAccessD1(x)); - let size:word = StorageSize.size(Proxy:Proxy(offsetType)); - assembly { - ptr := add(ptr, size) - } - return storageRef(ptr); - } -} - -instance ():StorageSize { - function size(x:Proxy(())) -> word { - return 0; - } -} - -instance word:StorageSize { - function size(x:Proxy(word)) -> word { - return 1; - } -} - -instance uint:StorageSize { - function size(x:Proxy(uint)) -> word { - return 1; - } -} - -instance address:StorageSize { - function size(x:Proxy(address)) -> word { - return 1; - } -} - - -/* -// fails Patterson cond -forall a b . a:Typedef(b), b:StorageSize -=> instance a:StorageSize { - function size(x:Proxy(a)) -> word { - return StorageSize.size(Proxy(b)); - } -} -*/ - -forall a b . a:StorageSize, b:StorageSize => instance (a,b):StorageSize { - function size(x:Proxy((a,b))) -> word { - let a_sz:word = StorageSize.size(Proxy:Proxy(a)); - let b_sz:word = StorageSize.size(Proxy:Proxy(b)); - assembly { - a_sz := add(a_sz, b_sz) - } - return a_sz; - } -} - -pragma no-patterson-condition RValueMemberAccess; // this is due to ContractStorage(cxt); probably not needed once we have local instances -pragma no-coverage-condition MemberAccessProxy, LValueMemberAccess, RValueMemberAccess; - -// ------------------------------------------------------------------ -// Contract field access -// ------------------------------------------------------------------ - -forall cxt fieldSelector fieldType offsetType - . StructField(ContractStorage(cxt), fieldSelector):CStructField(fieldType, offsetType) - , offsetType:StorageSize - => instance MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType):LValueMemberAccess(storageRef(fieldType)) { - function memberAccess(x:MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType)) -> storageRef(fieldType) { - let ptr:word = 0x100; // forge uses at least 1 storage slot - let offsetSize:word = StorageSize.size(Proxy:Proxy(offsetType)); - - assembly { - ptr := add(ptr, offsetSize) - } - return storageRef(ptr); // contract storage starts at 0 - } -} - -forall cxt fieldSelector fieldType offsetType - . StructField(ContractStorage(cxt), fieldSelector):CStructField(fieldType, offsetType) - , fieldType:StorageType - , offsetType:StorageSize - => instance MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType):RValueMemberAccess(fieldType) { - function memberAccess(x:MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType)) -> fieldType { - let ptr:word = 0x100; - let offsetSize:word = StorageSize.size(Proxy:Proxy(offsetType)); - return StorageType.sload(addW(ptr, offsetSize)):fieldType; - } -} - -/* -forall cxt fieldSelector fieldType offsetType - . StructField(ContractStorage(cxt), fieldSelector):CStructField(fieldType, offsetType) - , fieldType:StorageType - , offsetType:StorageSize - => instance MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType):RValueMemberAccess(fieldType) { - function memberAccess(x:MemberAccessProxy(ContractStorage(cxt), fieldSelector, offsetType)) -> fieldType { - let ptr:word = 0x100; - let offsetSize:word = StorageSize.size(Proxy:Proxy(offsetType)); - return StorageType.sload(addW(ptr, offsetSize)):fieldType; - } -} -*/ -// ------------------------------------------------------------------ -// Indexed access -// ------------------------------------------------------------------ - -data mapping(index, member) = mapping(word); - -forall member index . instance mapping(index, member):Typedef(word) { - function rep(x:mapping(index, member)) -> word { - match x { - | mapping(y) => return y; - } - } - function abs(x:word) -> mapping(index,member) { - return mapping(x); - } -} - - -// cf https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html#mappings-and-dynamic-arrays -forall index member . -instance mapping(index, member):StorageSize { - function size(x:Proxy(mapping(index, member))) -> word { - return 1; - } -} - -data IndexAccessProxy(map, index, member) = IndexAccessProxy(map, index); - -forall map index member. index:Typedef(word), map:Typedef(word) -=> instance IndexAccessProxy(map, index, member):LValueMemberAccess(storageRef(member)) { - function memberAccess(x:IndexAccessProxy(map, index, member)) -> storageRef(member) { - return storageRef(indexStorageSlot(x)); - } -} - -forall map index member . index:Typedef(word), member:StorageType, map:Typedef(word) -=> instance IndexAccessProxy(map, index, member):RValueMemberAccess(member) { - function memberAccess(x:IndexAccessProxy(map, index, member)) -> member { - let slot:word = indexStorageSlot(x); - return StorageType.sload(slot); - } -} - -forall index map member. map:Typedef(word), index:Typedef(word) => function indexStorageSlot(x:IndexAccessProxy(map, index, member)) -> word -//function indexStorageSlot(x) -{ - match x { - | IndexAccessProxy(map, i) => - let mapptr:word = Typedef.rep(map); - let rawidx:word = Typedef.rep(i); - let loc:word = hash2(mapptr, rawidx); - return loc; - } -} - -/* -forall index map member. map:Typedef(word), index:Typedef(word) -=> function indexedSlot(mapref : storageRef(mapping(index, member)), i: index) -> word -{ - match mapref { - | storageRef(mapptr) => - let rawidx:word = Typedef.rep(i); - let loc:word = hash2(mapptr, rawidx); - return loc; - } -} -*/ - -forall a b. a:RValueMemberAccess(b) => -function rval(x:a) -> b { - return RValueMemberAccess.memberAccess(x); -} - -data UintCxt = UintCxt ; -data reserved_sel = reserved_sel ; -instance StructField(ContractStorage(UintCxt), reserved_sel) :CStructField(word, ()) { -} -data owner_sel = owner_sel ; -instance StructField(ContractStorage(UintCxt), owner_sel) :CStructField(address, (word, ())) { -} -data decimals_sel = decimals_sel ; -instance StructField(ContractStorage(UintCxt), decimals_sel) :CStructField(uint, (word, (address, ()))) { -} -data totalSupply_sel = totalSupply_sel ; -instance StructField(ContractStorage(UintCxt), totalSupply_sel) :CStructField(uint, (word, (address, (uint, ())))) { -} -data balances_sel = balances_sel ; -instance StructField(ContractStorage(UintCxt), balances_sel) :CStructField(mapping(address, uint), (word, (address, (uint, (uint, ()))))) { -} -contract Uint { - public function mint (amount : uint) -> () { - Assign.assign(LValueMemberAccess.memberAccess(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)))), Num.add(rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)))), amount)); - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), totalSupply_sel)), Num.add(rval(MemberAccessProxy(ContractStorage(UintCxt), totalSupply_sel)), amount)); - } - public function init () -> () { - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)), address(81985529216486895)); - Assign.assign(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), decimals_sel)), Num.fromWord(18)); - } - public function main () -> uint { - init(); - mint(uint(1000)); - mint(uint(1000)); - return rval(IndexAccessProxy(LValueMemberAccess.memberAccess(MemberAccessProxy(ContractStorage(UintCxt), balances_sel)), rval(MemberAccessProxy(ContractStorage(UintCxt), owner_sel)))) : uint; - } -} - diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/undefined.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/undefined.sol new file mode 100644 index 00000000..5015f383 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/undefined.sol @@ -0,0 +1,13 @@ +function undefined() returns (any) { + assembly { + revert(0,0) + } +} + +function useWord(w: word) {} + +contract Magic { + function main() public { + useWord(undefined()); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/undefined.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/undefined.solc deleted file mode 100644 index 38e05d12..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/undefined.solc +++ /dev/null @@ -1,13 +0,0 @@ -forall any.function undefined() -> any { - assembly { - revert(0,0) - } -} - -function useWord(w:word) -> () {} - -contract Magic { - public function main() -> () { - useWord(undefined()); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/unit.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/unit.sol new file mode 100644 index 00000000..4fc1e729 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/unit.sol @@ -0,0 +1,35 @@ +contract Unit { +function one(x: ()) public returns (word) { + return 1; +} + +function unitVal() public { + return (); +} + +function unitMatch(x: ()) public returns (word) { + match (x) { +case () { +return 1; +} +} +} + +function foo(x: word) public { + return (); +} + +function main() public returns (word) { + return unitMatch(foo(one(unitVal()))); +} +} + +trait Def { + function def() returns (a) ; +} + +impl Def<()> { + function def() { + return (); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/unit.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/unit.solc deleted file mode 100644 index 98e93ae7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/unit.solc +++ /dev/null @@ -1,33 +0,0 @@ -contract Unit { -public function one (x : ()) -> word { - return 1; -} - -public function unitVal() -> () { - return (); -} - -public function unitMatch (x : ()) -> word { - match x { - | () => return 1; - } -} - -public function foo (x : word) -> () { - return (); -} - -public function main() -> word { - return unitMatch(foo(one(unitVal()))); -} -} - -forall a . class a : Def { - function def () -> a ; -} - -instance () : Def { - function def() -> () { - return (); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/word-match-default.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/word-match-default.sol new file mode 100644 index 00000000..e69d4611 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/word-match-default.sol @@ -0,0 +1,18 @@ +contract WordMatchDefault { + function f(n: word) public returns (word) { + let result : word; + match (n) { +case 0 { +assembly { result := 100 } +} +case x { +assembly { result := x } +} +} + return result; + } + + function main() public returns (word) { + return f(42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/word-match-default.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/word-match-default.solc deleted file mode 100644 index c1b17b95..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/word-match-default.solc +++ /dev/null @@ -1,14 +0,0 @@ -contract WordMatchDefault { - public function f(n : word) -> word { - let result : word; - match n { - | 0 => assembly { result := 100 } - | x => assembly { result := x } - } - return result; - } - - public function main() -> word { - return f(42); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/word-match.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/word-match.sol new file mode 100644 index 00000000..8a298dad --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/word-match.sol @@ -0,0 +1,12 @@ + +trait IsWord { function toWord(x: a) returns (word) ; } + +function kw(a: word, b: word) returns (word) {return a;} + +function bar(x: (a, b)) returns (word) where a: IsWord, b: IsWord { + match (x) { +case (t,u) { +return kw(IsWord.toWord(t), IsWord.toWord(u)); +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/word-match.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/word-match.solc deleted file mode 100644 index 07c20ad0..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/word-match.solc +++ /dev/null @@ -1,11 +0,0 @@ - -forall a . class a:IsWord { function toWord(x : a) -> word; } - -function kw(a:word, b:word) -> word {return a;} - -forall a b . a:IsWord, b:IsWord -=> function bar(x:(a,b)) -> word { - match x { - | (t,u) => return kw(IsWord.toWord(t), IsWord.toWord(u)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-break-continue-leave.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-break-continue-leave.sol new file mode 100644 index 00000000..47ce5cc7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-break-continue-leave.sol @@ -0,0 +1,29 @@ +import * from std; + +function yul_asm_break_continue_leave() { + let result : word = 0; + assembly { + function clamp(x) -> y { + y := x + if gt(x, 3) { + y := 3 + leave + } + } + for { let i := 0 } lt(i, 10) { i := add(i, 1) } { + if lt(i, 2) { + continue + } + if gt(i, 5) { + break + } + result := add(result, clamp(i)) + } + } +} + +contract Foo { + function main() public { + yul_asm_break_continue_leave() + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-break-continue-leave.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-break-continue-leave.solc deleted file mode 100644 index bee240d3..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-break-continue-leave.solc +++ /dev/null @@ -1,29 +0,0 @@ -import std.{*}; - -function yul_asm_break_continue_leave() -> () { - let result : word = 0; - assembly { - function clamp(x) -> y { - y := x - if gt(x, 3) { - y := 3 - leave - } - } - for { let i := 0 } lt(i, 10) { i := add(i, 1) } { - if lt(i, 2) { - continue - } - if gt(i, 5) { - break - } - result := add(result, clamp(i)) - } - } -} - -contract Foo { - public function main() -> () { - yul_asm_break_continue_leave() - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-for-body.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-for-body.sol new file mode 100644 index 00000000..7a94c2fd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-for-body.sol @@ -0,0 +1,16 @@ +import * from std; + +function yul_asm_for_body() { + let result : word = 0; + assembly { + for { let i := 0 } lt(i, 3) { i := add(i, 1) } { + result := callvalue() + } + } +} + +contract Foo { + function main() public { + yul_asm_for_body() + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-for-body.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-for-body.solc deleted file mode 100644 index 806a0d11..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-for-body.solc +++ /dev/null @@ -1,16 +0,0 @@ -import std.{*}; - -function yul_asm_for_body() -> () { - let result : word = 0; - assembly { - for { let i := 0 } lt(i, 3) { i := add(i, 1) } { - result := callvalue() - } - } -} - -contract Foo { - public function main() -> () { - yul_asm_for_body() - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-switch-body.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-switch-body.sol new file mode 100644 index 00000000..73e7e5cd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-switch-body.sol @@ -0,0 +1,17 @@ +import * from std; + +function yul_asm_switch_body() { + let result : word = 0; + let flag : word = 1; + assembly { + switch flag + case 0 { result := 0 } + default { result := callvalue() } + } +} + +contract Foo { + function main() public { + yul_asm_switch_body() + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-switch-body.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-switch-body.solc deleted file mode 100644 index 76e914ff..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-asm-switch-body.solc +++ /dev/null @@ -1,17 +0,0 @@ -import std.{*}; - -function yul_asm_switch_body() -> () { - let result : word = 0; - let flag : word = 1; - assembly { - switch flag - case 0 { result := 0 } - default { result := callvalue() } - } -} - -contract Foo { - public function main() -> () { - yul_asm_switch_body() - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-deposit-example.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-deposit-example.sol new file mode 100644 index 00000000..8e5b9147 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-deposit-example.sol @@ -0,0 +1,14 @@ +import * from std; + +function deposit(pubkey: memory, withdrawal_credentials: memory, signature: memory, deposit_data_root: uint256) { + let msg_value : word = 0; + assembly { + msg_value := callvalue() + } +} + +contract Foo { + function main() public { + deposit(memory(0), memory(0), memory(0), uint256(2)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-deposit-example.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-deposit-example.solc deleted file mode 100644 index 53992aa8..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-deposit-example.solc +++ /dev/null @@ -1,14 +0,0 @@ -import std.{*}; - -function deposit(pubkey: memory(string), withdrawal_credentials: memory(string), signature: memory(string), deposit_data_root: uint256) -> () { - let msg_value : word = 0; - assembly { - msg_value := callvalue() - } -} - -contract Foo { - public function main () -> () { - deposit(memory(0), memory(0), memory(0), uint256(2)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-for.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-for.sol new file mode 100644 index 00000000..020aa5b9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-for.sol @@ -0,0 +1,14 @@ +contract YulFor { + function main() public returns (word) { + let loopStart : word = 128; + let loopEnd : word = 256; + let res : word; + assembly { + let i := loopStart + for {} lt(i, loopEnd) { i := add(i, 32) } + { mstore(i, 42) } + res := mload(192) + } + return res; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-for.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-for.solc deleted file mode 100644 index f0a1497d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-for.solc +++ /dev/null @@ -1,14 +0,0 @@ -contract YulFor { - public function main() -> word { - let loopStart : word = 128; - let loopEnd : word = 256; - let res : word; - assembly { - let i := loopStart - for {} lt(i, loopEnd) { i := add(i, 32) } - { mstore(i, 42) } - res := mload(192) - } - return res; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-function-typing.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-function-typing.sol new file mode 100644 index 00000000..68bf6430 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-function-typing.sol @@ -0,0 +1,8 @@ +function foo(length: word, pos: word) returns (word) { + let ret: word; + assembly { + // ret := add(pos, mul(0x20, iszero(iszero(length)))) + ret := iszero(iszero(length)) + } + return ret; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-function-typing.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-function-typing.solc deleted file mode 100644 index 32812c24..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-function-typing.solc +++ /dev/null @@ -1,8 +0,0 @@ -function foo(length:word, pos:word) -> word { - let ret: word; - assembly { - // ret := add(pos, mul(0x20, iszero(iszero(length)))) - ret := iszero(iszero(length)) - } - return ret; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-multi-return.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-multi-return.sol new file mode 100644 index 00000000..3b6a6d10 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-multi-return.sol @@ -0,0 +1,18 @@ +// A Yul function with multiple named returns must keep its true return arity: +// 'x, y := pair()' assigns 2 values from a 2-return function and is valid Yul, +// so the type checker must accept it (regression for the arity check that used +// to collapse every non-empty return list to a single 'word'). +contract YulMultiRet { + function main() public returns (word) { + let x : word; + let y : word; + assembly { + function pair() -> a, b { + a := 1 + b := 2 + } + x, y := pair() + } + return x; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-multi-return.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-multi-return.solc deleted file mode 100644 index 4c2666ed..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-multi-return.solc +++ /dev/null @@ -1,18 +0,0 @@ -// A Yul function with multiple named returns must keep its true return arity: -// 'x, y := pair()' assigns 2 values from a 2-return function and is valid Yul, -// so the type checker must accept it (regression for the arity check that used -// to collapse every non-empty return list to a single 'word'). -contract YulMultiRet { - public function main() -> word { - let x : word; - let y : word; - assembly { - function pair() -> a, b { - a := 1 - b := 2 - } - x, y := pair() - } - return x; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-return.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-return.sol new file mode 100644 index 00000000..675e1b96 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-return.sol @@ -0,0 +1,7 @@ +contract C { + function main() public { + assembly { + return(0,0) + } + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-return.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-return.solc deleted file mode 100644 index 0dc00a80..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/cases/yul-return.solc +++ /dev/null @@ -1,7 +0,0 @@ -contract C { - public function main() -> () { - assembly { - return(0,0) - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/CondExpr.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/CondExpr.sol new file mode 100644 index 00000000..90eb4633 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/CondExpr.sol @@ -0,0 +1,12 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +function notAnswer(n: word) returns (word) { (n == 42) ? 0 : 42 } + +function answer(n: word) returns (word) { notAnswer(notAnswer(42)) } + +contract Fib { + function main() public returns (word) { answer(42) } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/CondExpr.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/CondExpr.solc deleted file mode 100644 index a2fac7f5..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/CondExpr.solc +++ /dev/null @@ -1,12 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -function notAnswer(n : word) -> word { if(n == 42) then 0 else 42 } - -function answer(n:word) -> word { notAnswer(notAnswer(42)) } - -contract Fib { - public function main() -> word { answer(42) } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/CondStmt.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/CondStmt.sol new file mode 100644 index 00000000..46aaef86 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/CondStmt.sol @@ -0,0 +1,17 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +function notAnswer(n: word) returns (word) { + if(n == 42) { return 0; } else {return 42; } +} + +function answer(n: word) returns (word) { + return notAnswer(notAnswer(42)); +} +contract Fib { +function main() public returns (word) { + return answer(42); +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/CondStmt.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/CondStmt.solc deleted file mode 100644 index c0bb72f0..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/CondStmt.solc +++ /dev/null @@ -1,17 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -function notAnswer(n : word) -> word { - if(n == 42) { return 0; } else {return 42; } -} - -function answer(n:word) -> word { - return notAnswer(notAnswer(42)); -} -contract Fib { -public function main() -> word { - return answer(42); -} -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/OneTwo.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/OneTwo.sol new file mode 100644 index 00000000..d27b73c6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/OneTwo.sol @@ -0,0 +1,28 @@ +// This function should be in stdlib +function addWord(l: word, r: word) returns (word) { + let rw : word; + assembly { + rw := add(l,r) + } + return rw; +} + + function zero() returns (word) { + return 0; + } + +function one() returns (word) { + return addWord(1, zero()) ; + } + +function two() returns (word) { + let x = zero(); + x = addWord(x, one()); + x = addWord(x,x); + return x; +} + +contract OneTwo { + function main() public returns (word) { return two(); } +} + diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/OneTwo.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/OneTwo.solc deleted file mode 100644 index 9a16738d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/OneTwo.solc +++ /dev/null @@ -1,28 +0,0 @@ -// This function should be in stdlib -function addWord(l: word, r: word) -> word { - let rw : word; - assembly { - rw := add(l,r) - } - return rw; -} - - function zero () -> word { - return 0; - } - -function one() -> word { - return addWord(1, zero()) ; - } - -function two () -> word { - let x = zero(); - x = addWord(x, one()); - x = addWord(x,x); - return x; -} - -contract OneTwo { - public function main() -> word { return two(); } -} - diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Plus.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Plus.sol new file mode 100644 index 00000000..d4a651f1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Plus.sol @@ -0,0 +1,22 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + function zero() returns (word) { + return 0; + } + +function one() returns (word) { + return 1 + zero() ; + } + +function two() returns (word) { + let x = zero(); + x = x + one(); + x = x + x ; + return x; +} + +contract Plus { + function main() public returns (word) { return two() + two(); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Plus.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Plus.solc deleted file mode 100644 index 2d34d846..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Plus.solc +++ /dev/null @@ -1,22 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - function zero () -> word { - return 0; - } - -function one() -> word { - return 1 + zero() ; - } - -function two () -> word { - let x = zero(); - x = x + one(); - x = x + x ; - return x; -} - -contract Plus { - public function main() -> word { return two() + two(); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Size.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Size.sol new file mode 100644 index 00000000..f7b3c124 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Size.sol @@ -0,0 +1,47 @@ +enum Proxy { Proxy } + +function addWord(l: word, r: word) returns (word) { + let rw : word; + assembly { + rw := add(l,r) + } + return rw; +} + +trait StorageSize { + function size(x: Proxy) returns (word) ; +} + + +default impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + +impl StorageSize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} + +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + +impl StorageSize<(a, b)> where a: StorageSize, b: StorageSize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz:word = StorageSize.size(@a); + let b_sz:word = StorageSize.size(@b); + return addWord(a_sz, b_sz); + } +} + + +contract Size { + function main() public returns (word) { + return + StorageSize.size(@(word, (word, ()))); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Size.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Size.solc deleted file mode 100644 index 292b99e7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Size.solc +++ /dev/null @@ -1,49 +0,0 @@ -data Proxy(t) = Proxy; - -function addWord(l: word, r: word) -> word { - let rw : word; - assembly { - rw := add(l,r) - } - return rw; -} - -forall self. -class self:StorageSize { - function size(x:Proxy(self)) -> word; -} - - -forall self. -default instance self:StorageSize { - function size(x:Proxy(self)) -> word { - return 1; - } -} - -instance ():StorageSize { - function size(x:Proxy(())) -> word { - return 0; - } -} - -instance word:StorageSize { - function size(x:Proxy(word)) -> word { - return 1; - } -} - -forall a b. a:StorageSize, b:StorageSize => instance (a,b):StorageSize { - function size(x:Proxy((a,b))) -> word { - let a_sz:word = StorageSize.size(Proxy:Proxy(a)); - let b_sz:word = StorageSize.size(Proxy:Proxy(b)); - return addWord(a_sz, b_sz); - } -} - - -contract Size { - public function main() -> word { - return - StorageSize.size(Proxy:Proxy( (word, (word, ())))); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/StdSize.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/StdSize.sol new file mode 100644 index 00000000..12a59f14 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/StdSize.sol @@ -0,0 +1,45 @@ +enum Proxy { Proxy } + +function addWord(l: word, r: word) returns (word) { + let rw: word; + assembly { + rw := add(l, r) + } + return rw; +} + +trait StorageSize { + function size(x: Proxy) returns (word) ; +} + +default impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + +impl StorageSize<()> { + function size(x: Proxy<()>) returns (word) { + return 0; + } +} + +impl StorageSize { + function size(x: Proxy) returns (word) { + return 1; + } +} + +impl StorageSize<(a, b)> where a: StorageSize, b: StorageSize { + function size(x: Proxy<(a, b)>) returns (word) { + let a_sz: word = StorageSize.size(@a); + let b_sz: word = StorageSize.size(@b); + return addWord(a_sz, b_sz); + } +} + +contract Size { + function main() public returns (word) { + return StorageSize.size(@(word, (word, ()))); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/StdSize.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/StdSize.solc deleted file mode 100644 index 17123de6..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/StdSize.solc +++ /dev/null @@ -1,48 +0,0 @@ -data Proxy(t) = Proxy; - -function addWord(l: word, r: word) -> word { - let rw: word; - assembly { - rw := add(l, r) - } - return rw; -} - -forall self. -class self:StorageSize { - function size(x: Proxy(self)) -> word; -} - -forall self. -default instance self:StorageSize { - function size(x: Proxy(self)) -> word { - return 1; - } -} - -instance ():StorageSize { - function size(x: Proxy(())) -> word { - return 0; - } -} - -instance word:StorageSize { - function size(x: Proxy(word)) -> word { - return 1; - } -} - -forall a b. a:StorageSize, b:StorageSize => -instance (a, b):StorageSize { - function size(x: Proxy((a, b))) -> word { - let a_sz: word = StorageSize.size(Proxy:Proxy(a)); - let b_sz: word = StorageSize.size(Proxy:Proxy(b)); - return addWord(a_sz, b_sz); - } -} - -contract Size { - public function main() -> word { - return StorageSize.size(Proxy:Proxy((word, (word, ())))); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/comptime_syntax.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/comptime_syntax.sol new file mode 100644 index 00000000..21fe626a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/comptime_syntax.sol @@ -0,0 +1,15 @@ +contract ComptimeSyntax { + + function f(comptime x: word) returns (comptime) { + return x; + } + + function g() returns (word) { + let y : comptime = f(42); + return y; + } + + function main() returns (word) { + return g(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/comptime_syntax.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/comptime_syntax.solc deleted file mode 100644 index 50c9ef37..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/comptime_syntax.solc +++ /dev/null @@ -1,15 +0,0 @@ -contract ComptimeSyntax { - - function f(comptime x : word) -> comptime word { - return x; - } - - function g() -> word { - let y : comptime word = f(42); - return y; - } - - function main() -> word { - return g(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/counter.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/counter.sol new file mode 100644 index 00000000..7d60ab20 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/counter.sol @@ -0,0 +1,25 @@ +import * from std; +import {uint256, address} from std; +import * from std.dispatch; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; +contract Counter { + // some dummy fields to test offset calculation + fld0 : word; + fld1 : uint256; + fld2 : address; + counter : word; + + constructor() { + counter = 41; + fld2 = address(0); + fld1 = uint256(11); + fld0 = 7; + } + + function main() public returns (word) { + counter = counter + 1; + return counter; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/counter.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/counter.solc deleted file mode 100644 index 8c93f43e..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/counter.solc +++ /dev/null @@ -1,25 +0,0 @@ -import std.{*}; -import std.{uint256, address}; -import std.dispatch.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; -contract Counter { - // some dummy fields to test offset calculation - fld0 : word; - fld1 : uint256; - fld2 : address; - counter : word; - - constructor() { - counter = 41; - fld2 = address(0); - fld1 = uint256(11); - fld0 = 7; - } - - public function main() -> word { - counter = counter + 1; - return counter; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_asm_mem.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_asm_mem.sol new file mode 100644 index 00000000..765d09d9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_asm_mem.sol @@ -0,0 +1,19 @@ +/* Positive: function using mstore+mload in assembly is comptime-evaluable + when its argument is known at compile time. + The evaluator runs in comptime mode for the RHS of `let x : comptime`. +*/ +function storeLoad(x: word) returns (word) { + let r : word; + assembly { + mstore(0, x) + r := mload(0) + } + return r; +} + +contract ComptimeAsmMem { + function main() returns (word) { + let res : comptime = storeLoad(42); + return res; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_asm_mem.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_asm_mem.solc deleted file mode 100644 index 94a8d86a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_asm_mem.solc +++ /dev/null @@ -1,19 +0,0 @@ -/* Positive: function using mstore+mload in assembly is comptime-evaluable - when its argument is known at compile time. - The evaluator runs in comptime mode for the RHS of `let x : comptime`. -*/ -function storeLoad(x : word) -> word { - let r : word; - assembly { - mstore(0, x) - r := mload(0) - } - return r; -} - -contract ComptimeAsmMem { - function main() -> word { - let res : comptime word = storeLoad(42); - return res; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_asm_ret.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_asm_ret.sol new file mode 100644 index 00000000..3ba34ae7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_asm_ret.sol @@ -0,0 +1,17 @@ +/* Negative: function annotated '-> comptime word' but body reads from + storage via sload — storage is mutable state, never comptime. + The verifier must reject this. +*/ + +contract ComptimeAsmRet { + function loadFromStorage() returns (comptime) { + let v : word; + assembly { + v := sload(0) + } + return v; + } + function main() returns (word) { + return loadFromStorage(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_asm_ret.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_asm_ret.solc deleted file mode 100644 index b0d3893b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_asm_ret.solc +++ /dev/null @@ -1,17 +0,0 @@ -/* Negative: function annotated '-> comptime word' but body reads from - storage via sload — storage is mutable state, never comptime. - The verifier must reject this. -*/ - -contract ComptimeAsmRet { - function loadFromStorage() -> comptime word { - let v : word; - assembly { - v := sload(0) - } - return v; - } - function main() -> word { - return loadFromStorage(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_chain_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_chain_ok.sol new file mode 100644 index 00000000..5dc8d050 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_chain_ok.sol @@ -0,0 +1,16 @@ +/* Positive: comptime result threaded through two comptime functions. + increment(20) is comptime, so it can be passed to double's comptime param. +*/ +import std; + +contract ComptimeChainOk { + function increment(comptime x: word) returns (comptime) { + return x + 1; + } + function double(comptime x: word) returns (comptime) { + return x + x; + } + function main() returns (word) { + return double(increment(20)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_chain_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_chain_ok.solc deleted file mode 100644 index a35f9f41..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_chain_ok.solc +++ /dev/null @@ -1,16 +0,0 @@ -/* Positive: comptime result threaded through two comptime functions. - increment(20) is comptime, so it can be passed to double's comptime param. -*/ -import std; - -contract ComptimeChainOk { - function increment(comptime x : word) -> comptime word { - return x + 1; - } - function double(comptime x : word) -> comptime word { - return x + x; - } - function main() -> word { - return double(increment(20)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_let_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_let_ok.sol new file mode 100644 index 00000000..6bad2af6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_let_ok.sol @@ -0,0 +1,12 @@ +/* Positive: comptime let binding fed from a comptime function call. */ +import std; + +contract ComptimeLetOk { + function double(comptime x: word) returns (comptime) { + return x + x; + } + function main() returns (word) { + let y : comptime = double(21); + return y; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_let_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_let_ok.solc deleted file mode 100644 index 4f3739a9..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_let_ok.solc +++ /dev/null @@ -1,12 +0,0 @@ -/* Positive: comptime let binding fed from a comptime function call. */ -import std; - -contract ComptimeLetOk { - function double(comptime x : word) -> comptime word { - return x + x; - } - function main() -> word { - let y : comptime word = double(21); - return y; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_let_runtime.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_let_runtime.sol new file mode 100644 index 00000000..16c9def3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_let_runtime.sol @@ -0,0 +1,21 @@ +/* Negative: comptime let bound to a runtime expression — must fail. + sloadWord reads from storage (sload); storage is mutable state, + so its result is runtime. Binding it with 'let y : comptime word' + must be rejected by the verifier. +*/ +import std; + +function sloadWord() returns (word) { + let v : word; + assembly { + v := sload(0) + } + return v; +} + +contract ComptimeLetRuntime { + function main() returns (word) { + let y : comptime = sloadWord(); + return y; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_let_runtime.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_let_runtime.solc deleted file mode 100644 index 2db7a7d6..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_let_runtime.solc +++ /dev/null @@ -1,21 +0,0 @@ -/* Negative: comptime let bound to a runtime expression — must fail. - sloadWord reads from storage (sload); storage is mutable state, - so its result is runtime. Binding it with 'let y : comptime word' - must be rejected by the verifier. -*/ -import std; - -function sloadWord() -> word { - let v : word; - assembly { - v := sload(0) - } - return v; -} - -contract ComptimeLetRuntime { - function main() -> word { - let y : comptime word = sloadWord(); - return y; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_overloaded_bad.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_overloaded_bad.sol new file mode 100644 index 00000000..19af3db5 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_overloaded_bad.sol @@ -0,0 +1,27 @@ +/* Negative: Scale instance whose 'scale' reads from storage — not comptime. + Despite the comptime annotations on the method signature, the word + instance body uses sload (mutable storage state), making the result + a runtime value. The verifier must reject the comptime let binding. +*/ +import std; + +trait Scale { + function scale(comptime factor: word, comptime x: a) returns (comptime) ; +} + +impl Scale { + function scale(comptime factor: word, comptime x: word) returns (comptime) { + let base : word; + assembly { + base := sload(0) + } + return base + x * factor; + } +} + +contract ComptimeOverloadedBad { + function main() returns (word) { + let a : comptime = Scale.scale(3, 10); + return a; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_overloaded_bad.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_overloaded_bad.solc deleted file mode 100644 index 68042e0a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_overloaded_bad.solc +++ /dev/null @@ -1,27 +0,0 @@ -/* Negative: Scale instance whose 'scale' reads from storage — not comptime. - Despite the comptime annotations on the method signature, the word - instance body uses sload (mutable storage state), making the result - a runtime value. The verifier must reject the comptime let binding. -*/ -import std; - -forall a. class a : Scale { - function scale(comptime factor : word, comptime x : a) -> comptime a; -} - -instance word : Scale { - function scale(comptime factor : word, comptime x : word) -> comptime word { - let base : word; - assembly { - base := sload(0) - } - return base + x * factor; - } -} - -contract ComptimeOverloadedBad { - function main() -> word { - let a : comptime word = Scale.scale(3, 10); - return a; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_overloaded_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_overloaded_ok.sol new file mode 100644 index 00000000..1b274e2c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_overloaded_ok.sol @@ -0,0 +1,29 @@ +/* Positive: comptime through an overloaded (type class) function. + Scale.scale takes a comptime factor; if factor == 1 it returns x + unchanged (conditional evaluated at comptime since factor is comptime). + mulWord is builtinPure, so multiplication of comptime values is comptime. + The verifier must follow specialization and accept this. +*/ +import * from std; + +trait Scale { + function scale(comptime factor: word, comptime x: a) returns (comptime) ; +} + +impl Scale { + function scale(comptime factor: word, comptime x: word) returns (comptime) { + if (factor == 1) { + return x; + } else { + return x * factor; + } + } +} + +contract ComptimeOverloadedOk { + function main() returns (word) { + let a : comptime = Scale.scale(1, 32); + let b : comptime = Scale.scale(3, 10); + return a + b; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_overloaded_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_overloaded_ok.solc deleted file mode 100644 index f6252490..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_overloaded_ok.solc +++ /dev/null @@ -1,29 +0,0 @@ -/* Positive: comptime through an overloaded (type class) function. - Scale.scale takes a comptime factor; if factor == 1 it returns x - unchanged (conditional evaluated at comptime since factor is comptime). - mulWord is builtinPure, so multiplication of comptime values is comptime. - The verifier must follow specialization and accept this. -*/ -import std.{*}; - -forall a. class a : Scale { - function scale(comptime factor : word, comptime x : a) -> comptime a; -} - -instance word : Scale { - function scale(comptime factor : word, comptime x : word) -> comptime word { - if (factor == 1) { - return x; - } else { - return x * factor; - } - } -} - -contract ComptimeOverloadedOk { - function main() -> word { - let a : comptime word = Scale.scale(1, 32); - let b : comptime word = Scale.scale(3, 10); - return a + b; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_param_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_param_ok.sol new file mode 100644 index 00000000..58d98c54 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_param_ok.sol @@ -0,0 +1,14 @@ +/* Positive: literal passed to comptime param. + x+x desugars to Add.add(x,x) -> addWord(x,x), which is builtinPure, + so the comptime annotation on the result is valid. +*/ +import std; + +contract ComptimeParamOk { + function double(comptime x: word) returns (comptime) { + return x + x; + } + function main() returns (word) { + return double(21); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_param_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_param_ok.solc deleted file mode 100644 index 68bf7247..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_param_ok.solc +++ /dev/null @@ -1,14 +0,0 @@ -/* Positive: literal passed to comptime param. - x+x desugars to Add.add(x,x) -> addWord(x,x), which is builtinPure, - so the comptime annotation on the result is valid. -*/ -import std; - -contract ComptimeParamOk { - function double(comptime x : word) -> comptime word { - return x + x; - } - function main() -> word { - return double(21); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_runtime_arg.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_runtime_arg.sol new file mode 100644 index 00000000..d40acb84 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_runtime_arg.sol @@ -0,0 +1,22 @@ +/* Negative: runtime value passed to a comptime parameter — must fail. + sloadWord uses sload; storage is mutable state, so its result is + a runtime value; passing it to double's comptime param is an error. +*/ +import std; + +function sloadWord() returns (word) { + let v : word; + assembly { + v := sload(0) + } + return v; +} + +contract ComptimeRuntimeArg { + function double(comptime x: word) returns (comptime) { + return x + x; + } + function main() returns (word) { + return double(sloadWord()); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_runtime_arg.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_runtime_arg.solc deleted file mode 100644 index ed9e0132..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/ct_runtime_arg.solc +++ /dev/null @@ -1,22 +0,0 @@ -/* Negative: runtime value passed to a comptime parameter — must fail. - sloadWord uses sload; storage is mutable state, so its result is - a runtime value; passing it to double's comptime param is an error. -*/ -import std; - -function sloadWord() -> word { - let v : word; - assembly { - v := sload(0) - } - return v; -} - -contract ComptimeRuntimeArg { - function double(comptime x : word) -> comptime word { - return x + x; - } - function main() -> word { - return double(sloadWord()); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/erc7201-lit.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/erc7201-lit.sol new file mode 100644 index 00000000..004f225b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/erc7201-lit.sol @@ -0,0 +1,17 @@ +import * from std; + +// erc7201 is comptime-only: given a string-literal namespace it folds the two +// nested keccaks and the word arithmetic down to a single bytes32 slot +// constant, with no runtime hashing. +contract Erc7201Lit { + function main() public returns (bytes32) { + // keccak256(abi.encode(uint256(keccak256("example.main")) - 1)) & ~0xff + // == 0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500 + return erc7201("example.main"); + } + + // keccakWordLit on its own: keccak of a word's 32-byte big-endian form. + function wordHash() public returns (word) { + return keccakWordLit(0); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/erc7201-lit.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/erc7201-lit.solc deleted file mode 100644 index 4431bc35..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/erc7201-lit.solc +++ /dev/null @@ -1,17 +0,0 @@ -import std.{*}; - -// erc7201 is comptime-only: given a string-literal namespace it folds the two -// nested keccaks and the word arithmetic down to a single bytes32 slot -// constant, with no runtime hashing. -contract Erc7201Lit { - public function main() -> bytes32 { - // keccak256(abi.encode(uint256(keccak256("example.main")) - 1)) & ~0xff - // == 0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500 - return erc7201("example.main"); - } - - // keccakWordLit on its own: keccak of a word's 32-byte big-endian form. - public function wordHash() -> word { - return keccakWordLit(0); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib.sol new file mode 100644 index 00000000..cf054116 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib.sol @@ -0,0 +1,14 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +function fib(n: word) returns (word) { + if(n < 2) { return n; } else {return fib(n-1) + fib(n-2); } +} + +contract Fib { +function main() public returns (word) { + return fib(10); +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib.solc deleted file mode 100644 index 46498180..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib.solc +++ /dev/null @@ -1,14 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -function fib(n : word) -> word { - if(n < 2) { return n; } else {return fib(n-1) + fib(n-2); } -} - -contract Fib { -public function main() -> word { - return fib(10); -} -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib2.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib2.sol new file mode 100644 index 00000000..48c31936 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib2.sol @@ -0,0 +1,12 @@ +import * from std; + +function fib2(n: word) returns (comptime) { + if(n < 2) { return n; } else {return fib2(n-1) + fib2(n-2); } +} + +contract Fib { + function main() returns (word) { + let res : comptime = fib2(10); + return res; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib2.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib2.solc deleted file mode 100644 index 5cd5c869..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib2.solc +++ /dev/null @@ -1,12 +0,0 @@ -import std.{*}; - -function fib2(n : word) -> comptime word { - if(n < 2) { return n; } else {return fib2(n-1) + fib2(n-2); } -} - -contract Fib { - function main() -> word { - let res : comptime word = fib2(10); - return res; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib3.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib3.sol new file mode 100644 index 00000000..4295ea80 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib3.sol @@ -0,0 +1,12 @@ +import * from std; + +function fib3(n: word) returns (word) { + if(n < 2) { return n; } else {return fib3(n-1) + fib3(n-2); } +} + +contract Fib { + function main() returns (word) { + let res : comptime = fib3(10); + return res; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib3.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib3.solc deleted file mode 100644 index 62d396e8..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/fib3.solc +++ /dev/null @@ -1,12 +0,0 @@ -import std.{*}; - -function fib3(n : word) -> word { - if(n < 2) { return n; } else {return fib3(n-1) + fib3(n-2); } -} - -contract Fib { - function main() -> word { - let res : comptime word = fib3(10); - return res; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/int-untyped-let.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/int-untyped-let.sol new file mode 100644 index 00000000..e727bd83 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/int-untyped-let.sol @@ -0,0 +1,20 @@ +// Bare integer literals with integer class instances from std. + +import {Eq,Ord,lt,Add,Sub} from std; + +function fib(comptime n: integer) returns (comptime) { + if (n < 2) { + return n; + } else { + return + fib(n - 1) + fib(n - 2); + } +} + +contract IntegerLit { + function main() returns (word) { + let x = 20; + let res : comptime = Int.fromInteger(fib(x)); + return res; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/int-untyped-let.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/int-untyped-let.solc deleted file mode 100644 index ffad1f7d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/int-untyped-let.solc +++ /dev/null @@ -1,20 +0,0 @@ -// Bare integer literals with integer class instances from std. - -import std.{Eq,Ord,lt,Add,Sub}; - -function fib(comptime n : integer) -> comptime integer { - if (n < 2) { - return n; - } else { - return - fib(n - 1) + fib(n - 2); - } -} - -contract IntegerLit { - function main() -> word { - let x = 20; - let res : comptime word = Int.fromInteger(fib(x)); - return res; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-basic.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-basic.sol new file mode 100644 index 00000000..a7072fc1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-basic.sol @@ -0,0 +1,11 @@ +// Exercises integer primitives: wordToInteger, wordFromInteger, integerAdd, integerMul. +// Integer-typed lets are implicitly comptime; literals are polymorphic via FromInteger. +// Expected: main() folds to word literal 100. + +contract IntegerBasic { + function main() returns (word) { + let x = 42; + let y = integerAdd(x, 8); + return wordFromInteger(integerMul(y, 2)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-basic.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-basic.solc deleted file mode 100644 index c58ee6ef..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-basic.solc +++ /dev/null @@ -1,11 +0,0 @@ -// Exercises integer primitives: wordToInteger, wordFromInteger, integerAdd, integerMul. -// Integer-typed lets are implicitly comptime; literals are polymorphic via FromInteger. -// Expected: main() folds to word literal 100. - -contract IntegerBasic { - function main() -> word { - let x = 42; - let y = integerAdd(x, 8); - return wordFromInteger(integerMul(y, 2)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-fib.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-fib.sol new file mode 100644 index 00000000..54b5d56b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-fib.sol @@ -0,0 +1,20 @@ +// Fibonacci using the comptime-only integer type. +// No import std needed: uses only compiler builtins. +// Expected: main() folds to word literal 55 (fib(10)). + +function fib(comptime n: integer) returns (comptime) { + if (integerLt(n, 2)) { + return n; + } else { + return integerAdd( + fib(integerSub(n, 1)), + fib(integerSub(n, 2)) + ); + } +} + +contract FibInteger { + function main() returns (word) { + return wordFromInteger(fib(10)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-fib.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-fib.solc deleted file mode 100644 index 9726d8e8..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-fib.solc +++ /dev/null @@ -1,20 +0,0 @@ -// Fibonacci using the comptime-only integer type. -// No import std needed: uses only compiler builtins. -// Expected: main() folds to word literal 55 (fib(10)). - -function fib(comptime n : integer) -> comptime integer { - if (integerLt(n, 2)) { - return n; - } else { - return integerAdd( - fib(integerSub(n, 1)), - fib(integerSub(n, 2)) - ); - } -} - -contract FibInteger { - function main() -> word { - return wordFromInteger(fib(10)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-from-integer.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-from-integer.sol new file mode 100644 index 00000000..c18110ae --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-from-integer.sol @@ -0,0 +1,28 @@ +import * from std; + +// Tests Num.fromInteger for word (Typedef.abs = identity) and uint256 (wraps in uint256(...)). +// Also tests the full design-doc pattern: comptime integer fib result converted via Num.fromInteger. + +function fib(comptime n: integer) returns (comptime) { + if (integerLt(n, wordToInteger(2))) { + return n; + } else { + return integerAdd( + fib(integerSub(n, wordToInteger(1))), + fib(integerSub(n, wordToInteger(2))) + ); + } +} + +// Exercises both instances. +// word path: Typedef.abs for word is identity => fromInteger(wordToInteger(42)) = 42 +// uint256 path: Typedef.abs wraps in uint256 => fromInteger(fib(10)) = uint256(55) +// Returns Typedef.rep(u) = 55, demonstrating the uint256 round-trip. +// Expected: main() folds to word literal 55. +contract IntegerFromInteger { + function main() returns (word) { + let w : comptime = Num.fromInteger(wordToInteger(42)); + let u : comptime = Num.fromInteger(fib(wordToInteger(10))); + return Typedef.rep(u); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-from-integer.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-from-integer.solc deleted file mode 100644 index e156e2d4..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-from-integer.solc +++ /dev/null @@ -1,28 +0,0 @@ -import std.{*}; - -// Tests Num.fromInteger for word (Typedef.abs = identity) and uint256 (wraps in uint256(...)). -// Also tests the full design-doc pattern: comptime integer fib result converted via Num.fromInteger. - -function fib(comptime n : integer) -> comptime integer { - if (integerLt(n, wordToInteger(2))) { - return n; - } else { - return integerAdd( - fib(integerSub(n, wordToInteger(1))), - fib(integerSub(n, wordToInteger(2))) - ); - } -} - -// Exercises both instances. -// word path: Typedef.abs for word is identity => fromInteger(wordToInteger(42)) = 42 -// uint256 path: Typedef.abs wraps in uint256 => fromInteger(fib(10)) = uint256(55) -// Returns Typedef.rep(u) = 55, demonstrating the uint256 round-trip. -// Expected: main() folds to word literal 55. -contract IntegerFromInteger { - function main() -> word { - let w : comptime word = Num.fromInteger(wordToInteger(42)); - let u : comptime uint256 = Num.fromInteger(fib(wordToInteger(10))); - return Typedef.rep(u); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-class.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-class.sol new file mode 100644 index 00000000..24319eea --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-class.sol @@ -0,0 +1,22 @@ +// Bare integer literals with integer class instances from std. +// The type checker infers the literal type from context: the integer:Ord/Add/Sub +// instances constrain unresolved literals to `integer`. + +import {Eq,Ord,lt,Add,Sub} from std; + +function fib(comptime n: integer) returns (comptime) { + if (n < 2) { + return n; + } else { + return + fib(n - 1) + fib(n - 2); + } +} + +contract IntegerLit { + function main() returns (word) { + let x : comptime = 20; + let res : comptime = wordFromInteger(fib(x)); + return res; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-class.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-class.solc deleted file mode 100644 index 636381a7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-class.solc +++ /dev/null @@ -1,22 +0,0 @@ -// Bare integer literals with integer class instances from std. -// The type checker infers the literal type from context: the integer:Ord/Add/Sub -// instances constrain unresolved literals to `integer`. - -import std.{Eq,Ord,lt,Add,Sub}; - -function fib(comptime n : integer) -> comptime integer { - if (n < 2) { - return n; - } else { - return - fib(n - 1) + fib(n - 2); - } -} - -contract IntegerLit { - function main() -> word { - let x : comptime integer = 20; - let res : comptime word = wordFromInteger(fib(x)); - return res; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-cond.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-cond.sol new file mode 100644 index 00000000..794e868e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-cond.sol @@ -0,0 +1,11 @@ +// Integer literals in conditional expression branches. +// The expected type is propagated to both branches of a Cond, so literals +// in branches infer the correct type. + +contract CondLit { + function main() returns (word) { + // Both literal branches should infer type word from the return annotation. + let x : word = (true) ? 1 : 2; + return x; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-cond.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-cond.solc deleted file mode 100644 index f6dcd8c5..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-cond.solc +++ /dev/null @@ -1,11 +0,0 @@ -// Integer literals in conditional expression branches. -// The expected type is propagated to both branches of a Cond, so literals -// in branches infer the correct type. - -contract CondLit { - function main() -> word { - // Both literal branches should infer type word from the return annotation. - let x : word = if (true) then 1 else 2; - return x; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-pat.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-pat.sol new file mode 100644 index 00000000..bc1b3fe8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-pat.sol @@ -0,0 +1,39 @@ +// Integer literal patterns against word and integer scrutinees. + +import {Add} from std; + +function classify_word(comptime n: word) returns (comptime) { + match (n) { +case 0 { +return 10; +} +case 1 { +return 20; +} +default { +return 0; +} +} +} + +function classify_integer(comptime n: integer) returns (comptime) { + match (n) { +case 0 { +return integerAdd(n, 10); +} +case 1 { +return integerAdd(n, 20); +} +default { +return n; +} +} +} + +contract PatternLit { + function main() returns (word) { + let a : comptime = classify_word(1); + let b : comptime = classify_integer(0); + return Add.add(a, wordFromInteger(b)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-pat.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-pat.solc deleted file mode 100644 index f5ec9007..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-pat.solc +++ /dev/null @@ -1,27 +0,0 @@ -// Integer literal patterns against word and integer scrutinees. - -import std.{Add}; - -function classify_word(comptime n : word) -> comptime word { - match n { - | 0 => return 10; - | 1 => return 20; - | _ => return 0; - } -} - -function classify_integer(comptime n : integer) -> comptime integer { - match n { - | 0 => return integerAdd(n, 10); - | 1 => return integerAdd(n, 20); - | _ => return n; - } -} - -contract PatternLit { - function main() -> word { - let a : comptime word = classify_word(1); - let b : comptime integer = classify_integer(0); - return Add.add(a, wordFromInteger(b)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-poly.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-poly.sol new file mode 100644 index 00000000..17fd000b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-poly.sol @@ -0,0 +1,19 @@ +// Polymorphic literal inference: the type of an unannotated integer literal is +// determined by unification with the surrounding context. +// +// Add.add(s, 1) with s:word => 1 infers as word (Add a => a->a->a, a=word) +// integerAdd(n, 1) with n:integer => 1 infers as integer (param type is integer) + +import {Add} from std; + +contract PolyLit { + function main() returns (word) { + let s : word = 0; + // 1 inferred as word via Add.add constraint + let s2 : word = Add.add(s, 1); + // literal in integer context; type and comptime inferred + let n = wordToInteger(s2); + let n2 = integerAdd(n, 1); + return wordFromInteger(n2); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-poly.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-poly.solc deleted file mode 100644 index d67ab32c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-poly.solc +++ /dev/null @@ -1,19 +0,0 @@ -// Polymorphic literal inference: the type of an unannotated integer literal is -// determined by unification with the surrounding context. -// -// Add.add(s, 1) with s:word => 1 infers as word (Add a => a->a->a, a=word) -// integerAdd(n, 1) with n:integer => 1 infers as integer (param type is integer) - -import std.{Add}; - -contract PolyLit { - function main() -> word { - let s : word = 0; - // 1 inferred as word via Add.add constraint - let s2 : word = Add.add(s, 1); - // literal in integer context; type and comptime inferred - let n = wordToInteger(s2); - let n2 = integerAdd(n, 1); - return wordFromInteger(n2); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-safe.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-safe.sol new file mode 100644 index 00000000..98bed62e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-safe.sol @@ -0,0 +1,28 @@ +import * from std; + +// Safety: verify literals pick up the correct type from context, no spurious coercions. +// +// addWord(1, 2) — word params, so 1 and 2 get wordFromInteger coercions +// wordToInteger(42) — word param, so 42 gets wordFromInteger coercion +// integerEq(wordToInteger(42), wordToInteger(42)) +// — the 42 literals are inside wordToInteger calls (word param) +// let z : word = 5 — explicit word annotation, wordFromInteger coercion inserted + +contract IntegerLitSafe { + function main() returns (word) { + // word arithmetic: 1 and 2 must stay as word literals + let a : word = addWord(1, 2); + + // already-explicit coercions: no double-wrapping of the inner 42 + let ok : comptime = integerEq(wordToInteger(42), wordToInteger(42)); + + // wordFromInteger param is integer, but wordToInteger(10) is a Call not a + // literal, so no double-wrap; b folds to 10 + let b : comptime = wordFromInteger(wordToInteger(10)); + + // word-annotated let: annotation is word, not integer -> no coercion + let z : word = 5; + + return addWord(a, addWord(b, z)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-safe.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-safe.solc deleted file mode 100644 index 4eef8d7b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-safe.solc +++ /dev/null @@ -1,28 +0,0 @@ -import std.{*}; - -// Safety: verify literals pick up the correct type from context, no spurious coercions. -// -// addWord(1, 2) — word params, so 1 and 2 get wordFromInteger coercions -// wordToInteger(42) — word param, so 42 gets wordFromInteger coercion -// integerEq(wordToInteger(42), wordToInteger(42)) -// — the 42 literals are inside wordToInteger calls (word param) -// let z : word = 5 — explicit word annotation, wordFromInteger coercion inserted - -contract IntegerLitSafe { - function main() -> word { - // word arithmetic: 1 and 2 must stay as word literals - let a : word = addWord(1, 2); - - // already-explicit coercions: no double-wrapping of the inner 42 - let ok : comptime bool = integerEq(wordToInteger(42), wordToInteger(42)); - - // wordFromInteger param is integer, but wordToInteger(10) is a Call not a - // literal, so no double-wrap; b folds to 10 - let b : comptime word = wordFromInteger(wordToInteger(10)); - - // word-annotated let: annotation is word, not integer -> no coercion - let z : word = 5; - - return addWord(a, addWord(b, z)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-word-site.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-word-site.sol new file mode 100644 index 00000000..37970467 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-word-site.sol @@ -0,0 +1,13 @@ +// Integer literals at word-typed sites receive automatic wordFromInteger coercions. +// Tests: +// let x : word = N -- explicit word annotation +// return N -- return in word-returning function +// passing literal to word parameter + +contract WordSite { + function main() returns (word) { + let a : word = 42; + let b : word = 0; + return a; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-word-site.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-word-site.solc deleted file mode 100644 index 8b52fbc9..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit-word-site.solc +++ /dev/null @@ -1,13 +0,0 @@ -// Integer literals at word-typed sites receive automatic wordFromInteger coercions. -// Tests: -// let x : word = N -- explicit word annotation -// return N -- return in word-returning function -// passing literal to word parameter - -contract WordSite { - function main() -> word { - let a : word = 42; - let b : word = 0; - return a; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit.sol new file mode 100644 index 00000000..96f827bd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit.sol @@ -0,0 +1,26 @@ +// Bare integer literals at `integer` sites, without explicit wordToInteger. +// The type checker infers the literal type from the expected type at each site: +// let x : comptime integer = 10 -- expected type is integer +// integerLt(n, 2) -- param type is integer +// integerSub(n, 1) -- param type is integer +// +// Expected: main() folds to word literal 55 (fib(10)). + +function fib(comptime n: integer) returns (comptime) { + if (integerLt(n, 2)) { + return n; + } else { + return integerAdd( + fib(integerSub(n, 1)), + fib(integerSub(n, 2)) + ); + } +} + +contract IntegerLit { + function main() returns (word) { + let x : comptime = 10; + let res : comptime = wordFromInteger(fib(x)); + return res; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit.solc deleted file mode 100644 index db376d78..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/integer-lit.solc +++ /dev/null @@ -1,26 +0,0 @@ -// Bare integer literals at `integer` sites, without explicit wordToInteger. -// The type checker infers the literal type from the expected type at each site: -// let x : comptime integer = 10 -- expected type is integer -// integerLt(n, 2) -- param type is integer -// integerSub(n, 1) -- param type is integer -// -// Expected: main() folds to word literal 55 (fib(10)). - -function fib(comptime n : integer) -> comptime integer { - if (integerLt(n, 2)) { - return n; - } else { - return integerAdd( - fib(integerSub(n, 1)), - fib(integerSub(n, 2)) - ); - } -} - -contract IntegerLit { - function main() -> word { - let x : comptime integer = 10; - let res : comptime word = wordFromInteger(fib(x)); - return res; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/match_labels.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/match_labels.sol new file mode 100644 index 00000000..5231ac7b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/match_labels.sol @@ -0,0 +1,29 @@ +/* Test comptime expression match labels: the intended use case is + matching function selectors against keccak hashes of signatures. + Covers: keccakLit of a literal, keccakLit of a concatenation, wildcard. +*/ + +import * from std; + +contract MatchLabels { + + function dispatch(selector: word) returns (word) { + match (selector) { +case comptime keccakLit("transfer(address,uint256)") { +return 1; +} +case comptime keccakLit("balanceOf" + "(" + "address" + ")") { +return 2; +} +default { +return 0; +} +} + } + + function main() returns (word) { + let t : comptime = keccakLit("transfer(address,uint256)"); + let b : comptime = keccakLit("balanceOf(address)"); + return dispatch(t) + dispatch(b) + dispatch(0); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/match_labels.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/match_labels.solc deleted file mode 100644 index f88edeaa..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/match_labels.solc +++ /dev/null @@ -1,23 +0,0 @@ -/* Test comptime expression match labels: the intended use case is - matching function selectors against keccak hashes of signatures. - Covers: keccakLit of a literal, keccakLit of a concatenation, wildcard. -*/ - -import std.{*}; - -contract MatchLabels { - - function dispatch(selector : word) -> word { - match selector { - | comptime keccakLit("transfer(address,uint256)") => return 1; - | comptime keccakLit("balanceOf" + "(" + "address" + ")") => return 2; - | _ => return 0; - } - } - - function main() -> word { - let t : comptime word = keccakLit("transfer(address,uint256)"); - let b : comptime word = keccakLit("balanceOf(address)"); - return dispatch(t) + dispatch(b) + dispatch(0); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-concat-mem.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-concat-mem.sol new file mode 100644 index 00000000..7aba662e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-concat-mem.sol @@ -0,0 +1,29 @@ +// Comptime string concatenation, materialized into runtime memory(string). +// Concatenation runs at comptime (in the `string` domain); the result is +// materialized at the memory(string) site. All four forms below build the same +// "Hello, world!" (length 13), so they fold to one StrLit and share ONE +// generated allocator (dedup). main returns 4 * 13 = 52. +// +// Forms exercised: +// a — `+` wrapped in Str.fromString (overloaded Add at the `string` site) +// b — terse concatLit (Str.fromString inserted by the desugarer) +// c — nested concatLit (intermediate wraps fold to identity) +// d — A2: a `string`-typed let, then convert (dead-let substitution) + +import std; +import * from std; + +contract StringConcat { + function viaLet() returns (memory) { + let s : string = "Hello, " + "world!"; + return Str.fromString(s); + } + + function main() public returns (word) { + let a : memory = Str.fromString("Hello, " + "world!"); + let b : memory = concatLit("Hello, ", "world!"); + let c : memory = concatLit(concatLit("Hello", ", "), "world!"); + let d : memory = viaLet(); + return strlen(a) + strlen(b) + strlen(c) + strlen(d); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-concat-mem.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-concat-mem.solc deleted file mode 100644 index aaab6c37..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-concat-mem.solc +++ /dev/null @@ -1,29 +0,0 @@ -// Comptime string concatenation, materialized into runtime memory(string). -// Concatenation runs at comptime (in the `string` domain); the result is -// materialized at the memory(string) site. All four forms below build the same -// "Hello, world!" (length 13), so they fold to one StrLit and share ONE -// generated allocator (dedup). main returns 4 * 13 = 52. -// -// Forms exercised: -// a — `+` wrapped in Str.fromString (overloaded Add at the `string` site) -// b — terse concatLit (Str.fromString inserted by the desugarer) -// c — nested concatLit (intermediate wraps fold to identity) -// d — A2: a `string`-typed let, then convert (dead-let substitution) - -import std; -import std.{*}; - -contract StringConcat { - function viaLet() -> memory(string) { - let s : string = "Hello, " + "world!"; - return Str.fromString(s); - } - - public function main() -> word { - let a : memory(string) = Str.fromString("Hello, " + "world!"); - let b : memory(string) = concatLit("Hello, ", "world!"); - let c : memory(string) = concatLit(concatLit("Hello", ", "), "world!"); - let d : memory(string) = viaLet(); - return strlen(a) + strlen(b) + strlen(c) + strlen(d); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-dedup.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-dedup.sol new file mode 100644 index 00000000..a4aefe18 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-dedup.sol @@ -0,0 +1,15 @@ +// Distinct string literals get distinct allocators; identical literals share +// one (dedup by content). "alpha" is used twice and "beta" once, so the +// generated hull must contain exactly two __strlit_* allocators. + +import std; +import * from std; + +contract StringDedup { + function main() public returns (word) { + let x : memory = "alpha"; + let y : memory = "beta"; + let z : memory = "alpha"; + return strlen(x) + strlen(y) + strlen(z); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-dedup.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-dedup.solc deleted file mode 100644 index 777838c2..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-dedup.solc +++ /dev/null @@ -1,15 +0,0 @@ -// Distinct string literals get distinct allocators; identical literals share -// one (dedup by content). "alpha" is used twice and "beta" once, so the -// generated hull must contain exactly two __strlit_* allocators. - -import std; -import std.{*}; - -contract StringDedup { - public function main() -> word { - let x : memory(string) = "alpha"; - let y : memory(string) = "beta"; - let z : memory(string) = "alpha"; - return strlen(x) + strlen(y) + strlen(z); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-keccak.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-keccak.sol new file mode 100644 index 00000000..6dfd3caf --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-keccak.sol @@ -0,0 +1,11 @@ +import std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract StringLitKeccak { + function main() public returns (word) { + // keccakLit folds to a 256-bit word (EVM/Yul semantics) + return std.keccakLit("abc"); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-keccak.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-keccak.solc deleted file mode 100644 index 5d950e41..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-keccak.solc +++ /dev/null @@ -1,11 +0,0 @@ -import std; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract StringLitKeccak { - public function main() -> word { - // keccakLit folds to a 256-bit word (EVM/Yul semantics) - return std.keccakLit("abc"); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-len.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-len.sol new file mode 100644 index 00000000..88a72daa --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-len.sol @@ -0,0 +1,11 @@ +import std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract StringLitLen { + function main() public returns (word) { + // strlenLit folds to a word + return std.strlenLit("hello"); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-len.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-len.solc deleted file mode 100644 index 06a1a8e1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-len.solc +++ /dev/null @@ -1,11 +0,0 @@ -import std; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract StringLitLen { - public function main() -> word { - // strlenLit folds to a word - return std.strlenLit("hello"); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-mem.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-mem.sol new file mode 100644 index 00000000..c7c730ed --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-mem.sol @@ -0,0 +1,14 @@ +// Materialize a string literal into a runtime memory(string). +// The literal is comptime; Str.fromString at memory(string) lowers to a +// per-literal allocator that writes the length and characters into memory. +// +// Expected: compiles; main() returns a memory(string) for "abcd". + +import std; +import * from std; + +contract StringLitMem { + function main() public returns (memory) { + return "abcd"; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-mem.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-mem.solc deleted file mode 100644 index 73a2c89c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-mem.solc +++ /dev/null @@ -1,14 +0,0 @@ -// Materialize a string literal into a runtime memory(string). -// The literal is comptime; Str.fromString at memory(string) lowers to a -// per-literal allocator that writes the length and characters into memory. -// -// Expected: compiles; main() returns a memory(string) for "abcd". - -import std; -import std.{*}; - -contract StringLitMem { - public function main() -> memory(string) { - return "abcd"; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-ops.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-ops.sol new file mode 100644 index 00000000..1639eec7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-ops.sol @@ -0,0 +1,15 @@ +import std; +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +// These functions are intended to be folded by MastEval at compile time. + +contract StringLitOps { + function main() public { + // concatLit folds to a string literal, enabling revertLit("...") lowering + let s : comptime = concatLit("ab", "cd"); + std.revertLit(s); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-ops.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-ops.solc deleted file mode 100644 index 95dad668..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-ops.solc +++ /dev/null @@ -1,15 +0,0 @@ -import std; -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -// These functions are intended to be folded by MastEval at compile time. - -contract StringLitOps { - public function main() -> () { - // concatLit folds to a string literal, enabling revertLit("...") lowering - let s : comptime string = concatLit("ab", "cd"); - std.revertLit(s); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-param-erasure.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-param-erasure.sol new file mode 100644 index 00000000..943ffa0a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-param-erasure.sol @@ -0,0 +1,40 @@ +// Comptime-only parameters are erased by substituting their literal argument. +// +// A source `Str` instance that *materializes* its argument only works if the +// literal reaches the instance body: inside `fromString` the argument is a +// parameter, so `memStringFromLit(s)` would never match EmitHull's intercept. +// MastEval clones the callee per literal, substitutes it, and drops the +// parameter, so the body ends up holding a `StrLit` again. +// +// Both call sites below go through that path; the second one only works +// because the clone happens after comptime folding, so `concatLit` has +// already collapsed to a single literal by then. +// +// main returns strlen("abcd") + strlen("abcd") = 8. + +import std; +import * from std; + +enum Wrapped { Wrapped(memory) } + +impl Str { + function fromString(s: string) returns (Wrapped) { + return Wrapped(Str.fromString(s)); + } +} + +function unwrap(w: Wrapped) returns (memory) { + match (w) { +case Wrapped(m) { +return m; +} +} +} + +contract StringParamErasure { + function main() returns (word) { + let direct : Wrapped = "abcd"; + let folded : Wrapped = concatLit("ab", "cd"); + return strlen(unwrap(direct)) + strlen(unwrap(folded)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-param-erasure.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-param-erasure.solc deleted file mode 100644 index ff64932d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-param-erasure.solc +++ /dev/null @@ -1,38 +0,0 @@ -// Comptime-only parameters are erased by substituting their literal argument. -// -// A source `Str` instance that *materializes* its argument only works if the -// literal reaches the instance body: inside `fromString` the argument is a -// parameter, so `memStringFromLit(s)` would never match EmitHull's intercept. -// MastEval clones the callee per literal, substitutes it, and drops the -// parameter, so the body ends up holding a `StrLit` again. -// -// Both call sites below go through that path; the second one only works -// because the clone happens after comptime folding, so `concatLit` has -// already collapsed to a single literal by then. -// -// main returns strlen("abcd") + strlen("abcd") = 8. - -import std; -import std.{*}; - -data Wrapped = Wrapped(memory(string)); - -instance Wrapped : Str { - function fromString(s: string) -> Wrapped { - return Wrapped(Str.fromString(s)); - } -} - -function unwrap(w: Wrapped) -> memory(string) { - match w { - | Wrapped(m) => return m; - } -} - -contract StringParamErasure { - function main() -> word { - let direct : Wrapped = "abcd"; - let folded : Wrapped = concatLit("ab", "cd"); - return strlen(unwrap(direct)) + strlen(unwrap(folded)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-user-instance.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-user-instance.sol new file mode 100644 index 00000000..0b8c0ac3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-user-instance.sol @@ -0,0 +1,35 @@ +// A user-defined instance of the primitive `Str` class. +// +// `Str` has two bodyless primitive instances (`string`, `memory(string)`) that +// Specialise rewrites directly. Any other instance head is ordinary source +// code with a body, so `Str.fromString` at that result type must resolve +// through the normal resolution table rather than falling back to identity. +// +// Here `Tag`'s instance measures the literal, so the whole conversion folds at +// comptime: main returns strlen("abcd") = 4. + +import std; +import * from std; + +enum Tag { Tag(word) } + +impl Str { + function fromString(s: string) returns (Tag) { + return Tag(strlenLit(s)); + } +} + +function tagLength(t: Tag) returns (word) { + match (t) { +case Tag(n) { +return n; +} +} +} + +contract StringUserInstance { + function main() returns (word) { + let t : Tag = "abcd"; + return tagLength(t); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-user-instance.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-user-instance.solc deleted file mode 100644 index a1747cf1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-user-instance.solc +++ /dev/null @@ -1,33 +0,0 @@ -// A user-defined instance of the primitive `Str` class. -// -// `Str` has two bodyless primitive instances (`string`, `memory(string)`) that -// Specialise rewrites directly. Any other instance head is ordinary source -// code with a body, so `Str.fromString` at that result type must resolve -// through the normal resolution table rather than falling back to identity. -// -// Here `Tag`'s instance measures the literal, so the whole conversion folds at -// comptime: main returns strlen("abcd") = 4. - -import std; -import std.{*}; - -data Tag = Tag(word); - -instance Tag : Str { - function fromString(s: string) -> Tag { - return Tag(strlenLit(s)); - } -} - -function tagLength(t: Tag) -> word { - match t { - | Tag(n) => return n; - } -} - -contract StringUserInstance { - function main() -> word { - let t : Tag = "abcd"; - return tagLength(t); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/uint256-lit.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/uint256-lit.sol new file mode 100644 index 00000000..56e1a256 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/uint256-lit.sol @@ -0,0 +1,13 @@ +// Bare integer literals at uint256-typed sites use `instance uint256 : Int`. +// The instance's fromInteger wraps `wordFromInteger`, so an out-of-range +// literal is truncated mod 2^256, matching the `word` site behaviour. +import * from std; + +contract Uint256Lit { + function main() returns (word) { + let a : uint256 = 3; + // 2^256 + 5 must truncate to 5. + let b : uint256 = 0x10000000000000000000000000000000000000000000000000000000000000005; + return Typedef.rep(a) + Typedef.rep(b); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/uint256-lit.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/uint256-lit.solc deleted file mode 100644 index 6eed609f..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/uint256-lit.solc +++ /dev/null @@ -1,13 +0,0 @@ -// Bare integer literals at uint256-typed sites use `instance uint256 : Int`. -// The instance's fromInteger wraps `wordFromInteger`, so an out-of-range -// literal is truncated mod 2^256, matching the `word` site behaviour. -import std.{*}; - -contract Uint256Lit { - function main() -> word { - let a : uint256 = 3; - // 2^256 + 5 must truncate to 5. - let b : uint256 = 0x10000000000000000000000000000000000000000000000000000000000000005; - return Typedef.rep(a) + Typedef.rep(b); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/Revert.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/Revert.sol new file mode 100644 index 00000000..e0962961 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/Revert.sol @@ -0,0 +1,19 @@ +import * from std; +import * from std.dispatch; + +function my_revert() returns (word) { + revertLit("regression"); + return 0; +} + +contract Foo { + constructor() {} + + function noAnswer() public returns (uint256) { + return uint256(my_revert()); + } + + function answer() public returns (uint256) { + return uint256(42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/Revert.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/Revert.solc deleted file mode 100644 index 88e8229d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/Revert.solc +++ /dev/null @@ -1,19 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -function my_revert() -> word { - revertLit("regression"); - return 0; -} - -contract Foo { - constructor() {} - - public function noAnswer() -> uint256 { - return uint256(my_revert()); - } - - public function answer() -> uint256 { - return uint256(42); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_address_array.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_address_array.sol new file mode 100644 index 00000000..fd7b92aa --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_address_array.sol @@ -0,0 +1,23 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; + +// calldata(array(address)) — a dynamic array of a STATIC value type. Unlike +// bytes[] (dynamic elements, offset table), address is static, so elements sit +// inline at a fixed 32-byte stride (headSize(address) = 32). Each element is a +// left-padded 20-byte address; decoding checks the high 12 bytes are zero +// (DirtyHigherBitsForAddress). Exercises the static-element abiArrayGet branch. +contract AddressArr { + constructor() {} + + // The i-th address. + function at(items: calldata>, i: uint256) public returns (address) { + return items[i]; + } + + // Number of elements. + function count(items: calldata>) public returns (uint256) { + return items.length(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_address_array.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_address_array.solc deleted file mode 100644 index f7449701..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_address_array.solc +++ /dev/null @@ -1,23 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; - -// calldata(array(address)) — a dynamic array of a STATIC value type. Unlike -// bytes[] (dynamic elements, offset table), address is static, so elements sit -// inline at a fixed 32-byte stride (headSize(address) = 32). Each element is a -// left-padded 20-byte address; decoding checks the high 12 bytes are zero -// (DirtyHigherBitsForAddress). Exercises the static-element abiArrayGet branch. -contract AddressArr { - constructor() {} - - // The i-th address. - public function at(items : calldata(array(address)), i : uint256) -> address { - return items[i]; - } - - // Number of elements. - public function count(items : calldata(array(address))) -> uint256 { - return items.length(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_array_sum.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_array_sum.sol new file mode 100644 index 00000000..49cdd1a8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_array_sum.sol @@ -0,0 +1,56 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; + +// ABI-decoding a dynamic array whose element is a sum-typed ADT. +// +// `Operation` has two constructors, so its Generic representation is the +// primitive sum `sum(uint256, uint256)` (Approve = inl, Reject = inr). Each +// wire element is therefore two words — a tag word then the payload — which the +// word-per-slot memory(DynArray(...)) representation cannot hold. The array is +// instead decoded lazily from calldata: the parameter becomes a +// `calldata>` handle to the length word, and elements are +// decoded on demand. Indexing uses the ordinary `ops[i]` sugar (calldata-array +// RValueIdxAccess) and `ops.length()` uses the Length-trait UFCS — the same +// surface syntax as storage arrays. `ops` is a parameter, so this relies on +// value-receiver UFCS (NameResolution), not just the field-receiver form. +enum Operation { Approve(uint256), Reject(uint256) } + +contract Batch { + constructor() {} + + // Number of operations in the array. + function count(ops: calldata>) public returns (uint256) { + return ops.length(); + } + + // Constructor of element i, mapped to a distinct sentinel: 16 for Approve, + // 32 for Reject. Deliberately not 0/1 — those coincide with the on-wire sum + // tag (inl=0, inr=1), so non-trivial values prove the match actually + // discriminates the constructor rather than echoing the raw tag word. + function tagOf(ops: calldata>, i: uint256) public returns (uint256) { + let op : Operation = ops[i]; + match (op) { +case Operation.Approve(_) { +return uint256(16); +} +case Operation.Reject(_) { +return uint256(32); +} +} + } + + // Payload (the uint256) of element i, regardless of constructor. + function amountOf(ops: calldata>, i: uint256) public returns (uint256) { + let op : Operation = ops[i]; + match (op) { +case Operation.Approve(v) { +return v; +} +case Operation.Reject(v) { +return v; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_array_sum.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_array_sum.solc deleted file mode 100644 index c6a790f6..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_array_sum.solc +++ /dev/null @@ -1,48 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; - -// ABI-decoding a dynamic array whose element is a sum-typed ADT. -// -// `Operation` has two constructors, so its Generic representation is the -// primitive sum `sum(uint256, uint256)` (Approve = inl, Reject = inr). Each -// wire element is therefore two words — a tag word then the payload — which the -// word-per-slot memory(DynArray(...)) representation cannot hold. The array is -// instead decoded lazily from calldata: the parameter becomes a -// `calldata(array(Operation))` handle to the length word, and elements are -// decoded on demand. Indexing uses the ordinary `ops[i]` sugar (calldata-array -// RValueIdxAccess) and `ops.length()` uses the Length-class UFCS — the same -// surface syntax as storage arrays. `ops` is a parameter, so this relies on -// value-receiver UFCS (NameResolution), not just the field-receiver form. -data Operation = Approve(uint256) | Reject(uint256); - -contract Batch { - constructor() {} - - // Number of operations in the array. - public function count(ops : calldata(array(Operation))) -> uint256 { - return ops.length(); - } - - // Constructor of element i, mapped to a distinct sentinel: 16 for Approve, - // 32 for Reject. Deliberately not 0/1 — those coincide with the on-wire sum - // tag (inl=0, inr=1), so non-trivial values prove the match actually - // discriminates the constructor rather than echoing the raw tag word. - public function tagOf(ops : calldata(array(Operation)), i : uint256) -> uint256 { - let op : Operation = ops[i]; - match op { - | Operation.Approve(_) => return uint256(16); - | Operation.Reject(_) => return uint256(32); - } - } - - // Payload (the uint256) of element i, regardless of constructor. - public function amountOf(ops : calldata(array(Operation)), i : uint256) -> uint256 { - let op : Operation = ops[i]; - match op { - | Operation.Approve(v) => return v; - | Operation.Reject(v) => return v; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_batch_adt.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_batch_adt.sol new file mode 100644 index 00000000..d704cb67 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_batch_adt.sol @@ -0,0 +1,89 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; + +// Complex nested-ADT ABI decode over a calldata dynamic array. The element is a +// three-level algebraic type built from sums *and* products: +// +// Operation : sum(address, address) -- static +// Signature : sum((bytes32, bytes32), address) -- static +// Batch : sum( (Operation, Signature) -- Queue : static, inline +// , (uint256, memory(bytes)) ) -- Execute : dynamic (carries bytes) +// +// `items[i]` dispatches through the calldata-array RValueIdxAccess instance to +// abiArrayGet, decoding the element on demand into a fully-formed `Batch` to +// match on — exercising the derived ABIDecode across three nested data types. +// +// `Batch` is a *dynamic* element (its Execute branch carries a memory(bytes)), +// so the array uses the offset-table layout: after the length word comes one +// 32-byte offset per element (relative to the element region), each pointing at +// that element's own encoding. abiArrayGet rebases onto the element start, and +// the element's inner offsets (the memory(bytes) leaf) resolve relative to the +// element — so both the static Queue path and the dynamic Execute path decode. +// +// This is currently registered via runDispatchTest, which compiles the contract +// through the whole pipeline. A runtime .json fixture (exercising the decode on +// real calldata) needs the exact solcore-generated selector for the nested-ADT +// signature, which has to be captured from a local sol-core run. + +enum Operation { AddSigner(address), RemoveSigner(address) } +enum Signature { ECDSA(bytes32, bytes32), Contract(address) } +enum Batch { Queue(Operation, Signature), Execute(uint256, memory) } + +// Address added by an AddSigner op (address(0) for a RemoveSigner). +function addedSigner(op: Operation) returns (address) { + match (op) { +case Operation.AddSigner(a) { +return a; +} +case Operation.RemoveSigner(_) { +return address(0); +} +} +} + +// Verifying contract address of a Contract signature (address(0) for ECDSA). +function contractVerifier(sig: Signature) returns (address) { + match (sig) { +case Signature.Contract(a) { +return a; +} +case Signature.ECDSA(_, _) { +return address(0); +} +} +} + +contract BatchDecoder { + constructor() {} + + // From a Queue(AddSigner(a), Contract(c)) element, return (a, c): the signer + // being added and the contract that verifies the queued action. + function queueSigner(items: calldata>, i: uint256) public returns (address, address) { + let b : Batch = items[i]; + match (b) { +case Batch.Queue(op, sig) { +return (addedSigner(op), contractVerifier(sig)); +} +case Batch.Execute(_, _) { +return (address(0), address(0)); +} +} + } + + // The payload bytes carried by an Execute element. + function execPayload(items: calldata>, i: uint256) public returns (memory) { + let b : Batch = items[i]; + let out : memory; + match (b) { +case Batch.Execute(_, payload) { +out = payload; +} +case Batch.Queue(_, _) { +revertEmpty(); +} +} + return out; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_batch_adt.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_batch_adt.solc deleted file mode 100644 index 74390c27..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_batch_adt.solc +++ /dev/null @@ -1,73 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; - -// Complex nested-ADT ABI decode over a calldata dynamic array. The element is a -// three-level algebraic type built from sums *and* products: -// -// Operation : sum(address, address) -- static -// Signature : sum((bytes32, bytes32), address) -- static -// Batch : sum( (Operation, Signature) -- Queue : static, inline -// , (uint256, memory(bytes)) ) -- Execute : dynamic (carries bytes) -// -// `items[i]` dispatches through the calldata-array RValueIdxAccess instance to -// abiArrayGet, decoding the element on demand into a fully-formed `Batch` to -// match on — exercising the derived ABIDecode across three nested data types. -// -// `Batch` is a *dynamic* element (its Execute branch carries a memory(bytes)), -// so the array uses the offset-table layout: after the length word comes one -// 32-byte offset per element (relative to the element region), each pointing at -// that element's own encoding. abiArrayGet rebases onto the element start, and -// the element's inner offsets (the memory(bytes) leaf) resolve relative to the -// element — so both the static Queue path and the dynamic Execute path decode. -// -// This is currently registered via runDispatchTest, which compiles the contract -// through the whole pipeline. A runtime .json fixture (exercising the decode on -// real calldata) needs the exact solcore-generated selector for the nested-ADT -// signature, which has to be captured from a local sol-core run. - -data Operation = AddSigner(address) | RemoveSigner(address); -data Signature = ECDSA(bytes32, bytes32) | Contract(address); -data Batch = Queue(Operation, Signature) | Execute(uint256, memory(bytes)); - -// Address added by an AddSigner op (address(0) for a RemoveSigner). -function addedSigner(op : Operation) -> address { - match op { - | Operation.AddSigner(a) => return a; - | Operation.RemoveSigner(_) => return address(0); - } -} - -// Verifying contract address of a Contract signature (address(0) for ECDSA). -function contractVerifier(sig : Signature) -> address { - match sig { - | Signature.Contract(a) => return a; - | Signature.ECDSA(_, _) => return address(0); - } -} - -contract BatchDecoder { - constructor() {} - - // From a Queue(AddSigner(a), Contract(c)) element, return (a, c): the signer - // being added and the contract that verifies the queued action. - public function queueSigner(items : calldata(array(Batch)), i : uint256) -> (address, address) { - let b : Batch = items[i]; - match b { - | Batch.Queue(op, sig) => return (addedSigner(op), contractVerifier(sig)); - | Batch.Execute(_, _) => return (address(0), address(0)); - } - } - - // The payload bytes carried by an Execute element. - public function execPayload(items : calldata(array(Batch)), i : uint256) -> memory(bytes) { - let b : Batch = items[i]; - let out : memory(bytes); - match b { - | Batch.Execute(_, payload) => out = payload; - | Batch.Queue(_, _) => revertEmpty(); - } - return out; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_bytes_array.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_bytes_array.sol new file mode 100644 index 00000000..e84854d1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_bytes_array.sol @@ -0,0 +1,25 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; + +// calldata(array(bytes)) — a dynamic array whose element is itself dynamic, the +// canonical Solidity `bytes[]`. After the length word the region is a table of +// 32-byte offsets (relative to the region base), one per element, each pointing +// at that element's `[length][data]` encoding. `items[i]` decodes the i-th +// element on demand: abiArrayGet hands the element decoder the region base + +// element i's slot, and the memory(bytes) decoder follows that offset to the +// element's length word — no ADT wrapper needed, unlike abi_batch_adt. +contract BytesArray { + constructor() {} + + // The i-th bytes element. + function at(items: calldata>>, i: uint256) public returns (memory) { + return items[i]; + } + + // Number of elements. + function count(items: calldata>>) public returns (uint256) { + return items.length(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_bytes_array.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_bytes_array.solc deleted file mode 100644 index 694c8f75..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_bytes_array.solc +++ /dev/null @@ -1,25 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; - -// calldata(array(bytes)) — a dynamic array whose element is itself dynamic, the -// canonical Solidity `bytes[]`. After the length word the region is a table of -// 32-byte offsets (relative to the region base), one per element, each pointing -// at that element's `[length][data]` encoding. `items[i]` decodes the i-th -// element on demand: abiArrayGet hands the element decoder the region base + -// element i's slot, and the memory(bytes) decoder follows that offset to the -// element's length word — no ADT wrapper needed, unlike abi_batch_adt. -contract BytesArray { - constructor() {} - - // The i-th bytes element. - public function at(items : calldata(array(memory(bytes))), i : uint256) -> memory(bytes) { - return items[i]; - } - - // Number of elements. - public function count(items : calldata(array(memory(bytes)))) -> uint256 { - return items.length(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_dyn_sum.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_dyn_sum.sol new file mode 100644 index 00000000..3c2c7fce --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_dyn_sum.sol @@ -0,0 +1,45 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; + +// Minimal dynamic sum in a calldata array — like abi_batch_adt but with NO +// nested ADTs: the constructors carry primitive / bytes fields directly. This +// isolates the dynamic-sum decode path (which abi_batch_adt exercises and +// abi_array_sum does not) from nested-ADT decode (an ADT field inside an ADT, +// which abi_batch_adt also has and this test does not). +// +// DynSum : sum(uint256, bytes) -- dynamic (Blob carries memory(bytes)) +enum DynSum { Small(uint256), Blob(memory) } + +contract DynSumArr { + constructor() {} + + // The uint256 in a Small element (0 for a Blob). + function smallOf(items: calldata>, i: uint256) public returns (uint256) { + let d : DynSum = items[i]; + match (d) { +case DynSum.Small(x) { +return x; +} +case DynSum.Blob(_) { +return uint256(0); +} +} + } + + // The bytes payload of a Blob element. + function blobOf(items: calldata>, i: uint256) public returns (memory) { + let d : DynSum = items[i]; + let out : memory; + match (d) { +case DynSum.Blob(b) { +out = b; +} +case DynSum.Small(_) { +revertEmpty(); +} +} + return out; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_dyn_sum.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_dyn_sum.solc deleted file mode 100644 index d1c0f66b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_dyn_sum.solc +++ /dev/null @@ -1,37 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; - -// Minimal dynamic sum in a calldata array — like abi_batch_adt but with NO -// nested ADTs: the constructors carry primitive / bytes fields directly. This -// isolates the dynamic-sum decode path (which abi_batch_adt exercises and -// abi_array_sum does not) from nested-ADT decode (an ADT field inside an ADT, -// which abi_batch_adt also has and this test does not). -// -// DynSum : sum(uint256, bytes) -- dynamic (Blob carries memory(bytes)) -data DynSum = Small(uint256) | Blob(memory(bytes)); - -contract DynSumArr { - constructor() {} - - // The uint256 in a Small element (0 for a Blob). - public function smallOf(items : calldata(array(DynSum)), i : uint256) -> uint256 { - let d : DynSum = items[i]; - match d { - | DynSum.Small(x) => return x; - | DynSum.Blob(_) => return uint256(0); - } - } - - // The bytes payload of a Blob element. - public function blobOf(items : calldata(array(DynSum)), i : uint256) -> memory(bytes) { - let d : DynSum = items[i]; - let out : memory(bytes); - match d { - | DynSum.Blob(b) => out = b; - | DynSum.Small(_) => revertEmpty(); - } - return out; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_dyn_sum_return.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_dyn_sum_return.sol new file mode 100644 index 00000000..09c72fcc --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_dyn_sum_return.sol @@ -0,0 +1,67 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; + +// Return a *dynamic sum by value* from a dispatched function — the case the +// generic ABI encoder used to get wrong (it wrote only the top-level tag word, +// collapsing the whole value to a single 0x00…0 head). The companion +// `abi_dyn_sum.sol` deliberately avoids this by returning `memory(bytes)` / +// individual words; here we exercise the fixed `sum(f,g):ABIEncode` head-offset +// path head-on. +// +// D2 : sum(uint256, bytes) -- dynamic (R carries bytes) +// D3 : sum(uint256, sum(uint256, bytes)) -- dynamic, deeply right-nested +// S2 : sum(uint256, uint256) -- static (control: inline, no offset) +// +// A dynamic sum is referenced by a 32-byte offset and laid out inline in the +// tail as [tag][branch]; each nested dynamic sum level emits its own offset +// word, so a deeply nested variant encodes as nested offsets, not flat tags. +// A static sum stays inline as [tag][branch] with no leading offset — its wire +// form is unchanged by the fix. +enum D2 { L(uint256), R(memory) } +enum D3 { X(uint256), Y(uint256), Z(memory) } +enum S2 { P(uint256), Q(uint256) } + +contract DynSumRet { + constructor() {} + + // ── shallow dynamic sum ──────────────────────────────────────────────── + // inl branch (static uint256 payload) of a dynamic sum: still takes the + // dynamic encode path (offset word + inline [tag][value] in the tail). + function makeL(n: uint256) public returns (D2) { + return D2.L(n); + } + + // inr branch carrying a dynamic bytes payload: [off][1][off][len][data]. + function makeR(b: memory) public returns (D2) { + return D2.R(b); + } + + // ── deeply right-nested dynamic sum ──────────────────────────────────── + // outer inl: [off][0][value] + function makeX(n: uint256) public returns (D3) { + return D3.X(n); + } + + // inr(inl …): two dynamic-sum levels, so two nested offsets: [off][1][off][0][value] + function makeY(n: uint256) public returns (D3) { + return D3.Y(n); + } + + // inr(inr bytes): nested offsets down to the bytes leaf: + // [off][1][off][1][off][len][data] + function makeZ(b: memory) public returns (D3) { + return D3.Z(b); + } + + // ── static sum control ───────────────────────────────────────────────── + // Byte-identical to the pre-fix output: inline [tag][value], no offset word. + function makeP(n: uint256) public returns (S2) { + return S2.P(n); + } + + function makeQ(n: uint256) public returns (S2) { + return S2.Q(n); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_dyn_sum_return.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_dyn_sum_return.solc deleted file mode 100644 index 1e7c9531..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_dyn_sum_return.solc +++ /dev/null @@ -1,67 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; - -// Return a *dynamic sum by value* from a dispatched function — the case the -// generic ABI encoder used to get wrong (it wrote only the top-level tag word, -// collapsing the whole value to a single 0x00…0 head). The companion -// `abi_dyn_sum.solc` deliberately avoids this by returning `memory(bytes)` / -// individual words; here we exercise the fixed `sum(f,g):ABIEncode` head-offset -// path head-on. -// -// D2 : sum(uint256, bytes) -- dynamic (R carries bytes) -// D3 : sum(uint256, sum(uint256, bytes)) -- dynamic, deeply right-nested -// S2 : sum(uint256, uint256) -- static (control: inline, no offset) -// -// A dynamic sum is referenced by a 32-byte offset and laid out inline in the -// tail as [tag][branch]; each nested dynamic sum level emits its own offset -// word, so a deeply nested variant encodes as nested offsets, not flat tags. -// A static sum stays inline as [tag][branch] with no leading offset — its wire -// form is unchanged by the fix. -data D2 = L(uint256) | R(memory(bytes)); -data D3 = X(uint256) | Y(uint256) | Z(memory(bytes)); -data S2 = P(uint256) | Q(uint256); - -contract DynSumRet { - constructor() {} - - // ── shallow dynamic sum ──────────────────────────────────────────────── - // inl branch (static uint256 payload) of a dynamic sum: still takes the - // dynamic encode path (offset word + inline [tag][value] in the tail). - public function makeL(n : uint256) -> D2 { - return D2.L(n); - } - - // inr branch carrying a dynamic bytes payload: [off][1][off][len][data]. - public function makeR(b : memory(bytes)) -> D2 { - return D2.R(b); - } - - // ── deeply right-nested dynamic sum ──────────────────────────────────── - // outer inl: [off][0][value] - public function makeX(n : uint256) -> D3 { - return D3.X(n); - } - - // inr(inl …): two dynamic-sum levels, so two nested offsets: [off][1][off][0][value] - public function makeY(n : uint256) -> D3 { - return D3.Y(n); - } - - // inr(inr bytes): nested offsets down to the bytes leaf: - // [off][1][off][1][off][len][data] - public function makeZ(b : memory(bytes)) -> D3 { - return D3.Z(b); - } - - // ── static sum control ───────────────────────────────────────────────── - // Byte-identical to the pre-fix output: inline [tag][value], no offset word. - public function makeP(n : uint256) -> S2 { - return S2.P(n); - } - - public function makeQ(n : uint256) -> S2 { - return S2.Q(n); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_encode_adt.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_encode_adt.sol new file mode 100644 index 00000000..eed61df3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_encode_adt.sol @@ -0,0 +1,73 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +// Direct tests for `abi_encode` over user-defined algebraic data types (ADTs). +// +// An ADT reaches `abi_encode` through its auto-derived `Generic` representation +// and the ABIGeneric bridges (std/ABIGeneric.sol): a product constructor +// represents as the primitive tuple of its fields, and a sum represents as the +// binary `sum(f, g)` type (inl = first constructor, inr = second). Each method +// encodes an ADT value and returns the `memory(bytes)` result, which the +// dispatcher then ABI-encodes as `bytes`, so the return data is +// [0x20 offset][length][ abi_encode output, padded ] +// and the inner payload is EXACTLY what `abi_encode` produced for the ADT. +// +// The data types are left to auto-derive their instances: this gives them not +// only a `Generic` instance but a CONCRETE `ABIAttribs` instance reporting the +// representation's real head size. That concrete instance is what makes +// `abi_encode` correct here — a manual `Generic` instance (which requires +// `pragma no-generic-instance-for`) would suppress the derived `ABIAttribs`, so +// `headSize` would fall back to the catch-all `default instance t:ABIAttribs` +// (32 bytes) and truncate the encoding to its first word. See DeriveGeneric.hs. +// +// Layouts pinned here: +// * static product (uint256, uint256) -> two head words (len 0x40) +// * static sum sum(uint256, uint256) -> [tag][branch] (len 0x40) +// * dynamic sum sum(uint256, string) -> a dynamic value: the head slot +// holds an offset (0x20) to the sum body [tag][branch...] laid out in the +// tail — even the static (Empty) branch keeps that offset wrapper. + +// static product +enum Point { Point(uint256, uint256) } + +// static sum +enum Choice { First(uint256), Second(uint256) } + +// dynamic sum (the Text branch carries a dynamic string) +enum StrBox { Empty(uint256), Text(memory) } + +contract AbiEncodeAdt { + constructor() {} + + // Static product: encodes as the tuple (a, b) — two inline head words. + function encPoint(a: uint256, b: uint256) public returns (memory) { + return abi_encode(Point(a, b)); + } + + // Static sum, left constructor: [tag = 0][x]. + function encFirst(x: uint256) public returns (memory) { + return abi_encode(Choice.First(x)); + } + + // Static sum, right constructor: [tag = 1][x]. + function encSecond(x: uint256) public returns (memory) { + return abi_encode(Choice.Second(x)); + } + + // Dynamic sum, static branch: still offset-wrapped — [0x20] -> [tag = 0][n]. + function encEmpty(n: uint256) public returns (memory) { + return abi_encode(StrBox.Empty(n)); + } + + // Dynamic sum, dynamic branch: [0x20] -> [tag = 1][branch offset][len][data]. + function encText() public returns (memory) { + let raw : string = "abc"; + let s : memory = Str.fromString(raw); + return abi_encode(StrBox.Text(s)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_encode_adt.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_encode_adt.solc deleted file mode 100644 index 51621d85..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_encode_adt.solc +++ /dev/null @@ -1,73 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -// Direct tests for `abi_encode` over user-defined algebraic data types (ADTs). -// -// An ADT reaches `abi_encode` through its auto-derived `Generic` representation -// and the ABIGeneric bridges (std/ABIGeneric.solc): a product constructor -// represents as the primitive tuple of its fields, and a sum represents as the -// binary `sum(f, g)` type (inl = first constructor, inr = second). Each method -// encodes an ADT value and returns the `memory(bytes)` result, which the -// dispatcher then ABI-encodes as `bytes`, so the return data is -// [0x20 offset][length][ abi_encode output, padded ] -// and the inner payload is EXACTLY what `abi_encode` produced for the ADT. -// -// The data types are left to auto-derive their instances: this gives them not -// only a `Generic` instance but a CONCRETE `ABIAttribs` instance reporting the -// representation's real head size. That concrete instance is what makes -// `abi_encode` correct here — a manual `Generic` instance (which requires -// `pragma no-generic-instance-for`) would suppress the derived `ABIAttribs`, so -// `headSize` would fall back to the catch-all `default instance t:ABIAttribs` -// (32 bytes) and truncate the encoding to its first word. See DeriveGeneric.hs. -// -// Layouts pinned here: -// * static product (uint256, uint256) -> two head words (len 0x40) -// * static sum sum(uint256, uint256) -> [tag][branch] (len 0x40) -// * dynamic sum sum(uint256, string) -> a dynamic value: the head slot -// holds an offset (0x20) to the sum body [tag][branch...] laid out in the -// tail — even the static (Empty) branch keeps that offset wrapper. - -// static product -data Point = Point(uint256, uint256); - -// static sum -data Choice = First(uint256) | Second(uint256); - -// dynamic sum (the Text branch carries a dynamic string) -data StrBox = Empty(uint256) | Text(memory(string)); - -contract AbiEncodeAdt { - constructor() {} - - // Static product: encodes as the tuple (a, b) — two inline head words. - public function encPoint(a : uint256, b : uint256) -> memory(bytes) { - return abi_encode(Point(a, b)); - } - - // Static sum, left constructor: [tag = 0][x]. - public function encFirst(x : uint256) -> memory(bytes) { - return abi_encode(Choice.First(x)); - } - - // Static sum, right constructor: [tag = 1][x]. - public function encSecond(x : uint256) -> memory(bytes) { - return abi_encode(Choice.Second(x)); - } - - // Dynamic sum, static branch: still offset-wrapped — [0x20] -> [tag = 0][n]. - public function encEmpty(n : uint256) -> memory(bytes) { - return abi_encode(StrBox.Empty(n)); - } - - // Dynamic sum, dynamic branch: [0x20] -> [tag = 1][branch offset][len][data]. - public function encText() -> memory(bytes) { - let raw : string = "abc"; - let s : memory(string) = Str.fromString(raw); - return abi_encode(StrBox.Text(s)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_encode_types.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_encode_types.sol new file mode 100644 index 00000000..df93783e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_encode_types.sol @@ -0,0 +1,62 @@ +import * from std; +import * from std.dispatch; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +// Direct tests for the top-level `abi_encode` function (std.sol) across both +// static and dynamic types. +// +// Each method encodes a value with `abi_encode` and returns the resulting +// `memory(bytes)`. The dispatcher then ABI-encodes that `memory(bytes)` return +// as a `bytes` value, so the return data is +// [0x20 offset][length][ abi_encode output, padded ] +// and the inner `[length][...]` payload is EXACTLY what `abi_encode` produced — +// which is what these tests pin down. +// +// Static types encode inline in the head, with no offset word: +// * uint256 / bool / address -> a single 32-byte word (length 0x20) +// * (uint256, uint256) -> two head words back to back (length 0x40) +// Dynamic types put an offset word in the head pointing at a tail: +// * string -> [0x20][len][data] (length 0x60 here) +// * uint256[] -> [0x20][len][elems] (length 0xa0 here) +contract AbiEncodeTypes { + constructor() {} + + // --- static --- + + // uint256 is written directly into the head as one word. + function encUint(x: uint256) public returns (memory) { + return abi_encode(x); + } + + // bool encodes as a single 0/1 word. + function encBool(x: bool) public returns (memory) { + return abi_encode(x); + } + + // address is left-padded into a single word. + function encAddr(x: address) public returns (memory) { + return abi_encode(x); + } + + // A fully static tuple has both words in the head, with no offset. + function encPair(a: uint256, b: uint256) public returns (memory) { + return abi_encode((a, b)); + } + + // --- dynamic --- + + // A string gets a head offset word pointing at a `[len][data]` tail. + function encStr() public returns (memory) { + let raw : string = "abc"; + let s : memory = Str.fromString(raw); + return abi_encode(s); + } + + // A dynamic array gets a head offset word pointing at a `[len][elems]` tail. + function encArr() public returns (memory) { + let a : memory> = [11, 22, 33]; + return abi_encode(a); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_encode_types.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_encode_types.solc deleted file mode 100644 index 0628338e..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_encode_types.solc +++ /dev/null @@ -1,62 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -// Direct tests for the top-level `abi_encode` function (std.solc) across both -// static and dynamic types. -// -// Each method encodes a value with `abi_encode` and returns the resulting -// `memory(bytes)`. The dispatcher then ABI-encodes that `memory(bytes)` return -// as a `bytes` value, so the return data is -// [0x20 offset][length][ abi_encode output, padded ] -// and the inner `[length][...]` payload is EXACTLY what `abi_encode` produced — -// which is what these tests pin down. -// -// Static types encode inline in the head, with no offset word: -// * uint256 / bool / address -> a single 32-byte word (length 0x20) -// * (uint256, uint256) -> two head words back to back (length 0x40) -// Dynamic types put an offset word in the head pointing at a tail: -// * string -> [0x20][len][data] (length 0x60 here) -// * uint256[] -> [0x20][len][elems] (length 0xa0 here) -contract AbiEncodeTypes { - constructor() {} - - // --- static --- - - // uint256 is written directly into the head as one word. - public function encUint(x : uint256) -> memory(bytes) { - return abi_encode(x); - } - - // bool encodes as a single 0/1 word. - public function encBool(x : bool) -> memory(bytes) { - return abi_encode(x); - } - - // address is left-padded into a single word. - public function encAddr(x : address) -> memory(bytes) { - return abi_encode(x); - } - - // A fully static tuple has both words in the head, with no offset. - public function encPair(a : uint256, b : uint256) -> memory(bytes) { - return abi_encode((a, b)); - } - - // --- dynamic --- - - // A string gets a head offset word pointing at a `[len][data]` tail. - public function encStr() -> memory(bytes) { - let raw : string = "abc"; - let s : memory(string) = Str.fromString(raw); - return abi_encode(s); - } - - // A dynamic array gets a head offset word pointing at a `[len][elems]` tail. - public function encArr() -> memory(bytes) { - let a : memory(DynArray(uint256)) = [11, 22, 33]; - return abi_encode(a); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_sum_roundtrip.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_sum_roundtrip.sol new file mode 100644 index 00000000..074225be --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_sum_roundtrip.sol @@ -0,0 +1,41 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; + +// Roundtrip tests for sum ABI coding: `roundtrip(x) -> x` makes the dispatcher +// DECODE the argument from calldata and then ENCODE it straight back into the +// return data. So the returned bytes must equal the input argument payload +// (the calldata after the 4-byte selector) — i.e. encode ∘ decode = identity. +// +// This pins encode and decode as exact inverses for BOTH: +// * static sums (inline [tag][branch], no offset word), and +// * dynamic sums (offset word in the head, [tag][branch] inline in the tail, +// one offset word per nested dynamic level). +// +// The dynamic direction is what the sum(f,g):ABIEncode fix restores: before it, +// encoding a decoded dynamic sum dropped everything but the tag, so the return +// bytes could not match the input. +enum D2 { L(uint256), R(memory) } // dynamic (shallow) +enum D3 { X(uint256), Y(uint256), Z(memory) } // dynamic (deeply nested) +enum S2 { P(uint256), Q(uint256) } // static + +contract SumRoundtrip { + constructor() {} + + // dynamic, shallow: decode a sum(uint256, bytes) then re-encode it. + function rtD2(x: D2) public returns (D2) { + return x; + } + + // dynamic, deeply right-nested: each nested dynamic level round-trips its own + // offset word. + function rtD3(x: D3) public returns (D3) { + return x; + } + + // static control: inline layout must round-trip unchanged. + function rtS2(x: S2) public returns (S2) { + return x; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_sum_roundtrip.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_sum_roundtrip.solc deleted file mode 100644 index dc13caf6..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/abi_sum_roundtrip.solc +++ /dev/null @@ -1,41 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; - -// Roundtrip tests for sum ABI coding: `roundtrip(x) -> x` makes the dispatcher -// DECODE the argument from calldata and then ENCODE it straight back into the -// return data. So the returned bytes must equal the input argument payload -// (the calldata after the 4-byte selector) — i.e. encode ∘ decode = identity. -// -// This pins encode and decode as exact inverses for BOTH: -// * static sums (inline [tag][branch], no offset word), and -// * dynamic sums (offset word in the head, [tag][branch] inline in the tail, -// one offset word per nested dynamic level). -// -// The dynamic direction is what the sum(f,g):ABIEncode fix restores: before it, -// encoding a decoded dynamic sum dropped everything but the tag, so the return -// bytes could not match the input. -data D2 = L(uint256) | R(memory(bytes)); // dynamic (shallow) -data D3 = X(uint256) | Y(uint256) | Z(memory(bytes)); // dynamic (deeply nested) -data S2 = P(uint256) | Q(uint256); // static - -contract SumRoundtrip { - constructor() {} - - // dynamic, shallow: decode a sum(uint256, bytes) then re-encode it. - public function rtD2(x : D2) -> D2 { - return x; - } - - // dynamic, deeply right-nested: each nested dynamic level round-trips its own - // offset word. - public function rtD3(x : D3) -> D3 { - return x; - } - - // static control: inline layout must round-trip unchanged. - public function rtS2(x : S2) -> S2 { - return x; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_copy.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_copy.sol new file mode 100644 index 00000000..9b275758 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_copy.sol @@ -0,0 +1,46 @@ +import * from std; +import * from std.dispatch; + +// Whole-array assignment `a = b` follows Solidity: it is a deep copy, not an +// alias; assigning an array to itself is a no-op; and a copy that shrinks the +// destination clears the slots it abandons, so regrowing yields zeros. +contract ArrayCopy { + a : array; + b : array; + + constructor() {} + + function pushA(v: uint256) public { + ArrayPush.push(a, v); + } + + function pushB(v: uint256) public { + ArrayPush.push(b, v); + } + + // a = b + function copyBintoA() public { + a = b; + } + + // a = a (must be a no-op, not a self-clobbering copy) + function copyAintoA() public { + a = a; + } + + function setB(i: uint256, v: uint256) public { + b[i] = v; + } + + function growA(n: uint256) public { + Array.setLength(a, n); + } + + function lenA() public returns (uint256) { + return Length.length(a); + } + + function getA(i: uint256) public returns (uint256) { + return a[i]; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_copy.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_copy.solc deleted file mode 100644 index 6d359c79..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_copy.solc +++ /dev/null @@ -1,46 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// Whole-array assignment `a = b` follows Solidity: it is a deep copy, not an -// alias; assigning an array to itself is a no-op; and a copy that shrinks the -// destination clears the slots it abandons, so regrowing yields zeros. -contract ArrayCopy { - a : array(uint256); - b : array(uint256); - - constructor() {} - - public function pushA(v : uint256) -> () { - ArrayPush.push(a, v); - } - - public function pushB(v : uint256) -> () { - ArrayPush.push(b, v); - } - - // a = b - public function copyBintoA() -> () { - a = b; - } - - // a = a (must be a no-op, not a self-clobbering copy) - public function copyAintoA() -> () { - a = a; - } - - public function setB(i : uint256, v : uint256) -> () { - b[i] = v; - } - - public function growA(n : uint256) -> () { - Array.setLength(a, n); - } - - public function lenA() -> uint256 { - return Length.length(a); - } - - public function getA(i : uint256) -> uint256 { - return a[i]; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_nested.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_nested.sol new file mode 100644 index 00000000..3fe2d26b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_nested.sol @@ -0,0 +1,57 @@ +import * from std; +import * from std.dispatch; + +// Nested storage arrays and aliasing, on the EVM. +// +// `grid[i]` yields the inner array's storage handle, so it can be pushed to and +// indexed again. Each inner array lives at its own slot keccak256(outer) + i, and +// its elements at keccak256(that slot) + j. +// +// Binding an array field to a local is an alias (Solidity's `T[] storage p`), not +// a copy: mutating through the local must be visible through the field. +contract NestedArray { + grid : array>; + flat : array; + + constructor() {} + + function growOuter(n: uint256) public { + Array.setLength(grid, n); + } + + // grid[i].push(v) -- the inner handle comes straight out of the index + function pushInner(i: uint256, v: uint256) public { + ArrayPush.push(grid[i], v); + } + + function innerLen(i: uint256) public returns (uint256) { + return Length.length(grid[i]); + } + + function get2(i: uint256, j: uint256) public returns (uint256) { + return grid[i][j]; + } + + function set2(i: uint256, j: uint256, v: uint256) public { + grid[i][j] = v; + } + + // Mutate `flat` through a local alias; the field must observe it. + function aliasPush(v: uint256) public { + let p : storage> = flat; + ArrayPush.push(p, v); + } + + function aliasSet(i: uint256, v: uint256) public { + let p : storage> = flat; + p[i] = v; + } + + function flatLen() public returns (uint256) { + return Length.length(flat); + } + + function getFlat(i: uint256) public returns (uint256) { + return flat[i]; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_nested.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_nested.solc deleted file mode 100644 index 81662976..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_nested.solc +++ /dev/null @@ -1,57 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// Nested storage arrays and aliasing, on the EVM. -// -// `grid[i]` yields the inner array's storage handle, so it can be pushed to and -// indexed again. Each inner array lives at its own slot keccak256(outer) + i, and -// its elements at keccak256(that slot) + j. -// -// Binding an array field to a local is an alias (Solidity's `T[] storage p`), not -// a copy: mutating through the local must be visible through the field. -contract NestedArray { - grid : array(array(uint256)); - flat : array(uint256); - - constructor() {} - - public function growOuter(n : uint256) -> () { - Array.setLength(grid, n); - } - - // grid[i].push(v) -- the inner handle comes straight out of the index - public function pushInner(i : uint256, v : uint256) -> () { - ArrayPush.push(grid[i], v); - } - - public function innerLen(i : uint256) -> uint256 { - return Length.length(grid[i]); - } - - public function get2(i : uint256, j : uint256) -> uint256 { - return grid[i][j]; - } - - public function set2(i : uint256, j : uint256, v : uint256) -> () { - grid[i][j] = v; - } - - // Mutate `flat` through a local alias; the field must observe it. - public function aliasPush(v : uint256) -> () { - let p : storage(array(uint256)) = flat; - ArrayPush.push(p, v); - } - - public function aliasSet(i : uint256, v : uint256) -> () { - let p : storage(array(uint256)) = flat; - p[i] = v; - } - - public function flatLen() -> uint256 { - return Length.length(flat); - } - - public function getFlat(i : uint256) -> uint256 { - return flat[i]; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_ops.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_ops.sol new file mode 100644 index 00000000..29347bfd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_ops.sol @@ -0,0 +1,33 @@ +import * from std; +import * from std.dispatch; + +// Storage-array primitives end to end: push / pop / length / indexed read, +// the two revert paths (index out of range, pop on empty), and the guarantee +// that abandoned slots are zeroed -- so regrowing an array never resurrects the +// values that `pop` or a shrinking `setLength` dropped. +contract ArrayOps { + xs : array; + + constructor() {} + + // NOTE: not named `add` -- that collides with the Yul builtin of the same name. + function pushVal(v: uint256) public { + ArrayPush.push(xs, v); + } + + function popArr() public { + Array.pop(xs); + } + + function len() public returns (uint256) { + return Length.length(xs); + } + + function get(i: uint256) public returns (uint256) { + return xs[i]; + } + + function grow(n: uint256) public { + Array.setLength(xs, n); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_ops.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_ops.solc deleted file mode 100644 index 5a162dbc..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_ops.solc +++ /dev/null @@ -1,33 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// Storage-array primitives end to end: push / pop / length / indexed read, -// the two revert paths (index out of range, pop on empty), and the guarantee -// that abandoned slots are zeroed -- so regrowing an array never resurrects the -// values that `pop` or a shrinking `setLength` dropped. -contract ArrayOps { - xs : array(uint256); - - constructor() {} - - // NOTE: not named `add` -- that collides with the Yul builtin of the same name. - public function pushVal(v : uint256) -> () { - ArrayPush.push(xs, v); - } - - public function popArr() -> () { - Array.pop(xs); - } - - public function len() -> uint256 { - return Length.length(xs); - } - - public function get(i : uint256) -> uint256 { - return xs[i]; - } - - public function grow(n : uint256) -> () { - Array.setLength(xs, n); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_string.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_string.sol new file mode 100644 index 00000000..d66564db --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_string.sol @@ -0,0 +1,44 @@ +import * from std; +import * from std.dispatch; + +// Storage arrays of a *dynamic* element type. `push` stores a `memory(string)` +// through `storage(string):CanStore(memory(string))`, `arr[i]` reads one back, +// and whole-array assignment deep-copies each element's payload -- not just the +// inline slot, which would otherwise leave the copy aliasing the source's tail. +// Both the short (<32 bytes, inline) and long (>=32 bytes, keccak tail) string +// encodings are exercised. +contract ArrayString { + names : array; + backup : array; + + constructor() {} + + function pushName(s: memory) public { + ArrayPush.push(names, s); + } + + function setName(i: uint256, s: memory) public { + names[i] = s; + } + + function getName(i: uint256) public returns (memory) { + return names[i]; + } + + function len() public returns (uint256) { + return Length.length(names); + } + + // backup = names + function saveBackup() public { + backup = names; + } + + function getBackup(i: uint256) public returns (memory) { + return backup[i]; + } + + function lenBackup() public returns (uint256) { + return Length.length(backup); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_string.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_string.solc deleted file mode 100644 index 6bdac8f3..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/array_string.solc +++ /dev/null @@ -1,44 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// Storage arrays of a *dynamic* element type. `push` stores a `memory(string)` -// through `storage(string):CanStore(memory(string))`, `arr[i]` reads one back, -// and whole-array assignment deep-copies each element's payload -- not just the -// inline slot, which would otherwise leave the copy aliasing the source's tail. -// Both the short (<32 bytes, inline) and long (>=32 bytes, keccak tail) string -// encodings are exercised. -contract ArrayString { - names : array(string); - backup : array(string); - - constructor() {} - - public function pushName(s : memory(string)) -> () { - ArrayPush.push(names, s); - } - - public function setName(i : uint256, s : memory(string)) -> () { - names[i] = s; - } - - public function getName(i : uint256) -> memory(string) { - return names[i]; - } - - public function len() -> uint256 { - return Length.length(names); - } - - // backup = names - public function saveBackup() -> () { - backup = names; - } - - public function getBackup(i : uint256) -> memory(string) { - return backup[i]; - } - - public function lenBackup() -> uint256 { - return Length.length(backup); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/arraylit.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/arraylit.sol new file mode 100644 index 00000000..86c450e3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/arraylit.sol @@ -0,0 +1,71 @@ +import * from std; +import * from std.dispatch; + +// Array literals, end to end. +// +// `[e1,...,en]` builds a memory array. Assigning one to a storage array field is +// Solidity's memory -> storage copy: it resizes the field and clears the +// abandoned tail, so shrinking must not leave old elements reachable. +contract ArrayLit { + xs : array; + + constructor() {} + + // --- memory literals --- + + // Reads back an element of a memory literal. Element 0 must be the first + // element, not the length word stored ahead of it. + function memAt(i: uint256) public returns (uint256) { + let m : memory> = [11, 22, 33]; + return m[i]; + } + + function memSum() public returns (uint256) { + let m : memory> = [1, 2, 3, 4]; + let acc : uint256 = uint256(0); + let i : uint256; + for (i = uint256(0); i < uint256(4); i = i + uint256(1)) { + acc = acc + m[i]; + } + return acc; + } + + // Nested literal: the element type is itself a memory array. + function nested() public returns (uint256) { + let g : memory>>> = [[1, 2], [3, 4]]; + let row : memory> = g[uint256(1)]; + return row[uint256(0)]; + } + + // --- storage literals --- + + function setThree() public { + xs = [10, 20, 30]; + } + + function setFive() public { + xs = [1, 2, 3, 4, 5]; + } + + function setTwo() public { + xs = [7, 8]; + } + + function clear() public { + xs = []; + } + + function len() public returns (uint256) { + return Length.length(xs); + } + + function get(i: uint256) public returns (uint256) { + return xs[i]; + } + + // Grow the array back without writing elements. Anything the shrink abandoned + // must read as zero, not as the old value. + function grow(n: uint256) public { + Array.setLength(xs, n); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/arraylit.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/arraylit.solc deleted file mode 100644 index db34347f..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/arraylit.solc +++ /dev/null @@ -1,71 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// Array literals, end to end. -// -// `[e1,...,en]` builds a memory array. Assigning one to a storage array field is -// Solidity's memory -> storage copy: it resizes the field and clears the -// abandoned tail, so shrinking must not leave old elements reachable. -contract ArrayLit { - xs : array(uint256); - - constructor() {} - - // --- memory literals --- - - // Reads back an element of a memory literal. Element 0 must be the first - // element, not the length word stored ahead of it. - public function memAt(i : uint256) -> uint256 { - let m : memory(DynArray(uint256)) = [11, 22, 33]; - return m[i]; - } - - public function memSum() -> uint256 { - let m : memory(DynArray(uint256)) = [1, 2, 3, 4]; - let acc : uint256 = uint256(0); - let i : uint256; - for (i = uint256(0); i < uint256(4); i = i + uint256(1)) { - acc = acc + m[i]; - } - return acc; - } - - // Nested literal: the element type is itself a memory array. - public function nested() -> uint256 { - let g : memory(DynArray(memory(DynArray(uint256)))) = [[1, 2], [3, 4]]; - let row : memory(DynArray(uint256)) = g[uint256(1)]; - return row[uint256(0)]; - } - - // --- storage literals --- - - public function setThree() -> () { - xs = [10, 20, 30]; - } - - public function setFive() -> () { - xs = [1, 2, 3, 4, 5]; - } - - public function setTwo() -> () { - xs = [7, 8]; - } - - public function clear() -> () { - xs = []; - } - - public function len() -> uint256 { - return Length.length(xs); - } - - public function get(i : uint256) -> uint256 { - return xs[i]; - } - - // Grow the array back without writing elements. Anything the shrink abandoned - // must read as zero, not as the old value. - public function grow(n : uint256) -> () { - Array.setLength(xs, n); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/asm_break_continue_leave.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/asm_break_continue_leave.sol new file mode 100644 index 00000000..6690d4c3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/asm_break_continue_leave.sol @@ -0,0 +1,52 @@ +import * from std; +import * from std.dispatch; + +// End-to-end (runs on evmone via the testrunner) check that Yul `break`, +// `continue` and `leave` in inline assembly don't just parse, but actually +// execute with the right control-flow semantics. +contract C { + constructor() {} + + // `continue` + `break`: sum i over [0, 10), skipping i < 2 (continue) and + // stopping once i > 5 (break). So only i in {2,3,4,5} contribute: + // 2 + 3 + 4 + 5 = 14 + // A miscompiled `continue` would also add 0 and 1 (=> 15); a broken `break` + // would keep going and add 6..9 as well. + function loopSum() public returns (uint256) { + let result : word; + assembly { + result := 0 + for { let i := 0 } lt(i, 10) { i := add(i, 1) } { + if lt(i, 2) { + continue + } + if gt(i, 5) { + break + } + result := add(result, i) + } + } + return uint256(result); + } + + // `leave`: clamp(x) returns 3 early (via `leave`) when x > 3, skipping the + // `+ 100`; otherwise it returns x + 100. + // clamp(2) = 102, clamp(9) = 3 => 102 + 3 = 105 + // A broken `leave` would fall through and add 100 to the x > 3 branch too + // (clamp(9) => 103 => total 205). + function clampSum() public returns (uint256) { + let result : word; + assembly { + function clamp(x) -> y { + y := x + if gt(x, 3) { + y := 3 + leave + } + y := add(y, 100) + } + result := add(clamp(2), clamp(9)) + } + return uint256(result); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/asm_break_continue_leave.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/asm_break_continue_leave.solc deleted file mode 100644 index 8b689c2d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/asm_break_continue_leave.solc +++ /dev/null @@ -1,52 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// End-to-end (runs on evmone via the testrunner) check that Yul `break`, -// `continue` and `leave` in inline assembly don't just parse, but actually -// execute with the right control-flow semantics. -contract C { - constructor() {} - - // `continue` + `break`: sum i over [0, 10), skipping i < 2 (continue) and - // stopping once i > 5 (break). So only i in {2,3,4,5} contribute: - // 2 + 3 + 4 + 5 = 14 - // A miscompiled `continue` would also add 0 and 1 (=> 15); a broken `break` - // would keep going and add 6..9 as well. - public function loopSum() -> uint256 { - let result : word; - assembly { - result := 0 - for { let i := 0 } lt(i, 10) { i := add(i, 1) } { - if lt(i, 2) { - continue - } - if gt(i, 5) { - break - } - result := add(result, i) - } - } - return uint256(result); - } - - // `leave`: clamp(x) returns 3 early (via `leave`) when x > 3, skipping the - // `+ 100`; otherwise it returns x + 100. - // clamp(2) = 102, clamp(9) = 3 => 102 + 3 = 105 - // A broken `leave` would fall through and add 100 to the x > 3 branch too - // (clamp(9) => 103 => total 205). - public function clampSum() -> uint256 { - let result : word; - assembly { - function clamp(x) -> y { - y := x - if gt(x, 3) { - y := 3 - leave - } - y := add(y, 100) - } - result := add(clamp(2), clamp(9)) - } - return uint256(result); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/assembly.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/assembly.sol new file mode 100644 index 00000000..dfa492ca --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/assembly.sol @@ -0,0 +1,19 @@ +import * from std; +import * from std.dispatch; + +contract C { + constructor() {} + + // Exercises a Yul block that declares an uninitialized `let y`, assigns the + // boolean literal `true` to it, and writes it back to the surrounding + // `word` local `x`. `true` is the word `1`, so this returns uint256(1). + function asmBool() public returns (uint256) { + let x : word; + assembly { + let y + y := true + x := y + } + return uint256(x); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/assembly.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/assembly.solc deleted file mode 100644 index a39e7cdd..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/assembly.solc +++ /dev/null @@ -1,19 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract C { - constructor() {} - - // Exercises a Yul block that declares an uninitialized `let y`, assigns the - // boolean literal `true` to it, and writes it back to the surrounding - // `word` local `x`. `true` is the word `1`, so this returns uint256(1). - public function asmBool() -> uint256 { - let x : word; - assembly { - let y - y := true - x := y - } - return uint256(x); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/basic.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/basic.sol new file mode 100644 index 00000000..4f4832c4 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/basic.sol @@ -0,0 +1,184 @@ +import * from std; +import * from std.dispatch; +import {address as address_} from std.opcodes; + +function self() returns (address) { + return address(address_()); +} + +contract C { + constructor() {} + function nothing() public {} + + // Re-enters this very contract via raw_call(address(this), ...). The payload + // is the 4-byte selector of an existing entry point (something(), 0xa7a0d537), + // built by left-aligning it in a bytes32 and truncating to 4 bytes. The inner + // call succeeds, so raw_call reports ok == true and returns its returndata + // (the abi-encoded uint256(1)). + function callSelf() public returns (bool, memory) { + let sel: bytes32 = bytes32(0xa7a0d53700000000000000000000000000000000000000000000000000000000); + let payload = truncate(to_bytes(sel), 4); + match (raw_call(self(), uint256(0), payload)) { +case (ok, ret) { +return (ok, ret); +} +} + } + + // Same shape, but the selector (0xdeadc0de) matches no entry point, so dispatch + // reverts (there is no fallback). raw_call swallows the inner revert and reports + // ok == false; this outer call itself still succeeds and returns the revert + // returndata (the 4-byte NoFallback error selector). + function callSelfInvalid() public returns (bool, memory) { + let sel: bytes32 = bytes32(0xdeadc0de00000000000000000000000000000000000000000000000000000000); + let payload = truncate(to_bytes(sel), 4); + match (raw_call(self(), uint256(0), payload)) { +case (ok, ret) { +return (ok, ret); +} +} + } + + function something() public returns (uint256) { + return uint256(1); + } + + function add2(x: uint256, y: uint256) public returns (uint256) { + return Add.add(x,y); + } + + function add3(x: uint256, y: uint256, z: uint256) public returns (uint256) { + return Add.add(z, Add.add(x,y)); + } + + function addmod3(x: uint256, y: uint256, k: uint256) public returns (uint256) { + return addmod(x, y, k); + } + + function mulmod3(x: uint256, y: uint256, k: uint256) public returns (uint256) { + return mulmod(x, y, k); + } + + // Bitwise / modulo via the syntactic sugar only (no explicit trait calls): + // `^` -> BitXor.bxor, `|` -> BitOr.bor, `&` -> BitAnd.band, `%` -> Mod.mod. + function bxor2(x: uint256, y: uint256) public returns (uint256) { + return x ^ y; + } + + function bor2(x: uint256, y: uint256) public returns (uint256) { + return x | y; + } + + function band2(x: uint256, y: uint256) public returns (uint256) { + return x & y; + } + + // Unary bitwise NOT via the sugar only: `~` -> BitNot.bnot. + function bnot1(x: uint256) public returns (uint256) { + return ~x; + } + + function mod2(x: uint256, y: uint256) public returns (uint256) { + return x % y; + } + + // `*` -> Mul.mul, `/` -> Div.div (completing the binary-operator sugar + // set alongside bxor2 / bor2 / band2 / mod2). + function mul2(x: uint256, y: uint256) public returns (uint256) { + return x * y; + } + + function div2(x: uint256, y: uint256) public returns (uint256) { + return x / y; + } + + // Compound assignment statement sugar: each `acc op= y` desugars to + // `acc := acc op y`, so these must agree with the binary operators above. + function pluseq(x: uint256, y: uint256) public returns (uint256) { + let acc : uint256 = x; + acc += y; + return acc; + } + + function minuseq(x: uint256, y: uint256) public returns (uint256) { + let acc : uint256 = x; + acc -= y; + return acc; + } + + function timeseq(x: uint256, y: uint256) public returns (uint256) { + let acc : uint256 = x; + acc *= y; + return acc; + } + + function divideeq(x: uint256, y: uint256) public returns (uint256) { + let acc : uint256 = x; + acc /= y; + return acc; + } + + function modeq(x: uint256, y: uint256) public returns (uint256) { + let acc : uint256 = x; + acc %= y; + return acc; + } + + function bxoreq(x: uint256, y: uint256) public returns (uint256) { + let acc : uint256 = x; + acc ^= y; + return acc; + } + + function bandeq(x: uint256, y: uint256) public returns (uint256) { + let acc : uint256 = x; + acc &= y; + return acc; + } + + function boreq(x: uint256, y: uint256) public returns (uint256) { + let acc : uint256 = x; + acc |= y; + return acc; + } + + // In-place unary bitwise NOT: `acc ~=` desugars to `acc := ~acc`. + function bnoteq(x: uint256) public returns (uint256) { + let acc : uint256 = x; + acc ~=; + return acc; + } + + function id_bytes(b: memory) public returns (memory) { + return b; + } + + function id_string(b: memory) public returns (memory) { + return b; + } + + function id_bytes32(b: bytes32) public returns (bytes32) { + return b; + } + + function id_bytes4(b: bytes4) public returns (bytes4) { + return b; + } + + function id_address(a: address) public returns (address) { + return a; + } + + // Exercises bool:ABIDecode (argument) and bool:ABIEncode (return). + function id_bool(b: bool) public returns (bool) { + return b; + } + + function id_pair() public returns (uint256, uint256) { + return (uint256(7), uint256(11)); + } + + function hidden() returns (uint256) { + return uint256(42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/basic.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/basic.solc deleted file mode 100644 index ac747855..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/basic.solc +++ /dev/null @@ -1,180 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.opcodes.{address as address_}; - -function self() -> address { - return address(address_()); -} - -contract C { - constructor() {} - public function nothing() -> () {} - - // Re-enters this very contract via raw_call(address(this), ...). The payload - // is the 4-byte selector of an existing entry point (something(), 0xa7a0d537), - // built by left-aligning it in a bytes32 and truncating to 4 bytes. The inner - // call succeeds, so raw_call reports ok == true and returns its returndata - // (the abi-encoded uint256(1)). - public function callSelf() -> (bool, memory(bytes)) { - let sel: bytes32 = bytes32(0xa7a0d53700000000000000000000000000000000000000000000000000000000); - let payload = truncate(to_bytes(sel), 4); - match raw_call(self(), uint256(0), payload) { - | (ok, ret) => return (ok, ret); - } - } - - // Same shape, but the selector (0xdeadc0de) matches no entry point, so dispatch - // reverts (there is no fallback). raw_call swallows the inner revert and reports - // ok == false; this outer call itself still succeeds and returns the revert - // returndata (the 4-byte NoFallback error selector). - public function callSelfInvalid() -> (bool, memory(bytes)) { - let sel: bytes32 = bytes32(0xdeadc0de00000000000000000000000000000000000000000000000000000000); - let payload = truncate(to_bytes(sel), 4); - match raw_call(self(), uint256(0), payload) { - | (ok, ret) => return (ok, ret); - } - } - - public function something() -> (uint256) { - return uint256(1); - } - - public function add2(x : uint256, y : uint256) -> uint256 { - return Add.add(x,y); - } - - public function add3(x : uint256, y : uint256, z : uint256) -> uint256 { - return Add.add(z, Add.add(x,y)); - } - - public function addmod3(x : uint256, y : uint256, k : uint256) -> uint256 { - return addmod(x, y, k); - } - - public function mulmod3(x : uint256, y : uint256, k : uint256) -> uint256 { - return mulmod(x, y, k); - } - - // Bitwise / modulo via the syntactic sugar only (no explicit class calls): - // `^` -> BitXor.bxor, `|` -> BitOr.bor, `&` -> BitAnd.band, `%` -> Mod.mod. - public function bxor2(x : uint256, y : uint256) -> uint256 { - return x ^ y; - } - - public function bor2(x : uint256, y : uint256) -> uint256 { - return x | y; - } - - public function band2(x : uint256, y : uint256) -> uint256 { - return x & y; - } - - // Unary bitwise NOT via the sugar only: `~` -> BitNot.bnot. - public function bnot1(x : uint256) -> uint256 { - return ~x; - } - - public function mod2(x : uint256, y : uint256) -> uint256 { - return x % y; - } - - // `*` -> Mul.mul, `/` -> Div.div (completing the binary-operator sugar - // set alongside bxor2 / bor2 / band2 / mod2). - public function mul2(x : uint256, y : uint256) -> uint256 { - return x * y; - } - - public function div2(x : uint256, y : uint256) -> uint256 { - return x / y; - } - - // Compound assignment statement sugar: each `acc op= y` desugars to - // `acc := acc op y`, so these must agree with the binary operators above. - public function pluseq(x : uint256, y : uint256) -> uint256 { - let acc : uint256 = x; - acc += y; - return acc; - } - - public function minuseq(x : uint256, y : uint256) -> uint256 { - let acc : uint256 = x; - acc -= y; - return acc; - } - - public function timeseq(x : uint256, y : uint256) -> uint256 { - let acc : uint256 = x; - acc *= y; - return acc; - } - - public function divideeq(x : uint256, y : uint256) -> uint256 { - let acc : uint256 = x; - acc /= y; - return acc; - } - - public function modeq(x : uint256, y : uint256) -> uint256 { - let acc : uint256 = x; - acc %= y; - return acc; - } - - public function bxoreq(x : uint256, y : uint256) -> uint256 { - let acc : uint256 = x; - acc ^= y; - return acc; - } - - public function bandeq(x : uint256, y : uint256) -> uint256 { - let acc : uint256 = x; - acc &= y; - return acc; - } - - public function boreq(x : uint256, y : uint256) -> uint256 { - let acc : uint256 = x; - acc |= y; - return acc; - } - - // In-place unary bitwise NOT: `acc ~=` desugars to `acc := ~acc`. - public function bnoteq(x : uint256) -> uint256 { - let acc : uint256 = x; - acc ~=; - return acc; - } - - public function id_bytes(b: memory(bytes)) -> memory(bytes) { - return b; - } - - public function id_string(b: memory(string)) -> memory(string) { - return b; - } - - public function id_bytes32(b: bytes32) -> bytes32 { - return b; - } - - public function id_bytes4(b: bytes4) -> bytes4 { - return b; - } - - public function id_address(a: address) -> address { - return a; - } - - // Exercises bool:ABIDecode (argument) and bool:ABIEncode (return). - public function id_bool(b: bool) -> bool { - return b; - } - - public function id_pair() -> (uint256, uint256) { - return (uint256(7), uint256(11)); - } - - function hidden() -> (uint256) { - return uint256(42); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/concat.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/concat.sol new file mode 100644 index 00000000..ce5ac428 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/concat.sol @@ -0,0 +1,42 @@ +import * from std; +import * from std.dispatch; + +contract C { + constructor() {} + + function concat_b32_b32(a: bytes32, b: bytes32) public returns (memory) { + return concat(a, b); + } + + function concat_b32_bytes(a: bytes32, b: memory) public returns (memory) { + return concat(a, b); + } + + function concat_bytes_bytes(a: memory, b: memory) public returns (memory) { + return concat(a, b); + } + + function to_bytes_b32(a: bytes32) public returns (memory) { + return to_bytes(a); + } + + function to_bytes_bytes(a: memory) public returns (memory) { + return to_bytes(a); + } + + function empty_area(n: uint256) public returns (memory) { + return to_bytes(empty(Typedef.rep(n))); + } + + function concat_b32_empty(a: bytes32, n: uint256) public returns (memory) { + return concat(a, empty(Typedef.rep(n))); + } + + function concat_nested_b32(a: bytes32, b: bytes32, c: bytes32) public returns (memory) { + return concat(a, concat(b, c)); + } + + function concat_nested_empty(a: bytes32, n: uint256, c: bytes32) public returns (memory) { + return concat(a, concat(empty(Typedef.rep(n)), c)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/concat.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/concat.solc deleted file mode 100644 index 4d0b59bf..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/concat.solc +++ /dev/null @@ -1,42 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract C { - constructor() {} - - public function concat_b32_b32(a: bytes32, b: bytes32) -> memory(bytes) { - return concat(a, b); - } - - public function concat_b32_bytes(a: bytes32, b: memory(bytes)) -> memory(bytes) { - return concat(a, b); - } - - public function concat_bytes_bytes(a: memory(bytes), b: memory(bytes)) -> memory(bytes) { - return concat(a, b); - } - - public function to_bytes_b32(a: bytes32) -> memory(bytes) { - return to_bytes(a); - } - - public function to_bytes_bytes(a: memory(bytes)) -> memory(bytes) { - return to_bytes(a); - } - - public function empty_area(n: uint256) -> memory(bytes) { - return to_bytes(empty(Typedef.rep(n))); - } - - public function concat_b32_empty(a: bytes32, n: uint256) -> memory(bytes) { - return concat(a, empty(Typedef.rep(n))); - } - - public function concat_nested_b32(a: bytes32, b: bytes32, c: bytes32) -> memory(bytes) { - return concat(a, concat(b, c)); - } - - public function concat_nested_empty(a: bytes32, n: uint256, c: bytes32) -> memory(bytes) { - return concat(a, concat(empty(Typedef.rep(n)), c)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/counter.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/counter.sol new file mode 100644 index 00000000..699c8612 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/counter.sol @@ -0,0 +1,14 @@ +import * from std; +import * from std.dispatch; +contract Counter { + counter : uint256; + + constructor() { + counter = 41; + } + + function test() public returns (uint256) { + counter = counter + 1; + return counter; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/counter.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/counter.solc deleted file mode 100644 index 5b795699..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/counter.solc +++ /dev/null @@ -1,14 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -contract Counter { - counter : uint256; - - constructor() { - counter = 41; - } - - public function test() -> uint256 { - counter = counter + 1; - return counter; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/deposit.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/deposit.sol new file mode 100644 index 00000000..7df57487 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/deposit.sol @@ -0,0 +1,166 @@ +import * from std; +import * from std.dispatch; +import {callvalue} from std.opcodes; + +// TODO: Should use uint64. +// Assumes 64-bit input. +function to_little_endian_64(v: uint256) returns (memory) { + let res: word = allocate_memory(32 + 8); + let value: word = Typedef.rep(v); + assembly { + mstore(res, 8) + mstore8(add(res, 32), and(value, 0xff)) + mstore8(add(res, 33), and(shr(8, value), 0xff)) + mstore8(add(res, 34), and(shr(16, value), 0xff)) + mstore8(add(res, 35), and(shr(24, value), 0xff)) + mstore8(add(res, 36), and(shr(32, value), 0xff)) + mstore8(add(res, 37), and(shr(40, value), 0xff)) + mstore8(add(res, 38), and(shr(48, value), 0xff)) + mstore8(add(res, 39), and(shr(56, value), 0xff)) + } + return memory(res); +} + +// No constants are supported yet, using this as a workaround. +// Defining variables outside of contract/function is not supported. +function DEPOSIT_CONTRACT_TREE_DEPTH() returns (uint256) { + return 32; +} + +function MAX_DEPOSIT_COUNT() returns (uint256) { + // uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1; + // TODO: Could use Bounded(uint32).maxVal() + return 0xFFFFFFFF; +} + +contract DepositContract { + deposit_count : uint256; + // TODO: use fixed-size arrays of DEPOSIT_CONTRACT_TREE_DEPTH() length + branch : array; + zero_hashes : array; + + constructor() { + // Dynamic storage arrays start empty and indexed access is bounds-checked, + // so grow both to DEPOSIT_CONTRACT_TREE_DEPTH() elements before indexing + // them at fixed positions. New slots are zero-initialised, which matches + // the empty sparse Merkle tree these arrays represent. + // Alternatively, `.push(..)` could be used, but this saves sstores. + Array.setLength(branch, DEPOSIT_CONTRACT_TREE_DEPTH()); + Array.setLength(zero_hashes, DEPOSIT_CONTRACT_TREE_DEPTH()); + + // Compute hashes in empty sparse Merkle tree + for (let height = 0; height < (DEPOSIT_CONTRACT_TREE_DEPTH() - 1); height += 1) { + zero_hashes[height + 1] = sha256(concat(zero_hashes[height], zero_hashes[height])); + } + } + + // TODO: this is for testing only + function get_zero_hash(index: uint256) public returns (bytes32) { + return zero_hashes[index]; + } + + function get_deposit_root() public returns (bytes32) { + let node: bytes32; + let size = deposit_count; + for (let height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH(); height += 1) { + if ((size & 1) == 1) { + node = sha256(concat(branch[height], node)); + } else { + node = sha256(concat(node, zero_hashes[height])); + } + size /= 2; + } + return sha256(concat( + concat( + node, + to_little_endian_64(deposit_count) + ), + empty(24) + )); + } + + function get_deposit_count() public returns (memory) { + return to_little_endian_64(deposit_count); + } + + // TODO: once string literals are properly supported, change errors to messages + // matching the deposit contract, full 100% identical behaviour. + function deposit(pubkey: memory, withdrawal_credentials: memory, signature: memory, deposit_data_root: bytes32) public payable { + // Extended ABI length checks since dynamic types are used. + require(MemorySize.len(pubkey) == 48, Error(0x9ca717ed)); // InvalidPubkeyLength() + require(MemorySize.len(withdrawal_credentials) == 32, Error(0x3debbf1e)); // InvalidWithdrawalCredentialsLength() + require(MemorySize.len(signature) == 96, Error(0x4be6321b)); // InvalidSignatureLength() + + // Check deposit amount + // >= 1 ether + require(callvalue() >= 1000000000000000000, Error(0xbdfc7472)); // DepositValueTooLow() + // % 1 gwei == 0 + require((callvalue() % 1000000000) == 0, Error(0x9c7417e9)); // DepositValueNotMultipleOfOneGwei() + + let deposit_amount = callvalue() / 1000000000; // 1 gwei + // <= type(uint64).max + require(deposit_amount <= 0xffffffffffffffff, Error(0x2aa66734)); // DepositValueTooHigh() + + let amount: memory = to_little_endian_64(uint256(deposit_amount)); + // TODO: emit DepositEvent + /* + event DepositEvent( + bytes pubkey, + bytes withdrawal_credentials, + bytes amount, + bytes signature, + bytes index + ); + + emit DepositEvent( + pubkey, + withdrawal_credentials, + amount, + signature, + to_little_endian_64(uint64(deposit_count)) + ); + */ + assembly { + // TODO: need to ABI-encode all the arguments + log1(0, 0, 0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5) + } + + // Compute deposit data root (`DepositData` hash tree root) + let pubkey_root = sha256(concat(pubkey, empty(16))); + let signature_root = sha256(concat( + sha256(truncate(signature, 64)), + sha256(concat(slice_(signature, 64), empty(32))) + )); + let node = sha256(concat( + sha256(concat(pubkey_root, withdrawal_credentials)), + sha256(concat(concat(amount, empty(24)), signature_root)) + )); + + // Verify computed and expected deposit data roots match + require(node == deposit_data_root, Error(0x2ec2f183)); // ReconstructedDepositDataMismatch() + + // Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`) + require(deposit_count < MAX_DEPOSIT_COUNT(), Error(0xef5ccf66)); // MerkleTreeFull() + + // Add deposit data root to Merkle tree (update a single `branch` node) + deposit_count += 1; + let size = deposit_count; + for (let height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH(); height += 1) { + if ((size & 1) == 1) { + branch[height] = node; + return (); + } + node = sha256(concat(branch[height], node)); + size /= 2; + } + + // As the loop should always end prematurely with the `return` statement, + // this code should be unreachable. We assert `false` just to be safe. + assert(false); + } + + function supportsInterface(interfaceId: bytes4) public returns (bool) { + unimplemented(); + return false; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/deposit.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/deposit.solc deleted file mode 100644 index 9656d84d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/deposit.solc +++ /dev/null @@ -1,166 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.opcodes.{callvalue}; - -// TODO: Should use uint64. -// Assumes 64-bit input. -function to_little_endian_64(v: uint256) -> memory(bytes) { - let res: word = allocate_memory(32 + 8); - let value: word = Typedef.rep(v); - assembly { - mstore(res, 8) - mstore8(add(res, 32), and(value, 0xff)) - mstore8(add(res, 33), and(shr(8, value), 0xff)) - mstore8(add(res, 34), and(shr(16, value), 0xff)) - mstore8(add(res, 35), and(shr(24, value), 0xff)) - mstore8(add(res, 36), and(shr(32, value), 0xff)) - mstore8(add(res, 37), and(shr(40, value), 0xff)) - mstore8(add(res, 38), and(shr(48, value), 0xff)) - mstore8(add(res, 39), and(shr(56, value), 0xff)) - } - return memory(res); -} - -// No constants are supported yet, using this as a workaround. -// Defining variables outside of contract/function is not supported. -function DEPOSIT_CONTRACT_TREE_DEPTH() -> uint256 { - return 32; -} - -function MAX_DEPOSIT_COUNT() -> uint256 { - // uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1; - // TODO: Could use Bounded(uint32).maxVal() - return 0xFFFFFFFF; -} - -contract DepositContract { - deposit_count : uint256; - // TODO: use fixed-size arrays of DEPOSIT_CONTRACT_TREE_DEPTH() length - branch : array(bytes32); - zero_hashes : array(bytes32); - - constructor() { - // Dynamic storage arrays start empty and indexed access is bounds-checked, - // so grow both to DEPOSIT_CONTRACT_TREE_DEPTH() elements before indexing - // them at fixed positions. New slots are zero-initialised, which matches - // the empty sparse Merkle tree these arrays represent. - // Alternatively, `.push(..)` could be used, but this saves sstores. - Array.setLength(branch, DEPOSIT_CONTRACT_TREE_DEPTH()); - Array.setLength(zero_hashes, DEPOSIT_CONTRACT_TREE_DEPTH()); - - // Compute hashes in empty sparse Merkle tree - for (let height = 0; height < (DEPOSIT_CONTRACT_TREE_DEPTH() - 1); height += 1) { - zero_hashes[height + 1] = sha256(concat(zero_hashes[height], zero_hashes[height])); - } - } - - // TODO: this is for testing only - public function get_zero_hash(index: uint256) -> bytes32 { - return zero_hashes[index]; - } - - public function get_deposit_root() -> bytes32 { - let node: bytes32; - let size = deposit_count; - for (let height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH(); height += 1) { - if ((size & 1) == 1) { - node = sha256(concat(branch[height], node)); - } else { - node = sha256(concat(node, zero_hashes[height])); - } - size /= 2; - } - return sha256(concat( - concat( - node, - to_little_endian_64(deposit_count) - ), - empty(24) - )); - } - - public function get_deposit_count() -> memory(bytes) { - return to_little_endian_64(deposit_count); - } - - // TODO: once string literals are properly supported, change errors to messages - // matching the deposit contract, full 100% identical behaviour. - public payable function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { - // Extended ABI length checks since dynamic types are used. - require(MemorySize.len(pubkey) == 48, Error(0x9ca717ed)); // InvalidPubkeyLength() - require(MemorySize.len(withdrawal_credentials) == 32, Error(0x3debbf1e)); // InvalidWithdrawalCredentialsLength() - require(MemorySize.len(signature) == 96, Error(0x4be6321b)); // InvalidSignatureLength() - - // Check deposit amount - // >= 1 ether - require(callvalue() >= 1000000000000000000, Error(0xbdfc7472)); // DepositValueTooLow() - // % 1 gwei == 0 - require((callvalue() % 1000000000) == 0, Error(0x9c7417e9)); // DepositValueNotMultipleOfOneGwei() - - let deposit_amount = callvalue() / 1000000000; // 1 gwei - // <= type(uint64).max - require(deposit_amount <= 0xffffffffffffffff, Error(0x2aa66734)); // DepositValueTooHigh() - - let amount: memory(bytes) = to_little_endian_64(uint256(deposit_amount)); - // TODO: emit DepositEvent - /* - event DepositEvent( - bytes pubkey, - bytes withdrawal_credentials, - bytes amount, - bytes signature, - bytes index - ); - - emit DepositEvent( - pubkey, - withdrawal_credentials, - amount, - signature, - to_little_endian_64(uint64(deposit_count)) - ); - */ - assembly { - // TODO: need to ABI-encode all the arguments - log1(0, 0, 0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5) - } - - // Compute deposit data root (`DepositData` hash tree root) - let pubkey_root = sha256(concat(pubkey, empty(16))); - let signature_root = sha256(concat( - sha256(truncate(signature, 64)), - sha256(concat(slice_(signature, 64), empty(32))) - )); - let node = sha256(concat( - sha256(concat(pubkey_root, withdrawal_credentials)), - sha256(concat(concat(amount, empty(24)), signature_root)) - )); - - // Verify computed and expected deposit data roots match - require(node == deposit_data_root, Error(0x2ec2f183)); // ReconstructedDepositDataMismatch() - - // Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`) - require(deposit_count < MAX_DEPOSIT_COUNT(), Error(0xef5ccf66)); // MerkleTreeFull() - - // Add deposit data root to Merkle tree (update a single `branch` node) - deposit_count += 1; - let size = deposit_count; - for (let height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH(); height += 1) { - if ((size & 1) == 1) { - branch[height] = node; - return (); - } - node = sha256(concat(branch[height], node)); - size /= 2; - } - - // As the loop should always end prematurely with the `return` statement, - // this code should be unreachable. We assert `false` just to be safe. - assert(false); - } - - public function supportsInterface(interfaceId: bytes4) -> bool { - unimplemented(); - return false; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/derive_contract_local.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/derive_contract_local.sol new file mode 100644 index 00000000..6af4fa78 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/derive_contract_local.sol @@ -0,0 +1,92 @@ +// Runtime test: `#[derive(Eq, Ord)]` on data types declared INSIDE a contract. +// The derived instances are top-level, but the types stay contract-local; each +// public function returns uint256(1) for true / uint256(0) for false. +// - Color (a contract-local enum) exercises the () and sum(f, g) instances; +// - Point (a contract-local product) exercises the pair (f, g) instance. + +import * from std; +import * from std.dispatch; +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +contract DeriveContractLocal { + #[derive(Eq, Ord)] + enum Color { Red, Green, Blue } + + #[derive(Eq, Ord)] + enum Point { Point(uint256, uint256) } + + constructor() {} + + // enum equality (reaches the () and sum universe instances) + function eqRedRed() public returns (uint256) { + match (Eq.eq(Color.Red, Color.Red)) { +case true { +return uint256(1); +} +case false { +return uint256(0); +} +} + } + + function eqRedBlue() public returns (uint256) { + match (Eq.eq(Color.Red, Color.Blue)) { +case true { +return uint256(1); +} +case false { +return uint256(0); +} +} + } + + // enum ordering follows declaration order: Red < Green < Blue + function gtGreenRed() public returns (uint256) { + match (Ord.gt(Color.Green, Color.Red)) { +case true { +return uint256(1); +} +case false { +return uint256(0); +} +} + } + + function gtRedGreen() public returns (uint256) { + match (Ord.gt(Color.Red, Color.Green)) { +case true { +return uint256(1); +} +case false { +return uint256(0); +} +} + } + + // product equality (reaches the pair universe instance) + function eqPointSame() public returns (uint256) { + match (Eq.eq(Point(uint256(1), uint256(2)), Point(uint256(1), uint256(2)))) { +case true { +return uint256(1); +} +case false { +return uint256(0); +} +} + } + + // product ordering is lexicographic: the second field breaks the tie + function gtPointLex() public returns (uint256) { + match (Ord.gt(Point(uint256(1), uint256(100)), Point(uint256(1), uint256(50)))) { +case true { +return uint256(1); +} +case false { +return uint256(0); +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/derive_contract_local.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/derive_contract_local.solc deleted file mode 100644 index c48fb312..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/derive_contract_local.solc +++ /dev/null @@ -1,68 +0,0 @@ -// Runtime test: `#[derive(Eq, Ord)]` on data types declared INSIDE a contract. -// The derived instances are top-level, but the types stay contract-local; each -// public function returns uint256(1) for true / uint256(0) for false. -// - Color (a contract-local enum) exercises the () and sum(f, g) instances; -// - Point (a contract-local product) exercises the pair (f, g) instance. - -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -contract DeriveContractLocal { - #[derive(Eq, Ord)] - data Color = Red | Green | Blue; - - #[derive(Eq, Ord)] - data Point = Point(uint256, uint256); - - constructor() {} - - // enum equality (reaches the () and sum universe instances) - public function eqRedRed() -> uint256 { - match Eq.eq(Color.Red, Color.Red) { - | true => return uint256(1); - | false => return uint256(0); - } - } - - public function eqRedBlue() -> uint256 { - match Eq.eq(Color.Red, Color.Blue) { - | true => return uint256(1); - | false => return uint256(0); - } - } - - // enum ordering follows declaration order: Red < Green < Blue - public function gtGreenRed() -> uint256 { - match Ord.gt(Color.Green, Color.Red) { - | true => return uint256(1); - | false => return uint256(0); - } - } - - public function gtRedGreen() -> uint256 { - match Ord.gt(Color.Red, Color.Green) { - | true => return uint256(1); - | false => return uint256(0); - } - } - - // product equality (reaches the pair universe instance) - public function eqPointSame() -> uint256 { - match Eq.eq(Point(uint256(1), uint256(2)), Point(uint256(1), uint256(2))) { - | true => return uint256(1); - | false => return uint256(0); - } - } - - // product ordering is lexicographic: the second field breaks the tie - public function gtPointLex() -> uint256 { - match Ord.gt(Point(uint256(1), uint256(100)), Point(uint256(1), uint256(50))) { - | true => return uint256(1); - | false => return uint256(0); - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/derive_ord.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/derive_ord.sol new file mode 100644 index 00000000..dd801111 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/derive_ord.sol @@ -0,0 +1,92 @@ +// Runtime test: `#[derive(Eq, Ord)]` instances executed on the EVM. +// Each public function returns uint256(1) for true / uint256(0) for false, +// pinning down the structural Eq/Ord instances over (), sum and pair: +// - Color (an enum) exercises the unit () and sum(f, g) instances; +// - Point (a product) exercises the pair (f, g) instance. + +import * from std; +import * from std.dispatch; +import * from std.Generic; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +#[derive(Eq, Ord)] +enum Color { Red, Green, Blue } + +#[derive(Eq, Ord)] +enum Point { Point(uint256, uint256) } + +contract DeriveOrd { + constructor() {} + + // enum equality (reaches the () and sum universe instances) + function eqRedRed() public returns (uint256) { + match (Eq.eq(Color.Red, Color.Red)) { +case true { +return uint256(1); +} +case false { +return uint256(0); +} +} + } + + function eqRedBlue() public returns (uint256) { + match (Eq.eq(Color.Red, Color.Blue)) { +case true { +return uint256(1); +} +case false { +return uint256(0); +} +} + } + + // enum ordering follows declaration order: Red < Green < Blue + function gtGreenRed() public returns (uint256) { + match (Ord.gt(Color.Green, Color.Red)) { +case true { +return uint256(1); +} +case false { +return uint256(0); +} +} + } + + function gtRedGreen() public returns (uint256) { + match (Ord.gt(Color.Red, Color.Green)) { +case true { +return uint256(1); +} +case false { +return uint256(0); +} +} + } + + // product equality (reaches the pair universe instance) + function eqPointSame() public returns (uint256) { + match (Eq.eq(Point(uint256(1), uint256(2)), Point(uint256(1), uint256(2)))) { +case true { +return uint256(1); +} +case false { +return uint256(0); +} +} + } + + // product ordering is lexicographic: the second field breaks the tie + function gtPointLex() public returns (uint256) { + match (Ord.gt(Point(uint256(1), uint256(100)), Point(uint256(1), uint256(50)))) { +case true { +return uint256(1); +} +case false { +return uint256(0); +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/derive_ord.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/derive_ord.solc deleted file mode 100644 index 6535e55e..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/derive_ord.solc +++ /dev/null @@ -1,68 +0,0 @@ -// Runtime test: `#[derive(Eq, Ord)]` instances executed on the EVM. -// Each public function returns uint256(1) for true / uint256(0) for false, -// pinning down the structural Eq/Ord instances over (), sum and pair: -// - Color (an enum) exercises the unit () and sum(f, g) instances; -// - Point (a product) exercises the pair (f, g) instance. - -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -#[derive(Eq, Ord)] -data Color = Red | Green | Blue; - -#[derive(Eq, Ord)] -data Point = Point(uint256, uint256); - -contract DeriveOrd { - constructor() {} - - // enum equality (reaches the () and sum universe instances) - public function eqRedRed() -> uint256 { - match Eq.eq(Color.Red, Color.Red) { - | true => return uint256(1); - | false => return uint256(0); - } - } - - public function eqRedBlue() -> uint256 { - match Eq.eq(Color.Red, Color.Blue) { - | true => return uint256(1); - | false => return uint256(0); - } - } - - // enum ordering follows declaration order: Red < Green < Blue - public function gtGreenRed() -> uint256 { - match Ord.gt(Color.Green, Color.Red) { - | true => return uint256(1); - | false => return uint256(0); - } - } - - public function gtRedGreen() -> uint256 { - match Ord.gt(Color.Red, Color.Green) { - | true => return uint256(1); - | false => return uint256(0); - } - } - - // product equality (reaches the pair universe instance) - public function eqPointSame() -> uint256 { - match Eq.eq(Point(uint256(1), uint256(2)), Point(uint256(1), uint256(2))) { - | true => return uint256(1); - | false => return uint256(0); - } - } - - // product ordering is lexicographic: the second field breaks the tie - public function gtPointLex() -> uint256 { - match Ord.gt(Point(uint256(1), uint256(100)), Point(uint256(1), uint256(50))) { - | true => return uint256(1); - | false => return uint256(0); - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ecrecover.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ecrecover.sol new file mode 100644 index 00000000..412fec69 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ecrecover.sol @@ -0,0 +1,39 @@ +import * from std; +import * from std.dispatch; + +contract EcrecoverTest { + function recover() public returns (address) { + let h: bytes32 = bytes32(0xaabbccddeeff00112233445566778899aabbccddeeff00112233445566778899); + let v: uint256 = uint256(27); + let r: bytes32 = bytes32(0xb3ba6dd3757d18f28736e84b1296af85362b7bdf4548710733c6325abf95311d); + let s: bytes32 = bytes32(0x3523e7d34da277c59af090e44cebddb10b73be11780f028d02cf5ae5f24109fc); + return ecrecover(h, v, r, s); + } + + // r = 0 is an invalid signature component: the precompile succeeds (ret != 0) + // but recovers nothing, so it returns empty output and `res` stays 0. This + // exercises the `ECRecoverFailed()` (0x4fbfae63) revert path. `v` and `s` + // are kept well-formed so neither the malleability nor call-failed guards fire. + function recoverFail() public returns (address) { + let h: bytes32 = bytes32(0xaabbccddeeff00112233445566778899aabbccddeeff00112233445566778899); + let v: uint256 = uint256(27); + let r: bytes32 = bytes32(0x0); + let s: bytes32 = bytes32(0x3523e7d34da277c59af090e44cebddb10b73be11780f028d02cf5ae5f24109fc); + return ecrecover(h, v, r, s); + } + + // v = 1 is not a valid recovery id (only 27 and 28 are accepted). The + // precompile still succeeds (ret != 0) but returns empty output, leaving the + // [0, 32] return-data area untouched. Only because `ecrecover` now clears + // that area with `mstore(0, 0)` before the call does `res` reliably read as + // 0 and hit the `ECRecoverFailed()` (0x4fbfae63) revert path — without the + // clear a stale non-zero word would be returned as a bogus address. `r` and + // `s` are the well-formed values from `recover()` so only `v` is at fault. + function recoverFailBadV() public returns (address) { + let h: bytes32 = bytes32(0xaabbccddeeff00112233445566778899aabbccddeeff00112233445566778899); + let v: uint256 = uint256(1); + let r: bytes32 = bytes32(0xb3ba6dd3757d18f28736e84b1296af85362b7bdf4548710733c6325abf95311d); + let s: bytes32 = bytes32(0x3523e7d34da277c59af090e44cebddb10b73be11780f028d02cf5ae5f24109fc); + return ecrecover(h, v, r, s); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ecrecover.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ecrecover.solc deleted file mode 100644 index e66122a1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ecrecover.solc +++ /dev/null @@ -1,39 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract EcrecoverTest { - public function recover() -> address { - let h: bytes32 = bytes32(0xaabbccddeeff00112233445566778899aabbccddeeff00112233445566778899); - let v: uint256 = uint256(27); - let r: bytes32 = bytes32(0xb3ba6dd3757d18f28736e84b1296af85362b7bdf4548710733c6325abf95311d); - let s: bytes32 = bytes32(0x3523e7d34da277c59af090e44cebddb10b73be11780f028d02cf5ae5f24109fc); - return ecrecover(h, v, r, s); - } - - // r = 0 is an invalid signature component: the precompile succeeds (ret != 0) - // but recovers nothing, so it returns empty output and `res` stays 0. This - // exercises the `ECRecoverFailed()` (0x4fbfae63) revert path. `v` and `s` - // are kept well-formed so neither the malleability nor call-failed guards fire. - public function recoverFail() -> address { - let h: bytes32 = bytes32(0xaabbccddeeff00112233445566778899aabbccddeeff00112233445566778899); - let v: uint256 = uint256(27); - let r: bytes32 = bytes32(0x0); - let s: bytes32 = bytes32(0x3523e7d34da277c59af090e44cebddb10b73be11780f028d02cf5ae5f24109fc); - return ecrecover(h, v, r, s); - } - - // v = 1 is not a valid recovery id (only 27 and 28 are accepted). The - // precompile still succeeds (ret != 0) but returns empty output, leaving the - // [0, 32] return-data area untouched. Only because `ecrecover` now clears - // that area with `mstore(0, 0)` before the call does `res` reliably read as - // 0 and hit the `ECRecoverFailed()` (0x4fbfae63) revert path — without the - // clear a stale non-zero word would be returned as a bogus address. `r` and - // `s` are the well-formed values from `recover()` so only `v` is at fault. - public function recoverFailBadV() -> address { - let h: bytes32 = bytes32(0xaabbccddeeff00112233445566778899aabbccddeeff00112233445566778899); - let v: uint256 = uint256(1); - let r: bytes32 = bytes32(0xb3ba6dd3757d18f28736e84b1296af85362b7bdf4548710733c6325abf95311d); - let s: bytes32 = bytes32(0x3523e7d34da277c59af090e44cebddb10b73be11780f028d02cf5ae5f24109fc); - return ecrecover(h, v, r, s); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/eip712.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/eip712.sol new file mode 100644 index 00000000..0408655b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/eip712.sol @@ -0,0 +1,97 @@ +import * from std; +import * from std.dispatch; +import * from std.eip712; + +// Canonical EIP-712 example from the specification +// (https://eips.ethereum.org/EIPS/eip-712): a `Mail` sent from one `Person` to +// another. It shows how to build the nested message struct hashes on top of the +// reusable `eip712DomainSeparator` / `eip712Digest` helpers in std, then recover +// the signer with `ecrecover`. +// +// struct EIP712Domain { string name; string version; uint256 chainId; address verifyingContract; } +// struct Person { string name; address wallet; } +// struct Mail { Person from; Person to; string contents; } +// +// Every value below (domain, message and signature) is a fixed vector published +// in the EIP, so `verify()` must recover the "Cow" signer +// 0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826. + +// A struct's members are ABI-encoded (each atomic member as its 32-byte word, a +// dynamic member as the keccak256 of its contents) and prefixed with the struct +// type hash, then hashed. We build the byte string with `concat` and hash it +// with `keccak256_`, exactly as in the slices example. + +// hashStruct(Person) = keccak256(PERSON_TYPEHASH ‖ keccak256(name) ‖ wallet) +function hashPerson(nameHash: bytes32, wallet: address) returns (bytes32) { + let typeHash = bytes32(keccakLit("Person(string name,address wallet)")); + return keccak256_( + concat(typeHash, concat(nameHash, bytes32(Typedef.rep(wallet)))) + ); +} + +// hashStruct(Mail) = keccak256(MAIL_TYPEHASH ‖ hashStruct(from) ‖ hashStruct(to) ‖ keccak256(contents)) +// The Mail type hash embeds the referenced Person type per the EIP-712 rule for +// nested structs (referenced types are appended, sorted by name). +function hashMail(fromHash: bytes32, toHash: bytes32, contentsHash: bytes32) returns (bytes32) { + let typeHash = bytes32( + keccakLit("Mail(Person from,Person to,string contents)Person(string name,address wallet)") + ); + return keccak256_( + concat(typeHash, concat(fromHash, concat(toHash, contentsHash))) + ); +} + +// Domain separator for name "Ether Mail", version "1", chainId 1 and the fixed +// verifying contract from the spec. Uses the std EIP712Domain helper. +function mailDomainSeparator() returns (bytes32) { + return eip712DomainSeparator( + bytes32(keccakLit("Ether Mail")), + bytes32(keccakLit("1")), + uint256(1), + address(0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC) + ); +} + +// hashStruct of the fixed Mail message. +function mailStructHash() returns (bytes32) { + let fromHash = hashPerson( + bytes32(keccakLit("Cow")), + address(0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826) + ); + let toHash = hashPerson( + bytes32(keccakLit("Bob")), + address(0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB) + ); + let contentsHash = bytes32(keccakLit("Hello, Bob!")); + return hashMail(fromHash, toHash, contentsHash); +} + +function mailDigest() returns (bytes32) { + return eip712Digest(mailDomainSeparator(), mailStructHash()); +} + +contract EIP712Mail { + constructor() {} + + // Intermediate hashes, exposed so each EIP-712 layer can be asserted. + function domainSeparator() public returns (bytes32) { + return mailDomainSeparator(); + } + + function structHash() public returns (bytes32) { + return mailStructHash(); + } + + function digest() public returns (bytes32) { + return mailDigest(); + } + + // Recovers the signer of the fixed Mail message using the published + // signature. Returns the "Cow" wallet 0xCD2a3d…D826. + function verify() public returns (address) { + let v: uint256 = uint256(28); + let r: bytes32 = bytes32(0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d); + let s: bytes32 = bytes32(0x07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b91562); + return ecrecover(mailDigest(), v, r, s); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/eip712.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/eip712.solc deleted file mode 100644 index a292b5b9..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/eip712.solc +++ /dev/null @@ -1,97 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.eip712.{*}; - -// Canonical EIP-712 example from the specification -// (https://eips.ethereum.org/EIPS/eip-712): a `Mail` sent from one `Person` to -// another. It shows how to build the nested message struct hashes on top of the -// reusable `eip712DomainSeparator` / `eip712Digest` helpers in std, then recover -// the signer with `ecrecover`. -// -// struct EIP712Domain { string name; string version; uint256 chainId; address verifyingContract; } -// struct Person { string name; address wallet; } -// struct Mail { Person from; Person to; string contents; } -// -// Every value below (domain, message and signature) is a fixed vector published -// in the EIP, so `verify()` must recover the "Cow" signer -// 0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826. - -// A struct's members are ABI-encoded (each atomic member as its 32-byte word, a -// dynamic member as the keccak256 of its contents) and prefixed with the struct -// type hash, then hashed. We build the byte string with `concat` and hash it -// with `keccak256_`, exactly as in the slices example. - -// hashStruct(Person) = keccak256(PERSON_TYPEHASH ‖ keccak256(name) ‖ wallet) -function hashPerson(nameHash: bytes32, wallet: address) -> bytes32 { - let typeHash = bytes32(keccakLit("Person(string name,address wallet)")); - return keccak256_( - concat(typeHash, concat(nameHash, bytes32(Typedef.rep(wallet)))) - ); -} - -// hashStruct(Mail) = keccak256(MAIL_TYPEHASH ‖ hashStruct(from) ‖ hashStruct(to) ‖ keccak256(contents)) -// The Mail type hash embeds the referenced Person type per the EIP-712 rule for -// nested structs (referenced types are appended, sorted by name). -function hashMail(fromHash: bytes32, toHash: bytes32, contentsHash: bytes32) -> bytes32 { - let typeHash = bytes32( - keccakLit("Mail(Person from,Person to,string contents)Person(string name,address wallet)") - ); - return keccak256_( - concat(typeHash, concat(fromHash, concat(toHash, contentsHash))) - ); -} - -// Domain separator for name "Ether Mail", version "1", chainId 1 and the fixed -// verifying contract from the spec. Uses the std EIP712Domain helper. -function mailDomainSeparator() -> bytes32 { - return eip712DomainSeparator( - bytes32(keccakLit("Ether Mail")), - bytes32(keccakLit("1")), - uint256(1), - address(0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC) - ); -} - -// hashStruct of the fixed Mail message. -function mailStructHash() -> bytes32 { - let fromHash = hashPerson( - bytes32(keccakLit("Cow")), - address(0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826) - ); - let toHash = hashPerson( - bytes32(keccakLit("Bob")), - address(0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB) - ); - let contentsHash = bytes32(keccakLit("Hello, Bob!")); - return hashMail(fromHash, toHash, contentsHash); -} - -function mailDigest() -> bytes32 { - return eip712Digest(mailDomainSeparator(), mailStructHash()); -} - -contract EIP712Mail { - constructor() {} - - // Intermediate hashes, exposed so each EIP-712 layer can be asserted. - public function domainSeparator() -> bytes32 { - return mailDomainSeparator(); - } - - public function structHash() -> bytes32 { - return mailStructHash(); - } - - public function digest() -> bytes32 { - return mailDigest(); - } - - // Recovers the signer of the fixed Mail message using the published - // signature. Returns the "Cow" wallet 0xCD2a3d…D826. - public function verify() -> address { - let v: uint256 = uint256(28); - let r: bytes32 = bytes32(0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d); - let s: bytes32 = bytes32(0x07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b91562); - return ecrecover(mailDigest(), v, r, s); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty.sol new file mode 100644 index 00000000..4e012055 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty.sol @@ -0,0 +1,6 @@ +import * from std; +import * from std.dispatch; + +contract C { + constructor() {} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty.solc deleted file mode 100644 index 87b82bbf..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty.solc +++ /dev/null @@ -1,6 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract C { - constructor() {} -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty_no_constructor.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty_no_constructor.sol new file mode 100644 index 00000000..ea5cab07 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty_no_constructor.sol @@ -0,0 +1,5 @@ +import * from std; +import * from std.dispatch; + +contract C { +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty_no_constructor.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty_no_constructor.solc deleted file mode 100644 index 66a42685..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/empty_no_constructor.solc +++ /dev/null @@ -1,5 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract C { -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/fallback.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/fallback.sol new file mode 100644 index 00000000..32bb5827 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/fallback.sol @@ -0,0 +1,14 @@ +import * from std; +import * from std.dispatch; + +contract WithFallback { + constructor() {} + + function answer() public returns (uint256) { + return uint256(42); + } + + fallback() { + revertLit("fallback-was-called"); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/fallback.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/fallback.solc deleted file mode 100644 index 9bf22452..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/fallback.solc +++ /dev/null @@ -1,14 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract WithFallback { - constructor() {} - - public function answer() -> uint256 { - return uint256(42); - } - - fallback() -> () { - revertLit("fallback-was-called"); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/forloops.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/forloops.sol new file mode 100644 index 00000000..23605b9d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/forloops.sol @@ -0,0 +1,95 @@ +import * from std; +import * from std.dispatch; + +contract C { + counter : uint256; + + constructor() { + counter = uint256(0); + } + + function bump() returns (uint256) { + counter = counter + uint256(1); + return counter; + } + + function getCounter() public returns (uint256) { + return counter; + } + + // Sum of 0..4 with early `break` at i == 5. + function break_sum() public returns (uint256) { + let s : uint256 = uint256(0); + for (let i : uint256 = uint256(0); i < uint256(10); i = i + uint256(1)) { + if (i == uint256(5)) { + break; + } else {} + s = s + i; + } + return s; + } + + // Sum of 5..9 using `continue` to skip the iterations where i < 5. + // The post-statement (i = i + 1) must still run on `continue`, otherwise + // the loop would never terminate. + function continue_sum() public returns (uint256) { + let s : uint256 = uint256(0); + for (let i : uint256 = uint256(0); i < uint256(10); i = i + uint256(1)) { + if (i < uint256(5)) { + continue; + } else {} + s = s + i; + } + return s; + } + + // Empty initializer: `i` is declared/initialised outside the loop. + function empty_init() public returns (uint256) { + let i : uint256 = uint256(3); + let s : uint256 = uint256(0); + for (; i < uint256(7); i = i + uint256(1)) { + s = s + i; + } + return s; + } + + // Empty post-body: the increment is done in the loop body. + function empty_post() public returns (uint256) { + let s : uint256 = uint256(0); + for (let i : uint256 = uint256(0); i < uint256(4); ) { + s = s + i; + i = i + uint256(1); + } + return s; + } + + // Side effect in the condition: `bump()` increments storage on every + // probe (including the failing one), so observing `counter` afterwards + // proves the condition ran the expected number of times. + function cond_side_effect() public returns (uint256) { + counter = uint256(0); + for (let i : uint256 = uint256(0); bump() < uint256(5); i = i + uint256(1)) {} + return counter; + } + + // Side effect in the post-body: `bump()` runs once per completed + // iteration, so `counter` ends equal to the iteration count. + function post_side_effect() public returns (uint256) { + counter = uint256(0); + for (let i : uint256 = uint256(0); i < uint256(3); bump()) { + i = i + uint256(1); + } + return counter; + } + + // Nested `for` -- sum of i*j for i,j in 1..3. + function double_loop() public returns (uint256) { + let s : uint256 = uint256(0); + for (let i : uint256 = uint256(1); i < uint256(4); i = i + uint256(1)) { + for (let j : uint256 = uint256(1); j < uint256(4); j = j + uint256(1)) { + s = s + i * j; + } + } + return s; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/forloops.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/forloops.solc deleted file mode 100644 index f2086ae4..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/forloops.solc +++ /dev/null @@ -1,95 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract C { - counter : uint256; - - constructor() { - counter = uint256(0); - } - - function bump() -> uint256 { - counter = counter + uint256(1); - return counter; - } - - public function getCounter() -> uint256 { - return counter; - } - - // Sum of 0..4 with early `break` at i == 5. - public function break_sum() -> uint256 { - let s : uint256 = uint256(0); - for (let i : uint256 = uint256(0); i < uint256(10); i = i + uint256(1)) { - if (i == uint256(5)) { - break; - } else {} - s = s + i; - } - return s; - } - - // Sum of 5..9 using `continue` to skip the iterations where i < 5. - // The post-statement (i = i + 1) must still run on `continue`, otherwise - // the loop would never terminate. - public function continue_sum() -> uint256 { - let s : uint256 = uint256(0); - for (let i : uint256 = uint256(0); i < uint256(10); i = i + uint256(1)) { - if (i < uint256(5)) { - continue; - } else {} - s = s + i; - } - return s; - } - - // Empty initializer: `i` is declared/initialised outside the loop. - public function empty_init() -> uint256 { - let i : uint256 = uint256(3); - let s : uint256 = uint256(0); - for (; i < uint256(7); i = i + uint256(1)) { - s = s + i; - } - return s; - } - - // Empty post-body: the increment is done in the loop body. - public function empty_post() -> uint256 { - let s : uint256 = uint256(0); - for (let i : uint256 = uint256(0); i < uint256(4); ) { - s = s + i; - i = i + uint256(1); - } - return s; - } - - // Side effect in the condition: `bump()` increments storage on every - // probe (including the failing one), so observing `counter` afterwards - // proves the condition ran the expected number of times. - public function cond_side_effect() -> uint256 { - counter = uint256(0); - for (let i : uint256 = uint256(0); bump() < uint256(5); i = i + uint256(1)) {} - return counter; - } - - // Side effect in the post-body: `bump()` runs once per completed - // iteration, so `counter` ends equal to the iteration count. - public function post_side_effect() -> uint256 { - counter = uint256(0); - for (let i : uint256 = uint256(0); i < uint256(3); bump()) { - i = i + uint256(1); - } - return counter; - } - - // Nested `for` -- sum of i*j for i,j in 1..3. - public function double_loop() -> uint256 { - let s : uint256 = uint256(0); - for (let i : uint256 = uint256(1); i < uint256(4); i = i + uint256(1)) { - for (let j : uint256 = uint256(1); j < uint256(4); j = j + uint256(1)) { - s = s + i * j; - } - } - return s; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/generic_product.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/generic_product.sol new file mode 100644 index 00000000..5a6b0a72 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/generic_product.sol @@ -0,0 +1,63 @@ +import * from std; +import * from std.dispatch; +import {mload, mstore} from std.opcodes; +import * from std.Generic; +import * from std.ABIGeneric; + +pragma no-generic-instance-for Point; + +enum Point { Point(uint256, uint256) } + +// Only requirement: a Generic impl using the primitive pair type. +// rep = (uint256, uint256) — primitive Solcore pair +impl Generic { + function from(p: Point) returns (uint256, uint256) { + match (p) { +case Point(x, y) { +return (x, y); +} +} + } + function to(t: (uint256, uint256)) returns (Point) { + match (t) { +case (x, y) { +return Point(x, y); +} +} + } +} + +contract GenericProduct { + constructor() {} + + // Calls encode; returns word at offset 0 (the x field). + function encodeX(a: uint256, b: uint256) public returns (uint256) { + let p : Point = Point(a, b); + let buf = allocate_zeroed_memory(64); + encode(p, buf, 0, 64); + return Typedef.abs(mload(buf)); + } + + // Calls encode; returns word at offset 32 (the y field). + function encodeY(a: uint256, b: uint256) public returns (uint256) { + let p : Point = Point(a, b); + let buf = allocate_zeroed_memory(64); + encode(p, buf, 0, 64); + return Typedef.abs(mload(buf + 32)); + } + + // Writes [a][b] into memory, calls decode, returns the x field. + function decodeX(a: uint256, b: uint256) public returns (uint256) { + let buf = allocate_zeroed_memory(64); + mstore(buf, Typedef.rep(a)); + mstore(buf + 32, Typedef.rep(b)); + let rdr : MemoryWordReader = MemoryWordReader(buf); + let dec : ABIDecoder = ABIDecoder(rdr); + let p : Point = decode(dec, 0); + match (p) { +case Point(x, _) { +return x; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/generic_product.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/generic_product.solc deleted file mode 100644 index 5a2ce10f..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/generic_product.solc +++ /dev/null @@ -1,51 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.opcodes.{mload, mstore}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; - -pragma no-generic-instance-for Point; - -data Point = Point(uint256, uint256); - -// Only requirement: Generic instance using the primitive pair type. -// rep = (uint256, uint256) — primitive Solcore pair -instance Point : Generic((uint256, uint256)) { - function from(p : Point) -> (uint256, uint256) { - match p { | Point(x, y) => return (x, y); } - } - function to(t : (uint256, uint256)) -> Point { - match t { | (x, y) => return Point(x, y); } - } -} - -contract GenericProduct { - constructor() {} - - // Calls encode; returns word at offset 0 (the x field). - public function encodeX(a : uint256, b : uint256) -> uint256 { - let p : Point = Point(a, b); - let buf = allocate_zeroed_memory(64); - encode(p, buf, 0, 64); - return Typedef.abs(mload(buf)); - } - - // Calls encode; returns word at offset 32 (the y field). - public function encodeY(a : uint256, b : uint256) -> uint256 { - let p : Point = Point(a, b); - let buf = allocate_zeroed_memory(64); - encode(p, buf, 0, 64); - return Typedef.abs(mload(buf + 32)); - } - - // Writes [a][b] into memory, calls decode, returns the x field. - public function decodeX(a : uint256, b : uint256) -> uint256 { - let buf = allocate_zeroed_memory(64); - mstore(buf, Typedef.rep(a)); - mstore(buf + 32, Typedef.rep(b)); - let rdr : MemoryWordReader = MemoryWordReader(buf); - let dec : ABIDecoder(Point, MemoryWordReader) = ABIDecoder(rdr); - let p : Point = decode(dec, 0); - match p { | Point(x, _) => return x; } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/generic_sum.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/generic_sum.sol new file mode 100644 index 00000000..34d9b128 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/generic_sum.sol @@ -0,0 +1,82 @@ +import * from std; +import * from std.dispatch; +import {mload, mstore} from std.opcodes; +import * from std.Generic; +import * from std.ABIGeneric; + +pragma no-generic-instance-for Option; + +enum Option { None, Some(a) } + +// Only requirement: a Generic impl using the primitive sum type. +// rep = sum((), uint256): inl(()) = None, inr(v) = Some(v) +impl Generic, sum<(), uint256>> { + function from(x: Option) returns (sum<(), uint256>) { + match (x) { +case Option.None { +return inl(()); +} +case Option.Some(v) { +return inr(v); +} +} + } + function to(r: sum<(), uint256>) returns (Option) { + match (r) { +case inl(_) { +return Option.None; +} +case inr(v) { +return Option.Some(v); +} +} + } +} + +contract GenericSum { + constructor() {} + + // Calls encode; returns the tag word (first 32 bytes). + // None → 0 + function encodeNone() public returns (uint256) { + let x : Option = Option.None; + let buf = allocate_zeroed_memory(64); + encode(x, buf, 0, 64); + return Typedef.abs(mload(buf)); + } + + // Calls encode; returns the tag word (first 32 bytes). + // Some(n) → 1 + function encodeSomeTag(n: uint256) public returns (uint256) { + let x : Option = Option.Some(n); + let buf = allocate_zeroed_memory(64); + encode(x, buf, 0, 64); + return Typedef.abs(mload(buf)); + } + + // Calls encode; returns the payload word (bytes 32-63). + function encodePayload(n: uint256) public returns (uint256) { + let x : Option = Option.Some(n); + let buf = allocate_zeroed_memory(64); + encode(x, buf, 0, 64); + return Typedef.abs(mload(buf + 32)); + } + + // Writes [tag][value] into memory, calls decode, returns the value or 0. + function decodeAndGet(tag: uint256, value: uint256) public returns (uint256) { + let buf = allocate_zeroed_memory(64); + mstore(buf, Typedef.rep(tag)); + mstore(buf + 32, Typedef.rep(value)); + let rdr : MemoryWordReader = MemoryWordReader(buf); + let dec : ABIDecoder, MemoryWordReader> = ABIDecoder(rdr); + let opt : Option = decode(dec, 0); + match (opt) { +case Option.None { +return uint256(0); +} +case Option.Some(v) { +return v; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/generic_sum.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/generic_sum.solc deleted file mode 100644 index 164f7bc7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/generic_sum.solc +++ /dev/null @@ -1,70 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.opcodes.{mload, mstore}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; - -pragma no-generic-instance-for Option; - -data Option(a) = None | Some(a); - -// Only requirement: Generic instance using the primitive sum type. -// rep = sum((), uint256): inl(()) = None, inr(v) = Some(v) -instance Option(uint256) : Generic(sum((), uint256)) { - function from(x : Option(uint256)) -> sum((), uint256) { - match x { - | Option.None => return inl(()); - | Option.Some(v) => return inr(v); - } - } - function to(r : sum((), uint256)) -> Option(uint256) { - match r { - | inl(_) => return Option.None; - | inr(v) => return Option.Some(v); - } - } -} - -contract GenericSum { - constructor() {} - - // Calls encode; returns the tag word (first 32 bytes). - // None → 0 - public function encodeNone() -> uint256 { - let x : Option(uint256) = Option.None; - let buf = allocate_zeroed_memory(64); - encode(x, buf, 0, 64); - return Typedef.abs(mload(buf)); - } - - // Calls encode; returns the tag word (first 32 bytes). - // Some(n) → 1 - public function encodeSomeTag(n : uint256) -> uint256 { - let x : Option(uint256) = Option.Some(n); - let buf = allocate_zeroed_memory(64); - encode(x, buf, 0, 64); - return Typedef.abs(mload(buf)); - } - - // Calls encode; returns the payload word (bytes 32-63). - public function encodePayload(n : uint256) -> uint256 { - let x : Option(uint256) = Option.Some(n); - let buf = allocate_zeroed_memory(64); - encode(x, buf, 0, 64); - return Typedef.abs(mload(buf + 32)); - } - - // Writes [tag][value] into memory, calls decode, returns the value or 0. - public function decodeAndGet(tag : uint256, value : uint256) -> uint256 { - let buf = allocate_zeroed_memory(64); - mstore(buf, Typedef.rep(tag)); - mstore(buf + 32, Typedef.rep(value)); - let rdr : MemoryWordReader = MemoryWordReader(buf); - let dec : ABIDecoder(Option(uint256), MemoryWordReader) = ABIDecoder(rdr); - let opt : Option(uint256) = decode(dec, 0); - match opt { - | Option.None => return uint256(0); - | Option.Some(v) => return v; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/hashes.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/hashes.sol new file mode 100644 index 00000000..2b54a846 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/hashes.sol @@ -0,0 +1,47 @@ +import * from std; +import * from std.dispatch; +import {mstore} from std.opcodes; + +// Build a memory(bytes) holding the three-byte string "abc". +function abcBytes() returns (memory) { + let p = allocate_memory(64); + mstore(p, 3); + mstore(p + 32, 0x6162630000000000000000000000000000000000000000000000000000000000); + return memory(p); +} + +contract C { + constructor() {} + + function keccak() public returns (bytes32) { + return keccak256_(abcBytes()); + } + + function sha() public returns (bytes32) { + return sha256(abcBytes()); + } + + function ripemd() public returns (bytes32) { + return ripemd160(abcBytes()); + } + + // keccakWordLit folds keccak256 of a word's 32-byte big-endian form at + // compile time; keccakWordLit(0) == keccak256(bytes32(0)). + function keccakWord() public returns (bytes32) { + return bytes32(keccakWordLit(0)); + } + + // ERC-7201 namespaced storage slots, folded to constants at compile time + // from the string-literal namespace (no runtime keccak of the id). + function erc7201Example() public returns (bytes32) { + return erc7201("example.main"); + } + + function erc7201Ownable() public returns (bytes32) { + return erc7201("openzeppelin.storage.Ownable"); + } + + function erc7201Empty() public returns (bytes32) { + return erc7201(""); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/hashes.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/hashes.solc deleted file mode 100644 index 53912a49..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/hashes.solc +++ /dev/null @@ -1,47 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.opcodes.{mstore}; - -// Build a memory(bytes) holding the three-byte string "abc". -function abcBytes() -> memory(bytes) { - let p = allocate_memory(64); - mstore(p, 3); - mstore(p + 32, 0x6162630000000000000000000000000000000000000000000000000000000000); - return memory(p); -} - -contract C { - constructor() {} - - public function keccak() -> bytes32 { - return keccak256_(abcBytes()); - } - - public function sha() -> bytes32 { - return sha256(abcBytes()); - } - - public function ripemd() -> bytes32 { - return ripemd160(abcBytes()); - } - - // keccakWordLit folds keccak256 of a word's 32-byte big-endian form at - // compile time; keccakWordLit(0) == keccak256(bytes32(0)). - public function keccakWord() -> bytes32 { - return bytes32(keccakWordLit(0)); - } - - // ERC-7201 namespaced storage slots, folded to constants at compile time - // from the string-literal namespace (no runtime keccak of the id). - public function erc7201Example() -> bytes32 { - return erc7201("example.main"); - } - - public function erc7201Ownable() -> bytes32 { - return erc7201("openzeppelin.storage.Ownable"); - } - - public function erc7201Empty() -> bytes32 { - return erc7201(""); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/memory.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/memory.sol new file mode 100644 index 00000000..6f79c9cd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/memory.sol @@ -0,0 +1,19 @@ +import * from std; +import * from std.dispatch; +import {mstore} from std.opcodes; + +contract C { + function dirty_allocate() public returns (memory) { + mstore(get_free_memory() + 32, 0xdeadc0de); + let ptr = allocate_memory(32 + 32); + mstore(ptr, 32); + return memory(ptr); + } + + function clear_allocate() public returns (memory) { + mstore(get_free_memory() + 32, 0xdeadc0de); + let ptr = allocate_zeroed_memory(32 + 32); + mstore(ptr, 32); + return memory(ptr); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/memory.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/memory.solc deleted file mode 100644 index eec43817..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/memory.solc +++ /dev/null @@ -1,19 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.opcodes.{mstore}; - -contract C { - public function dirty_allocate() -> memory(bytes) { - mstore(get_free_memory() + 32, 0xdeadc0de); - let ptr = allocate_memory(32 + 32); - mstore(ptr, 32); - return memory(ptr); - } - - public function clear_allocate() -> memory(bytes) { - mstore(get_free_memory() + 32, 0xdeadc0de); - let ptr = allocate_zeroed_memory(32 + 32); - mstore(ptr, 32); - return memory(ptr); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/miniERC20.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/miniERC20.sol new file mode 100644 index 00000000..bf23b85e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/miniERC20.sol @@ -0,0 +1,96 @@ +import * from std; +import * from std.dispatch; + +function caller() returns (address) { + let res: word; + assembly { + res := caller() + } + return address(res); +} + +contract MiniERC20 { + name : string; + symbol : string; + owner : address; + decimals : uint256; // should be uint8 when we get to it + totalSupply : uint256; + balances : mapping(address => uint256); + allowance : mapping(address => mapping(address => uint256)); + + constructor(name_ : memory, symbol_ : memory, totalSupply_:uint256) { + name = name_; + symbol = symbol_; + owner = caller(); + decimals = 18; + mint(totalSupply_); + } + + function name() public returns (memory) { + return name; + } + + function symbol() public returns (memory) { + return symbol; + } + + function decimals() public returns (uint256) { + return decimals; + } + + function allowance(owner_: address, spender: address) public returns (uint256) { + return allowance[owner_][spender]; // don't use "owner" here + } + + function balanceOf(account: address) public returns (uint256) { + return balances[account]; + } + + function totalSupply() public returns (uint256) { + return totalSupply; + } + + // Note that this is not access guarded — the minting always goes to the owner + function mint(amount: uint256) public { + balances[owner] = Num.add(balances[owner], amount); + totalSupply = Num.add(totalSupply, amount); + } + + function transfer(dst: address, amt: uint256) public returns (bool) { + return transferFrom(caller(), dst, amt); + } + + function transferFrom(src: address, dst: address, amt: uint256) public returns (bool) { + let msg_sender = caller(); + require(balances[src] >= amt, "transferFrom: insufficient balance"); + + if (src != msg_sender && allowance[src][msg_sender] != (Num.maxVal())) { + require(allowance[src][msg_sender] >= amt, "transferFrom: insufficient allowance"); + allowance[src][msg_sender] -= amt; + } + balances[src] = balances[src] - amt; + balances[dst] = balances[dst] + amt; + // emit Transfer(src, dst, amt); + return true; + } + + function approve(usr: address, amt: uint256) public returns (bool) { + let msg_sender = caller(); + allowance[msg_sender][usr] = amt; + // emit Approval(msg.sender, usr, amt); + return true; + } + + + // testing + function getMyBalance() public returns (uint256) { + return balances[caller()]; + } + + function test() public returns (uint256) { + approve(address(0), 10); + transferFrom(caller(), address(0), 958); + return getMyBalance(); + } + +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/miniERC20.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/miniERC20.solc deleted file mode 100644 index a5eb3554..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/miniERC20.solc +++ /dev/null @@ -1,96 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -function caller() -> address { - let res: word; - assembly { - res := caller() - } - return address(res); -} - -contract MiniERC20 { - name : string; - symbol : string; - owner : address; - decimals : uint256; // should be uint8 when we get to it - totalSupply : uint256; - balances : mapping(address,uint256); - allowance : mapping(address, mapping(address, uint256)); - - constructor(name_ : memory(string), symbol_ : memory(string), totalSupply_:uint256) { - name = name_; - symbol = symbol_; - owner = caller(); - decimals = 18; - mint(totalSupply_); - } - - public function name() -> memory(string) { - return name; - } - - public function symbol() -> memory(string) { - return symbol; - } - - public function decimals() -> uint256 { - return decimals; - } - - public function allowance(owner_ : address, spender: address) -> uint256 { - return allowance[owner_][spender]; // don't use "owner" here - } - - public function balanceOf(account : address) -> uint256 { - return balances[account]; - } - - public function totalSupply() -> uint256 { - return totalSupply; - } - - // Note that this is not access guarded — the minting always goes to the owner - public function mint(amount:uint256) -> () { - balances[owner] = Num.add(balances[owner], amount); - totalSupply = Num.add(totalSupply, amount); - } - - public function transfer(dst : address, amt : uint256) -> bool { - return transferFrom(caller(), dst, amt); - } - - public function transferFrom(src:address, dst:address, amt:uint256) -> bool { - let msg_sender = caller(); - require(balances[src] >= amt, "transferFrom: insufficient balance"); - - if (src != msg_sender && allowance[src][msg_sender] != (Num.maxVal():uint256)) { - require(allowance[src][msg_sender] >= amt, "transferFrom: insufficient allowance"); - allowance[src][msg_sender] -= amt; - } - balances[src] = balances[src] - amt; - balances[dst] = balances[dst] + amt; - // emit Transfer(src, dst, amt); - return true; - } - - public function approve(usr: address, amt: uint256) -> bool { - let msg_sender = caller(); - allowance[msg_sender][usr] = amt; - // emit Approval(msg.sender, usr, amt); - return true; - } - - - // testing - public function getMyBalance() -> uint256 { - return balances[caller()]; - } - - public function test() -> uint256 { - approve(address(0), 10); - transferFrom(caller(), address(0), 958); - return getMyBalance(); - } - -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/neg.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/neg.sol new file mode 100644 index 00000000..09356af6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/neg.sol @@ -0,0 +1,73 @@ +import * from std; +import * from std.dispatch; + +trait Neg { + function neg(x: a) returns (a) ; +} + +enum B { F, T } +enum Pair { Pair(a, b) } + +impl Neg { + function neg(x: B) returns (B) { + match (x) { +case B.F { +return B.T; +} +case B.T { +return B.F; +} +} + } +} + + +function pairfst(p: Pair) returns (a) { + match (p) { +case Pair(x,y) { +return x; +} +} +} + +function pairsnd(p: Pair) returns (b) { + match (p) { +case Pair(x,y) { +return y; +} +} +} + + +impl Neg> where a: Neg, b: Neg { + function neg(p: Pair) returns (Pair) { + return Pair(Neg.neg (pairfst(p)), Neg.neg(pairsnd(p))); + } +} + + function bnot(x: B) returns (B) { + match (x) { +case B.T { +return B.F; +} +case B.F { +return B.T; +} +} +} + + function fromB(b: B) returns (word) { + match (b) { +case B.F { +return 0; +} +case B.T { +return 1; +} +} +} + +contract NegPair { + constructor() {} + function negPair() public returns (uint256) { return uint256(fromB(pairfst(Neg.neg(Pair(B.F,B.T))))); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/neg.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/neg.solc deleted file mode 100644 index b04d0a8a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/neg.solc +++ /dev/null @@ -1,69 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -forall a. -class a : Neg { - function neg(x:a) -> a; -} - -data B = F | T; -data Pair(a,b) = Pair(a,b); - -instance B : Neg { - function neg (x : B) -> B { - match x { - | B.F => return B.T; - | B.T => return B.F; - } - } -} - - -forall a b . function pairfst (p : Pair(a,b)) -> a { - match p { - | Pair(x,y) => return x; - } -} - -forall a b . function pairsnd(p : Pair(a,b)) -> b { - match p { - | Pair(x,y) => return y; - } -} - - -forall a b. -a:Neg,b:Neg => instance Pair(a,b):Neg { - function neg(p:Pair(a,b)) -> Pair(a,b) { - return Pair(Neg.neg (pairfst(p)), Neg.neg(pairsnd(p))); - } -} - -/* -instance (a:Neg,b:Neg) => Pair(a,b):Neg { - function neg(p) { - match p { - | Pair(a,b) => return Pair(neg(a), neg(b)); - } - } -} -*/ - - function bnot(x:B) -> B { - match x { - | B.T => return B.F; - | B.F => return B.T; - } -} - - function fromB(b:B) -> word { - match b { - | B.F => return 0; - | B.T => return 1; - } -} - -contract NegPair { - constructor() {} - public function negPair() -> uint256 { return uint256(fromB(pairfst(Neg.neg(Pair(B.F,B.T))))); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/nonpayable_ctor.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/nonpayable_ctor.sol new file mode 100644 index 00000000..4eebca06 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/nonpayable_ctor.sol @@ -0,0 +1,17 @@ +import * from std; +import * from std.dispatch; + +// A contract whose constructor is NOT marked `payable`. Deploying it with an +// incoming value transfer must revert with the NonPayableReceivedValue error +// (selector 0xb5988ea3), exactly like calling a non-payable method with value. +contract NonPayableCtor { + constructor() {} + + function balance() public returns (uint256) { + let value; + assembly { + value := selfbalance() + } + return uint256(value); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/nonpayable_ctor.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/nonpayable_ctor.solc deleted file mode 100644 index c19c104b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/nonpayable_ctor.solc +++ /dev/null @@ -1,17 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// A contract whose constructor is NOT marked `payable`. Deploying it with an -// incoming value transfer must revert with the NonPayableReceivedValue error -// (selector 0xb5988ea3), exactly like calling a non-payable method with value. -contract NonPayableCtor { - constructor() {} - - public function balance() -> uint256 { - let value; - assembly { - value := selfbalance() - } - return uint256(value); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ownable.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ownable.sol new file mode 100644 index 00000000..c3c2fb65 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ownable.sol @@ -0,0 +1,31 @@ +import * from std; +import * from std.dispatch; + +// caller() is not in the std library yet, +// so every contract must define its own + +function caller() returns (address) { + let res: word; + assembly { + res := caller() + } + return address(res); +} + +contract Ownable { + owner : address; + + constructor() { + owner = caller(); + } + + // named getOwner() instead of owner() to avoid collision with the field name + function getOwner() public returns (address) { + return owner; + } + + function changeOwner(newOwner: address) public { + require(caller() == owner, Error(0x12b0c500)); // OwnableUnauthorizedAccount() + owner = newOwner; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ownable.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ownable.solc deleted file mode 100644 index b20be59b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ownable.solc +++ /dev/null @@ -1,31 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// caller() is not in the std library yet, -// so every contract must define its own - -function caller() -> address { - let res: word; - assembly { - res := caller() - } - return address(res); -} - -contract Ownable { - owner : address; - - constructor() { - owner = caller(); - } - - // named getOwner() instead of owner() to avoid collision with the field name - public function getOwner() -> address { - return owner; - } - - public function changeOwner(newOwner : address) -> () { - require(caller() == owner, Error(0x12b0c500)); // OwnableUnauthorizedAccount() - owner = newOwner; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/p256verify.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/p256verify.sol new file mode 100644 index 00000000..af8d978c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/p256verify.sol @@ -0,0 +1,30 @@ +import * from std; +import * from std.dispatch; +import {p256verify} from std.eip7951; + +// Exercises the P256VERIFY (secp256r1) precompile at address 0x100, introduced +// by EIP-7951, through the std `p256verify` helper. It returns true for a valid +// signature and false for an invalid one. +contract P256Test { + constructor() {} + + function verifyValid() public returns (bool) { + return p256verify( + bytes32(0xabcdef00112233445566778899aabbccddeeff00112233445566778899aabbcc), + bytes32(0xa29295460e251beea1bdc9b84b2f3fe8e3a3e4d872baa3c55b78c9e448190ea9), + bytes32(0x2d854575b092b3732d3d73c8414bda17f907776894cff2e8e25e733d200f3f5c), + bytes32(0x6079df2480f92e4cf526c08e32ab82aed6599fddb777a039612fe7c9ef0247ba), + bytes32(0xe2b4793e7c77585508c4780e4e53a36deefbb3548f4380ee6df04863cfc54c2d) + ); + } + + function verifyInvalid() public returns (bool) { + return p256verify( + bytes32(0xabcdef00112233445566778899aabbccddeeff00112233445566778899aabbcd), + bytes32(0xa29295460e251beea1bdc9b84b2f3fe8e3a3e4d872baa3c55b78c9e448190ea9), + bytes32(0x2d854575b092b3732d3d73c8414bda17f907776894cff2e8e25e733d200f3f5c), + bytes32(0x6079df2480f92e4cf526c08e32ab82aed6599fddb777a039612fe7c9ef0247ba), + bytes32(0xe2b4793e7c77585508c4780e4e53a36deefbb3548f4380ee6df04863cfc54c2d) + ); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/p256verify.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/p256verify.solc deleted file mode 100644 index e1ddc4f0..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/p256verify.solc +++ /dev/null @@ -1,30 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.eip7951.{p256verify}; - -// Exercises the P256VERIFY (secp256r1) precompile at address 0x100, introduced -// by EIP-7951, through the std `p256verify` helper. It returns true for a valid -// signature and false for an invalid one. -contract P256Test { - constructor() {} - - public function verifyValid() -> bool { - return p256verify( - bytes32(0xabcdef00112233445566778899aabbccddeeff00112233445566778899aabbcc), - bytes32(0xa29295460e251beea1bdc9b84b2f3fe8e3a3e4d872baa3c55b78c9e448190ea9), - bytes32(0x2d854575b092b3732d3d73c8414bda17f907776894cff2e8e25e733d200f3f5c), - bytes32(0x6079df2480f92e4cf526c08e32ab82aed6599fddb777a039612fe7c9ef0247ba), - bytes32(0xe2b4793e7c77585508c4780e4e53a36deefbb3548f4380ee6df04863cfc54c2d) - ); - } - - public function verifyInvalid() -> bool { - return p256verify( - bytes32(0xabcdef00112233445566778899aabbccddeeff00112233445566778899aabbcd), - bytes32(0xa29295460e251beea1bdc9b84b2f3fe8e3a3e4d872baa3c55b78c9e448190ea9), - bytes32(0x2d854575b092b3732d3d73c8414bda17f907776894cff2e8e25e733d200f3f5c), - bytes32(0x6079df2480f92e4cf526c08e32ab82aed6599fddb777a039612fe7c9ef0247ba), - bytes32(0xe2b4793e7c77585508c4780e4e53a36deefbb3548f4380ee6df04863cfc54c2d) - ); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable.sol new file mode 100644 index 00000000..a8d15a88 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable.sol @@ -0,0 +1,32 @@ +import * from std; +import * from std.dispatch; + +contract PayableTest { + constructor() {} + + function deposit() public payable returns (uint256) { + let value; + assembly { + value := callvalue() + } + return uint256(value); + } + + function balance() public returns (uint256) { + let value; + assembly { + value := selfbalance() + } + return uint256(value); + } + + fallback() payable { + let value; + assembly { + value := callvalue() + } + if (value == 0) { + revertLit("fallback-was-called-no-value"); + } + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable.solc deleted file mode 100644 index 553275b1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable.solc +++ /dev/null @@ -1,32 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract PayableTest { - constructor() {} - - public payable function deposit() -> uint256 { - let value; - assembly { - value := callvalue() - } - return uint256(value); - } - - public function balance() -> uint256 { - let value; - assembly { - value := selfbalance() - } - return uint256(value); - } - - payable fallback() -> () { - let value; - assembly { - value := callvalue() - } - if (value == 0) { - revertLit("fallback-was-called-no-value"); - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable_ctor.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable_ctor.sol new file mode 100644 index 00000000..93c6813a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable_ctor.sol @@ -0,0 +1,17 @@ +import * from std; +import * from std.dispatch; + +// A contract whose constructor is explicitly marked `payable`. +// Deploying it with an incoming value transfer must succeed and the +// transferred value is retained by the newly created contract. +contract PayableCtor { + constructor() payable {} + + function balance() public returns (uint256) { + let value; + assembly { + value := selfbalance() + } + return uint256(value); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable_ctor.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable_ctor.solc deleted file mode 100644 index ce2d3ce1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/payable_ctor.solc +++ /dev/null @@ -1,17 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// A contract whose constructor is explicitly marked `payable`. -// Deploying it with an incoming value transfer must succeed and the -// transferred value is retained by the newly created contract. -contract PayableCtor { - payable constructor() {} - - public function balance() -> uint256 { - let value; - assembly { - value := selfbalance() - } - return uint256(value); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/slices.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/slices.sol new file mode 100644 index 00000000..7d6025f4 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/slices.sol @@ -0,0 +1,65 @@ +import * from std; +import * from std.dispatch; + +// Exercises slice_/truncate (memory_slice) composed with concat, to_bytes, +// and the hashing precompiles (keccak256_, sha256). memory_slice implements +// MemorySize + MemoryPointer + MemoryEncode, so it is both sliceable again and +// a valid operand for concat/to_bytes/keccak256_/sha256 with zero copies. +contract C { + // --- slice_/truncate on a memory(bytes), materialized with to_bytes --- + + function slice_bytes(a: memory, start: uint256) public returns (memory) { + return to_bytes(slice_(a, Typedef.rep(start))); + } + + function truncate_bytes(a: memory, end: uint256) public returns (memory) { + return to_bytes(truncate(a, Typedef.rep(end))); + } + + // --- slice_/truncate over the result of a concat --- + + function slice_of_concat(a: bytes32, b: bytes32, start: uint256) public returns (memory) { + return to_bytes(slice_(concat(a, b), Typedef.rep(start))); + } + + function truncate_of_concat(a: bytes32, b: bytes32, end: uint256) public returns (memory) { + return to_bytes(truncate(concat(a, b), Typedef.rep(end))); + } + + // to_bytes(truncate(slice_(concat(a, b), start), end)) -- the headline nesting: + // drop `start` bytes, then keep `end` of what remains (re-slicing a memory_slice). + function window_of_concat(a: bytes32, b: bytes32, start: uint256, end: uint256) public returns (memory) { + return to_bytes(truncate(slice_(concat(a, b), Typedef.rep(start)), Typedef.rep(end))); + } + + // --- a slice used as a concat operand --- + + function concat_slice_b32(a: memory, start: uint256, c: bytes32) public returns (memory) { + return concat(slice_(a, Typedef.rep(start)), c); + } + + function concat_two_slices(a: memory, sa: uint256, b: memory, eb: uint256) public returns (memory) { + return concat(slice_(a, Typedef.rep(sa)), truncate(b, Typedef.rep(eb))); + } + + // --- re-slicing a memory_slice --- + + function slice_of_slice(a: memory, s1: uint256, s2: uint256) public returns (memory) { + return to_bytes(slice_(slice_(a, Typedef.rep(s1)), Typedef.rep(s2))); + } + + // --- hashing a slice directly (no intermediate copy) --- + + function keccak_slice(a: memory, start: uint256) public returns (bytes32) { + return keccak256_(slice_(a, Typedef.rep(start))); + } + + function sha_truncate(a: memory, end: uint256) public returns (bytes32) { + return sha256(truncate(a, Typedef.rep(end))); + } + + // keccak256_(truncate(slice_(concat(a, b), start), end)) -- nested chain, hash endpoint. + function keccak_window_concat(a: bytes32, b: bytes32, start: uint256, end: uint256) public returns (bytes32) { + return keccak256_(truncate(slice_(concat(a, b), Typedef.rep(start)), Typedef.rep(end))); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/slices.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/slices.solc deleted file mode 100644 index a44d6b38..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/slices.solc +++ /dev/null @@ -1,65 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// Exercises slice_/truncate (memory_slice) composed with concat, to_bytes, -// and the hashing precompiles (keccak256_, sha256). memory_slice implements -// MemorySize + MemoryPointer + MemoryEncode, so it is both sliceable again and -// a valid operand for concat/to_bytes/keccak256_/sha256 with zero copies. -contract C { - // --- slice_/truncate on a memory(bytes), materialized with to_bytes --- - - public function slice_bytes(a: memory(bytes), start: uint256) -> memory(bytes) { - return to_bytes(slice_(a, Typedef.rep(start))); - } - - public function truncate_bytes(a: memory(bytes), end: uint256) -> memory(bytes) { - return to_bytes(truncate(a, Typedef.rep(end))); - } - - // --- slice_/truncate over the result of a concat --- - - public function slice_of_concat(a: bytes32, b: bytes32, start: uint256) -> memory(bytes) { - return to_bytes(slice_(concat(a, b), Typedef.rep(start))); - } - - public function truncate_of_concat(a: bytes32, b: bytes32, end: uint256) -> memory(bytes) { - return to_bytes(truncate(concat(a, b), Typedef.rep(end))); - } - - // to_bytes(truncate(slice_(concat(a, b), start), end)) -- the headline nesting: - // drop `start` bytes, then keep `end` of what remains (re-slicing a memory_slice). - public function window_of_concat(a: bytes32, b: bytes32, start: uint256, end: uint256) -> memory(bytes) { - return to_bytes(truncate(slice_(concat(a, b), Typedef.rep(start)), Typedef.rep(end))); - } - - // --- a slice used as a concat operand --- - - public function concat_slice_b32(a: memory(bytes), start: uint256, c: bytes32) -> memory(bytes) { - return concat(slice_(a, Typedef.rep(start)), c); - } - - public function concat_two_slices(a: memory(bytes), sa: uint256, b: memory(bytes), eb: uint256) -> memory(bytes) { - return concat(slice_(a, Typedef.rep(sa)), truncate(b, Typedef.rep(eb))); - } - - // --- re-slicing a memory_slice --- - - public function slice_of_slice(a: memory(bytes), s1: uint256, s2: uint256) -> memory(bytes) { - return to_bytes(slice_(slice_(a, Typedef.rep(s1)), Typedef.rep(s2))); - } - - // --- hashing a slice directly (no intermediate copy) --- - - public function keccak_slice(a: memory(bytes), start: uint256) -> bytes32 { - return keccak256_(slice_(a, Typedef.rep(start))); - } - - public function sha_truncate(a: memory(bytes), end: uint256) -> bytes32 { - return sha256(truncate(a, Typedef.rep(end))); - } - - // keccak256_(truncate(slice_(concat(a, b), start), end)) -- nested chain, hash endpoint. - public function keccak_window_concat(a: bytes32, b: bytes32, start: uint256, end: uint256) -> bytes32 { - return keccak256_(truncate(slice_(concat(a, b), Typedef.rep(start)), Typedef.rep(end))); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/specialise_sum_of_product.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/specialise_sum_of_product.sol new file mode 100644 index 00000000..b28de52b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/specialise_sum_of_product.sol @@ -0,0 +1,96 @@ +// Regression test: specializer sum-of-product bug (specMatch substitution leak). +// +// A binary class method over the primitive `sum(f, g)` whose two sides have +// DIFFERENT shapes: the inl side carries a product (word, word), the inr side +// carries a plain word. Specializing the instance at sum((word, word), word) +// used to leak a substitution binding from one match alternative into the +// sibling alternative's nested `match`, mistyping its scrutinee. The frontend +// (sol-core) accepted the program, but `yule` then rejected the emitted .hull: +// +// Type mismatch +// expected: sum(word, word) +// actual: sum(pair(word, word), word) +// +// Root cause: in Specialise.hs, `specMatch` did not scope `spSubst` (a global +// accumulator) across match alternatives. While specializing the `inl` branch, +// a binding leaked into the `inr` branch's nested `match`, collapsing +// sum(f, g) to sum(g, g). The fix resets spSubst around each alternative. +// +// This isolates the SPECIALIZER: no #[derive], no Eq universe instances. The +// class and its instances are defined locally and exercised directly, so the +// program must now lower end-to-end and return the expected value. + +import * from std; +import * from std.dispatch; + +pragma no-patterson-condition; +pragma no-bounded-variable-condition; + +// total(x, y) sums every leaf word of both arguments. +trait Total { + function total(x: a, y: a) returns (word) ; +} + +impl Total { + function total(x: word, y: word) returns (word) { + return x + y; + } +} + +// product: recurse into both components (this is the shape inl carries). +impl Total<(f, g)> where f: Total, g: Total { + function total(x: (f, g), y: (f, g)) returns (word) { + match (x) { +case (xa, xb) { +match (y) { +case (ya, yb) { +return Total.total(xa, ya) + Total.total(xb, yb); +} +} +} +} + } +} + +// sum: the buggy shape. The inl branch recurses at f (a product here), the inr +// branch recurses at g (a word here); specializing one must not pollute the +// other's nested `match y`. +impl Total> where f: Total, g: Total { + function total(x: sum, y: sum) returns (word) { + match (x) { +case inl(xa) { +match (y) { +case inl(ya) { +return Total.total(xa, ya); +} +case inr(yb) { +return 0; +} +} +} +case inr(xb) { +match (y) { +case inl(ya) { +return 0; +} +case inr(yb) { +return Total.total(xb, yb); +} +} +} +} + } +} + +contract SpecialiseSumOfProduct { + constructor() {} + + // inl carries a product (word, word); the two sum sides differ in shape + // (pair vs word), which is what the specializer mishandled. + // total(inl((1,2)), inl((1,2))) = total((1,2),(1,2)) = (1+1)+(2+2) = 6. + function probe() public returns (uint256) { + let x : sum<(word, word), word> = inl((1, 2)); + let y : sum<(word, word), word> = inl((1, 2)); + return uint256(Total.total(x, y)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/specialise_sum_of_product.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/specialise_sum_of_product.solc deleted file mode 100644 index 22d60064..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/specialise_sum_of_product.solc +++ /dev/null @@ -1,81 +0,0 @@ -// Regression test: specializer sum-of-product bug (specMatch substitution leak). -// -// A binary class method over the primitive `sum(f, g)` whose two sides have -// DIFFERENT shapes: the inl side carries a product (word, word), the inr side -// carries a plain word. Specializing the instance at sum((word, word), word) -// used to leak a substitution binding from one match alternative into the -// sibling alternative's nested `match`, mistyping its scrutinee. The frontend -// (sol-core) accepted the program, but `yule` then rejected the emitted .hull: -// -// Type mismatch -// expected: sum(word, word) -// actual: sum(pair(word, word), word) -// -// Root cause: in Specialise.hs, `specMatch` did not scope `spSubst` (a global -// accumulator) across match alternatives. While specializing the `inl` branch, -// a binding leaked into the `inr` branch's nested `match`, collapsing -// sum(f, g) to sum(g, g). The fix resets spSubst around each alternative. -// -// This isolates the SPECIALIZER: no #[derive], no Eq universe instances. The -// class and its instances are defined locally and exercised directly, so the -// program must now lower end-to-end and return the expected value. - -import std.{*}; -import std.dispatch.{*}; - -pragma no-patterson-condition; -pragma no-bounded-variable-condition; - -// total(x, y) sums every leaf word of both arguments. -forall a. -class a : Total { - function total(x : a, y : a) -> word; -} - -instance word : Total { - function total(x : word, y : word) -> word { - return x + y; - } -} - -// product: recurse into both components (this is the shape inl carries). -forall f g . f : Total, g : Total => instance (f, g) : Total { - function total(x : (f, g), y : (f, g)) -> word { - match x { - | (xa, xb) => match y { - | (ya, yb) => return Total.total(xa, ya) + Total.total(xb, yb); - } - } - } -} - -// sum: the buggy shape. The inl branch recurses at f (a product here), the inr -// branch recurses at g (a word here); specializing one must not pollute the -// other's nested `match y`. -forall f g . f : Total, g : Total => instance sum(f, g) : Total { - function total(x : sum(f, g), y : sum(f, g)) -> word { - match x { - | inl(xa) => match y { - | inl(ya) => return Total.total(xa, ya); - | inr(yb) => return 0; - } - | inr(xb) => match y { - | inl(ya) => return 0; - | inr(yb) => return Total.total(xb, yb); - } - } - } -} - -contract SpecialiseSumOfProduct { - constructor() {} - - // inl carries a product (word, word); the two sum sides differ in shape - // (pair vs word), which is what the specializer mishandled. - // total(inl((1,2)), inl((1,2))) = total((1,2),(1,2)) = (1+1)+(2+2) = 6. - public function probe() -> uint256 { - let x : sum((word, word), word) = inl((1, 2)); - let y : sum((word, word), word) = inl((1, 2)); - return uint256(Total.total(x, y)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage.sol new file mode 100644 index 00000000..f7ff51e2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage.sol @@ -0,0 +1,17 @@ +import * from std; +import * from std.dispatch; + +// Storage support for a `memory(bytes)` contract field: assigning to the +// field copies the byte array into storage, reading it back loads it into +// fresh memory. Exercises StorageSize / CanStore for memory(bytes). +contract C { + content: bytes; + + function set(value: memory) public { + content = value; + } + + function get() public returns (memory) { + return content; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage.solc deleted file mode 100644 index a1a53781..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage.solc +++ /dev/null @@ -1,17 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// Storage support for a `memory(bytes)` contract field: assigning to the -// field copies the byte array into storage, reading it back loads it into -// fresh memory. Exercises StorageSize / CanStore for memory(bytes). -contract C { - content: bytes; - - public function set(value: memory(bytes)) -> () { - content = value; - } - - public function get() -> memory(bytes) { - return content; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_abi.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_abi.sol new file mode 100644 index 00000000..35e8cbcc --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_abi.sol @@ -0,0 +1,67 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; +import * from std.StorageGeneric; + +// An ADT crossing the ABI boundary AND living in storage at the same time. +// +// The public entry points take and return `Option(uint256)` directly, so the +// generated dispatch has to +// * derive an ABIDecode instance for the type (DeriveGeneric.buildABIDecode), +// * reach ABIEncode / ABIAttribs through the default Generic bridges, and +// * build a selector from the derived SigString, which for a sum is the +// structural string "sum(,)" — here rep = sum((), uint256) and +// sigStr(()) = "", so the signature is `setOpt(sum(,uint256))`. +// +// Wire layout of a sum (std.ABIGeneric): one tag word, then the branch payload +// at +32. So `Some(42)` is 0x...01 followed by 0x...2a, and `None` is 0x...00 +// followed by a don't-care word. + +enum Option { None, Some(a) } + +contract C { + stored : Option; + + constructor() { + stored = Option.None; + assert(StorageSize.size(@Option) == 2); + } + + // ADT as a parameter: decoded from calldata, then written to storage. + function setOpt(o: Option) public { + stored = o; + } + + // ADT as a return value: loaded from storage, then encoded into returndata. + function getOpt() public returns (Option) { + return stored; + } + + // Round-trip in one call, without touching storage. + function echo(o: Option) public returns (Option) { + return o; + } + + function isSome() public returns (bool) { + match (stored) { +case Option.None { +return false; +} +case Option.Some(_) { +return true; +} +} + } + + function unwrapOr(d: uint256) public returns (uint256) { + match (stored) { +case Option.None { +return d; +} +case Option.Some(v) { +return v; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_abi.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_abi.solc deleted file mode 100644 index 2dcdc0ba..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_abi.solc +++ /dev/null @@ -1,59 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; -import std.StorageGeneric.{*}; - -// An ADT crossing the ABI boundary AND living in storage at the same time. -// -// The public entry points take and return `Option(uint256)` directly, so the -// generated dispatch has to -// * derive an ABIDecode instance for the type (DeriveGeneric.buildABIDecode), -// * reach ABIEncode / ABIAttribs through the default Generic bridges, and -// * build a selector from the derived SigString, which for a sum is the -// structural string "sum(,)" — here rep = sum((), uint256) and -// sigStr(()) = "", so the signature is `setOpt(sum(,uint256))`. -// -// Wire layout of a sum (std.ABIGeneric): one tag word, then the branch payload -// at +32. So `Some(42)` is 0x...01 followed by 0x...2a, and `None` is 0x...00 -// followed by a don't-care word. - -data Option(a) = None | Some(a); - -contract C { - stored : Option(uint256); - - constructor() { - stored = Option.None; - assert(StorageSize.size(Proxy : Proxy(Option(uint256))) == 2); - } - - // ADT as a parameter: decoded from calldata, then written to storage. - public function setOpt(o : Option(uint256)) -> () { - stored = o; - } - - // ADT as a return value: loaded from storage, then encoded into returndata. - public function getOpt() -> Option(uint256) { - return stored; - } - - // Round-trip in one call, without touching storage. - public function echo(o : Option(uint256)) -> Option(uint256) { - return o; - } - - public function isSome() -> bool { - match stored { - | Option.None => return false; - | Option.Some(_) => return true; - } - } - - public function unwrapOr(d : uint256) -> uint256 { - match stored { - | Option.None => return d; - | Option.Some(v) => return v; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_bool.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_bool.sol new file mode 100644 index 00000000..ee7a810e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_bool.sol @@ -0,0 +1,101 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.StorageGeneric; + +// `bool` in storage, bare and inside an ADT. +// +// bool is a builtin rather than a Typedef(word), so it has no StorageType +// instance. It is storable only through the dedicated +// `storage(bool):CanStore(bool)` instance, which round-trips it via +// frombool / tobool. This test pins that instance down, both as a plain +// contract field and as a leaf reached through the structural CanStore +// decomposition of an ADT. +// +// Note the entry points take uint256 rather than bool: bool has no ABIDecode +// instance, so it cannot appear in a public parameter position. It can appear +// in a return position, which is what the getters below exercise. + +enum Flags { Flags(bool, bool) } +enum Toggle { Off, On(bool) } + +function toBool(v: uint256) returns (bool) { + return v != uint256(0); +} + +contract C { + bare : bool; + flags : Flags; + toggle : Toggle; + + constructor() { + bare = false; + flags = Flags(false, false); + toggle = Toggle.Off; + assert(StorageSize.size(@bool) == 1); + // product of two bools + assert(StorageSize.size(@Flags) == 2); + // 1 tag + max(size (), size bool) + assert(StorageSize.size(@Toggle) == 2); + } + + function setBare(v: uint256) public { + bare = toBool(v); + } + + function getBare() public returns (bool) { + return bare; + } + + function setFlags(a: uint256, b: uint256) public { + flags = Flags(toBool(a), toBool(b)); + } + + function firstFlag() public returns (bool) { + match (flags) { +case Flags(a, _) { +return a; +} +} + } + + function secondFlag() public returns (bool) { + match (flags) { +case Flags(_, b) { +return b; +} +} + } + + function turnOn(v: uint256) public { + toggle = Toggle.On(toBool(v)); + } + + function turnOff() public { + toggle = Toggle.Off; + } + + // Distinguishes Off from On(false): both leave a zero payload slot, so only + // the tag can tell them apart. + function isOn() public returns (bool) { + match (toggle) { +case Toggle.Off { +return false; +} +case Toggle.On(_) { +return true; +} +} + } + + function toggleValue() public returns (bool) { + match (toggle) { +case Toggle.Off { +revertEmpty(); return false; +} +case Toggle.On(b) { +return b; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_bool.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_bool.solc deleted file mode 100644 index 5d1a80b6..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_bool.solc +++ /dev/null @@ -1,89 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -// `bool` in storage, bare and inside an ADT. -// -// bool is a builtin rather than a Typedef(word), so it has no StorageType -// instance. It is storable only through the dedicated -// `storage(bool):CanStore(bool)` instance, which round-trips it via -// frombool / tobool. This test pins that instance down, both as a plain -// contract field and as a leaf reached through the structural CanStore -// decomposition of an ADT. -// -// Note the entry points take uint256 rather than bool: bool has no ABIDecode -// instance, so it cannot appear in a public parameter position. It can appear -// in a return position, which is what the getters below exercise. - -data Flags = Flags(bool, bool); -data Toggle = Off | On(bool); - -function toBool(v : uint256) -> bool { - return v != uint256(0); -} - -contract C { - bare : bool; - flags : Flags; - toggle : Toggle; - - constructor() { - bare = false; - flags = Flags(false, false); - toggle = Toggle.Off; - assert(StorageSize.size(Proxy : Proxy(bool)) == 1); - // product of two bools - assert(StorageSize.size(Proxy : Proxy(Flags)) == 2); - // 1 tag + max(size (), size bool) - assert(StorageSize.size(Proxy : Proxy(Toggle)) == 2); - } - - public function setBare(v : uint256) -> () { - bare = toBool(v); - } - - public function getBare() -> bool { - return bare; - } - - public function setFlags(a : uint256, b : uint256) -> () { - flags = Flags(toBool(a), toBool(b)); - } - - public function firstFlag() -> bool { - match flags { - | Flags(a, _) => return a; - } - } - - public function secondFlag() -> bool { - match flags { - | Flags(_, b) => return b; - } - } - - public function turnOn(v : uint256) -> () { - toggle = Toggle.On(toBool(v)); - } - - public function turnOff() -> () { - toggle = Toggle.Off; - } - - // Distinguishes Off from On(false): both leave a zero payload slot, so only - // the tag can tell them apart. - public function isOn() -> bool { - match toggle { - | Toggle.Off => return false; - | Toggle.On(_) => return true; - } - } - - public function toggleValue() -> bool { - match toggle { - | Toggle.Off => revertEmpty(); return false; - | Toggle.On(b) => return b; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_enum.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_enum.sol new file mode 100644 index 00000000..5f721fe2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_enum.sol @@ -0,0 +1,107 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.StorageGeneric; + +// An enumeration with more than two constructors. +// +// The SOP representation is a RIGHT-NESTED sum, so `Color` becomes +// `sum((), sum((), ()))` and the constructors are encoded as +// +// Red = inl(()) tag 0 at slot p +// Green = inr(inl(())) tag 1 at slot p, tag 0 at slot p+1 +// Blue = inr(inr(())) tag 1 at slot p, tag 1 at slot p+1 +// +// so the tag is spread unary-style across the nesting levels and the type +// occupies 1 + max(0, 1 + max(0, 0)) = 2 slots. +// +// `Green` is the only constructor that exercises the `inr(inl(...))` path, +// which is exactly the sum nesting that CanStore.load has to reconstruct. +enum Color { Red, Green, Blue } + +// A three-constructor sum whose branches carry payloads of different widths. +// rep = sum(uint256, sum((uint256, uint256), ())), so +// size = 1 + max(1, 1 + max(2, 0)) = 4. +enum Shape { Dot(uint256), Seg(uint256, uint256), Nothing } + +contract C { + color : Color; + shape : Shape; + + constructor() { + color = Color.Red; + shape = Shape.Nothing; + // 1 tag + max(size (), 1 tag + max(size (), size ())) = 1 + 1 + 0 = 2 + assert(StorageSize.size(@Color) == 2); + // 1 tag + max(size uint256, 1 tag + max(size (uint256,uint256), size ())) = 1 + 1 + 2 = 4 + assert(StorageSize.size(@Shape) == 4); + } + + function setRed() public { + color = Color.Red; + } + + // inr(inl(())) — the nested-tag branch. + function setGreen() public { + color = Color.Green; + } + + function setBlue() public { + color = Color.Blue; + } + + function tag() public returns (uint256) { + match (color) { +case Color.Red { +return uint256(0); +} +case Color.Green { +return uint256(1); +} +case Color.Blue { +return uint256(2); +} +} + } + + function setDot(a: uint256) public { + shape = Shape.Dot(a); + } + + // inr(inl(...)) again, this time with a product payload. + function setSeg(a: uint256, b: uint256) public { + shape = Shape.Seg(a, b); + } + + function setNothing() public { + shape = Shape.Nothing; + } + + function shapeSum() public returns (uint256) { + match (shape) { +case Shape.Dot(a) { +return a; +} +case Shape.Seg(a, b) { +return a + b; +} +case Shape.Nothing { +return uint256(0); +} +} + } + + function shapeTag() public returns (uint256) { + match (shape) { +case Shape.Dot(_) { +return uint256(0); +} +case Shape.Seg(_, _) { +return uint256(1); +} +case Shape.Nothing { +return uint256(2); +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_enum.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_enum.solc deleted file mode 100644 index 732d6036..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_enum.solc +++ /dev/null @@ -1,92 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -// An enumeration with more than two constructors. -// -// The SOP representation is a RIGHT-NESTED sum, so `Color` becomes -// `sum((), sum((), ()))` and the constructors are encoded as -// -// Red = inl(()) tag 0 at slot p -// Green = inr(inl(())) tag 1 at slot p, tag 0 at slot p+1 -// Blue = inr(inr(())) tag 1 at slot p, tag 1 at slot p+1 -// -// so the tag is spread unary-style across the nesting levels and the type -// occupies 1 + max(0, 1 + max(0, 0)) = 2 slots. -// -// `Green` is the only constructor that exercises the `inr(inl(...))` path, -// which is exactly the sum nesting that CanStore.load has to reconstruct. -data Color = Red | Green | Blue; - -// A three-constructor sum whose branches carry payloads of different widths. -// rep = sum(uint256, sum((uint256, uint256), ())), so -// size = 1 + max(1, 1 + max(2, 0)) = 4. -data Shape = - Dot(uint256) - | Seg(uint256, uint256) - | Nothing; - -contract C { - color : Color; - shape : Shape; - - constructor() { - color = Color.Red; - shape = Shape.Nothing; - // 1 tag + max(size (), 1 tag + max(size (), size ())) = 1 + 1 + 0 = 2 - assert(StorageSize.size(Proxy : Proxy(Color)) == 2); - // 1 tag + max(size uint256, 1 tag + max(size (uint256,uint256), size ())) = 1 + 1 + 2 = 4 - assert(StorageSize.size(Proxy : Proxy(Shape)) == 4); - } - - public function setRed() -> () { - color = Color.Red; - } - - // inr(inl(())) — the nested-tag branch. - public function setGreen() -> () { - color = Color.Green; - } - - public function setBlue() -> () { - color = Color.Blue; - } - - public function tag() -> uint256 { - match color { - | Color.Red => return uint256(0); - | Color.Green => return uint256(1); - | Color.Blue => return uint256(2); - } - } - - public function setDot(a : uint256) -> () { - shape = Shape.Dot(a); - } - - // inr(inl(...)) again, this time with a product payload. - public function setSeg(a : uint256, b : uint256) -> () { - shape = Shape.Seg(a, b); - } - - public function setNothing() -> () { - shape = Shape.Nothing; - } - - public function shapeSum() -> uint256 { - match shape { - | Shape.Dot(a) => return a; - | Shape.Seg(a, b) => return a + b; - | Shape.Nothing => return uint256(0); - } - } - - public function shapeTag() -> uint256 { - match shape { - | Shape.Dot(_) => return uint256(0); - | Shape.Seg(_, _) => return uint256(1); - | Shape.Nothing => return uint256(2); - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_field.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_field.sol new file mode 100644 index 00000000..c9bc2ef5 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_field.sol @@ -0,0 +1,106 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.StorageGeneric; + +// Algebraic data types used directly as contract storage fields, including a +// nested ADT (Option(Triple)). +// +// - someValue : Option(uint256) (sum, rep sum((), uint256) -> 2 slots) +// - triple : Triple (product, rep (uint256,(uint256,uint256)) -> 3 slots) +// - someTriple : Option(Triple) (sum of product, rep sum((), Triple) -> 4 slots) + +enum Option { None, Some(a) } +enum Triple { Triple(uint256, uint256, uint256) } + +contract C { + someValue : Option; + triple : Triple; + someTriple : Option; + + constructor() { + // sum: 1 tag + max(size (), size uint256) = 1 + 1 = 2 + assert(StorageSize.size(@Option) == 2); + // product: size uint256 * 3 = 3 + assert(StorageSize.size(@Triple) == 3); + // sum of product: 1 tag + max(size (), size Triple) = 1 + 3 = 4 + assert(StorageSize.size(@Option) == 4); + } + + function setValue(v: uint256) public { + someValue = Option.Some(v); + } + + function clearValue() public { + someValue = Option.None; + } + + function getValue() public returns (uint256) { + match (someValue) { +case Option.None { +revertEmpty(); return uint256(0); +} +case Option.Some(v) { +return v; +} +} + } + + function isSome() public returns (bool) { + match (someValue) { +case Option.None { +return false; +} +case Option.Some(_) { +return true; +} +} + } + + function setTriple(a: uint256, b: uint256, c: uint256) public { + triple = Triple(a, b, c); + } + + function tripleSum() public returns (uint256) { + match (triple) { +case Triple(a, b, c) { +return a + b + c; +} +} + } + + // Nested ADT: Option(Triple). + function setSomeTriple(a: uint256, b: uint256, c: uint256) public { + someTriple = Option.Some(Triple(a, b, c)); + } + + function clearSomeTriple() public { + someTriple = Option.None; + } + + function someTripleSum() public returns (uint256) { + match (someTriple) { +case Option.None { +revertEmpty(); return uint256(0); +} +case Option.Some(t) { +match (t) { +case Triple(a, b, c) { +return a + b + c; +} +} +} +} + } + + function hasSomeTriple() public returns (bool) { + match (someTriple) { +case Option.None { +return false; +} +case Option.Some(_) { +return true; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_field.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_field.solc deleted file mode 100644 index d1b68cb2..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_field.solc +++ /dev/null @@ -1,87 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -// Algebraic data types used directly as contract storage fields, including a -// nested ADT (Option(Triple)). -// -// - someValue : Option(uint256) (sum, rep sum((), uint256) -> 2 slots) -// - triple : Triple (product, rep (uint256,(uint256,uint256)) -> 3 slots) -// - someTriple : Option(Triple) (sum of product, rep sum((), Triple) -> 4 slots) - -data Option(a) = None | Some(a); -data Triple = Triple(uint256, uint256, uint256); - -contract C { - someValue : Option(uint256); - triple : Triple; - someTriple : Option(Triple); - - constructor() { - // sum: 1 tag + max(size (), size uint256) = 1 + 1 = 2 - assert(StorageSize.size(Proxy : Proxy(Option(uint256))) == 2); - // product: size uint256 * 3 = 3 - assert(StorageSize.size(Proxy : Proxy(Triple)) == 3); - // sum of product: 1 tag + max(size (), size Triple) = 1 + 3 = 4 - assert(StorageSize.size(Proxy : Proxy(Option(Triple))) == 4); - } - - public function setValue(v : uint256) -> () { - someValue = Option.Some(v); - } - - public function clearValue() -> () { - someValue = Option.None; - } - - public function getValue() -> uint256 { - match someValue { - | Option.None => revertEmpty(); return uint256(0); - | Option.Some(v) => return v; - } - } - - public function isSome() -> bool { - match someValue { - | Option.None => return false; - | Option.Some(_) => return true; - } - } - - public function setTriple(a : uint256, b : uint256, c : uint256) -> () { - triple = Triple(a, b, c); - } - - public function tripleSum() -> uint256 { - match triple { - | Triple(a, b, c) => return a + b + c; - } - } - - // Nested ADT: Option(Triple). - public function setSomeTriple(a : uint256, b : uint256, c : uint256) -> () { - someTriple = Option.Some(Triple(a, b, c)); - } - - public function clearSomeTriple() -> () { - someTriple = Option.None; - } - - public function someTripleSum() -> uint256 { - match someTriple { - | Option.None => revertEmpty(); return uint256(0); - | Option.Some(t) => - match t { - | Triple(a, b, c) => return a + b + c; - } - } - } - - public function hasSomeTriple() -> bool { - match someTriple { - | Option.None => return false; - | Option.Some(_) => return true; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_mapping.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_mapping.sol new file mode 100644 index 00000000..4cb752ac --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_mapping.sol @@ -0,0 +1,97 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.StorageGeneric; + +// An ADT used as the VALUE of a storage mapping. +// +// This is the path opened by routing mapping reads through CanStore instead of +// StorageType (std.sol: readStorage / ridx / RValueIdxAccess). The write side +// already went through Assign -> CanStore.store. +// +// A multi-slot value in a mapping occupies hash2(slot, key) .. + size(v) - 1, +// exactly as Solidity lays out a struct behind a mapping. + +enum Option { None, Some(a) } +enum Pair { Pair(uint256, uint256) } + +contract C { + // 2 slots per entry: tag + payload + opts : mapping(uint256 => Option); + // 2 slots per entry: no tag, two words + pairs : mapping(uint256 => Pair); + // 3 slots per entry: tag + max(0, 2) + optPairs : mapping(uint256 => Option); + + constructor() { + assert(StorageSize.size(@Option) == 2); + assert(StorageSize.size(@Pair) == 2); + assert(StorageSize.size(@Option) == 3); + } + + function putOpt(k: uint256, v: uint256) public { + opts[k] = Option.Some(v); + } + + function clearOpt(k: uint256) public { + opts[k] = Option.None; + } + + // Unset keys read back as the zero slot pattern, i.e. tag 0 = None. + function hasOpt(k: uint256) public returns (bool) { + match (opts[k]) { +case Option.None { +return false; +} +case Option.Some(_) { +return true; +} +} + } + + function getOpt(k: uint256) public returns (uint256) { + match (opts[k]) { +case Option.None { +revertEmpty(); return uint256(0); +} +case Option.Some(v) { +return v; +} +} + } + + function putPair(k: uint256, a: uint256, b: uint256) public { + pairs[k] = Pair(a, b); + } + + function pairSum(k: uint256) public returns (uint256) { + match (pairs[k]) { +case Pair(a, b) { +return a + b; +} +} + } + + function putOptPair(k: uint256, a: uint256, b: uint256) public { + optPairs[k] = Option.Some(Pair(a, b)); + } + + function clearOptPair(k: uint256) public { + optPairs[k] = Option.None; + } + + function optPairSum(k: uint256) public returns (uint256) { + match (optPairs[k]) { +case Option.None { +revertEmpty(); return uint256(0); +} +case Option.Some(p) { +match (p) { +case Pair(a, b) { +return a + b; +} +} +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_mapping.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_mapping.solc deleted file mode 100644 index ad2f4df8..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_adt_mapping.solc +++ /dev/null @@ -1,82 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -// An ADT used as the VALUE of a storage mapping. -// -// This is the path opened by routing mapping reads through CanStore instead of -// StorageType (std.solc: readStorage / ridx / RValueIdxAccess). The write side -// already went through Assign -> CanStore.store. -// -// A multi-slot value in a mapping occupies hash2(slot, key) .. + size(v) - 1, -// exactly as Solidity lays out a struct behind a mapping. - -data Option(a) = None | Some(a); -data Pair = Pair(uint256, uint256); - -contract C { - // 2 slots per entry: tag + payload - opts : mapping(uint256, Option(uint256)); - // 2 slots per entry: no tag, two words - pairs : mapping(uint256, Pair); - // 3 slots per entry: tag + max(0, 2) - optPairs : mapping(uint256, Option(Pair)); - - constructor() { - assert(StorageSize.size(Proxy : Proxy(Option(uint256))) == 2); - assert(StorageSize.size(Proxy : Proxy(Pair)) == 2); - assert(StorageSize.size(Proxy : Proxy(Option(Pair))) == 3); - } - - public function putOpt(k : uint256, v : uint256) -> () { - opts[k] = Option.Some(v); - } - - public function clearOpt(k : uint256) -> () { - opts[k] = Option.None; - } - - // Unset keys read back as the zero slot pattern, i.e. tag 0 = None. - public function hasOpt(k : uint256) -> bool { - match opts[k] { - | Option.None => return false; - | Option.Some(_) => return true; - } - } - - public function getOpt(k : uint256) -> uint256 { - match opts[k] { - | Option.None => revertEmpty(); return uint256(0); - | Option.Some(v) => return v; - } - } - - public function putPair(k : uint256, a : uint256, b : uint256) -> () { - pairs[k] = Pair(a, b); - } - - public function pairSum(k : uint256) -> uint256 { - match pairs[k] { - | Pair(a, b) => return a + b; - } - } - - public function putOptPair(k : uint256, a : uint256, b : uint256) -> () { - optPairs[k] = Option.Some(Pair(a, b)); - } - - public function clearOptPair(k : uint256) -> () { - optPairs[k] = Option.None; - } - - public function optPairSum(k : uint256) -> uint256 { - match optPairs[k] { - | Option.None => revertEmpty(); return uint256(0); - | Option.Some(p) => - match p { - | Pair(a, b) => return a + b; - } - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_array.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_array.sol new file mode 100644 index 00000000..65057965 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_array.sol @@ -0,0 +1,52 @@ +import * from std; +import * from std.dispatch; +import {mload, mstore} from std.opcodes; + +contract MemberRegistry { + members : array
; + + constructor() {} + + function addMember(addr: address) public { + ArrayPush.push(members, addr); + } + + // MemberNotFound() selector + function removeMember(addr: address) public { + // foundIdx == length() acts as the "not found" sentinel. + let foundIdx : uint256 = Length.length(members); + let i : uint256; + for (i = uint256(0); i < Length.length(members); i = i + uint256(1)) { + if (members[i] == addr) { + // NOTE: solcore has no `break`, so we keep scanning. + foundIdx = i; + } + } + require(foundIdx != Length.length(members), Error(0xdeadbeef)); + + // Shift subsequent elements down one slot to close the gap. + for (; foundIdx < Length.length(members) - uint256(1); foundIdx = foundIdx + uint256(1)) { + members[foundIdx] = members[foundIdx + uint256(1)]; + } + // Drop the (now-duplicated) last item and adjust the length. + Array.pop(members); + } + + function numberOfMembers() public returns (uint256) { + return Length.length(members); + } + + function getMembers() public returns (memory>) { + let count : word = Typedef.rep(Length.length(members)); + let totalBytes : word = (count + 1) * 32; + let ptr : word = allocate_memory(totalBytes); + mstore(ptr, count); + + let i : word; + for (i = 0; i < count; i = i + 1) { + let addr : address = members[uint256(i)]; + mstore(ptr + 32 + i * 32, Typedef.rep(addr)); + } + return Typedef.abs(ptr) ; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_array.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_array.solc deleted file mode 100644 index f652f8d5..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_array.solc +++ /dev/null @@ -1,52 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.opcodes.{mload, mstore}; - -contract MemberRegistry { - members : array(address); - - constructor() {} - - public function addMember(addr : address) -> () { - ArrayPush.push(members, addr); - } - - // MemberNotFound() selector - public function removeMember(addr : address) -> () { - // foundIdx == length() acts as the "not found" sentinel. - let foundIdx : uint256 = Length.length(members); - let i : uint256; - for (i = uint256(0); i < Length.length(members); i = i + uint256(1)) { - if (members[i] == addr) { - // NOTE: solcore has no `break`, so we keep scanning. - foundIdx = i; - } - } - require(foundIdx != Length.length(members), Error(0xdeadbeef)); - - // Shift subsequent elements down one slot to close the gap. - for (; foundIdx < Length.length(members) - uint256(1); foundIdx = foundIdx + uint256(1)) { - members[foundIdx] = members[foundIdx + uint256(1)]; - } - // Drop the (now-duplicated) last item and adjust the length. - Array.pop(members); - } - - public function numberOfMembers() -> uint256 { - return Length.length(members); - } - - public function getMembers() -> memory(DynArray(address)) { - let count : word = Typedef.rep(Length.length(members)); - let totalBytes : word = (count + 1) * 32; - let ptr : word = allocate_memory(totalBytes); - mstore(ptr, count); - - let i : word; - for (i = 0; i < count; i = i + 1) { - let addr : address = members[uint256(i)]; - mstore(ptr + 32 + i * 32, Typedef.rep(addr)); - } - return Typedef.abs(ptr) : memory(DynArray(address)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_dynamic_field.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_dynamic_field.sol new file mode 100644 index 00000000..7ab845fe --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_dynamic_field.sol @@ -0,0 +1,49 @@ +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.StorageGeneric; + +enum Blob { NoBlob, SomeBytes(memory) } + +contract C { + blob : Blob; + + constructor() { + blob = Blob.NoBlob; + // A dynamic field occupies one slot, so the sum is 1 (tag) + max(0, 1). + assert(StorageSize.size(@Blob) == 2); + } + + function clear() public { + blob = Blob.NoBlob; + } + + // Stores the memory(bytes) payload into the ADT field (round-trips the + // dynamic leaf through storage(bytes)). + function setBytes(b: memory) public { + blob = Blob.SomeBytes(b); + } + + function getBytes() public returns (memory) { + match (blob) { +case Blob.NoBlob { +revertEmpty(); return memory(0); +} +case Blob.SomeBytes(b) { +return b; +} +} + } + + // Loads the whole ADT back from storage and inspects its tag. + function isEmpty() public returns (bool) { + match (blob) { +case Blob.NoBlob { +return true; +} +case Blob.SomeBytes(_) { +return false; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_dynamic_field.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_dynamic_field.solc deleted file mode 100644 index fa863452..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage_dynamic_field.solc +++ /dev/null @@ -1,43 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.StorageGeneric.{*}; - -data Blob = - NoBlob - | SomeBytes(memory(bytes)); - -contract C { - blob : Blob; - - constructor() { - blob = Blob.NoBlob; - // A dynamic field occupies one slot, so the sum is 1 (tag) + max(0, 1). - assert(StorageSize.size(Proxy : Proxy(Blob)) == 2); - } - - public function clear() -> () { - blob = Blob.NoBlob; - } - - // Stores the memory(bytes) payload into the ADT field (round-trips the - // dynamic leaf through storage(bytes)). - public function setBytes(b: memory(bytes)) -> () { - blob = Blob.SomeBytes(b); - } - - public function getBytes() -> memory(bytes) { - match blob { - | Blob.NoBlob => revertEmpty(); return memory(0); - | Blob.SomeBytes(b) => return b; - } - } - - // Loads the whole ADT back from storage and inspects its tag. - public function isEmpty() -> bool { - match blob { - | Blob.NoBlob => return true; - | Blob.SomeBytes(_) => return false; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/stringid.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/stringid.sol new file mode 100644 index 00000000..298d7312 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/stringid.sol @@ -0,0 +1,43 @@ +import * from std; +import * from std.dispatch; +import {mstore, mload} from std.opcodes; + +contract C { + constructor() {} + function id(x: memory) public returns (memory) { + let ptr : word = Typedef.rep(x); + let len : word; + let n1 : word; + assembly { + len := mload(ptr) + n1 := mload(add(ptr,32)) + } + log1(len, 0xc001); + log1(n1, 0xc002); + + return x; + } + + function const_a() public returns (memory) { + let resPtr = allocate_memory(64); + let payload : word = 0x7777777777777777777777777777777777777777777777777777777777777777; + mstore(resPtr, 3); + mstore(resPtr+32, payload); + return memory(resPtr); + } + function mylen(x: memory) public returns (uint256) { + let ptr : word = Typedef.rep(x); + let l : word; + let n1 : word; + assembly { + l := mload(ptr) + n1 := mload(add(ptr,32)) + } + // log1(l, 0xc001); + // log1(n1, 0xc002); + + return uint256(l); + } + + // function answer() -> uint256 { return uint256(17); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/stringid.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/stringid.solc deleted file mode 100644 index 6e5c1d8a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/stringid.solc +++ /dev/null @@ -1,43 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.opcodes.{mstore, mload}; - -contract C { - constructor() {} - public function id(x:memory(string)) -> (memory(string)) { - let ptr : word = Typedef.rep(x); - let len : word; - let n1 : word; - assembly { - len := mload(ptr) - n1 := mload(add(ptr,32)) - } - log1(len, 0xc001); - log1(n1, 0xc002); - - return x; - } - - public function const_a() -> (memory(string)) { - let resPtr = allocate_memory(64); - let payload : word = 0x7777777777777777777777777777777777777777777777777777777777777777; - mstore(resPtr, 3); - mstore(resPtr+32, payload); - return memory(resPtr); - } - public function mylen(x:memory(string)) -> uint256 { - let ptr : word = Typedef.rep(x); - let l : word; - let n1 : word; - assembly { - l := mload(ptr) - n1 := mload(add(ptr,32)) - } - // log1(l, 0xc001); - // log1(n1, 0xc002); - - return uint256(l); - } - - // function answer() -> uint256 { return uint256(17); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/stringlit.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/stringlit.sol new file mode 100644 index 00000000..31d4260e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/stringlit.sol @@ -0,0 +1,26 @@ +import * from std; +import {memory, string, uint256} from std; +import * from std.dispatch; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +// End-to-end test of comptime string materialization into memory(string): +// each form must ABI-encode to the same "Hello, world!" return value. +contract C { + constructor() {} + + // terse: concatLit wrapped in Str.fromString by the desugarer + function greeting() public returns (memory) { + return concatLit("Hello, ", "world!"); + // fromString inserted automatically when using concatLit + // later we may have an operator for that e.g. <> + } + + // A2: via an intermediate string-typed let (dead-let substitution path) + function greetLet() public returns (memory) { + let s : string = "Hello, " + "world!"; + return Str.fromString(s); + // here fromString needs to be inserted manually + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/stringlit.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/stringlit.solc deleted file mode 100644 index 6136d366..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/stringlit.solc +++ /dev/null @@ -1,26 +0,0 @@ -import std.{*}; -import std.{memory, string, uint256}; -import std.dispatch.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -// End-to-end test of comptime string materialization into memory(string): -// each form must ABI-encode to the same "Hello, world!" return value. -contract C { - constructor() {} - - // terse: concatLit wrapped in Str.fromString by the desugarer - public function greeting() -> memory(string) { - return concatLit("Hello, ", "world!"); - // fromString inserted automatically when using concatLit - // later we may have an operator for that e.g. <> - } - - // A2: via an intermediate string-typed let (dead-let substitution path) - public function greetLet() -> memory(string) { - let s : string = "Hello, " + "world!"; - return Str.fromString(s); - // here fromString needs to be inserted manually - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/sum_wide_product.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/sum_wide_product.sol new file mode 100644 index 00000000..50a9ce58 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/sum_wide_product.sol @@ -0,0 +1,33 @@ +import * from std; +import * from std.dispatch; + +// Regression test for a yule backend bug, independent of the storage/Generic +// work: matching a sum constructor whose payload is a product of arity >= 3. +// +// On `match`, the scrutinee's location is flattened, and the constructor payload +// used to be bound as a flat slot sequence. Destructuring the inner product then +// did EFst on a >2-element sequence and crashed yule with "EFst: type mismatch". +// (A 2-field payload happened to work, since a flat 2-seq is a valid pair.) +// +// No storage and no Generic derivation involved — just constructing and matching +// an ordinary algebraic data type. + +enum Shape { Dot, Tri(uint256, uint256, uint256) } + +contract C { + constructor() {} + + // Build Tri(a,b,c) then match it back out: exercises a sum whose payload is + // a 3-field product. + function triSum(a: uint256, b: uint256, c: uint256) public returns (uint256) { + let s : Shape = Shape.Tri(a, b, c); + match (s) { +case Shape.Dot { +return uint256(0); +} +case Shape.Tri(x, y, z) { +return x + y + z; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/sum_wide_product.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/sum_wide_product.solc deleted file mode 100644 index 259047a9..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/sum_wide_product.solc +++ /dev/null @@ -1,29 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// Regression test for a yule backend bug, independent of the storage/Generic -// work: matching a sum constructor whose payload is a product of arity >= 3. -// -// On `match`, the scrutinee's location is flattened, and the constructor payload -// used to be bound as a flat slot sequence. Destructuring the inner product then -// did EFst on a >2-element sequence and crashed yule with "EFst: type mismatch". -// (A 2-field payload happened to work, since a flat 2-seq is a valid pair.) -// -// No storage and no Generic derivation involved — just constructing and matching -// an ordinary algebraic data type. - -data Shape = Dot | Tri(uint256, uint256, uint256); - -contract C { - constructor() {} - - // Build Tri(a,b,c) then match it back out: exercises a sum whose payload is - // a 3-field product. - public function triSum(a : uint256, b : uint256, c : uint256) -> uint256 { - let s : Shape = Shape.Tri(a, b, c); - match s { - | Shape.Dot => return uint256(0); - | Shape.Tri(x, y, z) => return x + y + z; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ufcs_array.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ufcs_array.sol new file mode 100644 index 00000000..afbe95ca --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ufcs_array.sol @@ -0,0 +1,68 @@ +import * from std; +import * from std.dispatch; +import {mload, mstore} from std.opcodes; + +// UFCS counterpart of storage_array.sol. +// +// This contract is byte-for-byte equivalent in behaviour to +// dispatch/storage_array.sol, but exercises the receiver-style method-call +// sugar resolved by NameResolution: when the receiver of recv.method(args) +// is an (unqualified) contract field and a unique trait exposes the method, the +// call is rewritten to Class.method(recv, args). So: +// +// members.push(addr) ==> ArrayPush.push(members, addr) +// members.length() ==> Length.length(members) +// members.pop() ==> Array.pop(members) +// +// It runs against the SAME assertions as storage_array.json (see +// ufcs_array.json), proving UFCS and the explicit qualified calls compile to +// the same runtime behaviour. Indexed access members[i] is unaffecte: it +// is handled by field-access desugaring, not UFCS. +contract MemberRegistry { + members : array
; + + constructor() {} + + function addMember(addr: address) public { + members.push(addr); + } + + // MemberNotFound() selector + function removeMember(addr: address) public { + // foundIdx == length() acts as the "not found" sentinel. + let foundIdx : uint256 = members.length(); + let i : uint256; + for (i = uint256(0); i < members.length(); i = i + uint256(1)) { + if (members[i] == addr) { + // NOTE: solcore has no break, so we keep scanning. + foundIdx = i; + } + } + require(foundIdx != members.length(), Error(0xdeadbeef)); + + // Shift subsequent elements down one slot to close the gap. + for (; foundIdx < members.length() - uint256(1); foundIdx = foundIdx + uint256(1)) { + members[foundIdx] = members[foundIdx + uint256(1)]; + } + // Drop the (now-duplicated) last item and adjust the length. + members.pop(); + } + + function numberOfMembers() public returns (uint256) { + return members.length(); + } + + function getMembers() public returns (memory>) { + let count : word = Typedef.rep(members.length()); + let totalBytes : word = (count + 1) * 32; + let ptr : word = allocate_memory(totalBytes); + mstore(ptr, count); + + let i : word; + for (i = 0; i < count; i = i + 1) { + let addr : address = members[uint256(i)]; + mstore(ptr + 32 + i * 32, Typedef.rep(addr)); + } + return Typedef.abs(ptr) ; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ufcs_array.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ufcs_array.solc deleted file mode 100644 index 53d7855e..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/ufcs_array.solc +++ /dev/null @@ -1,68 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; -import std.opcodes.{mload, mstore}; - -// UFCS counterpart of storage_array.solc. -// -// This contract is byte-for-byte equivalent in behaviour to -// dispatch/storage_array.solc, but exercises the receiver-style method-call -// sugar resolved by NameResolution: when the receiver of recv.method(args) -// is an (unqualified) contract field and a unique class exposes method, the -// call is rewritten to Class.method(recv, args). So: -// -// members.push(addr) ==> ArrayPush.push(members, addr) -// members.length() ==> Length.length(members) -// members.pop() ==> Array.pop(members) -// -// It runs against the SAME assertions as storage_array.json (see -// ufcs_array.json), proving UFCS and the explicit qualified calls compile to -// the same runtime behaviour. Indexed access members[i] is unaffecte: it -// is handled by field-access desugaring, not UFCS. -contract MemberRegistry { - members : array(address); - - constructor() {} - - public function addMember(addr : address) -> () { - members.push(addr); - } - - // MemberNotFound() selector - public function removeMember(addr : address) -> () { - // foundIdx == length() acts as the "not found" sentinel. - let foundIdx : uint256 = members.length(); - let i : uint256; - for (i = uint256(0); i < members.length(); i = i + uint256(1)) { - if (members[i] == addr) { - // NOTE: solcore has no break, so we keep scanning. - foundIdx = i; - } - } - require(foundIdx != members.length(), Error(0xdeadbeef)); - - // Shift subsequent elements down one slot to close the gap. - for (; foundIdx < members.length() - uint256(1); foundIdx = foundIdx + uint256(1)) { - members[foundIdx] = members[foundIdx + uint256(1)]; - } - // Drop the (now-duplicated) last item and adjust the length. - members.pop(); - } - - public function numberOfMembers() -> uint256 { - return members.length(); - } - - public function getMembers() -> memory(DynArray(address)) { - let count : word = Typedef.rep(members.length()); - let totalBytes : word = (count + 1) * 32; - let ptr : word = allocate_memory(totalBytes); - mstore(ptr, count); - - let i : word; - for (i = 0; i < count; i = i + 1) { - let addr : address = members[uint256(i)]; - mstore(ptr + 32 + i * 32, Typedef.rep(addr)); - } - return Typedef.abs(ptr) : memory(DynArray(address)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/weth9.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/weth9.sol new file mode 100644 index 00000000..b4a25395 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/weth9.sol @@ -0,0 +1,84 @@ +import * from std; +import {caller as caller_, callvalue as callvalue_, selfbalance, gas, call} from std.opcodes; +import * from std.dispatch; + +// Forward `wad` wei to `dst` via a zero-data CALL and revert on failure. +function sendValue(dst: address, wad: uint256) { + let ret = call(gas(), Typedef.rep(dst), Typedef.rep(wad), 0, 0, 0, 0); + require(ret != 0, Error(0x90b8ec18)); // TransferFailed() +} + +function caller() returns (address) { + return address(caller_()); +} + +function callvalue() returns (uint256) { + return uint256(callvalue_()); +} + +// Based on https://github.com/gnosis/canonical-weth/blob/master/contracts/WETH9.sol +// That code is written WITHOUT checked arithmetic. +contract WETH9 { + balances : mapping(address => uint256); + allowance : mapping(address => mapping(address => uint256)); + + constructor() {} + + // --- ETH <-> WETH --- + + function deposit() public payable { + let sender = caller(); + balances[sender] = balances[sender] + callvalue(); + } + + function withdraw(wad: uint256) public { + let sender = caller(); + require(balances[sender] >= wad, Error(0xf4d678b8)); // InsufficientBalance() + balances[sender] = balances[sender] - wad; + sendValue(sender, wad); + } + + // totalSupply == ETH held by this contract (matches canonical WETH9). + function totalSupply() public returns (uint256) { + return uint256(selfbalance()); + } + + // --- ERC20 surface --- + + function balanceOf(account: address) public returns (uint256) { + return balances[account]; + } + + function allowance(owner_: address, spender: address) public returns (uint256) { + return allowance[owner_][spender]; + } + + function approve(usr: address, wad: uint256) public returns (bool) { + let sender = caller(); + allowance[sender][usr] = wad; + return true; + } + + function transfer(dst: address, wad: uint256) public returns (bool) { + return transferFrom(caller(), dst, wad); + } + + function transferFrom(src: address, dst: address, wad: uint256) public returns (bool) { + let sender = caller(); + require(balances[src] >= wad, Error(0xf4d678b8)); // InsufficientBalance() + + if (src != sender && allowance[src][sender] != (maxVal())) { + require(allowance[src][sender] >= wad, Error(0x13be252b)); // InsufficientAllowance() + allowance[src][sender] -= wad; + } + balances[src] = balances[src] - wad; + balances[dst] = balances[dst] + wad; + return true; + } + + // Plain ETH transfers (no calldata, just value) auto-wrap into WETH. + fallback() payable { + let sender = caller(); + balances[sender] = balances[sender] + callvalue(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/weth9.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/weth9.solc deleted file mode 100644 index bd3125ea..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/weth9.solc +++ /dev/null @@ -1,84 +0,0 @@ -import std.{*}; -import std.opcodes.{caller as caller_, callvalue as callvalue_, selfbalance, gas, call}; -import std.dispatch.{*}; - -// Forward `wad` wei to `dst` via a zero-data CALL and revert on failure. -function sendValue(dst: address, wad: uint256) -> () { - let ret = call(gas(), Typedef.rep(dst), Typedef.rep(wad), 0, 0, 0, 0); - require(ret != 0, Error(0x90b8ec18)); // TransferFailed() -} - -function caller() -> address { - return address(caller_()); -} - -function callvalue() -> uint256 { - return uint256(callvalue_()); -} - -// Based on https://github.com/gnosis/canonical-weth/blob/master/contracts/WETH9.sol -// That code is written WITHOUT checked arithmetic. -contract WETH9 { - balances : mapping(address, uint256); - allowance : mapping(address, mapping(address, uint256)); - - constructor() {} - - // --- ETH <-> WETH --- - - public payable function deposit() -> () { - let sender = caller(); - balances[sender] = balances[sender] + callvalue(); - } - - public function withdraw(wad: uint256) -> () { - let sender = caller(); - require(balances[sender] >= wad, Error(0xf4d678b8)); // InsufficientBalance() - balances[sender] = balances[sender] - wad; - sendValue(sender, wad); - } - - // totalSupply == ETH held by this contract (matches canonical WETH9). - public function totalSupply() -> uint256 { - return uint256(selfbalance()); - } - - // --- ERC20 surface --- - - public function balanceOf(account: address) -> uint256 { - return balances[account]; - } - - public function allowance(owner_: address, spender: address) -> uint256 { - return allowance[owner_][spender]; - } - - public function approve(usr: address, wad: uint256) -> bool { - let sender = caller(); - allowance[sender][usr] = wad; - return true; - } - - public function transfer(dst: address, wad: uint256) -> bool { - return transferFrom(caller(), dst, wad); - } - - public function transferFrom(src: address, dst: address, wad: uint256) -> bool { - let sender = caller(); - require(balances[src] >= wad, Error(0xf4d678b8)); // InsufficientBalance() - - if (src != sender && allowance[src][sender] != (maxVal():uint256)) { - require(allowance[src][sender] >= wad, Error(0x13be252b)); // InsufficientAllowance() - allowance[src][sender] -= wad; - } - balances[src] = balances[src] - wad; - balances[dst] = balances[dst] + wad; - return true; - } - - // Plain ETH transfers (no calldata, just value) auto-wrap into WETH. - payable fallback() -> () { - let sender = caller(); - balances[sender] = balances[sender] + callvalue(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/opcodes/all-shapes.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/opcodes/all-shapes.sol new file mode 100644 index 00000000..efde6dbe --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/opcodes/all-shapes.sol @@ -0,0 +1,31 @@ +import * from std.opcodes; + +// Compilation test for the std/opcodes wrappers. +// Picks two opcodes from each of the four shape categories so the +// pipeline exercises every wrapper signature. + +// no inputs, no return +function shape_void_void() { + stop(); + invalid(); +} + +// no inputs, returns a word +function shape_void_word() returns (word) { + let a = address(); + let t = timestamp(); + return a; +} + +// inputs, no return +function shape_word_void(x: word) { + pop(x); + mstore(0, x); +} + +// inputs, returns a word +function shape_word_word(a: word, b: word) returns (word) { + let s = add(a, b); + let m = mload(0); + return s; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/opcodes/all-shapes.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/opcodes/all-shapes.solc deleted file mode 100644 index c09bb469..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/opcodes/all-shapes.solc +++ /dev/null @@ -1,31 +0,0 @@ -import std.opcodes.{*}; - -// Compilation test for the std/opcodes wrappers. -// Picks two opcodes from each of the four shape categories so the -// pipeline exercises every wrapper signature. - -// no inputs, no return -function shape_void_void() -> () { - stop(); - invalid(); -} - -// no inputs, returns a word -function shape_void_word() -> word { - let a = address(); - let t = timestamp(); - return a; -} - -// inputs, no return -function shape_word_void(x: word) -> () { - pop(x); - mstore(0, x); -} - -// inputs, returns a word -function shape_word_word(a: word, b: word) -> word { - let s = add(a, b); - let m = mload(0); - return s; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/opcodes/terminators.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/opcodes/terminators.sol new file mode 100644 index 00000000..59da9fb3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/opcodes/terminators.sol @@ -0,0 +1,40 @@ +// Terminators (stop, invalid, selfdestruct, revert) never return control, so +// a bare 'assembly' block ending in one is allowed as the last statement of a +// value-returning function — its polymorphic result unifies with any return +// type. Regression test for stop/invalid/selfdestruct being made polymorphic +// like revert/return (see Primitives.hs 'yulPrimOps'). + +function viaStop() returns (a) { + assembly { + stop() + } +} + +function viaInvalid() returns (a) { + assembly { + invalid() + } +} + +function viaSelfdestruct(beneficiary: word) returns (a) { + assembly { + selfdestruct(beneficiary) + } +} + +function viaRevert() returns (a) { + assembly { + revert(0, 0) + } +} + +function useWord(w: word) {} + +contract Terminators { + function main() public { + useWord(viaStop()); + useWord(viaInvalid()); + useWord(viaSelfdestruct(0)); + useWord(viaRevert()); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/opcodes/terminators.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/opcodes/terminators.solc deleted file mode 100644 index 4c16adfd..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/opcodes/terminators.solc +++ /dev/null @@ -1,40 +0,0 @@ -// Terminators (stop, invalid, selfdestruct, revert) never return control, so -// a bare 'assembly' block ending in one is allowed as the last statement of a -// value-returning function — its polymorphic result unifies with any return -// type. Regression test for stop/invalid/selfdestruct being made polymorphic -// like revert/return (see Primitives.hs 'yulPrimOps'). - -forall a.function viaStop() -> a { - assembly { - stop() - } -} - -forall a.function viaInvalid() -> a { - assembly { - invalid() - } -} - -forall a.function viaSelfdestruct(beneficiary: word) -> a { - assembly { - selfdestruct(beneficiary) - } -} - -forall a.function viaRevert() -> a { - assembly { - revert(0, 0) - } -} - -function useWord(w: word) -> () {} - -contract Terminators { - public function main() -> () { - useWord(viaStop()); - useWord(viaInvalid()); - useWord(viaSelfdestruct(0)); - useWord(viaRevert()); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/pragmas/coverage.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/pragmas/coverage.sol new file mode 100644 index 00000000..802699e2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/pragmas/coverage.sol @@ -0,0 +1,8 @@ +pragma no-coverage-condition ; + +enum List { Nil, Cons(a, List) } +enum Bool { True, False } + +trait C {} + +impl C, a, List> {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/pragmas/coverage.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/pragmas/coverage.solc deleted file mode 100644 index c412dc91..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/pragmas/coverage.solc +++ /dev/null @@ -1,8 +0,0 @@ -pragma no-coverage-condition ; - -data List(a) = Nil | Cons(a,List(a)); -data Bool = True | False ; - -forall a b c . class a : C(b,c) {} - -forall a b . instance List(b) : C (a, List(a)) {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/pragmas/patterson.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/pragmas/patterson.sol new file mode 100644 index 00000000..0d59a7a0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/pragmas/patterson.sol @@ -0,0 +1,16 @@ + +trait A {} +trait B {} +trait C {} +trait D {} + + +enum Uint256 { U } +enum T { T } +enum S { SCons } + +// This works. +impl D> where U: A {} + +// This should also work, but reports a violation of the Paterson condition. +impl D> where U: A, U: B, U: C {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/pragmas/patterson.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/pragmas/patterson.solc deleted file mode 100644 index f66a88f5..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/pragmas/patterson.solc +++ /dev/null @@ -1,16 +0,0 @@ - -forall self . class self:A {} -forall self . class self:B {} -forall self . class self:C {} -forall self . class self:D {} - - -data Uint256 = U; -data T(x) = T; -data S(x) = SCons; - -// This works. -forall U . U : A => instance T(U):D {} - -// This should also work, but reports a violation of the Paterson condition. -forall U . U : A, U : B, U : C => instance S(U):D {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/00answer.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/00answer.sol new file mode 100644 index 00000000..48c89978 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/00answer.sol @@ -0,0 +1,5 @@ +contract Answer { + function main() public returns (word) { + return 42; + } +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/00answer.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/00answer.solc deleted file mode 100644 index ba55aa25..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/00answer.solc +++ /dev/null @@ -1,5 +0,0 @@ -contract Answer { - public function main() -> word { - return 42; - } -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/01id.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/01id.sol new file mode 100644 index 00000000..9ab26b43 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/01id.sol @@ -0,0 +1,14 @@ +contract Id1 { + + enum Bool { False, True } + + function id(x: word) public returns (word) { + return x ; + } + + function const(x: word, y: Bool) public returns (word) { return x; } + + function main() public returns (word) { + return const(id(42), Bool.False); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/01id.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/01id.solc deleted file mode 100644 index 7e286843..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/01id.solc +++ /dev/null @@ -1,14 +0,0 @@ -contract Id1 { - - data Bool = False | True; - - public function id(x : word) -> word { - return x ; - } - - public function const(x : word, y : Bool) -> word { return x; } - - public function main() -> word { - return const(id(42), Bool.False); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/021not.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/021not.sol new file mode 100644 index 00000000..aeeb720c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/021not.sol @@ -0,0 +1,29 @@ +contract Not { + enum Bool { False, True } + + function main() public returns (word) { + return fromBool(bnot(Bool.False)); + } + + function fromBool(b: Bool) public returns (word) { + match (b) { +case Bool.False { +return 0; +} +case Bool.True { +return 1; +} +} + } + + function bnot(b: Bool) public returns (Bool) { + match (b) { +case Bool.False { +return Bool.True; +} +case Bool.True { +return Bool.False; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/021not.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/021not.solc deleted file mode 100644 index df5b9377..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/021not.solc +++ /dev/null @@ -1,21 +0,0 @@ -contract Not { - data Bool = False | True; - - public function main() -> word { - return fromBool(bnot(Bool.False)); - } - - public function fromBool(b : Bool) -> word { - match(b) { - | Bool.False => return 0; - | Bool.True => return 1; - } - } - - public function bnot(b : Bool) -> Bool { - match b { - | Bool.False => return Bool.True; - | Bool.True => return Bool.False; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/022add.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/022add.sol new file mode 100644 index 00000000..85258483 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/022add.sol @@ -0,0 +1,13 @@ +function add(x: word, y: word) returns (word) { + let res: word; + assembly { + res := add(x, y) + } + return res; +} + +contract Add1 { + function main() public returns (word) { + return add(40, 2); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/022add.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/022add.solc deleted file mode 100644 index 3ef65f35..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/022add.solc +++ /dev/null @@ -1,13 +0,0 @@ -function add(x : word, y : word) -> word { - let res: word; - assembly { - res := add(x, y) - } - return res; -} - -contract Add1 { - public function main() -> word { - return add(40, 2); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/024arith.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/024arith.sol new file mode 100644 index 00000000..4043007d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/024arith.sol @@ -0,0 +1,64 @@ + + +function add(x: word, y: word) returns (word) { + let res: word; + assembly { + res := add(x, y) + } + return res; +} + +function sub(x: word, y: word) returns (word) { + let res: word; + assembly { + res := sub(x, y) + } + return res; +} + +function div(x: word, y: word) returns (word) { + let res: word; + assembly { + res := div(x, y) + } + return res; +} + +function sdiv(x: word, y: word) returns (word) { + let res: word; + assembly { + res := sdiv(x, y) + } + return res; +} + +function mod(x: word, y: word) returns (word) { + let res: word; + assembly { + res := mod(x, y) + } + return res; +} + +function smod(x: word, y: word) returns (word) { + let res: word; + assembly { + res := smod(x, y) + } + return res; +} + +function exp(x: word, y: word) returns (word) { + let res: word; + assembly { + res := exp(x, y) + } + return res; +} + + +contract Arith { + function main() public returns (word) { + return add(mod(sub(div(exp(2,18),4), 1), 16), 27); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/024arith.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/024arith.solc deleted file mode 100644 index a79ab49c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/024arith.solc +++ /dev/null @@ -1,64 +0,0 @@ - - -function add(x : word, y : word) -> word { - let res: word; - assembly { - res := add(x, y) - } - return res; -} - -function sub(x : word, y : word) -> word { - let res: word; - assembly { - res := sub(x, y) - } - return res; -} - -function div(x : word, y: word) -> word { - let res: word; - assembly { - res := div(x, y) - } - return res; -} - -function sdiv(x : word, y: word) -> word { - let res: word; - assembly { - res := sdiv(x, y) - } - return res; -} - -function mod(x : word, y: word) -> word { - let res: word; - assembly { - res := mod(x, y) - } - return res; -} - -function smod(x : word, y: word) -> word { - let res: word; - assembly { - res := smod(x, y) - } - return res; -} - -function exp(x : word, y: word) -> word { - let res: word; - assembly { - res := exp(x, y) - } - return res; -} - - -contract Arith { - public function main() -> word { - return add(mod(sub(div(exp(2,18),4), 1), 16), 27); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/02nid.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/02nid.sol new file mode 100644 index 00000000..320943c0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/02nid.sol @@ -0,0 +1,15 @@ +contract Id1 { + function id(x: word) public returns (word) { + return x ; + } + + function nid(x: word) public returns (word) { + return id(x); + } + + function const(x: word, y: word) public returns (word) { return x; } + + function main() public returns (word) { + return const(nid(42), id(1)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/02nid.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/02nid.solc deleted file mode 100644 index 166d01e2..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/02nid.solc +++ /dev/null @@ -1,15 +0,0 @@ -contract Id1 { - public function id(x : word) -> word { - return x ; - } - - public function nid(x : word) -> word { - return id(x); - } - - public function const(x : word, y : word) -> word { return x; } - - public function main() -> word { - return const(nid(42), id(1)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/031maybe.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/031maybe.sol new file mode 100644 index 00000000..379bb9a1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/031maybe.sol @@ -0,0 +1,20 @@ +contract Option { + enum Option { None, Some(a) } + + function just(x: word) public returns (Option) { return Option.Some(x); } + + function maybe(n: word, o: Option) public returns (word) { + match (o) { +case Option.None { +return n; +} +case Option.Some(x) { +return x; +} +} + } + + function main() public returns (word) { + return maybe(0, Option.Some(42)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/031maybe.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/031maybe.solc deleted file mode 100644 index d1de1135..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/031maybe.solc +++ /dev/null @@ -1,16 +0,0 @@ -contract Option { - data Option(a) = None | Some(a); - - public function just(x : word) -> Option(word) { return Option.Some(x); } - - public function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.None => return n; - | Option.Some(x) => return x; - } - } - - public function main() -> word { - return maybe(0, Option.Some(42)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/032simplejoin.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/032simplejoin.sol new file mode 100644 index 00000000..303add65 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/032simplejoin.sol @@ -0,0 +1,53 @@ +contract Option { + enum Option { None, Some(a) } + + function just(x: word) public returns (Option) { return Option.Some(x); } + + function maybe(n: word, o: Option) public returns (word) { + match (o) { +case Option.None { +return n; +} +case Option.Some(x) { +return x; +} +} + } + + + function join(mmx: Option>) public returns (Option) { + match (mmx) { +case Option.None { +return Option.None; +} +case Option.Some(Option.None) { +return Option.None; +} +case Option.Some(Option.Some(x)) { +return Option.Some(x); +} +} + } + + function join2(mmx: Option>) public returns (Option) { + match (mmx) { +case Option.Some(m) { +match (m) { +case Option.None { +return Option.None; +} +case Option.Some(x) { +return Option.Some(x); +} +} +} +default { +return Option.None; +} +} + } + + function main() public returns (word) { + return maybe(0, join(Option.Some(Option.Some(42)))); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/032simplejoin.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/032simplejoin.solc deleted file mode 100644 index 074e2100..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/032simplejoin.solc +++ /dev/null @@ -1,35 +0,0 @@ -contract Option { - data Option(a) = None | Some(a); - - public function just(x : word) -> Option(word) { return Option.Some(x); } - - public function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.None => return n; - | Option.Some(x) => return x; - } - } - - - public function join(mmx : Option(Option(word))) -> Option(word) { - match mmx { - | Option.None => return Option.None; - | Option.Some(Option.None) => return Option.None; - | Option.Some(Option.Some(x)) => return Option.Some(x); - } - } - - public function join2(mmx : Option(Option(word))) -> Option(word) { - match mmx { - | Option.Some(m) => match m { - | Option.None => return Option.None; - | Option.Some(x) => return Option.Some(x); - } - | _ => return Option.None; - } - } - - public function main() -> word { - return maybe(0, join(Option.Some(Option.Some(42)))); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/033join.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/033join.sol new file mode 100644 index 00000000..6d68f60e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/033join.sol @@ -0,0 +1,31 @@ +contract Option { + enum Option { None, Some(a) } + + function just(x: word) public returns (Option) { return Option.Some(x); } + + function maybe(n: word, o: Option) public returns (word) { + match (o) { +case Option.None { +return n; +} +case Option.Some(x) { +return x; +} +} + } + + function join(mmx: Option>) public returns (Option) { + match (mmx) { +case Option.Some(Option.Some(x)) { +return Option.Some(x); +} +default { +return Option.None; +} +} + } + + function main() public returns (word) { + return maybe(0, join(Option.Some(Option.Some(42)))); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/033join.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/033join.solc deleted file mode 100644 index d6664528..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/033join.solc +++ /dev/null @@ -1,23 +0,0 @@ -contract Option { - data Option(a) = None | Some(a); - - public function just(x : word) -> Option(word) { return Option.Some(x); } - - public function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.None => return n; - | Option.Some(x) => return x; - } - } - - public function join(mmx : Option(Option(word))) -> Option(word) { - match mmx { - | Option.Some(Option.Some(x)) => return Option.Some(x); - | _ => return Option.None; - } - } - - public function main() -> word { - return maybe(0, join(Option.Some(Option.Some(42)))); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/034cojoin.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/034cojoin.sol new file mode 100644 index 00000000..3fdd1422 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/034cojoin.sol @@ -0,0 +1,57 @@ +contract Option { + enum Option { None, Some(a) } + + function just(x: word) public returns (Option) { return Option.Some(x); } + + function maybe(n: word, o: Option) public returns (word) { + match (o) { +case Option.None { +return n; +} +case Option.Some(x) { +return x; +} +} + } + + function join(mmx: Option>) public returns (Option) { + let result = Option.None; + match (mmx) { +case Option.Some(Option.Some(x)) { +result = Option.Some(x); +} +case Option.None { +result = Option.None; +} +case Option.Some(Option.None) { +result = Option.None; +} +default { +result = Option.None; +} +} + return result; + } + + function extract(mx: Option) public returns (word) { + match (mx) { +case Option.Some(x) { +return x; +} +case Option.None { +return 0; +} +} + } + + function cojoin(x: Option) public returns (Option>) { // Test that sum types can grow + let result = Option.None; + result = Option.Some(x); + return result; + } + + + function main() public returns (word) { + return maybe(0, join(cojoin(Option.Some(42)))); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/034cojoin.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/034cojoin.solc deleted file mode 100644 index f31954db..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/034cojoin.solc +++ /dev/null @@ -1,41 +0,0 @@ -contract Option { - data Option(a) = None | Some(a); - - public function just(x : word) -> Option(word) { return Option.Some(x); } - - public function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.None => return n; - | Option.Some(x) => return x; - } - } - - public function join(mmx : Option(Option(word))) -> Option(word) { - let result = Option.None; - match mmx { - | Option.Some(Option.Some(x)) => result = Option.Some(x); - | Option.None => result = Option.None; - | Option.Some(Option.None) => result = Option.None; - | _ => result = Option.None; - } - return result; - } - - public function extract(mx : Option(word)) -> word { - match mx { - | Option.Some(x) => return x; - | Option.None => return 0; - } - } - - public function cojoin(x : Option(word)) -> Option(Option(word)) { // Test that sum types can grow - let result = Option.None; - result = Option.Some(x); - return result; - } - - - public function main() -> word { - return maybe(0, join(cojoin(Option.Some(42)))); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/035padding.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/035padding.sol new file mode 100644 index 00000000..f6c07c19 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/035padding.sol @@ -0,0 +1,18 @@ +contract Option { + enum Option { None, Some(a) } + + function maybe(n: word, o: Option) public returns (word) { + match (o) { +case Option.Some(x) { +return x; +} +case Option.None { +return n; +} +} + } + + function main() public returns (word) { + return maybe(7, Option.None); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/035padding.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/035padding.solc deleted file mode 100644 index c7b687c9..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/035padding.solc +++ /dev/null @@ -1,14 +0,0 @@ -contract Option { - data Option(a) = None | Some(a); - - public function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.Some(x) => return x; - | Option.None => return n; - } - } - - public function main() -> word { - return maybe(7, Option.None); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/036wildcard.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/036wildcard.sol new file mode 100644 index 00000000..635bfe52 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/036wildcard.sol @@ -0,0 +1,18 @@ +contract Option { + enum Option { None, Some(a) } + + function maybe(n: word, o: Option) public returns (word) { + match (o) { +case Option.Some(x) { +return x; +} +default { +return n; +} +} + } + + function main() public returns (word) { + return maybe(7, Option.None); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/036wildcard.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/036wildcard.solc deleted file mode 100644 index 1e83f44f..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/036wildcard.solc +++ /dev/null @@ -1,14 +0,0 @@ -contract Option { - data Option(a) = None | Some(a); - - public function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.Some(x) => return x; - | _ => return n; - } - } - - public function main() -> word { - return maybe(7, Option.None); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/037dwarves.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/037dwarves.sol new file mode 100644 index 00000000..9e9a3e06 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/037dwarves.sol @@ -0,0 +1,29 @@ +contract Dwarves { + enum Dwarf { Doc, Grumpy, Sleepy, Bashful, Happy, Sneezy, Dopey } + + + function fromEnum(c: Dwarf) public returns (word) { + match (c) { +case Dwarf.Doc { +return 1; +} +case Dwarf.Grumpy { +return 2; +} +case Dwarf.Sleepy { +return 3; +} +case Dwarf.Bashful { +return 4; +} +case Dwarf.Happy { +return 5; +} +default { +return 0; +} +} + } + + function main() public returns (word) { return fromEnum(Dwarf.Happy); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/037dwarves.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/037dwarves.solc deleted file mode 100644 index 94c72529..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/037dwarves.solc +++ /dev/null @@ -1,17 +0,0 @@ -contract Dwarves { - data Dwarf = Doc | Grumpy | Sleepy | Bashful | Happy | Sneezy | Dopey; - - - public function fromEnum(c : Dwarf) -> word { - match c { - | Dwarf.Doc => return 1; - | Dwarf.Grumpy => return 2; - | Dwarf.Sleepy => return 3; - | Dwarf.Bashful => return 4; - | Dwarf.Happy => return 5; - | _ => return 0; - } - } - - public function main() -> word { return fromEnum(Dwarf.Happy); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/038food0.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/038food0.sol new file mode 100644 index 00000000..867de77c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/038food0.sol @@ -0,0 +1,29 @@ +enum Food { Curry, Beans, Other } +enum CFood { Red(Food), Green(Food), Nocolor } + + + + function fromEnum(x: CFood) returns (word) { + match (x) { +case CFood.Red(Food.Curry) { +return 1; +} +case CFood.Green(Food.Beans) { +return 42; +} +default { +return 3; +} +} + } + + +contract FoodContract { + function id(x: CFood) public returns (CFood) { + return(x); + } + + function main() public returns (word) { + return fromEnum(id(CFood.Green(Food.Beans))); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/038food0.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/038food0.solc deleted file mode 100644 index 9d7d33a9..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/038food0.solc +++ /dev/null @@ -1,23 +0,0 @@ -data Food = Curry | Beans | Other; -data CFood = Red(Food) | Green(Food) | Nocolor; - - - - function fromEnum(x : CFood) -> word { - match x { - | CFood.Red(Food.Curry) => return 1; - | CFood.Green(Food.Beans) => return 42; - | _ => return 3; - } - } - - -contract FoodContract { - public function id(x : CFood) -> CFood { - return(x); - } - - public function main() -> word { - return fromEnum(id(CFood.Green(Food.Beans))); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/039food.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/039food.sol new file mode 100644 index 00000000..0d1225a7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/039food.sol @@ -0,0 +1,41 @@ + +enum Food { Curry, Beans, Other } +enum CFood { Red(Food), Green(Food), Nocolor } + + + + + function fromEnum(x: Food) returns (word) { + match (x) { +case Food.Curry { +return 1; +} +case Food.Beans { +return 42; +} +case Food.Other { +return 3; +} +} + } + + +contract FoodContract { + function eat(x: CFood) public returns (Food) { + match (x) { +case CFood.Red(f) { +return f; +} +case CFood.Green(f) { +return f; +} +default { +return Food.Other; +} +} + } + + function main() public returns (word) { + return fromEnum(eat(CFood.Green(Food.Beans))); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/039food.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/039food.solc deleted file mode 100644 index ef63da67..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/039food.solc +++ /dev/null @@ -1,29 +0,0 @@ - -data Food = Curry | Beans | Other; -data CFood = Red(Food) | Green(Food) | Nocolor; - - - - - function fromEnum(x : Food) -> word { - match x { - | Food.Curry => return 1; - | Food.Beans => return 42; - | Food.Other => return 3; - } - } - - -contract FoodContract { - public function eat(x : CFood) -> Food { - match x { - | CFood.Red(f) => return f; - | CFood.Green(f) => return f; - | _ => return Food.Other; - } - } - - public function main() -> word { - return fromEnum(eat(CFood.Green(Food.Beans))); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/041pair.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/041pair.sol new file mode 100644 index 00000000..f0f1e963 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/041pair.sol @@ -0,0 +1,14 @@ +contract Pair { + + function fst(p: (word, word)) public returns (word) { + match (p) { +case (a,b) { +return a; +} +} + } + + function main() public returns (word) { + return fst((1,0)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/041pair.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/041pair.solc deleted file mode 100644 index b8180a0a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/041pair.solc +++ /dev/null @@ -1,12 +0,0 @@ -contract Pair { - - public function fst(p : (word, word)) -> word { - match p { - | (a,b) => return a; - } - } - - public function main() -> word { - return fst((1,0)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/042triple.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/042triple.sol new file mode 100644 index 00000000..d2013eca --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/042triple.sol @@ -0,0 +1,14 @@ +contract Triple { + + function asel(t: (word, word, word)) public returns (word) { + match (t) { +case (a,b,c) { +return c; +} +} + } + + function main() public returns (word) { + return asel((1,21,42)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/042triple.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/042triple.solc deleted file mode 100644 index 10c3724c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/042triple.solc +++ /dev/null @@ -1,12 +0,0 @@ -contract Triple { - - public function asel(t : (word, word, word)) -> word { - match t { - | (a,b,c) => return c; - } - } - - public function main() -> word { - return asel((1,21,42)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/043fstsnd.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/043fstsnd.sol new file mode 100644 index 00000000..ad05b49b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/043fstsnd.sol @@ -0,0 +1,37 @@ + + +enum B { F, T } +enum Pair { Pair(a, b) } + +function fst(p: Pair) returns (a) { + match (p) { +case Pair(x,y) { +return x; +} +} +} + +function snd(p: Pair) returns (b) { + match (p) { +case Pair(x,y) { +return y; +} +} +} + +function add(x: word, y: word) returns (word) { + let res: word; + assembly { + res := add(x, y) + } + return res; +} + + +function addPair(p: Pair) returns (word) { + return add(fst(p), snd(p)); +} + +contract FstSnd { + function main() public returns (word) { return addPair(Pair(41,1)); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/043fstsnd.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/043fstsnd.solc deleted file mode 100644 index 62db7ccf..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/043fstsnd.solc +++ /dev/null @@ -1,33 +0,0 @@ - - -data B = F | T; -data Pair(a,b) = Pair(a,b); - -forall a b . function fst (p : Pair(a, b)) -> a { - match p { - | Pair(x,y) => return x; - } -} - -forall a b . function snd(p : Pair(a, b)) -> b { - match p { - | Pair(x,y) => return y; - } -} - -function add(x : word, y : word) -> word { - let res: word; - assembly { - res := add(x, y) - } - return res; -} - - -function addPair(p : Pair(word, word)) -> word { - return add(fst(p), snd(p)); -} - -contract FstSnd { - public function main() -> word { return addPair(Pair(41,1)); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/047rgb.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/047rgb.sol new file mode 100644 index 00000000..87529c81 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/047rgb.sol @@ -0,0 +1,16 @@ +contract RGB { + enum Color { R, G, B } + function main() public returns (word) { + match (Color.B) { +case Color.R { +return 4; +} +case Color.G { +return 2; +} +case Color.B { +return 42; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/047rgb.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/047rgb.solc deleted file mode 100644 index 576182e5..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/047rgb.solc +++ /dev/null @@ -1,10 +0,0 @@ -contract RGB { - data Color = R | G | B; - public function main() -> word { - match Color.B { - | Color.R => return 4; - | Color.G => return 2; - | Color.B => return 42; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/048rgb2.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/048rgb2.sol new file mode 100644 index 00000000..063a823e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/048rgb2.sol @@ -0,0 +1,19 @@ +contract RGB { + enum Color { R, G, B } + + function fromEnum(c: Color) public returns (word) { + match (c) { +case Color.R { +return 4; +} +case Color.G { +return 2; +} +case Color.B { +return 42; +} +} + } + + function main() public returns (word) { return fromEnum(Color.B); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/048rgb2.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/048rgb2.solc deleted file mode 100644 index 5e33af5d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/048rgb2.solc +++ /dev/null @@ -1,13 +0,0 @@ -contract RGB { - data Color = R | G | B; - - public function fromEnum(c : Color) -> word { - match c { - | Color.R => return 4; - | Color.G => return 2; - | Color.B => return 42; - } - } - - public function main() -> word { return fromEnum(Color.B); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/049rgb3.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/049rgb3.sol new file mode 100644 index 00000000..2a7293b2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/049rgb3.sol @@ -0,0 +1,23 @@ +enum RGB { Red(word), Green(word), Blue(word) } + +contract RGB3 { + + function choose(c: RGB) public returns (word) { + let res : word; + match (c) { +case .Red(x) { +assembly { res := add(x,1) } +} +case .Green(x) { +assembly { res := add(x,2) } +} +case .Blue(x) { +assembly { res := add(x,3) } +} +} + return res; + } + function main() public returns (word) { + choose(RGB.Green(42)) + } +} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/049rgb3.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/049rgb3.solc deleted file mode 100644 index 8cfbaeca..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/049rgb3.solc +++ /dev/null @@ -1,17 +0,0 @@ -data RGB = Red(word) | Green(word) | Blue(word); - -contract RGB3 { - - public function choose(c:RGB) -> word { - let res : word; - match c { - | .Red(x) => assembly { res := add(x,1) } - | .Green(x) => assembly { res := add(x,2) } - | .Blue(x) => assembly { res := add(x,3) } - } - return res; - } - public function main() -> word { - choose(RGB.Green(42)) - } -} \ No newline at end of file diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/06comp.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/06comp.sol new file mode 100644 index 00000000..d0bc6f82 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/06comp.sol @@ -0,0 +1,9 @@ +contract Compose { + function id(x: word) public returns (word) { return x; } + + function idid(x: word) public returns (word) { return id(id(x)); } + + function main() public returns (word) { + return idid(42); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/06comp.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/06comp.solc deleted file mode 100644 index 301615d7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/06comp.solc +++ /dev/null @@ -1,9 +0,0 @@ -contract Compose { - public function id(x : word) -> word { return x; } - - public function idid(x : word) -> word { return id(id(x)); } - - public function main() -> word { - return idid(42); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/09not.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/09not.sol new file mode 100644 index 00000000..aeeb720c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/09not.sol @@ -0,0 +1,29 @@ +contract Not { + enum Bool { False, True } + + function main() public returns (word) { + return fromBool(bnot(Bool.False)); + } + + function fromBool(b: Bool) public returns (word) { + match (b) { +case Bool.False { +return 0; +} +case Bool.True { +return 1; +} +} + } + + function bnot(b: Bool) public returns (Bool) { + match (b) { +case Bool.False { +return Bool.True; +} +case Bool.True { +return Bool.False; +} +} + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/09not.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/09not.solc deleted file mode 100644 index df5b9377..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/09not.solc +++ /dev/null @@ -1,21 +0,0 @@ -contract Not { - data Bool = False | True; - - public function main() -> word { - return fromBool(bnot(Bool.False)); - } - - public function fromBool(b : Bool) -> word { - match(b) { - | Bool.False => return 0; - | Bool.True => return 1; - } - } - - public function bnot(b : Bool) -> Bool { - match b { - | Bool.False => return Bool.True; - | Bool.True => return Bool.False; - } - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/10negBool.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/10negBool.sol new file mode 100644 index 00000000..cf23be57 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/10negBool.sol @@ -0,0 +1,37 @@ + +trait Neg { + function neg(x: a) returns (a) ; +} + +enum B { F, T } + + +impl Neg { + function neg(x: B) returns (B) { + match (x) { +case B.F { +return B.T; +} +case B.T { +return B.F; +} +} + } +} + + +contract NegBool { + + function fromB(b: B) public returns (word) { + match (b) { +case B.F { +return 0; +} +case B.T { +return 1; +} +} + } + + function main() public returns (word) { return fromB(Neg.neg(B.F)); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/10negBool.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/10negBool.solc deleted file mode 100644 index af8297a9..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/10negBool.solc +++ /dev/null @@ -1,29 +0,0 @@ - -forall a . class a : Neg { - function neg(x:a) -> a; -} - -data B = F | T; - - -instance B : Neg { - function neg (x : B) -> B { - match x { - | B.F => return B.T; - | B.T => return B.F; - } - } -} - - -contract NegBool { - - public function fromB(b : B) -> word { - match b { - | B.F => return 0; - | B.T => return 1; - } - } - - public function main() -> word { return fromB(Neg.neg(B.F)); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/11negPair.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/11negPair.sol new file mode 100644 index 00000000..895624bb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/11negPair.sol @@ -0,0 +1,69 @@ + +trait Neg { + function neg(x: a) returns (a) ; +} + +enum B { F, T } + +impl Neg { + function neg(x: B) returns (B) { + match (x) { +case B.F { +return B.T; +} +case B.T { +return B.F; +} +} + } +} + +function fst(p: (a, b)) returns (a) { + match (p) { +case (x,y) { +return x; +} +} +} + +function snd(p: (a, b)) returns (b) { + match (p) { +case (x,y) { +return y; +} +} +} + + +impl Neg<(a, b)> where a: Neg, b: Neg { + function neg(p: (a, b)) returns (a, b) { + return (Neg.neg (fst(p)), Neg.neg(snd (p))); + } +} + +contract NegPair { + + function bnot(x: B) public returns (B) { + match (x) { +case B.T { +return B.F; +} +case B.F { +return B.T; +} +} +} + + function fromB(b: B) public returns (word) { + match (b) { +case B.F { +return 0; +} +case B.T { +return 1; +} +} +} + + function main() public returns (word) { return fromB(fst(Neg.neg((B.F,B.T)))); } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/11negPair.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/11negPair.solc deleted file mode 100644 index c18c0272..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/11negPair.solc +++ /dev/null @@ -1,53 +0,0 @@ - -forall a . class a : Neg { - function neg(x:a) -> a; -} - -data B = F | T; - -instance B : Neg { - function neg (x : B) -> B { - match x { - | B.F => return B.T; - | B.T => return B.F; - } - } -} - -forall a b . function fst (p : (a, b)) -> a { - match p { - | (x,y) => return x; - } -} - -forall a b . function snd(p : (a, b)) -> b { - match p { - | (x,y) => return y; - } -} - - -forall a b . a : Neg, b : Neg => instance (a,b):Neg { - function neg(p : (a,b)) -> (a,b) { - return (Neg.neg (fst(p)), Neg.neg(snd (p))); - } -} - -contract NegPair { - - public function bnot(x : B) -> B { - match x { - | B.T => return B.F; - | B.F => return B.T; - } -} - - public function fromB(b : B) -> word { - match b { - | B.F => return 0; - | B.T => return 1; - } -} - - public function main() -> word { return fromB(fst(Neg.neg((B.F,B.T)))); } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/120basicCounter.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/120basicCounter.sol new file mode 100644 index 00000000..6546c19b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/120basicCounter.sol @@ -0,0 +1,9 @@ +import * from std; +contract Counter { + counter : word; + + function main() public returns (word) { + counter = Num.add(counter, 42); + return counter; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/120basicCounter.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/120basicCounter.solc deleted file mode 100644 index 026e1e03..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/120basicCounter.solc +++ /dev/null @@ -1,9 +0,0 @@ -import std.{*}; -contract Counter { - counter : word; - - public function main() -> word { - counter = Num.add(counter, 42); - return counter; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/121counter.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/121counter.sol new file mode 100644 index 00000000..7bb74c2b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/121counter.sol @@ -0,0 +1,14 @@ +// test single contract field +import std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract Counter { + counter : word; + + function main() public returns (word) { + counter = std.addWord(counter, 1); + return counter; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/121counter.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/121counter.solc deleted file mode 100644 index 2908b6ef..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/121counter.solc +++ /dev/null @@ -1,14 +0,0 @@ -// test single contract field -import std; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract Counter { - counter : word; - - public function main() -> word { - counter = std.addWord(counter, 1); - return counter; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/122counters.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/122counters.sol new file mode 100644 index 00000000..d9ae193b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/122counters.sol @@ -0,0 +1,15 @@ +// test multiple contract fields +import * from std; +// import StorageLib; + + +contract Counter { + counter1 : word; + counter2 : uint256; + counter3 : word; + function main() public returns (word) { + counter1 += 1; + counter3 += 2; + return counter1 + counter3; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/122counters.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/122counters.solc deleted file mode 100644 index 4b13c41d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/122counters.solc +++ /dev/null @@ -1,15 +0,0 @@ -// test multiple contract fields -import std.{*}; -// import StorageLib; - - -contract Counter { - counter1 : word; - counter2 : uint256; - counter3 : word; - public function main() -> word { - counter1 += 1; - counter3 += 2; - return counter1 + counter3; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/123stackAndStorage.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/123stackAndStorage.sol new file mode 100644 index 00000000..9009c356 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/123stackAndStorage.sol @@ -0,0 +1,16 @@ +// test multiple contract fields +import * from std; + +contract Counter { + counter1 : word; + counter2 : uint256; + counter3 : word; + + function main() public returns (word) { + let x: word; + x = counter1 + 1; + counter1 = x; + counter3 += 2; + return counter1 + counter3; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/123stackAndStorage.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/123stackAndStorage.solc deleted file mode 100644 index 8da859fa..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/123stackAndStorage.solc +++ /dev/null @@ -1,16 +0,0 @@ -// test multiple contract fields -import std.{*}; - -contract Counter { - counter1 : word; - counter2 : uint256; - counter3 : word; - - public function main() -> word { - let x: word; - x = counter1 + 1; - counter1 = x; - counter3 += 2; - return counter1 + counter3; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/126nanoerc20.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/126nanoerc20.sol new file mode 100644 index 00000000..b5c06aa9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/126nanoerc20.sol @@ -0,0 +1,84 @@ +import * from std; +import {address, uint256, mapping, Num, Add, Sub, Bounded, Eq, Ord, Typedef, ge, not} from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +function caller() returns (address) { + let res: word; + assembly { + res := caller() + } + return address(res); +} + +function myrevert(msg: (word, word)) { + match (msg) { +case (str, len) { +let str1 = str; let len1 = len; + assembly { mstore(0, str1) revert(0, len1) } +} +} +} + +function myrequire(cond: bool, msg: (word, word)) { + if( not(cond) ) { myrevert(msg); } +} + +function require1(cond: bool) { + myrequire (cond, (0x72657175697265313a204641494c, 14) /* "require1: FAIL" */ ); +} + + +function nop() { return ();} + +contract Uint { + reserved : word; + msg_sender : address; // mock msg.sender + owner : address; + decimals : uint256; + totalSupply : uint256; + balances : mapping(address => uint256); + + function mint(amount: uint256) public { + balances[owner] = Num.add(balances[owner], amount); + totalSupply = Num.add(totalSupply, amount); + } + + // function transferFrom(address src, address dst, uint256 amt) public returns (bool) + function transferFrom(src: address, dst: address, amt: uint256) public returns (bool) { + require1(ge(balances[src], amt)); + + /* + balances[src] = Num.sub(balances[src], amt); + balances[dst] = Num.add(balances[dst], amt):uint256; + */ + withdraw(src, amt); //workaround typechecker quirk + deposit(dst, amt); + return true; + } + + + function withdraw(src: address, amt: uint256) public { + balances[src] = Num.sub(balances[src], amt); + } + + function deposit(dst: address, amt: uint256) public { + balances[dst] = Num.add(balances[dst], amt); + } + + function init() public { + owner = address(0x123456789abcdef); + msg_sender = caller(); + decimals = uint256(18); + } + + function main() public returns (uint256) { + init(); + mint(uint256(1000)); + let src : address = owner; + transferFrom(owner, msg_sender, uint256(42)); + + return balances[msg_sender] ; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/126nanoerc20.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/126nanoerc20.solc deleted file mode 100644 index 865965a7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/126nanoerc20.solc +++ /dev/null @@ -1,83 +0,0 @@ -import std.{*}; -import std.{address, uint256, mapping, Num, Add, Sub, Bounded, Eq, Ord, Typedef, ge, not}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -function caller() -> address { - let res: word; - assembly { - res := caller() - } - return address(res); -} - -function myrevert( msg: (word, word) ) -> () { - match msg { - | (str, len) => - let str1 = str; let len1 = len; - assembly { mstore(0, str1) revert(0, len1) } - } -} - -function myrequire(cond: bool, msg: (word, word) ) -> () { - if( not(cond) ) { myrevert(msg); } -} - -function require1(cond: bool) -> () { - myrequire (cond, (0x72657175697265313a204641494c, 14) /* "require1: FAIL" */ ); -} - - -function nop() -> () { return ();} - -contract Uint { - reserved : word; - msg_sender : address; // mock msg.sender - owner : address; - decimals : uint256; - totalSupply : uint256; - balances : mapping(address,uint256); - - public function mint(amount:uint256) -> () { - balances[owner] = Num.add(balances[owner], amount); - totalSupply = Num.add(totalSupply, amount); - } - - // function transferFrom(address src, address dst, uint256 amt) public returns (bool) - public function transferFrom(src:address, dst:address, amt:uint256) -> bool { - require1(ge(balances[src], amt)); - - /* - balances[src] = Num.sub(balances[src], amt); - balances[dst] = Num.add(balances[dst], amt):uint256; - */ - withdraw(src, amt); //workaround typechecker quirk - deposit(dst, amt); - return true; - } - - - public function withdraw(src:address, amt:uint256) -> () { - balances[src] = Num.sub(balances[src], amt):uint256; - } - - public function deposit(dst:address, amt:uint256) -> () { - balances[dst] = Num.add(balances[dst], amt):uint256; - } - - public function init() -> () { - owner = address(0x123456789abcdef); - msg_sender = caller(); - decimals = uint256(18); - } - - public function main() -> uint256 { - init(); - mint(uint256(1000)); - let src : address = owner; - transferFrom(owner, msg_sender, uint256(42)); - - return balances[msg_sender] : uint256; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/127microerc20.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/127microerc20.sol new file mode 100644 index 00000000..5f1380d3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/127microerc20.sol @@ -0,0 +1,119 @@ +import * from std; +import {address, uint256, mapping, Num, Add, Sub, Bounded, Eq, Ord, Typedef, ge, ne, not} from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +function caller() returns (address) { + let res: word; + assembly { + res := caller() + } + return address(res); +} + +function require1fail() { + let res: word; + assembly { + mstore(0x0, 0x72657175697265313a204641494c) // "require1: FAIL" + revert(0,32) + } + return (); // for the typechecker +} + +function require1(cond: bool) { + match (cond) { +case false { +return require1fail(); +} +case true { +return (); +} +} +} + +function nop() { return ();} + +contract Mini { + reserved : word; + msg_sender : address; // mock msg.sender + owner : address; + decimals : uint256; + totalSupply : uint256; + balances : mapping(address => uint256); + allowance : mapping(address => mapping(address => uint256)); + + function mint(amount: uint256) public { + balances[owner] = Num.add(balances[owner], amount); + totalSupply = Num.add(totalSupply, amount); + } + +/* // original: + function transferFrom(address src, address dst, uint256 amt) public returns (bool) { + require(balanceOf[src] >= amt, "token/insufficient-balance"); + if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { + require(allowance[src][msg.sender] >= amt, "token/insufficient-allowance"); + allowance[src][msg.sender] -= amt; + } + + balanceOf[src] -= amt; + balanceOf[dst] += amt; + emit Transfer(src, dst, amt); + return true; + } +*/ + +// function transferFrom(src:address, dst:address, amt:uint256) -> bool { + function transferFrom(src: address, dst: address, amt: uint256) public returns (bool) { + require1(ge(balances[src], amt)); + + match (Eq.eq(src, msg_sender)) { +case true { +match (ne(allowance[src][msg_sender], Num.maxVal())) { +case true { +require1(false); +} +case false { +(); +} +} +} +case false { +(); +} +} + +/* + if ((src != msg_sender) && (allowance [src][msg_sender] != (Num.maxVal():uint256)) ) { + require1(allowance[src][msg.sender] >= amt); + } +*/ + balances[src] = Num.sub(balances[src], amt); + balances[dst] = Num.add(balances[dst], amt); + return true; + } + +/* + function approve(address usr, uint256 amt) public returns (bool) { + allowance[msg.sender][usr] = amt; + emit Approval(msg.sender, usr, amt); + return true; + } +*/ + + + function init() public { + owner = address(0x123456789abcdef); + msg_sender = caller(); + decimals = uint256(18); + } + + function main() public returns (uint256) { + init(); + mint(uint256(1000)); + allowance[owner][msg_sender] = uint256(10000); + transferFrom(owner, msg_sender, uint256(42)); + + return balances[msg_sender] ; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/127microerc20.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/127microerc20.solc deleted file mode 100644 index 33920581..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/127microerc20.solc +++ /dev/null @@ -1,107 +0,0 @@ -import std.{*}; -import std.{address, uint256, mapping, Num, Add, Sub, Bounded, Eq, Ord, Typedef, ge, ne, not}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -function caller() -> address { - let res: word; - assembly { - res := caller() - } - return address(res); -} - -function require1fail() -> () { - let res: word; - assembly { - mstore(0x0, 0x72657175697265313a204641494c) // "require1: FAIL" - revert(0,32) - } - return (); // for the typechecker -} - -function require1(cond: bool) -> () { - match cond { - | false => return require1fail(); - | true => return (); - } -} - -function nop() -> () { return ();} - -contract Mini { - reserved : word; - msg_sender : address; // mock msg.sender - owner : address; - decimals : uint256; - totalSupply : uint256; - balances : mapping(address,uint256); - allowance : mapping(address, mapping(address, uint256)); - - public function mint(amount:uint256) -> () { - balances[owner] = Num.add(balances[owner], amount); - totalSupply = Num.add(totalSupply, amount); - } - -/* // original: - function transferFrom(address src, address dst, uint256 amt) public returns (bool) { - require(balanceOf[src] >= amt, "token/insufficient-balance"); - if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { - require(allowance[src][msg.sender] >= amt, "token/insufficient-allowance"); - allowance[src][msg.sender] -= amt; - } - - balanceOf[src] -= amt; - balanceOf[dst] += amt; - emit Transfer(src, dst, amt); - return true; - } -*/ - -// function transferFrom(src:address, dst:address, amt:uint256) -> bool { - public function transferFrom(src : address, dst : address, amt : uint256) -> bool { - require1(ge(balances[src], amt)); - - match (Eq.eq(src, msg_sender)) { - | true => match ne(allowance[src][msg_sender], Num.maxVal():uint256) { - | true => require1(false); - | false => (); - } - | false => (); - } - -/* - if ((src != msg_sender) && (allowance [src][msg_sender] != (Num.maxVal():uint256)) ) { - require1(allowance[src][msg.sender] >= amt); - } -*/ - balances[src] = Num.sub(balances[src], amt); - balances[dst] = Num.add(balances[dst], amt):uint256; - return true; - } - -/* - function approve(address usr, uint256 amt) public returns (bool) { - allowance[msg.sender][usr] = amt; - emit Approval(msg.sender, usr, amt); - return true; - } -*/ - - - public function init() -> () { - owner = address(0x123456789abcdef); - msg_sender = caller(); - decimals = uint256(18); - } - - public function main() -> uint256 { - init(); - mint(uint256(1000)); - allowance[owner][msg_sender] = uint256(10000); - transferFrom(owner, msg_sender, uint256(42)); - - return balances[msg_sender] : uint256; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/128minierc20.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/128minierc20.sol new file mode 100644 index 00000000..ad32a7d6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/128minierc20.sol @@ -0,0 +1,98 @@ +import * from std; +import {address, uint256, mapping, Num, Add, Sub, Bounded, Eq, Ord, Typedef, ge, ne, not} from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +function caller() returns (address) { + let res: word; + assembly { + res := caller() + } + return address(res); +} + +function myrevert(msg: word) { + assembly { mstore(0, msg) revert(0, 32) } +} + +function myrequire(cond: bool, msg: word) { + if( !cond ) { myrevert(msg); } +} + +contract MiniERC20 { + reserved : word; // forge idiosyncrasies + owner : address; + decimals : uint256; + totalSupply : uint256; + balances : mapping(address => uint256); + allowance : mapping(address => mapping(address => uint256)); + + function mint(amount: uint256) public { + balances[owner] = Num.add(balances[owner], amount); + totalSupply = Num.add(totalSupply, amount); + } + +/* // original: + function transferFrom(address src, address dst, uint256 amt) public returns (bool) { + myrequire(balanceOf[src] >= amt, "token/insufficient-balance"); + if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { + myrequire(allowance[src][msg.sender] >= amt, "token/insufficient-allowance"); + allowance[src][msg.sender] -= amt; + } + + balanceOf[src] -= amt; + balanceOf[dst] += amt; + emit Transfer(src, dst, amt); + return true; + } +*/ + + function transferFrom(src: address, dst: address, amt: uint256) public returns (bool) { + let msg_sender = caller(); + myrequire( balances[src] >= amt /* "token/insufficient-balance" */ + , 0x746f6b656e2f696e73756666696369656e742d62616c616e6365 + ); + + if (src != msg_sender && allowance[src][msg_sender] != (Num.maxVal())) { + myrequire( allowance[src][msg_sender] >= amt /* "token/insufficient-allowance" */ + , 0x746f6b656e2f696e73756666696369656e742d616c6c6f77616e6365 + ); + allowance[src][msg_sender] -= amt; + } + balances[src] = balances[src] - amt; + balances[dst] = balances[dst] + amt; + return true; + } + +/* + function approve(address usr, uint256 amt) public returns (bool) { + allowance[msg.sender][usr] = amt; + emit Approval(msg.sender, usr, amt); + return true; + } +*/ + + function approve(usr: address, amt: uint256) public returns (bool) { + let msg_sender = caller(); + allowance[msg_sender][usr] = amt; + // emit Approval(msg.sender, usr, amt); + return true; + + } + + function init() public { + owner = address(0x123456789abcdef); + decimals = uint256(18); // Num.fromWord(18) fails, which may be a problem + } + + function main() public returns (uint256) { + let msg_sender = caller(); + init(); + mint(uint256(1000)); + allowance[owner][msg_sender] = uint256(1000); + transferFrom(owner, msg_sender, uint256(42)); + + return allowance[owner][msg_sender]; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/128minierc20.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/128minierc20.solc deleted file mode 100644 index a3c21a15..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/128minierc20.solc +++ /dev/null @@ -1,98 +0,0 @@ -import std.{*}; -import std.{address, uint256, mapping, Num, Add, Sub, Bounded, Eq, Ord, Typedef, ge, ne, not}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -function caller() -> address { - let res: word; - assembly { - res := caller() - } - return address(res); -} - -function myrevert(msg: word) -> () { - assembly { mstore(0, msg) revert(0, 32) } -} - -function myrequire(cond: bool, msg: word ) -> () { - if( !cond ) { myrevert(msg); } -} - -contract MiniERC20 { - reserved : word; // forge idiosyncrasies - owner : address; - decimals : uint256; - totalSupply : uint256; - balances : mapping(address,uint256); - allowance : mapping(address, mapping(address, uint256)); - - public function mint(amount:uint256) -> () { - balances[owner] = Num.add(balances[owner], amount); - totalSupply = Num.add(totalSupply, amount); - } - -/* // original: - function transferFrom(address src, address dst, uint256 amt) public returns (bool) { - myrequire(balanceOf[src] >= amt, "token/insufficient-balance"); - if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { - myrequire(allowance[src][msg.sender] >= amt, "token/insufficient-allowance"); - allowance[src][msg.sender] -= amt; - } - - balanceOf[src] -= amt; - balanceOf[dst] += amt; - emit Transfer(src, dst, amt); - return true; - } -*/ - - public function transferFrom(src:address, dst:address, amt:uint256) -> bool { - let msg_sender = caller(); - myrequire( balances[src] >= amt /* "token/insufficient-balance" */ - , 0x746f6b656e2f696e73756666696369656e742d62616c616e6365 - ); - - if (src != msg_sender && allowance[src][msg_sender] != (Num.maxVal():uint256)) { - myrequire( allowance[src][msg_sender] >= amt /* "token/insufficient-allowance" */ - , 0x746f6b656e2f696e73756666696369656e742d616c6c6f77616e6365 - ); - allowance[src][msg_sender] -= amt; - } - balances[src] = balances[src] - amt; - balances[dst] = balances[dst] + amt; - return true; - } - -/* - function approve(address usr, uint256 amt) public returns (bool) { - allowance[msg.sender][usr] = amt; - emit Approval(msg.sender, usr, amt); - return true; - } -*/ - - public function approve(usr: address, amt: uint256) -> bool { - let msg_sender = caller(); - allowance[msg_sender][usr] = amt; - // emit Approval(msg.sender, usr, amt); - return true; - - } - - public function init() -> () { - owner = address(0x123456789abcdef); - decimals = uint256(18); // Num.fromWord(18) fails, which may be a problem - } - - public function main() -> uint256 { - let msg_sender = caller(); - init(); - mint(uint256(1000)); - allowance[owner][msg_sender] = uint256(1000); - transferFrom(owner, msg_sender, uint256(42)); - - return allowance[owner][msg_sender]; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/129arraystorage.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/129arraystorage.sol new file mode 100644 index 00000000..8b244bcf --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/129arraystorage.sol @@ -0,0 +1,24 @@ +// Exercises storage arrays (array(member)) modeled on storage mappings. +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract ArrayStorage { + reserved : word; // forge uses at least 1 storage slot + + function main() returns (uint256) { + // A storage array sitting at a fixed slot. The slot itself stores the + // length; elements live at keccak256(slot) + i. + let arr : storage> = storage(0x100); + + // push appends and grows the length automatically. + ArrayPush.push(arr, uint256(42)); + ArrayPush.push(arr, uint256(100)); + + // `arr[i]` syntax only desugars for contract fields, so use the + // explicit ridx helper for this local-variable array. ridx dispatches + // through RValueIdxAccess, which bounds-checks against Length.length. + return ridx(arr, uint256(0)) + ridx(arr, uint256(1)); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/129arraystorage.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/129arraystorage.solc deleted file mode 100644 index 247bd2b2..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/129arraystorage.solc +++ /dev/null @@ -1,24 +0,0 @@ -// Exercises storage arrays (array(member)) modeled on storage mappings. -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract ArrayStorage { - reserved : word; // forge uses at least 1 storage slot - - function main() -> uint256 { - // A storage array sitting at a fixed slot. The slot itself stores the - // length; elements live at keccak256(slot) + i. - let arr : storage(array(uint256)) = storage(0x100); - - // push appends and grows the length automatically. - ArrayPush.push(arr, uint256(42)); - ArrayPush.push(arr, uint256(100)); - - // `arr[i]` syntax only desugars for contract fields, so use the - // explicit ridx helper for this local-variable array. ridx dispatches - // through RValueIdxAccess, which bounds-checks against Length.length. - return ridx(arr, uint256(0)) + ridx(arr, uint256(1)); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/130arrayfield.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/130arrayfield.sol new file mode 100644 index 00000000..e02f5113 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/130arrayfield.sol @@ -0,0 +1,19 @@ +// Storage array as a contract field: `arr : array(uint256)`. +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract ArrayField { + reserved : word; // forge uses at least 1 storage slot + arr : array; + + function main() returns (uint256) { + // push appends and grows the length automatically. + ArrayPush.push(arr, uint256(42)); + ArrayPush.push(arr, uint256(100)); + + // arr[i] — bounds-checked indexed access. + return arr[uint256(0)] + arr[uint256(1)]; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/130arrayfield.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/130arrayfield.solc deleted file mode 100644 index 2a8f27e2..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/130arrayfield.solc +++ /dev/null @@ -1,19 +0,0 @@ -// Storage array as a contract field: `arr : array(uint256)`. -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract ArrayField { - reserved : word; // forge uses at least 1 storage slot - arr : array(uint256); - - function main() -> uint256 { - // push appends and grows the length automatically. - ArrayPush.push(arr, uint256(42)); - ArrayPush.push(arr, uint256(100)); - - // arr[i] — bounds-checked indexed access. - return arr[uint256(0)] + arr[uint256(1)]; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/131localindex.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/131localindex.sol new file mode 100644 index 00000000..6c75fedb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/131localindex.sol @@ -0,0 +1,27 @@ +// `arr[i]` on a *local* storage-array reference, not a contract field. +// The local already holds the storage reference, so the desugaring emits +// `ridx(arr, i)` / `lidx(arr, i)` directly (cf. 129arraystorage.sol, which +// had to spell out `ridx` by hand). +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract LocalIndex { + reserved : word; // forge uses at least 1 storage slot + + function main() returns (uint256) { + let arr : storage> = storage(0x100); + + ArrayPush.push(arr, uint256(42)); + ArrayPush.push(arr, uint256(100)); + + // Indexed read through a local. + let sum : uint256 = arr[uint256(0)] + arr[uint256(1)]; + + // Indexed write through a local. + arr[uint256(0)] = uint256(1); + + return sum + arr[uint256(0)]; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/131localindex.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/131localindex.solc deleted file mode 100644 index 98122c2e..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/131localindex.solc +++ /dev/null @@ -1,27 +0,0 @@ -// `arr[i]` on a *local* storage-array reference, not a contract field. -// The local already holds the storage reference, so the desugaring emits -// `ridx(arr, i)` / `lidx(arr, i)` directly (cf. 129arraystorage.solc, which -// had to spell out `ridx` by hand). -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract LocalIndex { - reserved : word; // forge uses at least 1 storage slot - - function main() -> uint256 { - let arr : storage(array(uint256)) = storage(0x100); - - ArrayPush.push(arr, uint256(42)); - ArrayPush.push(arr, uint256(100)); - - // Indexed read through a local. - let sum : uint256 = arr[uint256(0)] + arr[uint256(1)]; - - // Indexed write through a local. - arr[uint256(0)] = uint256(1); - - return sum + arr[uint256(0)]; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/132nestedarray.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/132nestedarray.sol new file mode 100644 index 00000000..be7b0ca4 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/132nestedarray.sol @@ -0,0 +1,26 @@ +// Nested storage arrays: `array(array(uint256))` with `grid[i][j]` used as both +// an l-value and an r-value. The inner index desugars as an l-value, yielding the +// `storage(array(uint256))` handle that the outer index then consumes. +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract NestedArray { + reserved : word; // forge uses at least 1 storage slot + grid : array>; + + function main() returns (uint256) { + // Grow the outer array; the inner arrays start empty. + Array.setLength(grid, uint256(2)); + + // `grid[i]` yields the inner array's handle, which push can grow. + ArrayPush.push(grid[uint256(0)], uint256(5)); + ArrayPush.push(grid[uint256(1)], uint256(7)); + + // Indexed write, then indexed read. + grid[uint256(1)][uint256(0)] = uint256(9); + + return grid[uint256(0)][uint256(0)] + grid[uint256(1)][uint256(0)]; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/132nestedarray.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/132nestedarray.solc deleted file mode 100644 index 04abe1be..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/132nestedarray.solc +++ /dev/null @@ -1,26 +0,0 @@ -// Nested storage arrays: `array(array(uint256))` with `grid[i][j]` used as both -// an l-value and an r-value. The inner index desugars as an l-value, yielding the -// `storage(array(uint256))` handle that the outer index then consumes. -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract NestedArray { - reserved : word; // forge uses at least 1 storage slot - grid : array(array(uint256)); - - function main() -> uint256 { - // Grow the outer array; the inner arrays start empty. - Array.setLength(grid, uint256(2)); - - // `grid[i]` yields the inner array's handle, which push can grow. - ArrayPush.push(grid[uint256(0)], uint256(5)); - ArrayPush.push(grid[uint256(1)], uint256(7)); - - // Indexed write, then indexed read. - grid[uint256(1)][uint256(0)] = uint256(9); - - return grid[uint256(0)][uint256(0)] + grid[uint256(1)][uint256(0)]; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/133arraystring.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/133arraystring.sol new file mode 100644 index 00000000..34163a6a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/133arraystring.sol @@ -0,0 +1,17 @@ +// Storage arrays whose element type is dynamic. Declaring the field and taking +// its length must work even before `push` accepts dynamic values; the element +// slot itself is what holds the length / short-string encoding. +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract ArrayOfDynamic { + reserved : word; // forge uses at least 1 storage slot + names : array; + blobs : array; + + function main() returns (uint256) { + return Length.length(names) + Length.length(blobs); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/133arraystring.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/133arraystring.solc deleted file mode 100644 index d801d42f..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/133arraystring.solc +++ /dev/null @@ -1,17 +0,0 @@ -// Storage arrays whose element type is dynamic. Declaring the field and taking -// its length must work even before `push` accepts dynamic values; the element -// slot itself is what holds the length / short-string encoding. -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract ArrayOfDynamic { - reserved : word; // forge uses at least 1 storage slot - names : array(string); - blobs : array(bytes); - - function main() -> uint256 { - return Length.length(names) + Length.length(blobs); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/135aliaspush.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/135aliaspush.sol new file mode 100644 index 00000000..df68cc49 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/135aliaspush.sol @@ -0,0 +1,18 @@ +// Binding a storage array field to a local is an *alias*, not a copy: the local +// holds the same slot, so growing it grows the field. (Solidity's `T[] storage p`.) +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract AliasPush { + reserved : word; // forge uses at least 1 storage slot + xs : array; + + function main() returns (uint256) { + let p : storage> = xs; + ArrayPush.push(p, uint256(1)); + // The push went through the alias, so the field sees it. + return Length.length(xs); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/135aliaspush.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/135aliaspush.solc deleted file mode 100644 index 583efec3..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/135aliaspush.solc +++ /dev/null @@ -1,18 +0,0 @@ -// Binding a storage array field to a local is an *alias*, not a copy: the local -// holds the same slot, so growing it grows the field. (Solidity's `T[] storage p`.) -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract AliasPush { - reserved : word; // forge uses at least 1 storage slot - xs : array(uint256); - - function main() -> uint256 { - let p : storage(array(uint256)) = xs; - ArrayPush.push(p, uint256(1)); - // The push went through the alias, so the field sees it. - return Length.length(xs); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/136arraylit.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/136arraylit.sol new file mode 100644 index 00000000..bf236e28 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/136arraylit.sol @@ -0,0 +1,13 @@ +// Array literal in memory: `[1,2,3]` builds a memory(DynArray(t)), whose +// elements are then readable through `m[i]`. +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract ArrayLit { + function main() returns (uint256) { + let m : memory> = [1, 2, 3]; + return m[uint256(0)] + m[uint256(2)]; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/136arraylit.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/136arraylit.solc deleted file mode 100644 index dee3b4d8..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/136arraylit.solc +++ /dev/null @@ -1,13 +0,0 @@ -// Array literal in memory: `[1,2,3]` builds a memory(DynArray(t)), whose -// elements are then readable through `m[i]`. -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract ArrayLit { - function main() -> uint256 { - let m : memory(DynArray(uint256)) = [1, 2, 3]; - return m[uint256(0)] + m[uint256(2)]; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/137arraylitstorage.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/137arraylitstorage.sol new file mode 100644 index 00000000..5cfe6fcb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/137arraylitstorage.sol @@ -0,0 +1,16 @@ +// Assigning an array literal to a storage array field is Solidity's +// memory -> storage copy: it resizes the field and clears any abandoned tail. +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract ArrayLitStorage { + reserved : word; // forge uses at least 1 storage slot + xs : array; + + function main() returns (uint256) { + xs = [10, 20, 30]; + return xs[uint256(0)] + xs[uint256(2)]; + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/137arraylitstorage.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/137arraylitstorage.solc deleted file mode 100644 index fec9f721..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/137arraylitstorage.solc +++ /dev/null @@ -1,16 +0,0 @@ -// Assigning an array literal to a storage array field is Solidity's -// memory -> storage copy: it resizes the field and clears any abandoned tail. -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract ArrayLitStorage { - reserved : word; // forge uses at least 1 storage slot - xs : array(uint256); - - function main() -> uint256 { - xs = [10, 20, 30]; - return xs[uint256(0)] + xs[uint256(2)]; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/903badassign.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/903badassign.sol new file mode 100644 index 00000000..9275c605 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/903badassign.sol @@ -0,0 +1,39 @@ +contract Option { + enum Option { None, Some(a) } + + function just(x: word) public returns (Option) { return Option.Some(x); } + + function maybe(n: word, o: Option) public returns (word) { + match (o) { +case Option.None { +return n; +} +case Option.Some(x) { +return x; +} +} + } + + function join(mmx: Option>) public returns (Option) { + let result = Option.None; + match (mmx) { +case Option.Some(Option.Some(x)) { +result = Option.Some(x); +} +case Option.None { +result = Option.None; +} +case Option.Some(Option.None) { +result = Option.None; +} +default { +result = Option.None; +} +} + return result; + } + + function main() public returns (word) { + return maybe(0, join(Option.Some(Option.Some(42)))); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/903badassign.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/903badassign.solc deleted file mode 100644 index d3efe69b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/903badassign.solc +++ /dev/null @@ -1,27 +0,0 @@ -contract Option { - data Option(a) = None | Some(a); - - public function just(x : word) -> Option(word) { return Option.Some(x); } - - public function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.None => return n; - | Option.Some(x) => return x; - } - } - - public function join(mmx : Option(Option(word))) -> Option(word) { - let result = Option.None; - match mmx { - | Option.Some(Option.Some(x)) => result = Option.Some(x); - | Option.None => result = Option.None; - | Option.Some(Option.None) => result = Option.None; - | _ => result = Option.None; - } - return result; - } - - public function main() -> word { - return maybe(0, join(Option.Some(Option.Some(42)))); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/939badfood.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/939badfood.sol new file mode 100644 index 00000000..c7e91ccf --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/939badfood.sol @@ -0,0 +1,27 @@ +trait Enum { + function fromEnum(x: a) returns (word) ; +} + +enum Food { Curry, Beans, Other } + +impl Enum { + function fromEnum(x: Food) returns (word) { + match (x) { +case Food.Curry { +return 1; +} +case Food.Beans { +return 2; +} +case Food.Other { +return 3; +} +} + } +} + +contract FoodContract { + function main() public returns (word) { + return Enum.fromEnum(Food.Beans); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/939badfood.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/939badfood.solc deleted file mode 100644 index eb81d6b1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/939badfood.solc +++ /dev/null @@ -1,21 +0,0 @@ -forall a . class a: Enum { - function fromEnum(x : a) -> word; -} - -data Food = Curry | Beans | Other; - -instance Food : Enum { - function fromEnum(x : Food) -> word { - match x { - | Food.Curry => return 1; - | Food.Beans => return 2; - | Food.Other => return 3; - } - } -} - -contract FoodContract { - public function main() -> word { - return Enum.fromEnum(Food.Beans); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/SimpleField.sol b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/SimpleField.sol new file mode 100644 index 00000000..2392e447 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/SimpleField.sol @@ -0,0 +1,16 @@ +import * from std; +pragma no-patterson-condition ; +pragma no-coverage-condition ; +pragma no-bounded-variable-condition ; + +contract Simple { + myval : word ; + + function getVal() public returns (word) { + return myval ; + } + + function main() public returns (word) { + return getVal(); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/SimpleField.solc b/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/SimpleField.solc deleted file mode 100644 index 3aa1d3e4..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/examples/spec/SimpleField.solc +++ /dev/null @@ -1,16 +0,0 @@ -import std.{*}; -pragma no-patterson-condition ; -pragma no-coverage-condition ; -pragma no-bounded-variable-condition ; - -contract Simple { - myval : word ; - - public function getVal () -> word { - return myval ; - } - - public function main () -> word { - return getVal(); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_dup.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_dup.sol new file mode 100644 index 00000000..7eadac1d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_dup.sol @@ -0,0 +1,6 @@ +import * as M from ambA; +import * as M from ambB; + +function main(x: word) returns (word) { + return M.pick(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_dup.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_dup.solc deleted file mode 100644 index f30b5a80..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_dup.solc +++ /dev/null @@ -1,6 +0,0 @@ -import ambA as M; -import ambB as M; - -function main(x: word) -> word { - return M.pick(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_hides_original_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_hides_original_fail.sol new file mode 100644 index 00000000..5defac0d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_hides_original_fail.sol @@ -0,0 +1,5 @@ +import * as FB from foo.bar; + +function main() returns (word) { + return foo.bar.value(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_hides_original_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_hides_original_fail.solc deleted file mode 100644 index f3cc209b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_hides_original_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import foo.bar as FB; - -function main() -> word { - return foo.bar.value(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_constr_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_constr_fail.sol new file mode 100644 index 00000000..1128347d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_constr_fail.sol @@ -0,0 +1,5 @@ +import * as B from booldef; + +function mkTrue() returns (B.Bool) { + return True; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_constr_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_constr_fail.solc deleted file mode 100644 index 1d03c05a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_constr_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef as B; - -function mkTrue() -> B.Bool { - return True; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_fun_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_fun_fail.sol new file mode 100644 index 00000000..41b8d3ba --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_fun_fail.sol @@ -0,0 +1,5 @@ +import * as F from foo; + +function main() returns (word) { + return base(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_fun_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_fun_fail.solc deleted file mode 100644 index 58389b0e..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_fun_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import foo as F; - -function main() -> word { - return base(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_type_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_type_fail.sol new file mode 100644 index 00000000..04f7a96d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_type_fail.sol @@ -0,0 +1,5 @@ +import * as B from booldef; + +function idBool(b: Bool) returns (Bool) { + return b; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_type_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_type_fail.solc deleted file mode 100644 index 2105a670..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/alias_unqualified_type_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef as B; - -function idBool(b: Bool) -> Bool { - return b; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/ambA.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/ambA.sol new file mode 100644 index 00000000..f3adceb8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/ambA.sol @@ -0,0 +1,5 @@ +export { pick }; + +function pick(x: word) returns (word) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/ambA.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/ambA.solc deleted file mode 100644 index ce94f824..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/ambA.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { pick }; - -function pick(x: word) -> word { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/ambB.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/ambB.sol new file mode 100644 index 00000000..f3adceb8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/ambB.sol @@ -0,0 +1,5 @@ +export { pick }; + +function pick(x: word) returns (word) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/ambB.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/ambB.solc deleted file mode 100644 index ce94f824..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/ambB.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { pick }; - -function pick(x: word) -> word { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/amb_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/amb_main.sol new file mode 100644 index 00000000..c0e3d961 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/amb_main.sol @@ -0,0 +1,6 @@ +import {pick} from ambA; +import {pick} from ambB; + +function main(x: word) returns (word) { + return pick(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/amb_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/amb_main.solc deleted file mode 100644 index d20d1d42..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/amb_main.solc +++ /dev/null @@ -1,6 +0,0 @@ -import ambA.{pick}; -import ambB.{pick}; - -function main(x: word) -> word { - return pick(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/amb_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/amb_ok.sol new file mode 100644 index 00000000..638e9d0b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/amb_ok.sol @@ -0,0 +1,6 @@ +import ambA; +import ambB; + +function main(x: word) returns (word) { + return ambA.pick(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/amb_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/amb_ok.solc deleted file mode 100644 index 5ae3f26c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/amb_ok.solc +++ /dev/null @@ -1,6 +0,0 @@ -import ambA; -import ambB; - -function main(x: word) -> word { - return ambA.pick(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolalias.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolalias.sol new file mode 100644 index 00000000..03a32680 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolalias.sol @@ -0,0 +1,5 @@ +import * as B from booldef; + +function fromAlias(b: B.Bool) returns (B.Bool) { + return B.not(b); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolalias.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolalias.solc deleted file mode 100644 index fa094354..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolalias.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef as B; - -function fromAlias(b: B.Bool) -> B.Bool { - return B.not(b); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolalias_open_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolalias_open_fail.sol new file mode 100644 index 00000000..69d5ce7a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolalias_open_fail.sol @@ -0,0 +1,5 @@ +import * as B from booldef; + +function bad(b: Bool) returns (Bool) { + return not(b); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolalias_open_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolalias_open_fail.solc deleted file mode 100644 index ea50f8dd..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolalias_open_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef as B; - -function bad(b: Bool) -> Bool { - return not(b); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolaliastype.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolaliastype.sol new file mode 100644 index 00000000..778718cf --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolaliastype.sol @@ -0,0 +1,5 @@ +import * as B from booldef; + +function fromAliasType(b: B.Bool) returns (B.Bool) { + return B.not(b); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolaliastype.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolaliastype.solc deleted file mode 100644 index bcf3e554..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolaliastype.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef as B; - -function fromAliasType(b: B.Bool) -> B.Bool { - return B.not(b); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolconselect_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolconselect_fail.sol new file mode 100644 index 00000000..915aaf30 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolconselect_fail.sol @@ -0,0 +1,5 @@ +import {Bool} from booldef; + +function mkTrue() returns (Bool) { + return True; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolconselect_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolconselect_fail.solc deleted file mode 100644 index f4143bbc..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolconselect_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef.{Bool}; - -function mkTrue() -> Bool { - return True; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolconselect_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolconselect_ok.sol new file mode 100644 index 00000000..2fbfea96 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolconselect_ok.sol @@ -0,0 +1,5 @@ +import {Bool} from booldef; + +function mkTrue() returns (Bool) { + return Bool.True; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolconselect_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolconselect_ok.solc deleted file mode 100644 index 5b719037..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolconselect_ok.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef.{Bool}; - -function mkTrue() -> Bool { - return Bool.True; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/booldef.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/booldef.sol new file mode 100644 index 00000000..0013b368 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/booldef.sol @@ -0,0 +1,26 @@ +export { Bool(*), not, C, D, id }; + +enum Bool { True, False } + +function not(b: Bool) returns (Bool) { + match (b) { +case Bool.True { +return Bool.False; +} +case Bool.False { +return Bool.True; +} +} +} + +trait C { + function c(x: a, y: a) returns (word) ; +} + +trait D { + function d() returns (a) ; +} + +function id(x: a) returns (word) where a: C, a: D { + return C.c(x, D.d()); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/booldef.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/booldef.solc deleted file mode 100644 index 639d21eb..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/booldef.solc +++ /dev/null @@ -1,22 +0,0 @@ -export { Bool(*), not, C, D, id }; - -data Bool = True | False; - -function not (b : Bool) -> Bool { - match b { - | Bool.True => return Bool.False; - | Bool.False => return Bool.True; - } -} - -forall a . class a : C { - function c (x : a, y : a) -> word ; -} - -forall a . class a : D { - function d() -> a ; -} - -forall a . a : C, a : D => function id (x : a) -> word { - return C.c(x, D.d()); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolmain.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolmain.sol new file mode 100644 index 00000000..d6b08815 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolmain.sol @@ -0,0 +1,5 @@ +import booldef; + +function and(b1: booldef.Bool, b2: booldef.Bool) returns (booldef.Bool) { + return b1; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolmain.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolmain.solc deleted file mode 100644 index a50de30f..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolmain.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef; - -function and(b1: booldef.Bool, b2: booldef.Bool) -> booldef.Bool { - return b1; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolqualified.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolqualified.sol new file mode 100644 index 00000000..4ffcf72f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolqualified.sol @@ -0,0 +1,5 @@ +import booldef; + +function fromQualified(b: booldef.Bool) returns (booldef.Bool) { + return booldef.not(b); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolqualified.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolqualified.solc deleted file mode 100644 index 01bf3a3e..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolqualified.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef; - -function fromQualified(b: booldef.Bool) -> booldef.Bool { - return booldef.not(b); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolqualifiedtype.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolqualifiedtype.sol new file mode 100644 index 00000000..5892f3cd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolqualifiedtype.sol @@ -0,0 +1,5 @@ +import booldef; + +function fromQualifiedType(b: booldef.Bool) returns (booldef.Bool) { + return booldef.not(b); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolqualifiedtype.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolqualifiedtype.solc deleted file mode 100644 index 0b4d2b3c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolqualifiedtype.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef; - -function fromQualifiedType(b: booldef.Bool) -> booldef.Bool { - return booldef.not(b); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolselect.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolselect.sol new file mode 100644 index 00000000..bc70b013 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolselect.sol @@ -0,0 +1,5 @@ +import {Bool, not} from booldef; + +function fromSelect(b: Bool) returns (Bool) { + return not(b); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolselect.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/boolselect.solc deleted file mode 100644 index 1041fc71..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/boolselect.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef.{Bool, not}; - -function fromSelect(b: Bool) -> Bool { - return not(b); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/cycleA.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/cycleA.sol new file mode 100644 index 00000000..30bca38e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/cycleA.sol @@ -0,0 +1,7 @@ +import cycleB; +export { fromCycleA }; +export cycleB.{fromCycleB}; + +function fromCycleA() returns (word) { + return cycleB.fromCycleB(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/cycleA.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/cycleA.solc deleted file mode 100644 index 1ce73fd6..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/cycleA.solc +++ /dev/null @@ -1,7 +0,0 @@ -import cycleB; -export { fromCycleA }; -export cycleB.{fromCycleB}; - -function fromCycleA() -> word { - return cycleB.fromCycleB(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/cycleB.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/cycleB.sol new file mode 100644 index 00000000..07d18774 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/cycleB.sol @@ -0,0 +1,7 @@ +import cycleA; +export { fromCycleB }; +export cycleA.{fromCycleA}; + +function fromCycleB() returns (word) { + return 2; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/cycleB.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/cycleB.solc deleted file mode 100644 index 71fb1cf5..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/cycleB.solc +++ /dev/null @@ -1,7 +0,0 @@ -import cycleA; -export { fromCycleB }; -export cycleA.{fromCycleA}; - -function fromCycleB() -> word { - return 2; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/cycle_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/cycle_main.sol new file mode 100644 index 00000000..e8784d8a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/cycle_main.sol @@ -0,0 +1,5 @@ +import cycleA; + +function main() returns (word) { + return cycleA.fromCycleB(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/cycle_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/cycle_main.solc deleted file mode 100644 index 77d87240..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/cycle_main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import cycleA; - -function main() -> word { - return cycleA.fromCycleB(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_context_expr.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_context_expr.sol new file mode 100644 index 00000000..28cd4f5d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_context_expr.sol @@ -0,0 +1,18 @@ +import dot_left; +import dot_right; + +function mkLeft() returns (dot_left.LeftOpt) { + let x: dot_left.LeftOpt = .Some(1); + return x; +} + +function main() returns (word) { + match (mkLeft()) { +case .Some(v) { +return v; +} +case .None { +return 0; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_context_expr.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_context_expr.solc deleted file mode 100644 index 02be5db6..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_context_expr.solc +++ /dev/null @@ -1,14 +0,0 @@ -import dot_left; -import dot_right; - -function mkLeft() -> dot_left.LeftOpt { - let x: dot_left.LeftOpt = .Some(1); - return x; -} - -function main() -> word { - match mkLeft() { - | .Some(v) => return v; - | .None => return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_left.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_left.sol new file mode 100644 index 00000000..30203ed9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_left.sol @@ -0,0 +1,3 @@ +export { LeftOpt(*) }; + +enum LeftOpt { None, Some(word) } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_left.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_left.solc deleted file mode 100644 index 5511a1c6..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_left.solc +++ /dev/null @@ -1,3 +0,0 @@ -export { LeftOpt(*) }; - -data LeftOpt = None | Some(word); diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_right.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_right.sol new file mode 100644 index 00000000..8cc9becd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_right.sol @@ -0,0 +1,3 @@ +export { RightOpt(*) }; + +enum RightOpt { None, Some(word) } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_right.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_right.solc deleted file mode 100644 index 82f8f8af..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/dot_right.solc +++ /dev/null @@ -1,3 +0,0 @@ -export { RightOpt(*) }; - -data RightOpt = None | Some(word); diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_a.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_a.sol new file mode 100644 index 00000000..61eae222 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_a.sol @@ -0,0 +1,5 @@ +export { foo }; + +function foo(x: word) returns (word) { + return 1; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_a.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_a.solc deleted file mode 100644 index ed7f99ed..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_a.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { foo }; - -function foo(x: word) -> word { - return 1; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_b.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_b.sol new file mode 100644 index 00000000..87b4d50d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_b.sol @@ -0,0 +1,5 @@ +export { foo }; + +function foo(x: word) returns (word) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_b.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_b.solc deleted file mode 100644 index 7ee6a4e5..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_b.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { foo }; - -function foo(x: word) -> word { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_main.sol new file mode 100644 index 00000000..e957ae83 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_main.sol @@ -0,0 +1,7 @@ +import * as m1 from dupqual_a; +import * as m2 from dupqual_b; + +function main(x: word) returns (word) { + let y = m1.foo(x); + return m2.foo(y); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_main.solc deleted file mode 100644 index cbe4de15..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_main.solc +++ /dev/null @@ -1,7 +0,0 @@ -import dupqual_a as m1; -import dupqual_b as m2; - -function main(x: word) -> word { - let y = m1.foo(x); - return m2.foo(y); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_module_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_module_main.sol new file mode 100644 index 00000000..6a887247 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_module_main.sol @@ -0,0 +1,7 @@ +import dupqual_a; +import dupqual_b; + +function main(x: word) returns (word) { + let y = dupqual_a.foo(x); + return dupqual_b.foo(y); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_module_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_module_main.solc deleted file mode 100644 index 5ef0d8eb..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/dupqual_module_main.solc +++ /dev/null @@ -1,7 +0,0 @@ -import dupqual_a; -import dupqual_b; - -function main(x: word) -> word { - let y = dupqual_a.foo(x); - return dupqual_b.foo(y); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/export_item_dup_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/export_item_dup_fail.sol new file mode 100644 index 00000000..d6d7abb8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/export_item_dup_fail.sol @@ -0,0 +1,6 @@ +export ambA.{pick}; +export ambB.{pick}; + +function main(x: word) returns (word) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/export_item_dup_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/export_item_dup_fail.solc deleted file mode 100644 index 8d7a33f1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/export_item_dup_fail.solc +++ /dev/null @@ -1,6 +0,0 @@ -export ambA.{pick}; -export ambB.{pick}; - -function main(x: word) -> word { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/export_module_dup_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/export_module_dup_fail.sol new file mode 100644 index 00000000..95cbdc5a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/export_module_dup_fail.sol @@ -0,0 +1,6 @@ +export foo as M; +export booldef as M; + +function main() returns (word) { + return 0; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/export_module_dup_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/export_module_dup_fail.solc deleted file mode 100644 index 118a875c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/export_module_dup_fail.solc +++ /dev/null @@ -1,6 +0,0 @@ -export foo as M; -export booldef as M; - -function main() -> word { - return 0; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_alias_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_alias_main.sol new file mode 100644 index 00000000..7bc7882a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_alias_main.sol @@ -0,0 +1,5 @@ +import * as MathApi from @extlib.math.api; + +function main() returns (word) { + return MathApi.sum(39); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_alias_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_alias_main.solc deleted file mode 100644 index 7853c68c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_alias_main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import @extlib.math.api as MathApi; - -function main() -> word { - return MathApi.sum(39); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_main.sol new file mode 100644 index 00000000..12bb126a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_main.sol @@ -0,0 +1,9 @@ +import @extlib.math.api; + +contract External { + constructor() {} + + function main() public returns (word) { + return math.api.sum(39); + } +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_main.solc deleted file mode 100644 index 5ffd122d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_main.solc +++ /dev/null @@ -1,9 +0,0 @@ -import @extlib.math.api; - -contract External { - constructor() {} - - public function main() -> word { - return math.api.sum(39); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_missing_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_missing_fail.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_missing_fail.solc rename to crates/parser/tests/fixtures/corpus/ok/test/imports/external_lib_missing_fail.sol diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/math/api.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/math/api.sol new file mode 100644 index 00000000..3ff33e31 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/math/api.sol @@ -0,0 +1,8 @@ +import internals.add; +import lib.util; + +export {sum}; + +function sum(x: word) returns (word) { + return add.inc(x) + util.offset(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/math/api.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/math/api.solc deleted file mode 100644 index 43dc18f7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/math/api.solc +++ /dev/null @@ -1,8 +0,0 @@ -import internals.add; -import lib.util; - -export {sum}; - -function sum(x: word) -> word { - return add.inc(x) + util.offset(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/math/internals/add.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/math/internals/add.sol new file mode 100644 index 00000000..dd5b16fd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/math/internals/add.sol @@ -0,0 +1,7 @@ +import {Add} from std; + +export {inc}; + +function inc(x: word) returns (word) { + return x + 1; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/math/internals/add.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/math/internals/add.solc deleted file mode 100644 index 06449de7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/math/internals/add.solc +++ /dev/null @@ -1,7 +0,0 @@ -import std.{Add}; - -export {inc}; - -function inc(x: word) -> word { - return x + 1; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/util.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/util.sol new file mode 100644 index 00000000..de93f722 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/util.sol @@ -0,0 +1,5 @@ +export {offset}; + +function offset() returns (word) { + return 2; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/util.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/util.solc deleted file mode 100644 index 21a00682..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/extlib/util.solc +++ /dev/null @@ -1,5 +0,0 @@ -export {offset}; - -function offset() -> word { - return 2; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/foo.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/foo.sol new file mode 100644 index 00000000..ebef1581 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/foo.sol @@ -0,0 +1,5 @@ +export { base }; + +function base() returns (word) { + return 3; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/foo.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/foo.solc deleted file mode 100644 index ca17ac81..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/foo.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { base }; - -function base() -> word { - return 3; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/foo/bar.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/foo/bar.sol new file mode 100644 index 00000000..b0daa7d9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/foo/bar.sol @@ -0,0 +1,5 @@ +export { value }; + +function value() returns (word) { + return 7; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/foo/bar.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/foo/bar.solc deleted file mode 100644 index 4f2e503d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/foo/bar.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { value }; - -function value() -> word { - return 7; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/foo/bar/baz.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/foo/bar/baz.sol new file mode 100644 index 00000000..aa7fac4b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/foo/bar/baz.sol @@ -0,0 +1,5 @@ +export { deep }; + +function deep() returns (word) { + return 9; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/foo/bar/baz.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/foo/bar/baz.solc deleted file mode 100644 index 73dd9ef1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/foo/bar/baz.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { deep }; - -function deep() -> word { - return 9; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_a.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_a.sol new file mode 100644 index 00000000..6970ebc0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_a.sol @@ -0,0 +1,5 @@ +export {*}; + +function shared(x: word) returns (word) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_a.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_a.solc deleted file mode 100644 index ccd8ec04..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_a.solc +++ /dev/null @@ -1,5 +0,0 @@ -export {*}; - -function shared(x: word) -> word { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_b.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_b.sol new file mode 100644 index 00000000..6970ebc0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_b.sol @@ -0,0 +1,5 @@ +export {*}; + +function shared(x: word) returns (word) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_b.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_b.solc deleted file mode 100644 index ccd8ec04..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_b.solc +++ /dev/null @@ -1,5 +0,0 @@ -export {*}; - -function shared(x: word) -> word { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_main_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_main_fail.sol new file mode 100644 index 00000000..5cb529f4 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_main_fail.sol @@ -0,0 +1,6 @@ +import * from glob_amb_a; +import * from glob_amb_b; + +function main(x: word) returns (word) { + return shared(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_main_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_main_fail.solc deleted file mode 100644 index 168a2d20..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_amb_main_fail.solc +++ /dev/null @@ -1,6 +0,0 @@ -import glob_amb_a.{*}; -import glob_amb_b.{*}; - -function main(x: word) -> word { - return shared(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_export_mixed.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_export_mixed.sol new file mode 100644 index 00000000..28313d8b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_export_mixed.sol @@ -0,0 +1,5 @@ +export {*, main}; + +function main(x: word) returns (word) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_export_mixed.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_export_mixed.solc deleted file mode 100644 index 0bed5cdc..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_export_mixed.solc +++ /dev/null @@ -1,5 +0,0 @@ -export {*, main}; - -function main(x: word) -> word { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_hiding_amb_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_hiding_amb_ok.sol new file mode 100644 index 00000000..8f3eae7f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_hiding_amb_ok.sol @@ -0,0 +1,6 @@ +import * from glob_amb_a hiding {shared}; +import * from glob_amb_b; + +function main(x: word) returns (word) { + return shared(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_hiding_amb_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_hiding_amb_ok.solc deleted file mode 100644 index 89bb50ab..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_hiding_amb_ok.solc +++ /dev/null @@ -1,6 +0,0 @@ -import glob_amb_a.{*} hiding {shared}; -import glob_amb_b.{*}; - -function main(x: word) -> word { - return shared(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_dup.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_dup.sol new file mode 100644 index 00000000..e94313b7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_dup.sol @@ -0,0 +1,5 @@ +import * from globlib; + +function main(x: word) returns (word) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_dup.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_dup.solc deleted file mode 100644 index 100a698c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_dup.solc +++ /dev/null @@ -1,5 +0,0 @@ -import globlib.{*, *}; - -function main(x: word) -> word { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_hiding.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_hiding.sol new file mode 100644 index 00000000..b13b196f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_hiding.sol @@ -0,0 +1,10 @@ +import * from globlib hiding {idWord}; + +function main(x: word) returns (word) { + let y: T = mkT(x); + match (y) { +case T.T(v) { +return v; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_hiding.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_hiding.solc deleted file mode 100644 index 385dff72..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_hiding.solc +++ /dev/null @@ -1,8 +0,0 @@ -import globlib.{*} hiding {idWord}; - -function main(x: word) -> word { - let y: T = mkT(x); - match y { - | T.T(v) => return v; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_hiding_unknown_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_hiding_unknown_fail.sol new file mode 100644 index 00000000..4c09071f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_hiding_unknown_fail.sol @@ -0,0 +1,5 @@ +import * from globlib hiding {missing}; + +function main(x: word) returns (word) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_hiding_unknown_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_hiding_unknown_fail.solc deleted file mode 100644 index 7877da11..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_hiding_unknown_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import globlib.{*} hiding {missing}; - -function main(x: word) -> word { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_mixed.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_mixed.sol new file mode 100644 index 00000000..93f6f6f1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_mixed.sol @@ -0,0 +1,5 @@ +import * from globlib; + +function main(x: word) returns (word) { + return idWord(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_mixed.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_mixed.solc deleted file mode 100644 index aabb81aa..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_mixed.solc +++ /dev/null @@ -1,5 +0,0 @@ -import globlib.{*, idWord}; - -function main(x: word) -> word { - return idWord(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_ok.sol new file mode 100644 index 00000000..61703a55 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_ok.sol @@ -0,0 +1,10 @@ +import * from globlib; + +function main(x: word) returns (word) { + let y: T = mkT(x); + match (y) { +case T.T(v) { +return idWord(v); +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_ok.solc deleted file mode 100644 index e87a4f80..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/glob_import_ok.solc +++ /dev/null @@ -1,8 +0,0 @@ -import globlib.{*}; - -function main(x: word) -> word { - let y: T = mkT(x); - match y { - | T.T(v) => return idWord(v); - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/globlib.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/globlib.sol new file mode 100644 index 00000000..431b3dd2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/globlib.sol @@ -0,0 +1,11 @@ +export {*, T(*)}; + +enum T { T(word) } + +function idWord(x: word) returns (word) { + return x; +} + +function mkT(x: word) returns (T) { + return T.T(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/globlib.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/globlib.solc deleted file mode 100644 index d433e74d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/globlib.solc +++ /dev/null @@ -1,11 +0,0 @@ -export {*, T(*)}; - -data T = T(word); - -function idWord(x: word) -> word { - return x; -} - -function mkT(x: word) -> T { - return T.T(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_dot_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_dot_fail.sol new file mode 100644 index 00000000..286cd96b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_dot_fail.sol @@ -0,0 +1,5 @@ +import {Token} from hidden_ctor_lib; + +function main() returns (Token) { + return .Err(1); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_dot_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_dot_fail.solc deleted file mode 100644 index e6e2a41d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_dot_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import hidden_ctor_lib.{Token}; - -function main() -> Token { - return .Err(1); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_expr_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_expr_fail.sol new file mode 100644 index 00000000..c2df2197 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_expr_fail.sol @@ -0,0 +1,5 @@ +import {Token} from hidden_ctor_lib; + +function main() returns (Token) { + return Token.Err(0); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_expr_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_expr_fail.solc deleted file mode 100644 index 1515e1f7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_expr_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import hidden_ctor_lib.{Token}; - -function main() -> Token { - return Token.Err(0); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_lib.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_lib.sol new file mode 100644 index 00000000..038b849d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_lib.sol @@ -0,0 +1,11 @@ +export {Token(Ok), mkOk, mkErr}; + +enum Token { Ok(word), Err(word) } + +function mkOk(x: word) returns (Token) { + return Token.Ok(x); +} + +function mkErr(x: word) returns (Token) { + return Token.Err(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_lib.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_lib.solc deleted file mode 100644 index 4ecb42b8..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_lib.solc +++ /dev/null @@ -1,11 +0,0 @@ -export {Token(Ok), mkOk, mkErr}; - -data Token = Ok(word) | Err(word); - -function mkOk(x: word) -> Token { - return Token.Ok(x); -} - -function mkErr(x: word) -> Token { - return Token.Err(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_nonexhaustive_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_nonexhaustive_fail.sol new file mode 100644 index 00000000..6a0e9cda --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_nonexhaustive_fail.sol @@ -0,0 +1,9 @@ +import {Token, mkOk} from hidden_ctor_lib; + +function main() returns (word) { + match (mkOk(1)) { +case Token.Ok(v) { +return v; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_nonexhaustive_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_nonexhaustive_fail.solc deleted file mode 100644 index d13d0167..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_nonexhaustive_fail.solc +++ /dev/null @@ -1,7 +0,0 @@ -import hidden_ctor_lib.{Token, mkOk}; - -function main() -> word { - match mkOk(1) { - | Token.Ok(v) => return v; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_pattern_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_pattern_fail.sol new file mode 100644 index 00000000..3865c58b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_pattern_fail.sol @@ -0,0 +1,12 @@ +import {Token, mkErr} from hidden_ctor_lib; + +function main() returns (word) { + match (mkErr(1)) { +case Token.Err(v) { +return v; +} +default { +return 0; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_pattern_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_pattern_fail.solc deleted file mode 100644 index 3637f613..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_pattern_fail.solc +++ /dev/null @@ -1,8 +0,0 @@ -import hidden_ctor_lib.{Token, mkErr}; - -function main() -> word { - match mkErr(1) { - | Token.Err(v) => return v; - | _ => return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_wildcard_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_wildcard_ok.sol new file mode 100644 index 00000000..221d066a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_wildcard_ok.sol @@ -0,0 +1,12 @@ +import {Token, mkErr} from hidden_ctor_lib; + +function main() returns (word) { + match (mkErr(1)) { +case Token.Ok(v) { +return v; +} +default { +return 0; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_wildcard_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_wildcard_ok.solc deleted file mode 100644 index 25f93ee3..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/hidden_ctor_wildcard_ok.solc +++ /dev/null @@ -1,8 +0,0 @@ -import hidden_ctor_lib.{Token, mkErr}; - -function main() -> word { - match mkErr(1) { - | Token.Ok(v) => return v; - | _ => return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/import_std_minimal.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/import_std_minimal.sol new file mode 100644 index 00000000..697fbd82 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/import_std_minimal.sol @@ -0,0 +1,3 @@ +import std; + +function main() {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/import_std_minimal.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/import_std_minimal.solc deleted file mode 100644 index f54d9a7f..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/import_std_minimal.solc +++ /dev/null @@ -1,3 +0,0 @@ -import std; - -function main() -> () {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_a.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_a.sol new file mode 100644 index 00000000..982d6b27 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_a.sol @@ -0,0 +1,5 @@ +export { fromA }; + +function fromA() returns (word) { + return 1; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_a.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_a.solc deleted file mode 100644 index 560e4fbf..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_a.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { fromA }; - -function fromA() -> word { - return 1; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_b.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_b.sol new file mode 100644 index 00000000..3b997491 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_b.sol @@ -0,0 +1,5 @@ +export { fromB }; + +function fromB() returns (word) { + return fromA(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_b.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_b.solc deleted file mode 100644 index 198768b2..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_b.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { fromB }; - -function fromB() -> word { - return fromA(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_main.sol new file mode 100644 index 00000000..3efbc01b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_main.sol @@ -0,0 +1,6 @@ +import leak_a; +import leak_b; + +function main() returns (word) { + return fromB(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_main.solc deleted file mode 100644 index 7a52277c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/leak_main.solc +++ /dev/null @@ -1,6 +0,0 @@ -import leak_a; -import leak_b; - -function main() -> word { - return fromB(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/mirror/api.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/mirror/api.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/ok/test/imports/mirror/api.solc rename to crates/parser/tests/fixtures/corpus/ok/test/imports/mirror/api.sol diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/mirror/helper.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/mirror/helper.sol new file mode 100644 index 00000000..f03838e7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/mirror/helper.sol @@ -0,0 +1,3 @@ +export {T}; + +enum T { T } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/mirror/helper.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/mirror/helper.solc deleted file mode 100644 index d2d38ce7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/mirror/helper.solc +++ /dev/null @@ -1,3 +0,0 @@ -export {T}; - -data T = T; diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_name_shadow.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_name_shadow.sol new file mode 100644 index 00000000..303ec7db --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_name_shadow.sol @@ -0,0 +1,9 @@ +import * as keep from foo; + +function keep() returns (word) { + return 1; +} + +function main() returns (word) { + return keep(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_name_shadow.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_name_shadow.solc deleted file mode 100644 index a22bc04b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_name_shadow.solc +++ /dev/null @@ -1,9 +0,0 @@ -import foo as keep; - -function keep() -> word { - return 1; -} - -function main() -> word { - return keep(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor.sol new file mode 100644 index 00000000..f02f35ca --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor.sol @@ -0,0 +1,5 @@ +import booldef; + +function mk() returns (booldef.Bool) { + return booldef.Bool.True; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor.solc deleted file mode 100644 index 7f3d8640..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef; - -function mk() -> booldef.Bool { - return booldef.Bool.True; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor_alias.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor_alias.sol new file mode 100644 index 00000000..83b06e50 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor_alias.sol @@ -0,0 +1,5 @@ +import * as b from booldef; + +function mk() returns (b.Bool) { + return b.Bool.True; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor_alias.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor_alias.solc deleted file mode 100644 index f3896448..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor_alias.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef as b; - -function mk() -> b.Bool { - return b.Bool.True; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor_pattern.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor_pattern.sol new file mode 100644 index 00000000..ca54b396 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor_pattern.sol @@ -0,0 +1,12 @@ +import booldef; + +function main(x: booldef.Bool) returns (word) { + match (x) { +case booldef.Bool.True { +return 1; +} +default { +return 0; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor_pattern.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor_pattern.solc deleted file mode 100644 index 84fb72dd..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_qualified_constructor_pattern.solc +++ /dev/null @@ -1,8 +0,0 @@ -import booldef; - -function main(x: booldef.Bool) -> word { - match x { - | booldef.Bool.True => return 1; - | _ => return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_constr_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_constr_fail.sol new file mode 100644 index 00000000..9e0d64d2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_constr_fail.sol @@ -0,0 +1,5 @@ +import booldef; + +function mkTrue() returns (booldef.Bool) { + return True; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_constr_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_constr_fail.solc deleted file mode 100644 index cc250ccf..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_constr_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef; - -function mkTrue() -> booldef.Bool { - return True; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_fun_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_fun_fail.sol new file mode 100644 index 00000000..ff8ddb46 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_fun_fail.sol @@ -0,0 +1,5 @@ +import foo; + +function main() returns (word) { + return base(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_fun_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_fun_fail.solc deleted file mode 100644 index 9a4b3611..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_fun_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import foo; - -function main() -> word { - return base(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_type_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_type_fail.sol new file mode 100644 index 00000000..0cd983cb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_type_fail.sol @@ -0,0 +1,5 @@ +import booldef; + +function idBool(b: Bool) returns (Bool) { + return b; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_type_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_type_fail.solc deleted file mode 100644 index f8ddc777..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/module_unqualified_type_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef; - -function idBool(b: Bool) -> Bool { - return b; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_alias.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_alias.sol new file mode 100644 index 00000000..1e207504 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_alias.sol @@ -0,0 +1,5 @@ +import * as FB from foo.bar; + +function main() returns (word) { + return FB.value(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_alias.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_alias.solc deleted file mode 100644 index 0e8f0059..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_alias.solc +++ /dev/null @@ -1,5 +0,0 @@ -import foo.bar as FB; - -function main() -> word { - return FB.value(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_deep_qualifier.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_deep_qualifier.sol new file mode 100644 index 00000000..49770daa --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_deep_qualifier.sol @@ -0,0 +1,5 @@ +import foo.bar.baz; + +function main() returns (word) { + return foo.bar.baz.deep(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_deep_qualifier.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_deep_qualifier.solc deleted file mode 100644 index 8c8d43b7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_deep_qualifier.solc +++ /dev/null @@ -1,5 +0,0 @@ -import foo.bar.baz; - -function main() -> word { - return foo.bar.baz.deep(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_direct_qualifier.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_direct_qualifier.sol new file mode 100644 index 00000000..81a902b0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_direct_qualifier.sol @@ -0,0 +1,5 @@ +import foo.bar; + +function main() returns (word) { + return foo.bar.value(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_direct_qualifier.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_direct_qualifier.solc deleted file mode 100644 index 8d1b89fd..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_direct_qualifier.solc +++ /dev/null @@ -1,5 +0,0 @@ -import foo.bar; - -function main() -> word { - return foo.bar.value(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_foo_and_bar.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_foo_and_bar.sol new file mode 100644 index 00000000..72b98647 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_foo_and_bar.sol @@ -0,0 +1,8 @@ +import foo; +import * as Bar from foo.bar; + +function main() returns (word) { + let x: word = foo.base(); + let y: word = Bar.value(); + return y; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_foo_and_bar.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_foo_and_bar.solc deleted file mode 100644 index abe3fe99..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_foo_and_bar.solc +++ /dev/null @@ -1,8 +0,0 @@ -import foo; -import foo.bar as Bar; - -function main() -> word { - let x: word = foo.base(); - let y: word = Bar.value(); - return y; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_select.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_select.sol new file mode 100644 index 00000000..b90ecff1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_select.sol @@ -0,0 +1,5 @@ +import {value} from foo.bar; + +function main() returns (word) { + return value(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_select.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_select.solc deleted file mode 100644 index 62047c36..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/nested_select.solc +++ /dev/null @@ -1,5 +0,0 @@ -import foo.bar.{value}; - -function main() -> word { - return value(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/ns_constr_dup.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/ns_constr_dup.sol new file mode 100644 index 00000000..6099075c --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/ns_constr_dup.sol @@ -0,0 +1,6 @@ +enum Foo { Same } +enum Bar { Same } + +function main() returns (word) { + return 0; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/ns_constr_dup.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/ns_constr_dup.solc deleted file mode 100644 index 8dbef8db..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/ns_constr_dup.solc +++ /dev/null @@ -1,6 +0,0 @@ -data Foo = Same; -data Bar = Same; - -function main() -> word { - return 0; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/ns_cross_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/ns_cross_ok.sol new file mode 100644 index 00000000..ca4d7e96 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/ns_cross_ok.sol @@ -0,0 +1,5 @@ +enum Foo { Foo } + +function main() returns (Foo) { + return Foo.Foo; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/ns_cross_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/ns_cross_ok.solc deleted file mode 100644 index 34b8a18f..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/ns_cross_ok.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Foo = Foo; - -function main() -> Foo { - return Foo.Foo; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_leak_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_leak_fail.sol new file mode 100644 index 00000000..2db0ff03 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_leak_fail.sol @@ -0,0 +1,5 @@ +import * as M from opaque_alias_mid; + +function bad(x: word) returns (T) { + return M.make(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_leak_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_leak_fail.solc deleted file mode 100644 index c6f221a4..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_leak_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import opaque_alias_mid as M; - -function bad(x: word) -> T { - return M.make(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_main.sol new file mode 100644 index 00000000..d0c8f9f2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_main.sol @@ -0,0 +1,6 @@ +import * as M from opaque_alias_mid; + +function main(x: word) returns (word) { + let t = M.make(x); + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_main.solc deleted file mode 100644 index a21644c3..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_main.solc +++ /dev/null @@ -1,6 +0,0 @@ -import opaque_alias_mid as M; - -function main(x: word) -> word { - let t = M.make(x); - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_mid.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_mid.sol new file mode 100644 index 00000000..2c071630 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_mid.sol @@ -0,0 +1,7 @@ +import * as Base from opaque_dep_base; + +export { make }; + +function make(x: word) returns (Base.T) { + return Base.mkT(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_mid.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_mid.solc deleted file mode 100644 index 75523351..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_mid.solc +++ /dev/null @@ -1,7 +0,0 @@ -import opaque_dep_base as Base; - -export { make }; - -function make(x: word) -> Base.T { - return Base.mkT(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_qualifier_leak_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_qualifier_leak_fail.sol new file mode 100644 index 00000000..2646b7b2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_qualifier_leak_fail.sol @@ -0,0 +1,5 @@ +import * as M from opaque_alias_mid; + +function bad(x: word) returns (Base.T) { + return M.make(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_qualifier_leak_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_qualifier_leak_fail.solc deleted file mode 100644 index fd0e0518..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_alias_qualifier_leak_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import opaque_alias_mid as M; - -function bad(x: word) -> Base.T { - return M.make(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_dep_base.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_dep_base.sol new file mode 100644 index 00000000..390f62d1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_dep_base.sol @@ -0,0 +1,7 @@ +export { T(*), mkT }; + +enum T { T(word) } + +function mkT(x: word) returns (T) { + return T.T(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_dep_base.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_dep_base.solc deleted file mode 100644 index 95a10f3e..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_dep_base.solc +++ /dev/null @@ -1,7 +0,0 @@ -export { T(*), mkT }; - -data T = T(word); - -function mkT(x: word) -> T { - return T.T(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_alias_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_alias_main.sol new file mode 100644 index 00000000..51e23026 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_alias_main.sol @@ -0,0 +1,6 @@ +import * as M from opaque_select_alias_mid; + +function main(x: word) returns (word) { + let t = M.make(x); + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_alias_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_alias_main.solc deleted file mode 100644 index 8ec20765..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_alias_main.solc +++ /dev/null @@ -1,6 +0,0 @@ -import opaque_select_alias_mid as M; - -function main(x: word) -> word { - let t = M.make(x); - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_alias_mid.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_alias_mid.sol new file mode 100644 index 00000000..9ca5df84 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_alias_mid.sol @@ -0,0 +1,7 @@ +import {T as U, mkT} from opaque_dep_base; + +export { make }; + +function make(x: word) returns (U) { + return mkT(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_alias_mid.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_alias_mid.solc deleted file mode 100644 index b8f71be7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_alias_mid.solc +++ /dev/null @@ -1,7 +0,0 @@ -import opaque_dep_base.{T as U, mkT}; - -export { make }; - -function make(x: word) -> U { - return mkT(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_direct_leak_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_direct_leak_fail.sol new file mode 100644 index 00000000..af1c1343 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_direct_leak_fail.sol @@ -0,0 +1,5 @@ +import * as M from opaque_select_direct_mid; + +function bad(x: word) returns (T) { + return M.make(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_direct_leak_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_direct_leak_fail.solc deleted file mode 100644 index 47a953ca..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_direct_leak_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import opaque_select_direct_mid as M; - -function bad(x: word) -> T { - return M.make(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_direct_mid.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_direct_mid.sol new file mode 100644 index 00000000..dce75db0 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_direct_mid.sol @@ -0,0 +1,7 @@ +import {T, mkT} from opaque_dep_base; + +export { make }; + +function make(x: word) returns (T) { + return mkT(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_direct_mid.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_direct_mid.solc deleted file mode 100644 index bdb833a7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/opaque_select_direct_mid.solc +++ /dev/null @@ -1,7 +0,0 @@ -import opaque_dep_base.{T, mkT}; - -export { make }; - -function make(x: word) -> T { - return mkT(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/pragma_scope_lib.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/pragma_scope_lib.sol new file mode 100644 index 00000000..100259b8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/pragma_scope_lib.sol @@ -0,0 +1,7 @@ +export { helper }; + +pragma no-patterson-condition C; + +function helper() returns (word) { + return 1; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/pragma_scope_lib.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/pragma_scope_lib.solc deleted file mode 100644 index 035f940a..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/pragma_scope_lib.solc +++ /dev/null @@ -1,7 +0,0 @@ -export { helper }; - -pragma no-patterson-condition C; - -function helper() -> word { - return 1; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/pragma_scope_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/pragma_scope_main.sol new file mode 100644 index 00000000..ebaccf71 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/pragma_scope_main.sol @@ -0,0 +1,7 @@ +import pragma_scope_lib; + +enum List { Nil, Cons(a, List) } + +trait C {} + +impl C, a, List> {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/pragma_scope_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/pragma_scope_main.solc deleted file mode 100644 index 0d4f0b22..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/pragma_scope_main.solc +++ /dev/null @@ -1,7 +0,0 @@ -import pragma_scope_lib; - -data List(a) = Nil | Cons(a, List(a)); - -forall a b c . class a : C(b, c) {} - -forall a b . instance List(b) : C(a, List(a)) {} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/private_bad_lib.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/private_bad_lib.sol new file mode 100644 index 00000000..fd65036a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/private_bad_lib.sol @@ -0,0 +1,9 @@ +export {ok}; + +function ok() returns (word) { + return 1; +} + +function broken() returns (word) { + return true; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/private_bad_lib.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/private_bad_lib.solc deleted file mode 100644 index f7f1d072..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/private_bad_lib.solc +++ /dev/null @@ -1,9 +0,0 @@ -export {ok}; - -function ok() -> word { - return 1; -} - -function broken() -> word { - return true; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/private_bad_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/private_bad_main.sol new file mode 100644 index 00000000..7a32366f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/private_bad_main.sol @@ -0,0 +1,5 @@ +import private_bad_lib; + +function main() returns (word) { + return private_bad_lib.ok(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/private_bad_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/private_bad_main.solc deleted file mode 100644 index 79e69d91..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/private_bad_main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import private_bad_lib; - -function main() -> word { - return private_bad_lib.ok(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/private_helper_a.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/private_helper_a.sol new file mode 100644 index 00000000..e002f7f4 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/private_helper_a.sol @@ -0,0 +1,9 @@ +export { foo }; + +function helper(x: word) returns (word) { + return x; +} + +function foo(x: word) returns (word) { + return helper(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/private_helper_a.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/private_helper_a.solc deleted file mode 100644 index 9bfb5216..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/private_helper_a.solc +++ /dev/null @@ -1,9 +0,0 @@ -export { foo }; - -function helper(x: word) -> word { - return x; -} - -function foo(x: word) -> word { - return helper(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/private_helper_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/private_helper_main.sol new file mode 100644 index 00000000..050cf3fb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/private_helper_main.sol @@ -0,0 +1,5 @@ +import private_helper_a; + +function main(x: word) returns (word) { + return private_helper_a.foo(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/private_helper_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/private_helper_main.solc deleted file mode 100644 index b6eee902..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/private_helper_main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import private_helper_a; - -function main(x: word) -> word { - return private_helper_a.foo(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_expr_hidden_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_expr_hidden_fail.sol new file mode 100644 index 00000000..47cf70c6 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_expr_hidden_fail.sol @@ -0,0 +1,5 @@ +import reexport_ctor_mid; + +function main() returns (reexport_ctor_mid.Token) { + return reexport_ctor_mid.Token.Err(1); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_expr_hidden_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_expr_hidden_fail.solc deleted file mode 100644 index 77bf9dd4..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_expr_hidden_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import reexport_ctor_mid; - -function main() -> reexport_ctor_mid.Token { - return reexport_ctor_mid.Token.Err(1); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_expr_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_expr_ok.sol new file mode 100644 index 00000000..16fcaaaf --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_expr_ok.sol @@ -0,0 +1,5 @@ +import reexport_ctor_mid; + +function main() returns (reexport_ctor_mid.Token) { + return reexport_ctor_mid.Token.Ok(1); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_expr_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_expr_ok.solc deleted file mode 100644 index 774ffd23..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_expr_ok.solc +++ /dev/null @@ -1,5 +0,0 @@ -import reexport_ctor_mid; - -function main() -> reexport_ctor_mid.Token { - return reexport_ctor_mid.Token.Ok(1); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_hidden_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_hidden_fail.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_hidden_fail.solc rename to crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_hidden_fail.sol diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_mid.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_mid.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_mid.solc rename to crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_mid.sol diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_pattern.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_pattern.sol new file mode 100644 index 00000000..8bbc2bbc --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_pattern.sol @@ -0,0 +1,12 @@ +import reexport_ctor_mid; + +function main() returns (word) { + match (reexport_ctor_mid.mkErr(1)) { +case reexport_ctor_mid.Token.Ok(v) { +return v; +} +default { +return 0; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_pattern.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_pattern.solc deleted file mode 100644 index 3e474451..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_ctor_pattern.solc +++ /dev/null @@ -1,8 +0,0 @@ -import reexport_ctor_mid; - -function main() -> word { - match reexport_ctor_mid.mkErr(1) { - | reexport_ctor_mid.Token.Ok(v) => return v; - | _ => return 0; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items/pkg/api.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items/pkg/api.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items/pkg/api.solc rename to crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items/pkg/api.sol diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items/pkg/util.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items/pkg/util.sol new file mode 100644 index 00000000..2aad9851 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items/pkg/util.sol @@ -0,0 +1,21 @@ +export {Wrap(*), unwrap, Unbox}; + +enum Wrap { Mk(word) } + +trait Unbox { + function unbox(x: self) returns (word) ; +} + +impl Unbox { + function unbox(x: Wrap) returns (word) { + match (x) { +case Wrap.Mk(w) { +return w; +} +} + } +} + +function unwrap(x: Wrap) returns (word) { + return Unbox.unbox(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items/pkg/util.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items/pkg/util.solc deleted file mode 100644 index af8af05d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items/pkg/util.solc +++ /dev/null @@ -1,19 +0,0 @@ -export {Wrap(*), unwrap, Unbox}; - -data Wrap = Mk(word); - -forall self . class self:Unbox { - function unbox(x:self) -> word; -} - -instance Wrap:Unbox { - function unbox(x:Wrap) -> word { - match x { - | Wrap.Mk(w) => return w; - } - } -} - -function unwrap(x:Wrap) -> word { - return Unbox.unbox(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items_main.sol new file mode 100644 index 00000000..c8bc5b42 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items_main.sol @@ -0,0 +1,5 @@ +import {unwrap, Wrap} from reexport_items.pkg.api; + +function main() returns (word) { + return unwrap(Wrap.Mk(1)); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items_main.solc deleted file mode 100644 index 54befbc3..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_items_main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import reexport_items.pkg.api.{unwrap, Wrap}; - -function main() -> word { - return unwrap(Wrap.Mk(1)); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/api.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/api.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/api.solc rename to crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/api.sol diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/api_alias.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/api_alias.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/api_alias.solc rename to crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/api_alias.sol diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/util.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/util.sol new file mode 100644 index 00000000..2aad9851 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/util.sol @@ -0,0 +1,21 @@ +export {Wrap(*), unwrap, Unbox}; + +enum Wrap { Mk(word) } + +trait Unbox { + function unbox(x: self) returns (word) ; +} + +impl Unbox { + function unbox(x: Wrap) returns (word) { + match (x) { +case Wrap.Mk(w) { +return w; +} +} + } +} + +function unwrap(x: Wrap) returns (word) { + return Unbox.unbox(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/util.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/util.solc deleted file mode 100644 index af8af05d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module/pkg/util.solc +++ /dev/null @@ -1,19 +0,0 @@ -export {Wrap(*), unwrap, Unbox}; - -data Wrap = Mk(word); - -forall self . class self:Unbox { - function unbox(x:self) -> word; -} - -instance Wrap:Unbox { - function unbox(x:Wrap) -> word { - match x { - | Wrap.Mk(w) => return w; - } - } -} - -function unwrap(x:Wrap) -> word { - return Unbox.unbox(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module_alias_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module_alias_main.sol new file mode 100644 index 00000000..648e2e78 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module_alias_main.sol @@ -0,0 +1,5 @@ +import reexport_module.pkg.api_alias; + +function main() returns (word) { + return api_alias.Utils.unwrap(api_alias.Utils.Wrap.Mk(1)); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module_alias_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module_alias_main.solc deleted file mode 100644 index 55900f24..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module_alias_main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import reexport_module.pkg.api_alias; - -function main() -> word { - return api_alias.Utils.unwrap(api_alias.Utils.Wrap.Mk(1)); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module_main.sol new file mode 100644 index 00000000..96c1e546 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module_main.sol @@ -0,0 +1,5 @@ +import reexport_module.pkg.api; + +function main() returns (word) { + return api.util.unwrap(api.util.Wrap.Mk(1)); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module_main.solc deleted file mode 100644 index 396eccaa..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_module_main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import reexport_module.pkg.api; - -function main() -> word { - return api.util.unwrap(api.util.Wrap.Mk(1)); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_alias_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_alias_main.sol new file mode 100644 index 00000000..970c881d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_alias_main.sol @@ -0,0 +1,5 @@ +import {keep_} from reexport_select_alias_wrapper; + +function main(x: word) returns (word) { + return keep_(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_alias_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_alias_main.solc deleted file mode 100644 index b2754ef1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_alias_main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import reexport_select_alias_wrapper.{keep_}; - -function main(x: word) -> word { - return keep_(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_alias_wrapper.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_alias_wrapper.sol new file mode 100644 index 00000000..fac4bf0f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_alias_wrapper.sol @@ -0,0 +1,3 @@ +import {keep as keep_} from selectlib; + +export { keep_ }; diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_alias_wrapper.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_alias_wrapper.solc deleted file mode 100644 index c3b5dc9d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_alias_wrapper.solc +++ /dev/null @@ -1,3 +0,0 @@ -import selectlib.{keep as keep_}; - -export { keep_ }; diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_base.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_base.sol new file mode 100644 index 00000000..310722d5 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_base.sol @@ -0,0 +1,5 @@ +export { mstore }; + +function mstore(x: word) returns (word) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_base.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_base.solc deleted file mode 100644 index 3bafbc63..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_base.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { mstore }; - -function mstore(x: word) -> word { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_main.sol new file mode 100644 index 00000000..9a033021 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_main.sol @@ -0,0 +1,5 @@ +import {mstore} from reexport_select_wrapper; + +function main(x: word) returns (word) { + return mstore(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_main.solc deleted file mode 100644 index 48476677..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import reexport_select_wrapper.{mstore}; - -function main(x: word) -> word { - return mstore(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_wrapper.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_wrapper.sol new file mode 100644 index 00000000..097fb4bd --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_wrapper.sol @@ -0,0 +1,3 @@ +import {mstore} from reexport_select_base; + +export { mstore }; diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_wrapper.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_wrapper.solc deleted file mode 100644 index a6ea114e..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/reexport_select_wrapper.solc +++ /dev/null @@ -1,3 +0,0 @@ -import reexport_select_base.{mstore}; - -export { mstore }; diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/main.sol new file mode 100644 index 00000000..e525eaf1 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/main.sol @@ -0,0 +1,5 @@ +import lib.rootcheck.provider; + +function main() returns (word) { + return provider.value(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/main.solc deleted file mode 100644 index be1d2653..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import lib.rootcheck.provider; - -function main() -> word { - return provider.value(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/provider.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/provider.sol new file mode 100644 index 00000000..c49577b8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/provider.sol @@ -0,0 +1,5 @@ +export {value}; + +function value() returns (word) { + return 11; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/provider.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/provider.solc deleted file mode 100644 index a269930d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/provider.solc +++ /dev/null @@ -1,5 +0,0 @@ -export {value}; - -function value() -> word { - return 11; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/relative_and_lib_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/relative_and_lib_main.sol new file mode 100644 index 00000000..f224a512 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/relative_and_lib_main.sol @@ -0,0 +1,7 @@ +import provider; +import * as RootProvider from lib.rootcheck.provider; + +function main() returns (word) { + let rootValue: word = RootProvider.value(); + return provider.value(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/relative_and_lib_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/relative_and_lib_main.solc deleted file mode 100644 index 37e0223b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/nested/relative_and_lib_main.solc +++ /dev/null @@ -1,7 +0,0 @@ -import provider; -import lib.rootcheck.provider as RootProvider; - -function main() -> word { - let rootValue: word = RootProvider.value(); - return provider.value(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/provider.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/provider.sol new file mode 100644 index 00000000..aaa99865 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/provider.sol @@ -0,0 +1,5 @@ +export {value}; + +function value() returns (word) { + return 7; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/provider.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/provider.solc deleted file mode 100644 index 46073d4d..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/rootcheck/provider.solc +++ /dev/null @@ -1,5 +0,0 @@ -export {value}; - -function value() -> word { - return 7; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_alias_item_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_alias_item_ok.sol new file mode 100644 index 00000000..38ab250e --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_alias_item_ok.sol @@ -0,0 +1,5 @@ +import {keep as keep_} from selectlib; + +function main(x: word) returns (word) { + return keep_(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_alias_item_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_alias_item_ok.solc deleted file mode 100644 index 7a264705..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_alias_item_ok.solc +++ /dev/null @@ -1,5 +0,0 @@ -import selectlib.{keep as keep_}; - -function main(x: word) -> word { - return keep_(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_alias_multi_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_alias_multi_ok.sol new file mode 100644 index 00000000..33de2fb7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_alias_multi_ok.sol @@ -0,0 +1,5 @@ +import {keep as keep_, drop as drop_} from selectlib; + +function main(x: word) returns (word) { + return drop_(keep_(x)); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_alias_multi_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_alias_multi_ok.solc deleted file mode 100644 index f3bc28b4..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_alias_multi_ok.solc +++ /dev/null @@ -1,5 +0,0 @@ -import selectlib.{keep as keep_, drop as drop_}; - -function main(x: word) -> word { - return drop_(keep_(x)); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_dup_item.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_dup_item.sol new file mode 100644 index 00000000..e87f1e4d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_dup_item.sol @@ -0,0 +1,5 @@ +import {keep, keep} from selectlib; + +function main(x: word) returns (word) { + return keep(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_dup_item.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_dup_item.solc deleted file mode 100644 index c61b1654..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_dup_item.solc +++ /dev/null @@ -1,5 +0,0 @@ -import selectlib.{keep, keep}; - -function main(x: word) -> word { - return keep(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_fail.sol new file mode 100644 index 00000000..a718ca67 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_fail.sol @@ -0,0 +1,5 @@ +import {keep} from selectlib; + +function main(x: word) returns (word) { + return drop(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_fail.solc deleted file mode 100644 index 02a1c4b7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import selectlib.{keep}; - -function main(x: word) -> word { - return drop(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_hiding_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_hiding_fail.sol new file mode 100644 index 00000000..53f22966 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_hiding_fail.sol @@ -0,0 +1,5 @@ +import {keep, drop} from selectlib hiding {drop}; + +function main(x: word) returns (word) { + return drop(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_hiding_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_hiding_fail.solc deleted file mode 100644 index 901f7186..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_hiding_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import selectlib.{keep, drop} hiding {drop}; - -function main(x: word) -> word { - return drop(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_hiding_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_hiding_ok.sol new file mode 100644 index 00000000..bfc57cfb --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_hiding_ok.sol @@ -0,0 +1,5 @@ +import {keep, drop} from selectlib hiding {drop}; + +function main(x: word) returns (word) { + return keep(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_hiding_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_hiding_ok.solc deleted file mode 100644 index 806aa125..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_hiding_ok.solc +++ /dev/null @@ -1,5 +0,0 @@ -import selectlib.{keep, drop} hiding {drop}; - -function main(x: word) -> word { - return keep(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_ok.sol new file mode 100644 index 00000000..13152352 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_ok.sol @@ -0,0 +1,5 @@ +import {keep} from selectlib; + +function main(x: word) returns (word) { + return keep(x); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_ok.solc deleted file mode 100644 index 8d0ae999..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_ok.solc +++ /dev/null @@ -1,5 +0,0 @@ -import selectlib.{keep}; - -function main(x: word) -> word { - return keep(x); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_shadow_local.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_shadow_local.sol new file mode 100644 index 00000000..276a1664 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_shadow_local.sol @@ -0,0 +1,9 @@ +import {keep} from selectlib; + +function keep() returns (word) { + return 10; +} + +function main() returns (word) { + return keep(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_shadow_local.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_shadow_local.solc deleted file mode 100644 index 4c854e33..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_shadow_local.solc +++ /dev/null @@ -1,9 +0,0 @@ -import selectlib.{keep}; - -function keep() -> word { - return 10; -} - -function main() -> word { - return keep(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_shadow_param_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_shadow_param_ok.sol new file mode 100644 index 00000000..e12cbea2 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_shadow_param_ok.sol @@ -0,0 +1,5 @@ +import {keep} from selectlib; + +function main(keep: word) returns (word) { + return keep; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_shadow_param_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_shadow_param_ok.solc deleted file mode 100644 index d619f498..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_shadow_param_ok.solc +++ /dev/null @@ -1,5 +0,0 @@ -import selectlib.{keep}; - -function main(keep: word) -> word { - return keep; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_unknown.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_unknown.sol new file mode 100644 index 00000000..677074db --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_unknown.sol @@ -0,0 +1,5 @@ +import {missing} from selectlib; + +function main(x: word) returns (word) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_unknown.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/select_unknown.solc deleted file mode 100644 index c4ed6b15..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/select_unknown.solc +++ /dev/null @@ -1,5 +0,0 @@ -import selectlib.{missing}; - -function main(x: word) -> word { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/selective_unqualified_fun_ok.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/selective_unqualified_fun_ok.sol new file mode 100644 index 00000000..eb490d13 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/selective_unqualified_fun_ok.sol @@ -0,0 +1,5 @@ +import {base} from foo; + +function main() returns (word) { + return base(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/selective_unqualified_fun_ok.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/selective_unqualified_fun_ok.solc deleted file mode 100644 index f3b631bc..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/selective_unqualified_fun_ok.solc +++ /dev/null @@ -1,5 +0,0 @@ -import foo.{base}; - -function main() -> word { - return base(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/selectlib.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/selectlib.sol new file mode 100644 index 00000000..0ef4bed9 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/selectlib.sol @@ -0,0 +1,9 @@ +export { keep, drop }; + +function keep(x: word) returns (word) { + return x; +} + +function drop(x: word) returns (word) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/selectlib.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/selectlib.solc deleted file mode 100644 index 60fe6f7c..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/selectlib.solc +++ /dev/null @@ -1,9 +0,0 @@ -export { keep, drop }; - -function keep(x: word) -> word { - return x; -} - -function drop(x: word) -> word { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/selfcycle.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/selfcycle.sol new file mode 100644 index 00000000..69b8fc0f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/selfcycle.sol @@ -0,0 +1,5 @@ +import selfcycle; + +function main() returns (word) { + return 0; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/selfcycle.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/selfcycle.solc deleted file mode 100644 index 99aff9cc..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/selfcycle.solc +++ /dev/null @@ -1,5 +0,0 @@ -import selfcycle; - -function main() -> word { - return 0; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/strict_open_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/strict_open_fail.sol new file mode 100644 index 00000000..18bfb93d --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/strict_open_fail.sol @@ -0,0 +1,5 @@ +import booldef; + +function bad(b: Bool) returns (Bool) { + return not(b); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/strict_open_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/strict_open_fail.solc deleted file mode 100644 index 6561a3a1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/strict_open_fail.solc +++ /dev/null @@ -1,5 +0,0 @@ -import booldef; - -function bad(b: Bool) -> Bool { - return not(b); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/symlink_identity_fail.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/symlink_identity_fail.sol new file mode 100644 index 00000000..eb2f260b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/symlink_identity_fail.sol @@ -0,0 +1,6 @@ +import * as Vendor from vendor.math.api; +import * as Mirror from mirror.api; + +function bad(x: Vendor.T) returns (Mirror.T) { + return x; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/symlink_identity_fail.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/symlink_identity_fail.solc deleted file mode 100644 index 56338bb2..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/symlink_identity_fail.solc +++ /dev/null @@ -1,6 +0,0 @@ -import vendor.math.api as Vendor; -import mirror.api as Mirror; - -function bad(x: Vendor.T) -> Mirror.T { - return x; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/symlink_impl/api.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/symlink_impl/api.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/ok/test/imports/symlink_impl/api.solc rename to crates/parser/tests/fixtures/corpus/ok/test/imports/symlink_impl/api.sol diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_base.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_base.sol new file mode 100644 index 00000000..7f973e9a --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_base.sol @@ -0,0 +1,5 @@ +export { g }; + +function g() returns (word) { + return 1; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_base.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_base.solc deleted file mode 100644 index 53690077..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_base.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { g }; - -function g() -> word { - return 1; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_main_module.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_main_module.sol new file mode 100644 index 00000000..fbe4ea8b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_main_module.sol @@ -0,0 +1,5 @@ +import * as M from transitive_dep_mid; + +function main() returns (word) { + return M.f(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_main_module.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_main_module.solc deleted file mode 100644 index 76736927..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_main_module.solc +++ /dev/null @@ -1,5 +0,0 @@ -import transitive_dep_mid as M; - -function main() -> word { - return M.f(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_main_select.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_main_select.sol new file mode 100644 index 00000000..cb0010df --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_main_select.sol @@ -0,0 +1,5 @@ +import {f} from transitive_dep_mid; + +function main() returns (word) { + return f(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_main_select.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_main_select.solc deleted file mode 100644 index 87deb02b..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_main_select.solc +++ /dev/null @@ -1,5 +0,0 @@ -import transitive_dep_mid.{f}; - -function main() -> word { - return f(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_mid.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_mid.sol new file mode 100644 index 00000000..8b1f863f --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_mid.sol @@ -0,0 +1,7 @@ +import {g} from transitive_dep_base; + +export { f }; + +function f() returns (word) { + return g(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_mid.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_mid.solc deleted file mode 100644 index 1164443e..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/transitive_dep_mid.solc +++ /dev/null @@ -1,7 +0,0 @@ -import transitive_dep_base.{g}; - -export { f }; - -function f() -> word { - return g(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_a.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_a.sol new file mode 100644 index 00000000..49de8fea --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_a.sol @@ -0,0 +1,7 @@ +export { T(A), mk }; + +enum T { A } + +function mk() returns (T) { + return T.A; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_a.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_a.solc deleted file mode 100644 index cf8fc305..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_a.solc +++ /dev/null @@ -1,7 +0,0 @@ -export { T(A), mk }; - -data T = A; - -function mk() -> T { - return T.A; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_b.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_b.sol new file mode 100644 index 00000000..26cdca89 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_b.sol @@ -0,0 +1,7 @@ +export { T(B), mk }; + +enum T { B } + +function mk() returns (T) { + return T.B; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_b.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_b.solc deleted file mode 100644 index 9a4857a1..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_b.solc +++ /dev/null @@ -1,7 +0,0 @@ -export { T(B), mk }; - -data T = B; - -function mk() -> T { - return T.B; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_main.sol new file mode 100644 index 00000000..68aaa5ce --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_main.sol @@ -0,0 +1,8 @@ +import type_collision_a; +import type_collision_b; + +function main() returns (word) { + let x = type_collision_a.mk(); + let y = type_collision_b.mk(); + return 0; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_main.solc deleted file mode 100644 index c190d2c7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/type_collision_main.solc +++ /dev/null @@ -1,8 +0,0 @@ -import type_collision_a; -import type_collision_b; - -function main() -> word { - let x = type_collision_a.mk(); - let y = type_collision_b.mk(); - return 0; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/unordered_imports_lib.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/unordered_imports_lib.sol new file mode 100644 index 00000000..c12a5f7b --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/unordered_imports_lib.sol @@ -0,0 +1,14 @@ +export { Bool(*), not }; + +enum Bool { True, False } + +function not(b: Bool) returns (Bool) { + match (b) { +case Bool.True { +return Bool.False; +} +case Bool.False { +return Bool.True; +} +} +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/unordered_imports_lib.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/unordered_imports_lib.solc deleted file mode 100644 index 0b596b69..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/unordered_imports_lib.solc +++ /dev/null @@ -1,10 +0,0 @@ -export { Bool(*), not }; - -data Bool = True | False; - -function not(b : Bool) -> Bool { - match b { - | Bool.True => return Bool.False; - | Bool.False => return Bool.True; - } -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/unordered_imports_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/unordered_imports_main.sol new file mode 100644 index 00000000..0a4594f3 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/unordered_imports_main.sol @@ -0,0 +1,9 @@ +export { main }; + +pragma no-patterson-condition; + +function main(b: unordered_imports_lib.Bool) returns (unordered_imports_lib.Bool) { + return unordered_imports_lib.not(b); +} + +import unordered_imports_lib; diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/unordered_imports_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/unordered_imports_main.solc deleted file mode 100644 index d9b608fb..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/unordered_imports_main.solc +++ /dev/null @@ -1,9 +0,0 @@ -export { main }; - -pragma no-patterson-condition; - -function main(b : unordered_imports_lib.Bool) -> unordered_imports_lib.Bool { - return unordered_imports_lib.not(b); -} - -import unordered_imports_lib; diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/vendor/math/api.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/vendor/math/api.sol similarity index 100% rename from crates/parser/tests/fixtures/corpus/ok/test/imports/vendor/math/api.solc rename to crates/parser/tests/fixtures/corpus/ok/test/imports/vendor/math/api.sol diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/vendor/math/helper.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/vendor/math/helper.sol new file mode 100644 index 00000000..f03838e7 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/vendor/math/helper.sol @@ -0,0 +1,3 @@ +export {T}; + +enum T { T } diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/vendor/math/helper.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/vendor/math/helper.solc deleted file mode 100644 index d2d38ce7..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/vendor/math/helper.solc +++ /dev/null @@ -1,3 +0,0 @@ -export {T}; - -data T = T; diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/wildA.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/wildA.sol new file mode 100644 index 00000000..eaf999ff --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/wildA.sol @@ -0,0 +1,6 @@ +import wildB; +export {wildB.*, *}; + +function fromWildA() returns (word) { + return wildB.fromWildB(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/wildA.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/wildA.solc deleted file mode 100644 index e9cc4661..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/wildA.solc +++ /dev/null @@ -1,6 +0,0 @@ -import wildB; -export {wildB.*, *}; - -function fromWildA() -> word { - return wildB.fromWildB(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/wildB.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/wildB.sol new file mode 100644 index 00000000..1f4903f8 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/wildB.sol @@ -0,0 +1,6 @@ +import wildA; +export {wildA.*, *}; + +function fromWildB() returns (word) { + return 3; +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/wildB.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/wildB.solc deleted file mode 100644 index 2b4ed1c0..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/wildB.solc +++ /dev/null @@ -1,6 +0,0 @@ -import wildA; -export {wildA.*, *}; - -function fromWildB() -> word { - return 3; -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/wild_main.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/wild_main.sol new file mode 100644 index 00000000..70f90751 --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/wild_main.sol @@ -0,0 +1,5 @@ +import wildA; + +function main() returns (word) { + return wildA.fromWildB(); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/wild_main.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/wild_main.solc deleted file mode 100644 index 11bf7e23..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/wild_main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import wildA; - -function main() -> word { - return wildA.fromWildB(); -} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/wrapper_shadow_success.sol b/crates/parser/tests/fixtures/corpus/ok/test/imports/wrapper_shadow_success.sol new file mode 100644 index 00000000..809ec8ad --- /dev/null +++ b/crates/parser/tests/fixtures/corpus/ok/test/imports/wrapper_shadow_success.sol @@ -0,0 +1,9 @@ +import booldef; + +function not(x: word) returns (word) { + return x; +} + +function main(b: booldef.Bool) returns (booldef.Bool) { + return booldef.not(b); +} diff --git a/crates/parser/tests/fixtures/corpus/ok/test/imports/wrapper_shadow_success.solc b/crates/parser/tests/fixtures/corpus/ok/test/imports/wrapper_shadow_success.solc deleted file mode 100644 index 2e516ded..00000000 --- a/crates/parser/tests/fixtures/corpus/ok/test/imports/wrapper_shadow_success.solc +++ /dev/null @@ -1,9 +0,0 @@ -import booldef; - -function not(x: word) -> word { - return x; -} - -function main(b: booldef.Bool) -> booldef.Bool { - return booldef.not(b); -} diff --git a/crates/parser/tests/fixtures/corpus/reference-frontend.tsv b/crates/parser/tests/fixtures/corpus/reference-frontend.tsv index c7f76e56..6a41a651 100644 --- a/crates/parser/tests/fixtures/corpus/reference-frontend.tsv +++ b/crates/parser/tests/fixtures/corpus/reference-frontend.tsv @@ -1,500 +1,500 @@ path status code -Convertible.solc fail SC0001 -cases/Ackermann.solc pass - -cases/Add1.solc pass - -cases/BadInstance.solc fail SC0102 -cases/BoolNot.solc pass - -cases/Compose.solc pass - -cases/Compose3.solc pass - -cases/CondExp.solc pass - -cases/DupFun.solc fail SC0108 -cases/DuplicateFun.solc pass - -cases/EitherModule.solc pass - -cases/Enum.solc fail SC0108 -cases/Eq.solc fail SC0102 -cases/EqQual.solc pass - -cases/EvenOdd.solc pass - -cases/Filter.solc fail SC0102 -cases/Foo.solc pass - -cases/GetSet.solc fail SC0103 -cases/GoodInstance.solc fail SC0102 -cases/Id.solc pass - -cases/IncompleteInstDef.solc fail SC0299 -cases/Invokable.solc fail SC0102 -cases/KindTest.solc fail SC0103 -cases/ListModule.solc pass - -cases/Logic.solc pass - -cases/MatchCall.solc pass - -cases/Memory1.solc pass - -cases/Memory2.solc pass - -cases/Mutuals.solc pass - -cases/NegPair.solc pass - -cases/Option.solc pass - -cases/Pair.solc pass - -cases/PairMatch1.solc fail SC0209 -cases/PairMatch2.solc fail SC0209 -cases/Peano.solc pass - -cases/PeanoMatch.solc pass - -cases/Ref.solc fail SC0102 -cases/RefDeref.solc pass - -cases/SillyReturn.solc fail SC0220 -cases/SimpleInvoke.solc fail SC0102 -cases/SimpleLambda.solc pass - -cases/SingleFun.solc pass - -cases/StructMembers.solc fail SC0001 -cases/Uncurry.solc pass - -cases/abigeneric.solc pass - -cases/add-moritz.solc fail SC0102 -cases/another-subst.solc pass - -cases/app.solc pass - -cases/array-elem-no-storagecopy.solc fail SC0223 -cases/array-push-no-canstore.solc fail SC0223 -cases/array.solc pass - -cases/arraylit-bad-target.solc fail SC0201 -cases/arraylit-mixed-types.solc fail SC0201 -cases/asm-assign-no-return.solc fail SC0220 -cases/asm-assign-non-word.solc fail SC0001 -cases/asm-let-bool-lit.solc pass - -cases/asm-let-no-return.solc fail SC0220 -cases/asm-let-uninit.solc pass - -cases/asm-match-tuple-read.solc pass - -cases/asm-match-tuple-write-read.solc pass - -cases/assembly.solc pass - -cases/bal.solc pass - -cases/bar.solc pass - -cases/bitwise.solc pass - -cases/bool-elim.solc pass - -cases/bound-merge-case.solc pass - -cases/bound-minimal.solc fail SC0103 -cases/bound-only-test.solc fail SC0103 -cases/bound-with-pragma.solc pass - -cases/bug-call-expected-nontail-return.solc pass - -cases/bug-import-default-inst-shadow.solc pass - -cases/bug-rep-name-capture.solc pass - -cases/bug-spec-generic-let.solc fail - -cases/catch-all.solc pass - -cases/catenable-err.solc fail SC0001 -cases/class-context.solc pass - -cases/class-return-type-miss.solc fail SC0221 -cases/class-type-name-collision.solc fail SC0108 -cases/clone-deriving.solc pass - -cases/closure-capture-only.solc pass - -cases/closure-free-bound-test.solc pass - -cases/closure-free-var-local.solc pass - -cases/closure-free-var-std.solc pass - -cases/closure-free-var.solc pass - -cases/closure.solc pass - -cases/comp.solc fail SC0220 -cases/comparisons.solc pass - -cases/complexproxy.solc fail SC0102 -cases/compose0.solc pass - -cases/compose_desugared.solc fail SC0209 -cases/compound-operators.solc pass - -cases/const-array.solc fail SC0221 -cases/const.solc pass - -cases/constrained-instance-context.solc pass - -cases/constrained-instance.solc pass - -cases/constructor-weak-args.solc pass - -cases/contract-local-derive.solc pass - -cases/contract-local-type-escapes-fail.solc fail SC0103 -cases/contract-local-type-same-name.solc pass - -cases/copytomem.solc pass - -cases/cyclical-defs-inferred.solc pass - -cases/cyclical-defs.solc pass - -cases/default-inst.solc fail SC0102 -cases/default-instance-missing.solc fail SC0102 -cases/default-instance-weak.solc fail SC0102 -cases/derive-custom-hash.solc pass - -cases/derive-eq-action.solc pass - -cases/derive-eq-enum.solc pass - -cases/derive-eq-pair.solc pass - -cases/derive-generic-excluded.solc pass - -cases/derive-generic-sum.solc pass - -cases/derive-universe-instances.solc pass - -cases/derive-unknown-class.solc fail SC0105 -cases/deriving-empty-type.solc pass - -cases/dispatch.solc fail SC0103 -cases/dot-expression-assignment-context.solc pass - -cases/dot-expression-call-arg-context.solc pass - -cases/dot-expression-constructor.solc pass - -cases/dot-expression-match-return.solc pass - -cases/dot-expression-nested-context.solc pass - -cases/dot-expression-no-context-fail.solc fail SC0224 -cases/dot-expression-unknown-fail.solc fail SC0224 -cases/dot-pattern-constructor.solc pass - -cases/dot-pattern-nested-constructor.solc pass - -cases/dot-primitive-constructor.solc pass - -cases/duplicated-contract-name.solc fail SC0108 -cases/duplicated-type-name.solc fail SC0108 -cases/empty-asm.solc pass - -cases/encoder.solc pass - -cases/encoder1.solc pass - -cases/fallback-with-args.solc fail SC0001 -cases/fallback-with-return.solc fail SC0001 -cases/false-redundant-warning.solc pass - -cases/field-access.solc fail SC0201 -cases/field-helper-cxt-collision.solc pass - -cases/field-name-error.solc pass - -cases/foo-class.solc pass - -cases/for-body-shadow.solc pass - -cases/for-break.solc pass - -cases/for-continue.solc pass - -cases/for-empty-init.solc pass - -cases/for-init-shadow.solc pass - -cases/for-inner-block.solc pass - -cases/for-let-post.solc fail SC0001 -cases/for-let.solc pass - -cases/for-loop.solc pass - -cases/for-multi-init.solc pass - -cases/for-multi-post.solc pass - -cases/fresh-pat-arg-synonym.solc pass - -cases/fresh-pat-arg.solc pass - -cases/fresh-variable-shadowing.solc pass - -cases/generic-manual-no-pragma.solc fail - -cases/generic-product-no-pragma.solc fail - -cases/generic-sum-no-pragma.solc fail - -cases/if-examples.solc pass - -cases/import-std.solc pass - -cases/inc-closure.solc pass - -cases/index-example.solc fail SC0108 -cases/instance-closure-error-invalid-member.solc fail SC0201 -cases/instance-closure-error.solc pass - -cases/instance-context-wrong-kind.solc fail SC0299 -cases/instance-synonym-int.solc pass - -cases/instance-synonym.solc pass - -cases/instance-wrong-sig.solc fail SC0299 -cases/invokable-issue.solc pass - -cases/ixa.solc pass - -cases/join.solc pass - -cases/joinErr.solc fail SC0201 -cases/listeq.solc fail SC0220 -cases/listid.solc pass - -cases/ltimp.solc pass - -cases/ltproxy.solc pass - -cases/mainproxy.solc fail SC0102 -cases/match-bitwise.solc pass - -cases/match-compiler-undef-asm.solc fail SC0299 -cases/match-yul.solc pass - -cases/memory.solc pass - -cases/missing-instance.solc fail SC0223 -cases/mod-example.solc pass - -cases/modifier.solc pass - -cases/modulo.solc pass - -cases/monomorphic-require.solc pass - -cases/morefun.solc pass - -cases/mptc-both-templates.solc pass - -cases/mptc-chain-phantom.solc pass - -cases/mptc-guard-extras-concrete.solc pass - -cases/mptc-multi-instance.solc pass - -cases/mptc-nop-mainty-free.solc pass - -cases/mptc-partial-instance.solc pass - -cases/mptc-template-a-only.solc pass - -cases/mptc-template-b-only.solc pass - -cases/multi-stmt-var-leaf.solc pass - -cases/nano-desugared.solc fail SC0108 -cases/nid.solc pass - -cases/noclosure.solc pass - -cases/noconstr.solc fail SC0102 -cases/notif.solc pass - -cases/option2.solc pass - -cases/overlap-synonym-detected.solc fail SC0299 -cases/overlap-synonym-missed-order.solc fail SC0299 -cases/overlap-synonym-missed-two-synonyms.solc fail SC0299 -cases/overlapping-heads.solc fail SC0299 -cases/pair-bug.solc pass - -cases/pars.solc pass - -cases/patterson-bug.solc fail SC0108 -cases/payable-toplevel-function.solc fail SC0001 -cases/phantom-type-return-con.solc pass - -cases/polymatch-error.solc pass - -cases/polymorphic-require.solc pass - -cases/pragma_merge_base.solc pass - -cases/pragma_merge_fail_coverage.solc fail SC0299 -cases/pragma_merge_fail_patterson.solc fail SC0105 -cases/pragma_merge_import.solc fail SC0105 -cases/pragma_merge_verify.solc fail SC0105 -cases/pragma_test_patterson.solc pass - -cases/proxy-desugar.solc pass - -cases/proxy.solc pass - -cases/proxy1.solc fail SC0223 -cases/public-constructor.solc fail SC0001 -cases/public-fallback.solc fail SC0001 -cases/public-top-level-function.solc fail SC0001 -cases/rec.solc pass - -cases/redundant-match.solc pass - -cases/reference-encoding-good.solc pass - -cases/reference-encoding-good1.solc pass - -cases/reference-encoding.solc fail SC0102 -cases/reference-test.solc fail SC0102 -cases/reference.solc fail SC0001 -cases/references-daniel.solc fail SC0102 -cases/require-annotation-contract-method.solc fail SC0220 -cases/require-annotation-missing-both.solc fail SC0220 -cases/require-annotation-missing-param.solc fail SC0220 -cases/require-annotation-missing-return.solc fail SC0220 -cases/require-annotation-mutual.solc fail SC0220 -cases/return-fun-adder.solc pass - -cases/return-fun-bad-arity.solc fail SC0201 -cases/return-fun-bad-param.solc fail SC0201 -cases/return-fun-bad-return.solc fail SC0201 -cases/return-fun-bad-sig.solc fail SC0201 -cases/return-fun-const.solc pass - -cases/return-fun-eq.solc pass - -cases/return-fun-instance.solc pass - -cases/return-fun-not-fun.solc fail SC0201 -cases/same-name-constructor-qualifier.solc pass - -cases/signature.solc fail SC0001 -cases/simpleDiscount.solc pass - -cases/simpleIfExpr.solc fail SC0220 -cases/simpleIfStmt.solc fail SC0220 -cases/simpleid.solc pass - -cases/single-lambda.solc pass - -cases/skolem-let.solc fail SC0209 -cases/snds.solc pass - -cases/spec-fail-ungrounded.solc pass - -cases/storage-adt-mapping-field-fail.solc fail SC0201 -cases/storage-adt-recursive-fail.solc pass - -cases/storage-adt-recursive-ok.solc pass - -cases/strange-unbound.solc pass - -cases/string-const.solc fail SC0220 -cases/subject-index.solc fail SC0108 -cases/subject-reduction.solc fail SC0108 -cases/subsumption-constraint.solc fail SC0223 -cases/subsumption-test.solc fail SC0209 -cases/sum-match-default.solc pass - -cases/super-class-cycle-fail.solc fail SC0223 -cases/super-class-cycle.solc pass - -cases/super-class-num.solc pass - -cases/super-class-recursive-arg.solc fail SC0223 -cases/super-class.solc pass - -cases/synonym-arity-mismatch.solc fail SC0299 -cases/synonym-basic.solc pass - -cases/synonym-in-function.solc pass - -cases/synonym-long-cycle.solc fail SC0299 -cases/synonym-nested.solc pass - -cases/synonym-param.solc pass - -cases/synonym-recursive.solc fail SC0299 -cases/synonym-self-recursive.solc fail SC0299 -cases/tabled-answer-reuse.solc fail SC0299 -cases/tabled-cycle-fail.solc timeout - -cases/tabled-default-instance.solc pass - -cases/tabled-given-order.solc pass - -cases/tabled-left-recursive-fail.solc timeout - -cases/tabled-mutual-chain.solc fail SC0299 -cases/tabled-residual-given.solc pass - -cases/td.solc pass - -cases/tiamat.solc pass - -cases/toplevel-constructor.solc fail SC0001 -cases/toplevel-fallback.solc fail SC0001 -cases/tuple-trick.solc pass - -cases/tuva.solc pass - -cases/tyexp.solc pass - -cases/type-synonym-arg.solc pass - -cases/typedef.solc pass - -cases/ufcs-no-conflict.solc pass - -cases/uintdesugared.solc pass - -cases/unbound-instance-var.solc fail SC0103 -cases/unconstrained-instance.solc fail SC0001 -cases/undefined.solc pass - -cases/unit.solc pass - -cases/user-op-lambda.solc fail SC0001 -cases/vartyped.solc fail SC0220 -cases/weird-error-foo.solc fail SC0220 -cases/weirdfoo.solc fail SC0001 -cases/word-match-default.solc pass - -cases/word-match.solc pass - -cases/xref.solc fail SC0221 -cases/yul-asm-break-continue-leave.solc pass - -cases/yul-asm-for-body.solc pass - -cases/yul-asm-switch-body.solc pass - -cases/yul-deposit-example.solc pass - -cases/yul-for.solc pass - -cases/yul-function-typing.solc pass - -cases/yul-multi-return-arity-fail.solc fail SC0299 -cases/yul-multi-return.solc pass - -cases/yul-return.solc pass - -comptime/CondExpr.solc pass - -comptime/CondStmt.solc pass - -comptime/OneOne.solc fail SC0001 -comptime/OneTwo.solc pass - -comptime/Plus.solc pass - -comptime/Size.solc pass - -comptime/StdSize.solc pass - -comptime/comptime_syntax.solc pass - -comptime/counter.solc pass - -comptime/ct_asm_mem.solc pass - -comptime/ct_asm_ret.solc pass - -comptime/ct_chain_ok.solc pass - -comptime/ct_let_ok.solc pass - -comptime/ct_let_runtime.solc pass - -comptime/ct_overloaded_bad.solc pass - -comptime/ct_overloaded_ok.solc pass - -comptime/ct_param_ok.solc pass - -comptime/ct_param_poly_runtime.solc fail SC0299 -comptime/ct_param_runtime.solc fail - -comptime/ct_runtime_arg.solc pass - -comptime/erc7201-lit.solc pass - -comptime/fib.solc pass - -comptime/fib2.solc pass - -comptime/fib3.solc pass - -comptime/fromInt.solc fail SC0103 -comptime/fromInt2.solc fail SC0103 -comptime/fromInt3.solc fail SC0103 -comptime/fromLit.solc fail SC0103 -comptime/int-untyped-let.solc pass - -comptime/integer-basic.solc pass - -comptime/integer-fib.solc pass - -comptime/integer-from-integer.solc pass - -comptime/integer-lit-class.solc pass - -comptime/integer-lit-cond.solc pass - -comptime/integer-lit-pat.solc pass - -comptime/integer-lit-poly.solc pass - -comptime/integer-lit-safe.solc pass - -comptime/integer-lit-word-site.solc pass - -comptime/integer-lit.solc pass - -comptime/match_labels.solc pass - -comptime/string-concat-mem.solc pass - -comptime/string-lit-dedup.solc pass - -comptime/string-lit-keccak.solc pass - -comptime/string-lit-len.solc pass - -comptime/string-lit-mem.solc pass - -comptime/string-lit-ops.solc pass - -comptime/string-mem-runtime-fail.solc fail SC0201 -comptime/string-param-erasure.solc pass - -comptime/string-user-instance.solc pass - -comptime/uint256-lit.solc pass - -dispatch/Revert.solc pass - -dispatch/abi_address_array.solc pass - -dispatch/abi_array_sum.solc pass - -dispatch/abi_batch_adt.solc pass - -dispatch/abi_bytes_array.solc pass - -dispatch/abi_dyn_sum.solc pass - -dispatch/abi_dyn_sum_return.solc pass - -dispatch/abi_encode_adt.solc pass - -dispatch/abi_encode_types.solc pass - -dispatch/abi_sum_roundtrip.solc pass - -dispatch/array_copy.solc pass - -dispatch/array_nested.solc pass - -dispatch/array_ops.solc pass - -dispatch/array_string.solc pass - -dispatch/arraylit.solc pass - -dispatch/asm_break_continue_leave.solc pass - -dispatch/assembly.solc pass - -dispatch/basic.solc pass - -dispatch/concat.solc pass - -dispatch/counter.solc pass - -dispatch/deposit.solc pass - -dispatch/derive_contract_local.solc pass - -dispatch/derive_ord.solc pass - -dispatch/ecrecover.solc pass - -dispatch/eip712.solc pass - -dispatch/empty.solc pass - -dispatch/empty_no_constructor.solc pass - -dispatch/fallback.solc pass - -dispatch/fib.solc fail SC0103 -dispatch/forloops.solc pass - -dispatch/generic_product.solc pass - -dispatch/generic_sum.solc pass - -dispatch/hashes.solc pass - -dispatch/memory.solc pass - -dispatch/miniERC20.solc pass - -dispatch/neg.solc pass - -dispatch/nonpayable_ctor.solc pass - -dispatch/ownable.solc pass - -dispatch/p256verify.solc pass - -dispatch/payable.solc pass - -dispatch/payable_ctor.solc pass - -dispatch/slices.solc pass - -dispatch/specialise_sum_of_product.solc pass - -dispatch/storage.solc pass - -dispatch/storage_adt_abi.solc pass - -dispatch/storage_adt_bool.solc pass - -dispatch/storage_adt_enum.solc pass - -dispatch/storage_adt_field.solc pass - -dispatch/storage_adt_mapping.solc pass - -dispatch/storage_array.solc pass - -dispatch/storage_dynamic_field.solc pass - -dispatch/stringid.solc pass - -dispatch/stringlit.solc pass - -dispatch/sum_wide_product.solc pass - -dispatch/ufcs_array.solc pass - -dispatch/weth9.solc pass - -invokable/021nid.solc fail SC0220 -invokable/022nid-invoke.solc fail SC0001 -invokable/024lamid.solc fail SC0220 -invokable/025lamid-invoke.solc fail SC0001 -invokable/026capture.solc fail SC0001 -invokable/027retfun.solc fail SC0001 -invokable/028modifier.solc fail SC0001 -invokable/031enum.solc fail SC0001 -opcodes/all-shapes.solc pass - -opcodes/terminators.solc pass - -pragmas/bound.solc fail SC0001 -pragmas/coverage.solc pass - -pragmas/patterson.solc pass - -spec/00answer.solc pass - -spec/010answer.solc fail SC0220 -spec/011id.solc fail SC0220 -spec/012nid.solc fail SC0220 -spec/013comp.solc fail SC0220 -spec/01id.solc pass - -spec/021not.solc pass - -spec/022add.solc pass - -spec/024arith.solc pass - -spec/027sstore.solc fail SC0220 -spec/02nid.solc pass - -spec/031maybe.solc pass - -spec/032simplejoin.solc pass - -spec/033join.solc pass - -spec/034cojoin.solc pass - -spec/035padding.solc pass - -spec/036wildcard.solc pass - -spec/037dwarves.solc pass - -spec/038food0.solc pass - -spec/039food.solc pass - -spec/041pair.solc pass - -spec/042triple.solc pass - -spec/043fstsnd.solc pass - -spec/047rgb.solc pass - -spec/048rgb2.solc pass - -spec/049rgb3.solc pass - -spec/051expreturn.solc fail SC0103 -spec/051negBool.solc fail SC0102 -spec/052negPair.solc fail SC0001 -spec/052return.solc fail SC0103 -spec/053return.solc fail SC0103 -spec/06comp.solc pass - -spec/09not.solc pass - -spec/101struct1Field.solc fail SC0102 -spec/102uintField.solc fail SC0102 -spec/103struct3Fields.solc fail SC0102 -spec/105nestedStruct.solc fail SC0102 -spec/10negBool.solc pass - -spec/111storageStruct.solc fail SC0102 -spec/112ContractStorage.solc fail SC0105 -spec/113counter.solc fail SC0105 -spec/11negPair.solc pass - -spec/120basicCounter.solc pass - -spec/121counter.solc pass - -spec/122counters.solc pass - -spec/123stackAndStorage.solc pass - -spec/126nanoerc20.solc pass - -spec/127microerc20.solc pass - -spec/128minierc20.solc pass - -spec/129arraystorage.solc pass - -spec/130arrayfield.solc pass - -spec/131constructor.solc fail SC0220 -spec/131localindex.solc pass - -spec/132nestedarray.solc pass - -spec/133arraystring.solc pass - -spec/135aliaspush.solc pass - -spec/135cons3.solc fail SC0108 -spec/136arraylit.solc pass - -spec/137arraylitstorage.solc pass - -spec/903badassign.solc pass - -spec/939badfood.solc pass - -spec/SimpleField.solc pass - -spec/StorageLib.solc fail SC0220 -spec/attic/051expreturn.solc fail SC0001 -spec/attic/052return.solc fail SC0001 -spec/attic/053return.solc fail SC0001 +Convertible.sol fail SC0001 +cases/Ackermann.sol pass - +cases/Add1.sol pass - +cases/BadInstance.sol fail SC0102 +cases/BoolNot.sol pass - +cases/Compose.sol pass - +cases/Compose3.sol pass - +cases/CondExp.sol pass - +cases/DupFun.sol fail SC0108 +cases/DuplicateFun.sol pass - +cases/EitherModule.sol pass - +cases/Enum.sol fail SC0108 +cases/Eq.sol fail SC0102 +cases/EqQual.sol pass - +cases/EvenOdd.sol pass - +cases/Filter.sol fail SC0102 +cases/Foo.sol pass - +cases/GetSet.sol fail SC0103 +cases/GoodInstance.sol fail SC0102 +cases/Id.sol pass - +cases/IncompleteInstDef.sol fail SC0299 +cases/Invokable.sol fail SC0102 +cases/KindTest.sol fail SC0103 +cases/ListModule.sol pass - +cases/Logic.sol pass - +cases/MatchCall.sol pass - +cases/Memory1.sol pass - +cases/Memory2.sol pass - +cases/Mutuals.sol pass - +cases/NegPair.sol pass - +cases/Option.sol pass - +cases/Pair.sol pass - +cases/PairMatch1.sol fail SC0209 +cases/PairMatch2.sol fail SC0209 +cases/Peano.sol pass - +cases/PeanoMatch.sol pass - +cases/Ref.sol fail SC0102 +cases/RefDeref.sol pass - +cases/SillyReturn.sol fail SC0220 +cases/SimpleInvoke.sol fail SC0102 +cases/SimpleLambda.sol pass - +cases/SingleFun.sol pass - +cases/StructMembers.sol fail SC0001 +cases/Uncurry.sol pass - +cases/abigeneric.sol pass - +cases/add-moritz.sol fail SC0102 +cases/another-subst.sol pass - +cases/app.sol pass - +cases/array-elem-no-storagecopy.sol fail SC0223 +cases/array-push-no-canstore.sol fail SC0223 +cases/array.sol pass - +cases/arraylit-bad-target.sol fail SC0201 +cases/arraylit-mixed-types.sol fail SC0201 +cases/asm-assign-no-return.sol fail SC0220 +cases/asm-assign-non-word.sol fail SC0001 +cases/asm-let-bool-lit.sol pass - +cases/asm-let-no-return.sol fail SC0220 +cases/asm-let-uninit.sol pass - +cases/asm-match-tuple-read.sol pass - +cases/asm-match-tuple-write-read.sol pass - +cases/assembly.sol pass - +cases/bal.sol pass - +cases/bar.sol pass - +cases/bitwise.sol pass - +cases/bool-elim.sol pass - +cases/bound-merge-case.sol pass - +cases/bound-minimal.sol fail SC0103 +cases/bound-only-test.sol fail SC0103 +cases/bound-with-pragma.sol pass - +cases/bug-call-expected-nontail-return.sol pass - +cases/bug-import-default-inst-shadow.sol pass - +cases/bug-rep-name-capture.sol pass - +cases/bug-spec-generic-let.sol fail - +cases/catch-all.sol pass - +cases/catenable-err.sol fail SC0001 +cases/class-context.sol pass - +cases/class-return-type-miss.sol fail SC0221 +cases/class-type-name-collision.sol fail SC0108 +cases/clone-deriving.sol pass - +cases/closure-capture-only.sol pass - +cases/closure-free-bound-test.sol pass - +cases/closure-free-var-local.sol pass - +cases/closure-free-var-std.sol pass - +cases/closure-free-var.sol pass - +cases/closure.sol pass - +cases/comp.sol fail SC0220 +cases/comparisons.sol pass - +cases/complexproxy.sol fail SC0102 +cases/compose0.sol pass - +cases/compose_desugared.sol fail SC0209 +cases/compound-operators.sol pass - +cases/const-array.sol fail SC0221 +cases/const.sol pass - +cases/constrained-instance-context.sol pass - +cases/constrained-instance.sol pass - +cases/constructor-weak-args.sol pass - +cases/contract-local-derive.sol pass - +cases/contract-local-type-escapes-fail.sol fail SC0103 +cases/contract-local-type-same-name.sol pass - +cases/copytomem.sol pass - +cases/cyclical-defs-inferred.sol pass - +cases/cyclical-defs.sol pass - +cases/default-inst.sol fail SC0102 +cases/default-instance-missing.sol fail SC0102 +cases/default-instance-weak.sol fail SC0102 +cases/derive-custom-hash.sol pass - +cases/derive-eq-action.sol pass - +cases/derive-eq-enum.sol pass - +cases/derive-eq-pair.sol pass - +cases/derive-generic-excluded.sol pass - +cases/derive-generic-sum.sol pass - +cases/derive-universe-instances.sol pass - +cases/derive-unknown-class.sol fail SC0105 +cases/deriving-empty-type.sol pass - +cases/dispatch.sol fail SC0103 +cases/dot-expression-assignment-context.sol pass - +cases/dot-expression-call-arg-context.sol pass - +cases/dot-expression-constructor.sol pass - +cases/dot-expression-match-return.sol pass - +cases/dot-expression-nested-context.sol pass - +cases/dot-expression-no-context-fail.sol fail SC0224 +cases/dot-expression-unknown-fail.sol fail SC0224 +cases/dot-pattern-constructor.sol pass - +cases/dot-pattern-nested-constructor.sol pass - +cases/dot-primitive-constructor.sol pass - +cases/duplicated-contract-name.sol fail SC0108 +cases/duplicated-type-name.sol fail SC0108 +cases/empty-asm.sol pass - +cases/encoder.sol pass - +cases/encoder1.sol pass - +cases/fallback-with-args.sol fail SC0001 +cases/fallback-with-return.sol fail SC0001 +cases/false-redundant-warning.sol pass - +cases/field-access.sol fail SC0201 +cases/field-helper-cxt-collision.sol pass - +cases/field-name-error.sol pass - +cases/foo-class.sol pass - +cases/for-body-shadow.sol pass - +cases/for-break.sol pass - +cases/for-continue.sol pass - +cases/for-empty-init.sol pass - +cases/for-init-shadow.sol pass - +cases/for-inner-block.sol pass - +cases/for-let-post.sol fail SC0001 +cases/for-let.sol pass - +cases/for-loop.sol pass - +cases/for-multi-init.sol pass - +cases/for-multi-post.sol pass - +cases/fresh-pat-arg-synonym.sol pass - +cases/fresh-pat-arg.sol pass - +cases/fresh-variable-shadowing.sol pass - +cases/generic-manual-no-pragma.sol fail - +cases/generic-product-no-pragma.sol fail - +cases/generic-sum-no-pragma.sol fail - +cases/if-examples.sol pass - +cases/import-std.sol pass - +cases/inc-closure.sol pass - +cases/index-example.sol fail SC0108 +cases/instance-closure-error-invalid-member.sol fail SC0201 +cases/instance-closure-error.sol pass - +cases/instance-context-wrong-kind.sol fail SC0299 +cases/instance-synonym-int.sol pass - +cases/instance-synonym.sol pass - +cases/instance-wrong-sig.sol fail SC0299 +cases/invokable-issue.sol pass - +cases/ixa.sol pass - +cases/join.sol pass - +cases/joinErr.sol fail SC0201 +cases/listeq.sol fail SC0220 +cases/listid.sol pass - +cases/ltimp.sol pass - +cases/ltproxy.sol pass - +cases/mainproxy.sol fail SC0102 +cases/match-bitwise.sol pass - +cases/match-compiler-undef-asm.sol fail SC0299 +cases/match-yul.sol pass - +cases/memory.sol pass - +cases/missing-instance.sol fail SC0223 +cases/mod-example.sol pass - +cases/modifier.sol pass - +cases/modulo.sol pass - +cases/monomorphic-require.sol pass - +cases/morefun.sol pass - +cases/mptc-both-templates.sol pass - +cases/mptc-chain-phantom.sol pass - +cases/mptc-guard-extras-concrete.sol pass - +cases/mptc-multi-instance.sol pass - +cases/mptc-nop-mainty-free.sol pass - +cases/mptc-partial-instance.sol pass - +cases/mptc-template-a-only.sol pass - +cases/mptc-template-b-only.sol pass - +cases/multi-stmt-var-leaf.sol pass - +cases/nano-desugared.sol fail SC0108 +cases/nid.sol pass - +cases/noclosure.sol pass - +cases/noconstr.sol fail SC0102 +cases/notif.sol pass - +cases/option2.sol pass - +cases/overlap-synonym-detected.sol fail SC0299 +cases/overlap-synonym-missed-order.sol fail SC0299 +cases/overlap-synonym-missed-two-synonyms.sol fail SC0299 +cases/overlapping-heads.sol fail SC0299 +cases/pair-bug.sol pass - +cases/pars.sol pass - +cases/patterson-bug.sol fail SC0108 +cases/payable-toplevel-function.sol fail SC0001 +cases/phantom-type-return-con.sol pass - +cases/polymatch-error.sol pass - +cases/polymorphic-require.sol pass - +cases/pragma_merge_base.sol pass - +cases/pragma_merge_fail_coverage.sol fail SC0299 +cases/pragma_merge_fail_patterson.sol fail SC0105 +cases/pragma_merge_import.sol fail SC0105 +cases/pragma_merge_verify.sol fail SC0105 +cases/pragma_test_patterson.sol pass - +cases/proxy-desugar.sol pass - +cases/proxy.sol pass - +cases/proxy1.sol fail SC0223 +cases/public-constructor.sol fail SC0001 +cases/public-fallback.sol fail SC0001 +cases/public-top-level-function.sol fail SC0001 +cases/rec.sol pass - +cases/redundant-match.sol pass - +cases/reference-encoding-good.sol pass - +cases/reference-encoding-good1.sol pass - +cases/reference-encoding.sol fail SC0102 +cases/reference-test.sol fail SC0102 +cases/reference.sol fail SC0001 +cases/references-daniel.sol fail SC0102 +cases/require-annotation-contract-method.sol fail SC0220 +cases/require-annotation-missing-both.sol fail SC0220 +cases/require-annotation-missing-param.sol fail SC0220 +cases/require-annotation-missing-return.sol fail SC0220 +cases/require-annotation-mutual.sol fail SC0220 +cases/return-fun-adder.sol pass - +cases/return-fun-bad-arity.sol fail SC0201 +cases/return-fun-bad-param.sol fail SC0201 +cases/return-fun-bad-return.sol fail SC0201 +cases/return-fun-bad-sig.sol fail SC0201 +cases/return-fun-const.sol pass - +cases/return-fun-eq.sol pass - +cases/return-fun-instance.sol pass - +cases/return-fun-not-fun.sol fail SC0201 +cases/same-name-constructor-qualifier.sol pass - +cases/signature.sol fail SC0001 +cases/simpleDiscount.sol pass - +cases/simpleIfExpr.sol fail SC0220 +cases/simpleIfStmt.sol fail SC0220 +cases/simpleid.sol pass - +cases/single-lambda.sol pass - +cases/skolem-let.sol fail SC0209 +cases/snds.sol pass - +cases/spec-fail-ungrounded.sol pass - +cases/storage-adt-mapping-field-fail.sol fail SC0201 +cases/storage-adt-recursive-fail.sol pass - +cases/storage-adt-recursive-ok.sol pass - +cases/strange-unbound.sol pass - +cases/string-const.sol fail SC0220 +cases/subject-index.sol fail SC0108 +cases/subject-reduction.sol fail SC0108 +cases/subsumption-constraint.sol fail SC0223 +cases/subsumption-test.sol fail SC0209 +cases/sum-match-default.sol pass - +cases/super-class-cycle-fail.sol fail SC0223 +cases/super-class-cycle.sol pass - +cases/super-class-num.sol pass - +cases/super-class-recursive-arg.sol fail SC0223 +cases/super-class.sol pass - +cases/synonym-arity-mismatch.sol fail SC0299 +cases/synonym-basic.sol pass - +cases/synonym-in-function.sol pass - +cases/synonym-long-cycle.sol fail SC0299 +cases/synonym-nested.sol pass - +cases/synonym-param.sol pass - +cases/synonym-recursive.sol fail SC0299 +cases/synonym-self-recursive.sol fail SC0299 +cases/tabled-answer-reuse.sol fail SC0299 +cases/tabled-cycle-fail.sol timeout - +cases/tabled-default-instance.sol pass - +cases/tabled-given-order.sol pass - +cases/tabled-left-recursive-fail.sol timeout - +cases/tabled-mutual-chain.sol fail SC0299 +cases/tabled-residual-given.sol pass - +cases/td.sol pass - +cases/tiamat.sol pass - +cases/toplevel-constructor.sol fail SC0001 +cases/toplevel-fallback.sol fail SC0001 +cases/tuple-trick.sol pass - +cases/tuva.sol pass - +cases/tyexp.sol pass - +cases/type-synonym-arg.sol pass - +cases/typedef.sol pass - +cases/ufcs-no-conflict.sol pass - +cases/uintdesugared.sol pass - +cases/unbound-instance-var.sol fail SC0103 +cases/unconstrained-instance.sol fail SC0001 +cases/undefined.sol pass - +cases/unit.sol pass - +cases/user-op-lambda.sol fail SC0001 +cases/vartyped.sol fail SC0220 +cases/weird-error-foo.sol fail SC0220 +cases/weirdfoo.sol fail SC0001 +cases/word-match-default.sol pass - +cases/word-match.sol pass - +cases/xref.sol fail SC0221 +cases/yul-asm-break-continue-leave.sol pass - +cases/yul-asm-for-body.sol pass - +cases/yul-asm-switch-body.sol pass - +cases/yul-deposit-example.sol pass - +cases/yul-for.sol pass - +cases/yul-function-typing.sol pass - +cases/yul-multi-return-arity-fail.sol fail SC0299 +cases/yul-multi-return.sol pass - +cases/yul-return.sol pass - +comptime/CondExpr.sol pass - +comptime/CondStmt.sol pass - +comptime/OneOne.sol fail SC0001 +comptime/OneTwo.sol pass - +comptime/Plus.sol pass - +comptime/Size.sol pass - +comptime/StdSize.sol pass - +comptime/comptime_syntax.sol pass - +comptime/counter.sol pass - +comptime/ct_asm_mem.sol pass - +comptime/ct_asm_ret.sol pass - +comptime/ct_chain_ok.sol pass - +comptime/ct_let_ok.sol pass - +comptime/ct_let_runtime.sol pass - +comptime/ct_overloaded_bad.sol pass - +comptime/ct_overloaded_ok.sol pass - +comptime/ct_param_ok.sol pass - +comptime/ct_param_poly_runtime.sol fail SC0299 +comptime/ct_param_runtime.sol fail - +comptime/ct_runtime_arg.sol pass - +comptime/erc7201-lit.sol pass - +comptime/fib.sol pass - +comptime/fib2.sol pass - +comptime/fib3.sol pass - +comptime/fromInt.sol fail SC0103 +comptime/fromInt2.sol fail SC0103 +comptime/fromInt3.sol fail SC0103 +comptime/fromLit.sol fail SC0103 +comptime/int-untyped-let.sol pass - +comptime/integer-basic.sol pass - +comptime/integer-fib.sol pass - +comptime/integer-from-integer.sol pass - +comptime/integer-lit-class.sol pass - +comptime/integer-lit-cond.sol pass - +comptime/integer-lit-pat.sol pass - +comptime/integer-lit-poly.sol pass - +comptime/integer-lit-safe.sol pass - +comptime/integer-lit-word-site.sol pass - +comptime/integer-lit.sol pass - +comptime/match_labels.sol pass - +comptime/string-concat-mem.sol pass - +comptime/string-lit-dedup.sol pass - +comptime/string-lit-keccak.sol pass - +comptime/string-lit-len.sol pass - +comptime/string-lit-mem.sol pass - +comptime/string-lit-ops.sol pass - +comptime/string-mem-runtime-fail.sol fail SC0201 +comptime/string-param-erasure.sol pass - +comptime/string-user-instance.sol pass - +comptime/uint256-lit.sol pass - +dispatch/Revert.sol pass - +dispatch/abi_address_array.sol pass - +dispatch/abi_array_sum.sol pass - +dispatch/abi_batch_adt.sol pass - +dispatch/abi_bytes_array.sol pass - +dispatch/abi_dyn_sum.sol pass - +dispatch/abi_dyn_sum_return.sol pass - +dispatch/abi_encode_adt.sol pass - +dispatch/abi_encode_types.sol pass - +dispatch/abi_sum_roundtrip.sol pass - +dispatch/array_copy.sol pass - +dispatch/array_nested.sol pass - +dispatch/array_ops.sol pass - +dispatch/array_string.sol pass - +dispatch/arraylit.sol pass - +dispatch/asm_break_continue_leave.sol pass - +dispatch/assembly.sol pass - +dispatch/basic.sol pass - +dispatch/concat.sol pass - +dispatch/counter.sol pass - +dispatch/deposit.sol pass - +dispatch/derive_contract_local.sol pass - +dispatch/derive_ord.sol pass - +dispatch/ecrecover.sol pass - +dispatch/eip712.sol pass - +dispatch/empty.sol pass - +dispatch/empty_no_constructor.sol pass - +dispatch/fallback.sol pass - +dispatch/fib.sol fail SC0103 +dispatch/forloops.sol pass - +dispatch/generic_product.sol pass - +dispatch/generic_sum.sol pass - +dispatch/hashes.sol pass - +dispatch/memory.sol pass - +dispatch/miniERC20.sol pass - +dispatch/neg.sol pass - +dispatch/nonpayable_ctor.sol pass - +dispatch/ownable.sol pass - +dispatch/p256verify.sol pass - +dispatch/payable.sol pass - +dispatch/payable_ctor.sol pass - +dispatch/slices.sol pass - +dispatch/specialise_sum_of_product.sol pass - +dispatch/storage.sol pass - +dispatch/storage_adt_abi.sol pass - +dispatch/storage_adt_bool.sol pass - +dispatch/storage_adt_enum.sol pass - +dispatch/storage_adt_field.sol pass - +dispatch/storage_adt_mapping.sol pass - +dispatch/storage_array.sol pass - +dispatch/storage_dynamic_field.sol pass - +dispatch/stringid.sol pass - +dispatch/stringlit.sol pass - +dispatch/sum_wide_product.sol pass - +dispatch/ufcs_array.sol pass - +dispatch/weth9.sol pass - +invokable/021nid.sol fail SC0220 +invokable/022nid-invoke.sol fail SC0001 +invokable/024lamid.sol fail SC0220 +invokable/025lamid-invoke.sol fail SC0001 +invokable/026capture.sol fail SC0001 +invokable/027retfun.sol fail SC0001 +invokable/028modifier.sol fail SC0001 +invokable/031enum.sol fail SC0001 +opcodes/all-shapes.sol pass - +opcodes/terminators.sol pass - +pragmas/bound.sol fail SC0001 +pragmas/coverage.sol pass - +pragmas/patterson.sol pass - +spec/00answer.sol pass - +spec/010answer.sol fail SC0220 +spec/011id.sol fail SC0220 +spec/012nid.sol fail SC0220 +spec/013comp.sol fail SC0220 +spec/01id.sol pass - +spec/021not.sol pass - +spec/022add.sol pass - +spec/024arith.sol pass - +spec/027sstore.sol fail SC0220 +spec/02nid.sol pass - +spec/031maybe.sol pass - +spec/032simplejoin.sol pass - +spec/033join.sol pass - +spec/034cojoin.sol pass - +spec/035padding.sol pass - +spec/036wildcard.sol pass - +spec/037dwarves.sol pass - +spec/038food0.sol pass - +spec/039food.sol pass - +spec/041pair.sol pass - +spec/042triple.sol pass - +spec/043fstsnd.sol pass - +spec/047rgb.sol pass - +spec/048rgb2.sol pass - +spec/049rgb3.sol pass - +spec/051expreturn.sol fail SC0103 +spec/051negBool.sol fail SC0102 +spec/052negPair.sol fail SC0001 +spec/052return.sol fail SC0103 +spec/053return.sol fail SC0103 +spec/06comp.sol pass - +spec/09not.sol pass - +spec/101struct1Field.sol fail SC0102 +spec/102uintField.sol fail SC0102 +spec/103struct3Fields.sol fail SC0102 +spec/105nestedStruct.sol fail SC0102 +spec/10negBool.sol pass - +spec/111storageStruct.sol fail SC0102 +spec/112ContractStorage.sol fail SC0105 +spec/113counter.sol fail SC0105 +spec/11negPair.sol pass - +spec/120basicCounter.sol pass - +spec/121counter.sol pass - +spec/122counters.sol pass - +spec/123stackAndStorage.sol pass - +spec/126nanoerc20.sol pass - +spec/127microerc20.sol pass - +spec/128minierc20.sol pass - +spec/129arraystorage.sol pass - +spec/130arrayfield.sol pass - +spec/131constructor.sol fail SC0220 +spec/131localindex.sol pass - +spec/132nestedarray.sol pass - +spec/133arraystring.sol pass - +spec/135aliaspush.sol pass - +spec/135cons3.sol fail SC0108 +spec/136arraylit.sol pass - +spec/137arraylitstorage.sol pass - +spec/903badassign.sol pass - +spec/939badfood.sol pass - +spec/SimpleField.sol pass - +spec/StorageLib.sol fail SC0220 +spec/attic/051expreturn.sol fail SC0001 +spec/attic/052return.sol fail SC0001 +spec/attic/053return.sol fail SC0001 diff --git a/crates/parser/tests/fixtures/corpus/rust-accepted-reference-failures.tsv b/crates/parser/tests/fixtures/corpus/rust-accepted-reference-failures.tsv index c82dc360..fd62a5f5 100644 --- a/crates/parser/tests/fixtures/corpus/rust-accepted-reference-failures.tsv +++ b/crates/parser/tests/fixtures/corpus/rust-accepted-reference-failures.tsv @@ -1,7 +1,17 @@ # pathreason -cases/compose_desugared.solc SC0209 reference rejects the explicitly desugared closure because its inferred invoke implementation is not polymorphic enough; Rust accepts it -cases/for-let-post.solc SC0001 reference frontend rejects this for-loop let form while the Rust grammar accepts it -cases/super-class-recursive-arg.solc SC0223 reference legacy solver rejects this recursive superclass argument, while the reference tabled mode and Rust both accept it -cases/tabled-answer-reuse.solc SC0299 reference legacy solver reports an ambiguous inferred type, while the reference tabled mode and Rust both accept it -cases/tabled-mutual-chain.solc SC0299 reference legacy solver reports an ambiguous inferred type, while the reference tabled mode and Rust both accept it -comptime/ct_param_poly_runtime.solc SC0299 reference legacy frontend reports ambiguity; both tabled Haskell and Rust reject the runtime argument during their full specialization pipelines, while this frontend-only parity gate intentionally defers that check +cases/Enum.sol canonical trait parameters and method result types are explicit, eliminating the reference source's legacy implicit-binder failure +cases/Eq.sol canonical impl method parameter and result types are explicit, eliminating the reference source's legacy signature-inference failure +cases/Filter.sol canonical function and trait signatures are explicit; Rust accepts the resulting fully annotated higher-order program +cases/GoodInstance.sol canonical trait and impl signatures are explicit; Rust accepts the resulting fully annotated enum conversion program +cases/class-return-type-miss.sol canonical omitted results mean unit consistently in both the trait and impl member, so the legacy inferred-result mismatch no longer applies +cases/compose_desugared.sol SC0209 reference rejects the explicitly desugared closure because its inferred invoke implementation is not polymorphic enough; Rust accepts it +cases/for-let-post.sol SC0001 reference frontend rejects this for-loop let form while the Rust grammar accepts it +cases/signature.sol canonical trait parameters and function constraints make every binder explicit, eliminating the reference source's legacy binder failure +cases/super-class-recursive-arg.sol SC0223 reference legacy solver rejects this recursive superclass argument, while the reference tabled mode and Rust both accept it +cases/tabled-answer-reuse.sol SC0299 reference legacy solver reports an ambiguous inferred type, while the reference tabled mode and Rust both accept it +cases/tabled-mutual-chain.sol SC0299 reference legacy solver reports an ambiguous inferred type, while the reference tabled mode and Rust both accept it +comptime/ct_param_poly_runtime.sol SC0299 reference legacy frontend reports ambiguity; both tabled Haskell and Rust reject the runtime argument during their full specialization pipelines, while this frontend-only parity gate intentionally defers that check +comptime/OneOne.sol canonical function result annotations make the formerly inferred word-valued helpers explicit +spec/051negBool.sol canonical named-parameter and result annotations make the formerly inferred signatures explicit +spec/052negPair.sol canonical generics, named-parameter types, and result annotations make the formerly inferred signatures explicit +spec/131constructor.sol canonical public function signatures make the formerly inferred contract entry signatures explicit; Rust accepts the resulting constructor program diff --git a/crates/parser/tests/fixtures/corpus/rust-rejected-reference-passes.tsv b/crates/parser/tests/fixtures/corpus/rust-rejected-reference-passes.tsv index f62ce181..771680a0 100644 --- a/crates/parser/tests/fixtures/corpus/rust-rejected-reference-passes.tsv +++ b/crates/parser/tests/fixtures/corpus/rust-rejected-reference-passes.tsv @@ -1,92 +1,92 @@ # pathphasediagnostic-prefixreason -cases/Uncurry.solc typeck SC0206: non-callable value of type word legacy reference accepts invoking a word-typed parameter; Rust frontend deliberately requires a callable type -cases/contract-local-derive.solc typeck SC0101: undefined name: Contract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/contract-local-derive.solc typeck SC0101: undefined name: Fallback reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/contract-local-derive.solc typeck SC0101: undefined name: Method reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/contract-local-derive.solc typeck SC0101: undefined name: RunContract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/contract-local-derive.solc typeck SC0101: undefined name: fallback_default_implementation reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/contract-local-derive.solc typeck SC0103: undefined type constructor: NonPayable reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/contract-local-derive.solc typeck SC0105: undefined class: SigString reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/contract-local-type-same-name.solc typeck SC0101: undefined name: Contract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/contract-local-type-same-name.solc typeck SC0101: undefined name: Fallback reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/contract-local-type-same-name.solc typeck SC0101: undefined name: Method reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/contract-local-type-same-name.solc typeck SC0101: undefined name: RunContract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/contract-local-type-same-name.solc typeck SC0101: undefined name: fallback_default_implementation reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/contract-local-type-same-name.solc typeck SC0103: undefined type constructor: NonPayable reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/contract-local-type-same-name.solc typeck SC0105: undefined class: SigString reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/field-helper-cxt-collision.solc typeck SC0101: undefined name: Contract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/field-helper-cxt-collision.solc typeck SC0101: undefined name: Fallback reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/field-helper-cxt-collision.solc typeck SC0101: undefined name: Method reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/field-helper-cxt-collision.solc typeck SC0101: undefined name: RunContract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/field-helper-cxt-collision.solc typeck SC0101: undefined name: fallback_default_implementation reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/field-helper-cxt-collision.solc typeck SC0103: undefined type constructor: NonPayable reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/field-helper-cxt-collision.solc typeck SC0105: undefined class: SigString reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/ixa.solc typeck SC0221: invalid instance member signature for `size` reference accepts this legacy instance member with a narrowed array signature; Rust frontend enforces the declared class signature -cases/multi-stmt-var-leaf.solc typeck SC0236: contract runtime `main` must not take parameters reference accepts the legacy parameterized contract main form; Rust frontend reserves runtime main as a zero-argument entrypoint -cases/pair-bug.solc typeck SC0101: undefined name: Contract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/pair-bug.solc typeck SC0101: undefined name: Fallback reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/pair-bug.solc typeck SC0101: undefined name: RunContract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/pair-bug.solc typeck SC0101: undefined name: fallback_default_implementation reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/pair-bug.solc typeck SC0103: undefined type constructor: NonPayable reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/rec.solc typeck SC0206: non-callable value of type word legacy reference accepts invoking a word-typed parameter; Rust frontend deliberately requires a callable type -cases/storage-adt-recursive-fail.solc typeck SC0207: cannot satisfy class constraint: storage(IntList) : reference verdict used -g and left the constructor/storage obligation unreachable; the Rust full-frontend gate generates dispatch and correctly rejects recursive ADT storage through its CanStore or Assign obligation -cases/ufcs-no-conflict.solc typeck SC0101: undefined name: Contract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/ufcs-no-conflict.solc typeck SC0101: undefined name: Fallback reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/ufcs-no-conflict.solc typeck SC0101: undefined name: Method reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/ufcs-no-conflict.solc typeck SC0101: undefined name: RunContract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/ufcs-no-conflict.solc typeck SC0101: undefined name: fallback_default_implementation reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/ufcs-no-conflict.solc typeck SC0103: undefined type constructor: NonPayable reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -cases/ufcs-no-conflict.solc typeck SC0105: undefined class: SigString reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch -dispatch/storage_array.solc typeck SC0231: ABI output cannot be represented in the ABI: adt:DynArray(adt:address) (only memory(string) and memory(bytes) have canonical ABI evidence) reference verdict used -g and skipped ABI validation; Rust deliberately rejects the externally undescribed memory(DynArray(address)) result in its full-frontend gate -dispatch/ufcs_array.solc typeck SC0231: ABI output cannot be represented in the ABI: adt:DynArray(adt:address) (only memory(string) and memory(bytes) have canonical ABI evidence) reference verdict used -g and skipped ABI validation; Rust deliberately rejects the externally undescribed memory(DynArray(address)) result in its full-frontend gate -imports/alias_dup.solc frontend SC0116: duplicate import qualifier `M` intentional negative import fixture for duplicate aliases -imports/alias_hides_original_fail.solc frontend SC0101: undefined name: foo intentional negative import fixture proving an alias hides the original qualifier -imports/alias_unqualified_constr_fail.solc frontend SC0106: unqualified constructor: True intentional negative import fixture proving module imports do not open constructors -imports/alias_unqualified_fun_fail.solc frontend SC0101: undefined name: base intentional negative import fixture proving aliased module imports do not open terms -imports/alias_unqualified_type_fail.solc frontend SC0103: undefined type constructor: Bool intentional negative import fixture proving aliased module imports do not open types -imports/amb_main.solc frontend SC0120: ambiguous selected import `pick` in term namespace intentional negative import fixture for ambiguous selected imports -imports/boolalias_open_fail.solc frontend SC0101: undefined name: not intentional negative import fixture proving an aliased module import is not an open import -imports/boolalias_open_fail.solc frontend SC0103: undefined type constructor: Bool intentional negative import fixture proving an aliased module import is not an open import -imports/boolconselect_fail.solc frontend SC0106: unqualified constructor: True intentional negative import fixture proving a selected type import does not expose its constructors -imports/export_item_dup_fail.solc frontend SC0111: duplicate exported item name `pick` intentional negative import fixture for duplicate item re-exports -imports/export_module_dup_fail.solc frontend SC0112: duplicate exported module name `M` intentional negative import fixture for duplicate module re-exports -imports/external_lib_missing_fail.solc frontend SC0118: external library root is not configured: @missing intentional negative import fixture for an unconfigured external library -imports/external_lib_missing_fail.solc frontend unresolved-import: external_lib_missing_fail imports `@missing.math.api` intentional negative import fixture for an unconfigured external library -imports/external_lib_missing_fail.solc typeck SC0101: undefined name: Contract unresolved external import intentionally causes generated contract-helper cascades in this negative fixture -imports/external_lib_missing_fail.solc typeck SC0101: undefined name: Fallback unresolved external import intentionally causes generated contract-helper cascades in this negative fixture -imports/external_lib_missing_fail.solc typeck SC0101: undefined name: Proxy unresolved external import intentionally causes generated contract-helper cascades in this negative fixture -imports/external_lib_missing_fail.solc typeck SC0101: undefined name: RunContract unresolved external import intentionally causes generated contract-helper cascades in this negative fixture -imports/external_lib_missing_fail.solc typeck SC0101: undefined name: fallback_default_implementation unresolved external import intentionally causes generated contract-helper cascades in this negative fixture -imports/external_lib_missing_fail.solc typeck SC0103: undefined type constructor: NonPayable unresolved external import intentionally causes generated contract-helper cascades in this negative fixture -imports/external_lib_missing_fail.solc typeck SC0103: undefined type constructor: Proxy unresolved external import intentionally causes generated contract-helper cascades in this negative fixture -imports/glob_amb_main_fail.solc frontend SC0120: ambiguous selected import `shared` in term namespace intentional negative import fixture for colliding wildcard imports -imports/glob_import_hiding_unknown_fail.solc frontend SC0110: unknown import item `missing` intentional negative import fixture for hiding an unknown wildcard-imported name -imports/hidden_ctor_dot_fail.solc frontend SC0101: undefined name: Err intentional negative import fixture proving hidden constructors are unavailable to dot syntax -imports/hidden_ctor_expr_fail.solc frontend SC0101: undefined name: Err intentional negative import fixture proving hidden constructors are unavailable in expressions -imports/hidden_ctor_nonexhaustive_fail.solc typeck SC0223: pattern match on type with hidden constructors requires a wildcard arm: Token intentional negative import fixture for exhaustiveness with a partially visible data type -imports/hidden_ctor_pattern_fail.solc frontend SC0101: undefined name: Token.Err intentional negative import fixture proving hidden constructors are unavailable in patterns -imports/leak_b.solc frontend SC0101: undefined name: fromA intentional negative import fixture proving private imported terms do not leak through an intermediate module -imports/leak_main.solc frontend SC0101: undefined name: fromA intentional negative import fixture proving private imported terms do not leak through an intermediate module -imports/leak_main.solc frontend SC0101: undefined name: fromB intentional negative import fixture proving private imported terms do not leak through an intermediate module -imports/module_name_shadow.solc frontend SC0121: conflicting unqualified name `keep` intentional negative import fixture for a module qualifier colliding with a selected term -imports/module_unqualified_constr_fail.solc frontend SC0106: unqualified constructor: True intentional negative import fixture proving module imports do not open constructors -imports/module_unqualified_fun_fail.solc frontend SC0101: undefined name: base intentional negative import fixture proving module imports do not open terms -imports/module_unqualified_type_fail.solc frontend SC0103: undefined type constructor: Bool intentional negative import fixture proving module imports do not open types -imports/opaque_alias_leak_fail.solc frontend SC0103: undefined type constructor: T intentional negative import fixture proving opaque type aliases do not leak through imports -imports/opaque_alias_qualifier_leak_fail.solc frontend SC0103: undefined type constructor: Base.T intentional negative import fixture proving opaque type aliases do not leak through qualifiers -imports/opaque_select_direct_leak_fail.solc frontend SC0103: undefined type constructor: T intentional negative import fixture proving opaque type aliases cannot be selected through re-exports -imports/pragma_scope_main.solc typeck SC0212: Coverage condition fails for class: intentional negative import fixture proving a dependency pragma does not disable checks in its importer -imports/private_bad_lib.solc typeck SC0201: type mismatch: expected word, found bool intentional negative import fixture containing a type error in a private helper body -imports/private_bad_main.solc typeck SC0201: type mismatch: expected word, found bool intentional negative import fixture proving reachable private helper bodies are type-checked -imports/reexport_ctor_expr_hidden_fail.solc frontend SC0101: undefined name: Err intentional negative import fixture proving hidden constructors do not leak through re-exports -imports/reexport_ctor_hidden_fail.solc frontend SC0115: unknown re-exported constructor `Token.Err` intentional negative import fixture for explicitly re-exporting a hidden constructor -imports/select_dup_item.solc frontend SC0117: duplicate name `keep` in selective import intentional negative import fixture for duplicate names in one selective import -imports/select_fail.solc frontend SC0101: undefined name: drop intentional negative import fixture proving unselected terms remain unavailable -imports/select_hiding_fail.solc frontend SC0101: undefined name: drop intentional negative import fixture proving hidden selected terms remain unavailable -imports/select_shadow_local.solc frontend SC0108: duplicate declaration `keep` in term namespace intentional negative import fixture for a selected term colliding with a local declaration -imports/select_unknown.solc frontend SC0110: unknown import item `missing` intentional negative import fixture for an unknown selected item -imports/strict_open_fail.solc frontend SC0101: undefined name: not intentional negative import fixture proving a strict module import is not an open import -imports/strict_open_fail.solc frontend SC0103: undefined type constructor: Bool intentional negative import fixture proving a strict module import is not an open import -imports/symlink_identity_fail.solc typeck SC0201: type mismatch: expected Mirror.T, found T intentional negative import fixture proving equivalent source paths retain distinct module type identities -imports/symlink_impl/api.solc frontend SC0109: import helper: file not found auxiliary symlink fixture is materialized by the module-system test and is not independently complete in the checked-in corpus -imports/symlink_impl/api.solc frontend unresolved-import: failed to read auxiliary symlink fixture is materialized by the module-system test and is not independently complete in the checked-in corpus +cases/Uncurry.sol typeck SC0206: non-callable value of type word legacy reference accepts invoking a word-typed parameter; Rust frontend deliberately requires a callable type +cases/contract-local-derive.sol typeck SC0101: undefined name: Contract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/contract-local-derive.sol typeck SC0101: undefined name: Fallback reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/contract-local-derive.sol typeck SC0101: undefined name: Method reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/contract-local-derive.sol typeck SC0101: undefined name: RunContract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/contract-local-derive.sol typeck SC0101: undefined name: fallback_default_implementation reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/contract-local-derive.sol typeck SC0103: undefined type constructor: NonPayable reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/contract-local-derive.sol typeck SC0105: undefined trait: SigString reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/contract-local-type-same-name.sol typeck SC0101: undefined name: Contract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/contract-local-type-same-name.sol typeck SC0101: undefined name: Fallback reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/contract-local-type-same-name.sol typeck SC0101: undefined name: Method reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/contract-local-type-same-name.sol typeck SC0101: undefined name: RunContract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/contract-local-type-same-name.sol typeck SC0101: undefined name: fallback_default_implementation reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/contract-local-type-same-name.sol typeck SC0103: undefined type constructor: NonPayable reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/contract-local-type-same-name.sol typeck SC0105: undefined trait: SigString reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/field-helper-cxt-collision.sol typeck SC0101: undefined name: Contract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/field-helper-cxt-collision.sol typeck SC0101: undefined name: Fallback reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/field-helper-cxt-collision.sol typeck SC0101: undefined name: Method reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/field-helper-cxt-collision.sol typeck SC0101: undefined name: RunContract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/field-helper-cxt-collision.sol typeck SC0101: undefined name: fallback_default_implementation reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/field-helper-cxt-collision.sol typeck SC0103: undefined type constructor: NonPayable reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/field-helper-cxt-collision.sol typeck SC0105: undefined trait: SigString reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/ixa.sol typeck SC0221: invalid impl member signature for `size` reference accepts this legacy impl member with a narrowed array signature; Rust frontend enforces the declared trait signature +cases/multi-stmt-var-leaf.sol typeck SC0236: contract runtime `main` must not take parameters reference accepts the legacy parameterized contract main form; Rust frontend reserves runtime main as a zero-argument entrypoint +cases/pair-bug.sol typeck SC0101: undefined name: Contract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/pair-bug.sol typeck SC0101: undefined name: Fallback reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/pair-bug.sol typeck SC0101: undefined name: RunContract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/pair-bug.sol typeck SC0101: undefined name: fallback_default_implementation reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/pair-bug.sol typeck SC0103: undefined type constructor: NonPayable reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/rec.sol typeck SC0206: non-callable value of type word legacy reference accepts invoking a word-typed parameter; Rust frontend deliberately requires a callable type +cases/storage-adt-recursive-fail.sol typeck SC0207: cannot satisfy trait constraint: storage: reference verdict used -g and left the constructor/storage obligation unreachable; the Rust full-frontend gate generates dispatch and correctly rejects recursive ADT storage through its CanStore or Assign obligation +cases/ufcs-no-conflict.sol typeck SC0101: undefined name: Contract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/ufcs-no-conflict.sol typeck SC0101: undefined name: Fallback reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/ufcs-no-conflict.sol typeck SC0101: undefined name: Method reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/ufcs-no-conflict.sol typeck SC0101: undefined name: RunContract reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/ufcs-no-conflict.sol typeck SC0101: undefined name: fallback_default_implementation reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/ufcs-no-conflict.sol typeck SC0103: undefined type constructor: NonPayable reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +cases/ufcs-no-conflict.sol typeck SC0105: undefined trait: SigString reference verdict used -g and skipped generated dispatch; the Rust full-frontend gate generates it, but this fixture does not import std.dispatch +dispatch/storage_array.sol typeck SC0231: ABI output cannot be represented in the ABI: adt:DynArray (only memory and memory have canonical ABI evidence) reference verdict used -g and skipped ABI validation; Rust deliberately rejects the externally undescribed memory> result in its full-frontend gate +dispatch/ufcs_array.sol typeck SC0231: ABI output cannot be represented in the ABI: adt:DynArray (only memory and memory have canonical ABI evidence) reference verdict used -g and skipped ABI validation; Rust deliberately rejects the externally undescribed memory> result in its full-frontend gate +imports/alias_dup.sol frontend SC0116: duplicate import qualifier `M` intentional negative import fixture for duplicate aliases +imports/alias_hides_original_fail.sol frontend SC0101: undefined name: foo intentional negative import fixture proving an alias hides the original qualifier +imports/alias_unqualified_constr_fail.sol frontend SC0106: unqualified constructor: True intentional negative import fixture proving module imports do not open constructors +imports/alias_unqualified_fun_fail.sol frontend SC0101: undefined name: base intentional negative import fixture proving aliased module imports do not open terms +imports/alias_unqualified_type_fail.sol frontend SC0103: undefined type constructor: Bool intentional negative import fixture proving aliased module imports do not open types +imports/amb_main.sol frontend SC0120: ambiguous selected import `pick` in term namespace intentional negative import fixture for ambiguous selected imports +imports/boolalias_open_fail.sol frontend SC0101: undefined name: not intentional negative import fixture proving an aliased module import is not an open import +imports/boolalias_open_fail.sol frontend SC0103: undefined type constructor: Bool intentional negative import fixture proving an aliased module import is not an open import +imports/boolconselect_fail.sol frontend SC0106: unqualified constructor: True intentional negative import fixture proving a selected type import does not expose its constructors +imports/export_item_dup_fail.sol frontend SC0111: duplicate exported item name `pick` intentional negative import fixture for duplicate item re-exports +imports/export_module_dup_fail.sol frontend SC0112: duplicate exported module name `M` intentional negative import fixture for duplicate module re-exports +imports/external_lib_missing_fail.sol frontend SC0118: external library root is not configured: @missing intentional negative import fixture for an unconfigured external library +imports/external_lib_missing_fail.sol frontend unresolved-import: external_lib_missing_fail imports `@missing.math.api` intentional negative import fixture for an unconfigured external library +imports/external_lib_missing_fail.sol typeck SC0101: undefined name: Contract unresolved external import intentionally causes generated contract-helper cascades in this negative fixture +imports/external_lib_missing_fail.sol typeck SC0101: undefined name: Fallback unresolved external import intentionally causes generated contract-helper cascades in this negative fixture +imports/external_lib_missing_fail.sol typeck SC0101: undefined name: Proxy unresolved external import intentionally causes generated contract-helper cascades in this negative fixture +imports/external_lib_missing_fail.sol typeck SC0101: undefined name: RunContract unresolved external import intentionally causes generated contract-helper cascades in this negative fixture +imports/external_lib_missing_fail.sol typeck SC0101: undefined name: fallback_default_implementation unresolved external import intentionally causes generated contract-helper cascades in this negative fixture +imports/external_lib_missing_fail.sol typeck SC0103: undefined type constructor: NonPayable unresolved external import intentionally causes generated contract-helper cascades in this negative fixture +imports/external_lib_missing_fail.sol typeck SC0103: undefined type constructor: Proxy unresolved external import intentionally causes generated contract-helper cascades in this negative fixture +imports/glob_amb_main_fail.sol frontend SC0120: ambiguous selected import `shared` in term namespace intentional negative import fixture for colliding wildcard imports +imports/glob_import_hiding_unknown_fail.sol frontend SC0110: unknown import item `missing` intentional negative import fixture for hiding an unknown wildcard-imported name +imports/hidden_ctor_dot_fail.sol frontend SC0101: undefined name: Err intentional negative import fixture proving hidden constructors are unavailable to dot syntax +imports/hidden_ctor_expr_fail.sol frontend SC0101: undefined name: Err intentional negative import fixture proving hidden constructors are unavailable in expressions +imports/hidden_ctor_nonexhaustive_fail.sol typeck SC0223: pattern match on type with hidden constructors requires a wildcard arm: Token intentional negative import fixture for exhaustiveness with a partially visible data type +imports/hidden_ctor_pattern_fail.sol frontend SC0101: undefined name: Token.Err intentional negative import fixture proving hidden constructors are unavailable in patterns +imports/leak_b.sol frontend SC0101: undefined name: fromA intentional negative import fixture proving private imported terms do not leak through an intermediate module +imports/leak_main.sol frontend SC0101: undefined name: fromA intentional negative import fixture proving private imported terms do not leak through an intermediate module +imports/leak_main.sol frontend SC0101: undefined name: fromB intentional negative import fixture proving private imported terms do not leak through an intermediate module +imports/module_name_shadow.sol frontend SC0121: conflicting unqualified name `keep` intentional negative import fixture for a module qualifier colliding with a selected term +imports/module_unqualified_constr_fail.sol frontend SC0106: unqualified constructor: True intentional negative import fixture proving module imports do not open constructors +imports/module_unqualified_fun_fail.sol frontend SC0101: undefined name: base intentional negative import fixture proving module imports do not open terms +imports/module_unqualified_type_fail.sol frontend SC0103: undefined type constructor: Bool intentional negative import fixture proving module imports do not open types +imports/opaque_alias_leak_fail.sol frontend SC0103: undefined type constructor: T intentional negative import fixture proving opaque type aliases do not leak through imports +imports/opaque_alias_qualifier_leak_fail.sol frontend SC0103: undefined type constructor: Base.T intentional negative import fixture proving opaque type aliases do not leak through qualifiers +imports/opaque_select_direct_leak_fail.sol frontend SC0103: undefined type constructor: T intentional negative import fixture proving opaque type aliases cannot be selected through re-exports +imports/pragma_scope_main.sol typeck SC0212: Coverage condition fails for trait: intentional negative import fixture proving a dependency pragma does not disable checks in its importer +imports/private_bad_lib.sol typeck SC0201: type mismatch: expected word, found bool intentional negative import fixture containing a type error in a private helper body +imports/private_bad_main.sol typeck SC0201: type mismatch: expected word, found bool intentional negative import fixture proving reachable private helper bodies are type-checked +imports/reexport_ctor_expr_hidden_fail.sol frontend SC0101: undefined name: Err intentional negative import fixture proving hidden constructors do not leak through re-exports +imports/reexport_ctor_hidden_fail.sol frontend SC0115: unknown re-exported constructor `Token.Err` intentional negative import fixture for explicitly re-exporting a hidden constructor +imports/select_dup_item.sol frontend SC0117: duplicate name `keep` in selective import intentional negative import fixture for duplicate names in one selective import +imports/select_fail.sol frontend SC0101: undefined name: drop intentional negative import fixture proving unselected terms remain unavailable +imports/select_hiding_fail.sol frontend SC0101: undefined name: drop intentional negative import fixture proving hidden selected terms remain unavailable +imports/select_shadow_local.sol frontend SC0108: duplicate declaration `keep` in term namespace intentional negative import fixture for a selected term colliding with a local declaration +imports/select_unknown.sol frontend SC0110: unknown import item `missing` intentional negative import fixture for an unknown selected item +imports/strict_open_fail.sol frontend SC0101: undefined name: not intentional negative import fixture proving a strict module import is not an open import +imports/strict_open_fail.sol frontend SC0103: undefined type constructor: Bool intentional negative import fixture proving a strict module import is not an open import +imports/symlink_identity_fail.sol typeck SC0201: type mismatch: expected Mirror.T, found T intentional negative import fixture proving equivalent source paths retain distinct module type identities +imports/symlink_impl/api.sol frontend SC0109: import helper: file not found auxiliary symlink fixture is materialized by the module-system test and is not independently complete in the checked-in corpus +imports/symlink_impl/api.sol frontend unresolved-import: failed to read auxiliary symlink fixture is materialized by the module-system test and is not independently complete in the checked-in corpus diff --git a/crates/parser/tests/fixtures/ok/body_return_min.sol b/crates/parser/tests/fixtures/ok/body_return_min.sol new file mode 100644 index 00000000..b5f26d73 --- /dev/null +++ b/crates/parser/tests/fixtures/ok/body_return_min.sol @@ -0,0 +1,3 @@ +function main() returns (word) { + return 1; +} diff --git a/crates/parser/tests/fixtures/ok/body_return_min.solc b/crates/parser/tests/fixtures/ok/body_return_min.solc deleted file mode 100644 index fe8c43b6..00000000 --- a/crates/parser/tests/fixtures/ok/body_return_min.solc +++ /dev/null @@ -1,3 +0,0 @@ -function main() { - return 1; -} diff --git a/crates/parser/tests/fixtures/ok/comptime_match_label.sol b/crates/parser/tests/fixtures/ok/comptime_match_label.sol new file mode 100644 index 00000000..f405ffc0 --- /dev/null +++ b/crates/parser/tests/fixtures/ok/comptime_match_label.sol @@ -0,0 +1,10 @@ +function classify(x: word) returns (word) { + match (x) { +case comptime 1 { +return 1; +} +default { +return 0; +} +} +} diff --git a/crates/parser/tests/fixtures/ok/comptime_match_label.solc b/crates/parser/tests/fixtures/ok/comptime_match_label.solc deleted file mode 100644 index c039e31f..00000000 --- a/crates/parser/tests/fixtures/ok/comptime_match_label.solc +++ /dev/null @@ -1,6 +0,0 @@ -function classify(x : word) -> word { - match x { - | comptime 1 => return 1; - | _ => return 0; - } -} diff --git a/crates/parser/tests/fixtures/ok/comptime_modifier.sol b/crates/parser/tests/fixtures/ok/comptime_modifier.sol new file mode 100644 index 00000000..3d9e70d3 --- /dev/null +++ b/crates/parser/tests/fixtures/ok/comptime_modifier.sol @@ -0,0 +1,13 @@ +type comptime = word; + +contract ComptimeModifier { + function f(comptime x: word) returns (comptime) { + return x; + } + + function identifier(x: comptime) returns (comptime) { + let comptime : word = 1; + let y : comptime = f(comptime); + return y; + } +} diff --git a/crates/parser/tests/fixtures/ok/comptime_modifier.solc b/crates/parser/tests/fixtures/ok/comptime_modifier.solc deleted file mode 100644 index 1bbc6bbf..00000000 --- a/crates/parser/tests/fixtures/ok/comptime_modifier.solc +++ /dev/null @@ -1,13 +0,0 @@ -type comptime = word; - -contract ComptimeModifier { - function f(comptime x : word) -> comptime word { - return x; - } - - function identifier(x : comptime) -> comptime { - let comptime : word = 1; - let y : comptime word = f(comptime); - return y; - } -} diff --git a/crates/parser/tests/fixtures/ok/contract_modifiers_constructor_fallback.sol b/crates/parser/tests/fixtures/ok/contract_modifiers_constructor_fallback.sol new file mode 100644 index 00000000..3f88a5c8 --- /dev/null +++ b/crates/parser/tests/fixtures/ok/contract_modifiers_constructor_fallback.sol @@ -0,0 +1,11 @@ +contract Modifiers { + constructor() {} + + function ping() public {} + + function deposit() public payable returns (uint256) { + return 0; + } + + fallback() payable {} +} diff --git a/crates/parser/tests/fixtures/ok/contract_modifiers_constructor_fallback.solc b/crates/parser/tests/fixtures/ok/contract_modifiers_constructor_fallback.solc deleted file mode 100644 index 1a59df45..00000000 --- a/crates/parser/tests/fixtures/ok/contract_modifiers_constructor_fallback.solc +++ /dev/null @@ -1,11 +0,0 @@ -contract Modifiers { - constructor() {} - - public function ping() -> () {} - - public payable function deposit() -> uint256 { - return 0; - } - - payable fallback() -> () {} -} diff --git a/crates/parser/tests/fixtures/ok/dot_ctor_expr_pattern.sol b/crates/parser/tests/fixtures/ok/dot_ctor_expr_pattern.sol new file mode 100644 index 00000000..2fa6950a --- /dev/null +++ b/crates/parser/tests/fixtures/ok/dot_ctor_expr_pattern.sol @@ -0,0 +1,16 @@ +enum Option { None, Some(word) } + +function mkSome(x: word) returns (Option) { + return .Some(x); +} + +function fromOption(x: Option) returns (word) { + match (x) { +case .Some(v) { +return v; +} +case .None { +return 0; +} +} +} diff --git a/crates/parser/tests/fixtures/ok/dot_ctor_expr_pattern.solc b/crates/parser/tests/fixtures/ok/dot_ctor_expr_pattern.solc deleted file mode 100644 index dac1fb48..00000000 --- a/crates/parser/tests/fixtures/ok/dot_ctor_expr_pattern.solc +++ /dev/null @@ -1,12 +0,0 @@ -data Option = None | Some(word); - -function mkSome(x: word) -> Option { - return .Some(x); -} - -function fromOption(x: Option) -> word { - match x { - | .Some(v) => return v; - | .None => return 0; - } -} diff --git a/crates/parser/tests/fixtures/ok/export_operator_list.solc b/crates/parser/tests/fixtures/ok/export_operator_list.sol similarity index 100% rename from crates/parser/tests/fixtures/ok/export_operator_list.solc rename to crates/parser/tests/fixtures/ok/export_operator_list.sol diff --git a/crates/parser/tests/fixtures/ok/expression_bodied.sol b/crates/parser/tests/fixtures/ok/expression_bodied.sol new file mode 100644 index 00000000..0388d778 --- /dev/null +++ b/crates/parser/tests/fixtures/ok/expression_bodied.sol @@ -0,0 +1,15 @@ +function zero() returns (word) { + 0 +} + +function apply(f: function(a) returns (b), x: a) returns (b) { + f(x) +} + +function choose(c: bool, a: a, b: a) returns (a) { + c ? a : b +} + +function keepThen(then: word) returns (word) { + then +} diff --git a/crates/parser/tests/fixtures/ok/expression_bodied.solc b/crates/parser/tests/fixtures/ok/expression_bodied.solc deleted file mode 100644 index 377ad401..00000000 --- a/crates/parser/tests/fixtures/ok/expression_bodied.solc +++ /dev/null @@ -1,15 +0,0 @@ -function zero() { - 0 -} - -function apply(f, x) { - f(x) -} - -function choose(c, a, b) { - if c then a else b -} - -function keepThen(then: word) -> word { - then -} diff --git a/crates/parser/tests/fixtures/ok/for_loop.sol b/crates/parser/tests/fixtures/ok/for_loop.sol new file mode 100644 index 00000000..8bf38cff --- /dev/null +++ b/crates/parser/tests/fixtures/ok/for_loop.sol @@ -0,0 +1,7 @@ +function sum10() returns (word) { + let s : word = 0; + for (let i = 1; i <= 10; i = i + 1) { + s = s + i; + } + return s; +} diff --git a/crates/parser/tests/fixtures/ok/for_loop.solc b/crates/parser/tests/fixtures/ok/for_loop.solc deleted file mode 100644 index fca554cd..00000000 --- a/crates/parser/tests/fixtures/ok/for_loop.solc +++ /dev/null @@ -1,7 +0,0 @@ -function sum10() -> word { - let s : word = 0; - for (let i = 1; i <= 10; i = i + 1) { - s = s + i; - } - return s; -} diff --git a/crates/parser/tests/fixtures/ok/import_alias_operator_hiding.sol b/crates/parser/tests/fixtures/ok/import_alias_operator_hiding.sol new file mode 100644 index 00000000..60042cec --- /dev/null +++ b/crates/parser/tests/fixtures/ok/import_alias_operator_hiding.sol @@ -0,0 +1 @@ +import {A as B, (^^)} from mod hiding {C}; diff --git a/crates/parser/tests/fixtures/ok/import_alias_operator_hiding.solc b/crates/parser/tests/fixtures/ok/import_alias_operator_hiding.solc deleted file mode 100644 index a6df80bf..00000000 --- a/crates/parser/tests/fixtures/ok/import_alias_operator_hiding.solc +++ /dev/null @@ -1 +0,0 @@ -import mod.{A as B, (^^)} hiding {C}; diff --git a/crates/parser/tests/fixtures/ok/import_external_alias.sol b/crates/parser/tests/fixtures/ok/import_external_alias.sol new file mode 100644 index 00000000..11484c62 --- /dev/null +++ b/crates/parser/tests/fixtures/ok/import_external_alias.sol @@ -0,0 +1 @@ +import * as X from @lib.a.b; diff --git a/crates/parser/tests/fixtures/ok/import_external_alias.solc b/crates/parser/tests/fixtures/ok/import_external_alias.solc deleted file mode 100644 index 37da0169..00000000 --- a/crates/parser/tests/fixtures/ok/import_external_alias.solc +++ /dev/null @@ -1 +0,0 @@ -import @lib.a.b as X; diff --git a/crates/parser/tests/fixtures/ok/import_mixed_wildcard.sol b/crates/parser/tests/fixtures/ok/import_mixed_wildcard.sol new file mode 100644 index 00000000..6774bea0 --- /dev/null +++ b/crates/parser/tests/fixtures/ok/import_mixed_wildcard.sol @@ -0,0 +1,3 @@ +import * from glob; +import * from glob2; +import * from glob3; diff --git a/crates/parser/tests/fixtures/ok/import_mixed_wildcard.solc b/crates/parser/tests/fixtures/ok/import_mixed_wildcard.solc deleted file mode 100644 index 31ded143..00000000 --- a/crates/parser/tests/fixtures/ok/import_mixed_wildcard.solc +++ /dev/null @@ -1,3 +0,0 @@ -import glob.{*, idWord}; -import glob2.{idWord, *}; -import glob3.{*, *}; diff --git a/crates/parser/tests/fixtures/ok/import_wildcard_selector.sol b/crates/parser/tests/fixtures/ok/import_wildcard_selector.sol new file mode 100644 index 00000000..ccd19daf --- /dev/null +++ b/crates/parser/tests/fixtures/ok/import_wildcard_selector.sol @@ -0,0 +1 @@ +import * from mod; diff --git a/crates/parser/tests/fixtures/ok/import_wildcard_selector.solc b/crates/parser/tests/fixtures/ok/import_wildcard_selector.solc deleted file mode 100644 index 8bfe2b25..00000000 --- a/crates/parser/tests/fixtures/ok/import_wildcard_selector.solc +++ /dev/null @@ -1 +0,0 @@ -import mod.{*}; diff --git a/crates/parser/tests/fixtures/ok/match_arm_block.sol b/crates/parser/tests/fixtures/ok/match_arm_block.sol new file mode 100644 index 00000000..31eab5ff --- /dev/null +++ b/crates/parser/tests/fixtures/ok/match_arm_block.sol @@ -0,0 +1,12 @@ +function main(foo: (word, word)) returns (word) { + let res: word; + match (foo) { +case (v0, v1) { +{ + let x: word = v1; + res = x; + } +} +} + return res; +} diff --git a/crates/parser/tests/fixtures/ok/match_arm_block.solc b/crates/parser/tests/fixtures/ok/match_arm_block.solc deleted file mode 100644 index ae8c80de..00000000 --- a/crates/parser/tests/fixtures/ok/match_arm_block.solc +++ /dev/null @@ -1,10 +0,0 @@ -function main(foo: (word, word)) -> word { - let res: word; - match foo { - | (v0, v1) => { - let x: word = v1; - res = x; - } - } - return res; -} diff --git a/crates/parser/tests/fixtures/ok/match_trailing_semicolon.sol b/crates/parser/tests/fixtures/ok/match_trailing_semicolon.sol new file mode 100644 index 00000000..beff201a --- /dev/null +++ b/crates/parser/tests/fixtures/ok/match_trailing_semicolon.sol @@ -0,0 +1,7 @@ +function f() { + match (0) { +default { +return (); +} +} +} diff --git a/crates/parser/tests/fixtures/ok/match_trailing_semicolon.solc b/crates/parser/tests/fixtures/ok/match_trailing_semicolon.solc deleted file mode 100644 index 4cc77387..00000000 --- a/crates/parser/tests/fixtures/ok/match_trailing_semicolon.solc +++ /dev/null @@ -1,5 +0,0 @@ -function f() { - match 0 { - | _ => return (); - }; -} diff --git a/crates/parser/tests/fixtures/ok/no_diagnostics.solc b/crates/parser/tests/fixtures/ok/no_diagnostics.sol similarity index 100% rename from crates/parser/tests/fixtures/ok/no_diagnostics.solc rename to crates/parser/tests/fixtures/ok/no_diagnostics.sol diff --git a/crates/parser/tests/fixtures/ok/operators_compound_assign.sol b/crates/parser/tests/fixtures/ok/operators_compound_assign.sol new file mode 100644 index 00000000..c31ffe95 --- /dev/null +++ b/crates/parser/tests/fixtures/ok/operators_compound_assign.sol @@ -0,0 +1,11 @@ +function operators(x: word, y: word, z: word) returns (word) { + let acc = x % y; + acc = (acc & y) | (x ^ z); + acc += x; + acc -= y; + acc ^= z; + acc &= x; + acc |= y; + acc %= z; + return acc; +} diff --git a/crates/parser/tests/fixtures/ok/operators_compound_assign.solc b/crates/parser/tests/fixtures/ok/operators_compound_assign.solc deleted file mode 100644 index 5ceda12a..00000000 --- a/crates/parser/tests/fixtures/ok/operators_compound_assign.solc +++ /dev/null @@ -1,11 +0,0 @@ -function operators(x, y, z) { - let acc = x % y; - acc = (acc & y) | (x ^ z); - acc += x; - acc -= y; - acc ^= z; - acc &= x; - acc |= y; - acc %= z; - return acc; -} diff --git a/crates/parser/tests/fixtures/ok/parser_catchup_h.sol b/crates/parser/tests/fixtures/ok/parser_catchup_h.sol new file mode 100644 index 00000000..85e2c5c6 --- /dev/null +++ b/crates/parser/tests/fixtures/ok/parser_catchup_h.sol @@ -0,0 +1,9 @@ +enum First { First(word) } +enum Second { Second } + +export mod; +export mod as M; +export mod.{a}; +export { T(*) }; + +import {T} from m; diff --git a/crates/parser/tests/fixtures/ok/parser_catchup_h.solc b/crates/parser/tests/fixtures/ok/parser_catchup_h.solc deleted file mode 100644 index ab02b9db..00000000 --- a/crates/parser/tests/fixtures/ok/parser_catchup_h.solc +++ /dev/null @@ -1,9 +0,0 @@ -data First = First(word); -data Second = Second; - -export mod; -export mod as M; -export mod.{a}; -export { T(*) }; - -import m.{T}; diff --git a/crates/parser/tests/fixtures/ok/proxy_expression.sol b/crates/parser/tests/fixtures/ok/proxy_expression.sol new file mode 100644 index 00000000..cb0925b5 --- /dev/null +++ b/crates/parser/tests/fixtures/ok/proxy_expression.sol @@ -0,0 +1,6 @@ +function main(x: word) returns (word) { + let p = @word; + let pairProxy = @(word, word); + let annotated = p ; + return x; +} diff --git a/crates/parser/tests/fixtures/ok/proxy_expression.solc b/crates/parser/tests/fixtures/ok/proxy_expression.solc deleted file mode 100644 index 853bd1e7..00000000 --- a/crates/parser/tests/fixtures/ok/proxy_expression.solc +++ /dev/null @@ -1,6 +0,0 @@ -function main(x: word) -> word { - let p = @word; - let pairProxy = @(word, word); - let annotated = p : @word; - return x; -} diff --git a/crates/parser/tests/fixtures/ok/proxy_type_sugar.sol b/crates/parser/tests/fixtures/ok/proxy_type_sugar.sol new file mode 100644 index 00000000..2bf9677e --- /dev/null +++ b/crates/parser/tests/fixtures/ok/proxy_type_sugar.sol @@ -0,0 +1 @@ +function proxy_sig(x: @word) returns (@word) {} diff --git a/crates/parser/tests/fixtures/ok/proxy_type_sugar.solc b/crates/parser/tests/fixtures/ok/proxy_type_sugar.solc deleted file mode 100644 index 35206d4e..00000000 --- a/crates/parser/tests/fixtures/ok/proxy_type_sugar.solc +++ /dev/null @@ -1 +0,0 @@ -function proxy_sig(x: @word) -> @word {} diff --git a/crates/parser/tests/fixtures/ok/qualified_constructor_pattern_3_segment.sol b/crates/parser/tests/fixtures/ok/qualified_constructor_pattern_3_segment.sol new file mode 100644 index 00000000..843d6c30 --- /dev/null +++ b/crates/parser/tests/fixtures/ok/qualified_constructor_pattern_3_segment.sol @@ -0,0 +1,10 @@ +function main(x: mod.Type.Bool) returns (word) { + match (x) { +case mod.Type.True { +return 1; +} +default { +return 0; +} +} +} diff --git a/crates/parser/tests/fixtures/ok/qualified_constructor_pattern_3_segment.solc b/crates/parser/tests/fixtures/ok/qualified_constructor_pattern_3_segment.solc deleted file mode 100644 index cbbc9861..00000000 --- a/crates/parser/tests/fixtures/ok/qualified_constructor_pattern_3_segment.solc +++ /dev/null @@ -1,6 +0,0 @@ -function main(x: mod.Type.Bool) -> word { - match x { - | mod.Type.True => return 1; - | _ => return 0; - } -} diff --git a/crates/parser/tests/fixtures/ok/qualified_constructor_patterns.sol b/crates/parser/tests/fixtures/ok/qualified_constructor_patterns.sol new file mode 100644 index 00000000..b6331ecb --- /dev/null +++ b/crates/parser/tests/fixtures/ok/qualified_constructor_patterns.sol @@ -0,0 +1,17 @@ +contract QualifiedConstructorPatterns { + enum Option { None, Some(a) } + + function join(mmx: Option>) returns (Option) { + match (mmx) { +case Option.None { +return Option.None; +} +case Option.Some(Option.Some(x)) { +return Option.Some(x); +} +case Option.Some(Option.None) { +return Option.None; +} +} + } +} diff --git a/crates/parser/tests/fixtures/ok/qualified_constructor_patterns.solc b/crates/parser/tests/fixtures/ok/qualified_constructor_patterns.solc deleted file mode 100644 index e7538cd4..00000000 --- a/crates/parser/tests/fixtures/ok/qualified_constructor_patterns.solc +++ /dev/null @@ -1,11 +0,0 @@ -contract QualifiedConstructorPatterns { - data Option(a) = None | Some(a); - - function join(mmx) { - match mmx { - | Option.None => return Option.None; - | Option.Some(Option.Some(x)) => return Option.Some(x); - | Option.Some(Option.None) => return Option.None; - } - } -} diff --git a/crates/parser/tests/fixtures/ok/qualified_type_return.sol b/crates/parser/tests/fixtures/ok/qualified_type_return.sol new file mode 100644 index 00000000..3ec301e7 --- /dev/null +++ b/crates/parser/tests/fixtures/ok/qualified_type_return.sol @@ -0,0 +1 @@ +function qualified_ret() returns (mod.Type) {} diff --git a/crates/parser/tests/fixtures/ok/qualified_type_return.solc b/crates/parser/tests/fixtures/ok/qualified_type_return.solc deleted file mode 100644 index d38bacd5..00000000 --- a/crates/parser/tests/fixtures/ok/qualified_type_return.solc +++ /dev/null @@ -1 +0,0 @@ -function qualified_ret() -> mod.Type {} diff --git a/crates/parser/tests/fixtures/ok/tuple_unit_sail.sol b/crates/parser/tests/fixtures/ok/tuple_unit_sail.sol new file mode 100644 index 00000000..edfc217b --- /dev/null +++ b/crates/parser/tests/fixtures/ok/tuple_unit_sail.sol @@ -0,0 +1,37 @@ +enum Pair { Pair(a, b) } + +function fst(p: (a, b)) returns (a) { + match (p) { +case (x, y) { +return x; +} +} +} + +function tupleValue() returns (word, word) { + return (1, 0); +} + +function unitValue() { + return (); +} + +function nestedTupleUnitPattern(p: ((), (word, word))) returns (word) { + match (p) { +case ((), (x, y)) { +return x; +} +} +} + +function groupedSinglePattern(p: word) returns (word) { + match (p) { +case (y) { +return y; +} +} +} + +function pairData(x: word, y: word) returns (Pair) { + return Pair(x, y); +} diff --git a/crates/parser/tests/fixtures/ok/tuple_unit_sail.solc b/crates/parser/tests/fixtures/ok/tuple_unit_sail.solc deleted file mode 100644 index 654e2025..00000000 --- a/crates/parser/tests/fixtures/ok/tuple_unit_sail.solc +++ /dev/null @@ -1,31 +0,0 @@ -data Pair(a, b) = Pair(a, b); - -forall a b . function fst(p : (a, b)) -> a { - match p { - | (x, y) => return x; - } -} - -function tupleValue() -> (word, word) { - return (1, 0); -} - -function unitValue() -> () { - return (); -} - -function nestedTupleUnitPattern(p) { - match p { - | ((), (x, y)) => return x; - } -} - -function groupedSinglePattern(p) { - match p { - | (y) => return y; - } -} - -function pairData(x : word, y : word) -> Pair(word, word) { - return Pair(x, y); -} diff --git a/crates/parser/tests/incremental_spans.rs b/crates/parser/tests/incremental_spans.rs index b66b1f3f..11912195 100644 --- a/crates/parser/tests/incremental_spans.rs +++ b/crates/parser/tests/incremental_spans.rs @@ -200,7 +200,7 @@ fn nested_item_defs<'db>( #[test] fn top_level_error_item_has_recovery_span() { let db = TestDb::default(); - let url = "memory:///recovery.solc".parse().expect("valid url"); + let url = "memory:///recovery.sol".parse().expect("valid url"); let src = "function first() {}\nunknown nonsense tokens\nfunction second() {}\n"; let file = SourceFile::new(&db, url, Some(src.to_owned())); @@ -222,8 +222,8 @@ fn top_level_error_item_has_recovery_span() { #[test] fn relative_span_query_backdates_after_edit_above_def() { let mut db = TestDb::default(); - let url = "memory:///incr.solc".parse().expect("valid url"); - let src = "function id(x: word) -> word {\n return x;\n}\n"; + let url = "memory:///incr.sol".parse().expect("valid url"); + let src = "function id(x: word) returns (word) {\n return x;\n}\n"; let file = SourceFile::new(&db, url, Some(src.to_owned())); // Baseline: execute the semantic-style query once, then drop all `'db` @@ -268,11 +268,11 @@ fn relative_span_query_backdates_after_edit_above_def() { #[test] fn editing_leading_comment_invalidates_only_comment_consumers() { let mut db = TestDb::default(); - let url = "memory:///comment-incr.solc".parse().expect("valid url"); + let url = "memory:///comment-incr.sol".parse().expect("valid url"); let file = SourceFile::new( &db, url, - Some("// one\nfunction id(x: word) -> word { return x; }\n".to_owned()), + Some("// one\nfunction id(x: word) returns (word) { return x; }\n".to_owned()), ); let (before_identity, before_span) = { @@ -292,7 +292,7 @@ fn editing_leading_comment_invalidates_only_comment_consumers() { }; file.set_content(&mut db).to(Some( - "// two\nfunction id(x: word) -> word { return x; }\n".to_owned(), + "// two\nfunction id(x: word) returns (word) { return x; }\n".to_owned(), )); let function = first_function(&db, file); @@ -317,15 +317,16 @@ fn editing_leading_comment_invalidates_only_comment_consumers() { #[test] fn editing_nested_item_comments_preserves_semantic_fields() { let mut db = TestDb::default(); - let url = "memory:///nested-comment-incr.solc" + let url = "memory:///nested-comment-incr.sol" .parse() .expect("valid url"); - let before_src = "data Choice = + let before_src = "enum Choice { // alpha - First; -class a:Documented { + First +} +trait Documented { // alpha - function describe(x: a) -> word; + function describe(x: a) returns (word); } contract C { // alpha @@ -364,12 +365,13 @@ contract C { // Keep the payload byte length unchanged so every nested declaration keeps // the same owner-relative span. Only the parallel comment fields change. file.set_content(&mut db).to(Some( - "data Choice = + "enum Choice { // bravo - First; -class a:Documented { + First +} +trait Documented { // bravo - function describe(x: a) -> word; + function describe(x: a) returns (word); } contract C { // bravo @@ -401,8 +403,10 @@ contract C { #[test] fn lambda_body_relative_span_backdates_after_cosmetic_signature_edit() { let mut db = TestDb::default(); - let url = "memory:///lambda-incr.solc".parse().expect("valid url"); - let before_src = "function make(z: word) -> word { + let url = "memory:///lambda-incr.sol".parse().expect("valid url"); + // `->` remains the canonical result annotation for lambdas; only named + // function declarations moved to `returns (...)`. + let before_src = "function make(z: word) returns (word) { let n = lam (x: word) -> word { return x; }; @@ -421,7 +425,7 @@ fn lambda_body_relative_span_backdates_after_cosmetic_signature_edit() { }; file.set_content(&mut db).to(Some( - "function make(z: word) -> word { + "function make(z: word) returns (word) { let n = lam ( x /* same binder */ : /* same parameter type */ word ) -> /* same return type */ word { @@ -445,7 +449,7 @@ fn lambda_body_relative_span_backdates_after_cosmetic_signature_edit() { assert_eq!(after_cosmetic_fact, before_fact); file.set_content(&mut db).to(Some( - "function make(z: word) -> word { + "function make(z: word) returns (word) { let n = lam (x: uint) -> word { return x; }; diff --git a/crates/parser/tests/lowering_regressions.rs b/crates/parser/tests/lowering_regressions.rs index b0fb9929..4346d94b 100644 --- a/crates/parser/tests/lowering_regressions.rs +++ b/crates/parser/tests/lowering_regressions.rs @@ -34,7 +34,7 @@ impl hir::Db for TestDb { impl solcore_parser::Db for TestDb {} fn source_file(db: &TestDb, name: &str, src: &str) -> SourceFile { - let url = format!("memory:///{name}.solc").parse().expect("valid url"); + let url = format!("memory:///{name}.sol").parse().expect("valid url"); SourceFile::new(db, url, Some(src.to_owned())) } @@ -119,6 +119,8 @@ fn block_comments_do_not_swallow_following_items_and_unterminated_comments_diagn #[test] fn function_hir_retains_only_directly_leading_source_comments() { let db = TestDb::default(); + // The arrow-like text below is comment payload under test, not a legacy + // function result annotation. let (_, module) = parse_module( &db, "function-comments", @@ -127,7 +129,7 @@ contract C { // ordinary documentation // #[(0, 1) -> 1] /* block /* nested */ documentation */ - public function add(x: word, y: word) -> word { return x; } + function add(x: word, y: word) public returns (word) { return x; } function body_comment() { // this belongs to the body @@ -192,20 +194,21 @@ export dependency; pragma feature Example; // top alias type Alias = word; -// top data -data TopData = // first constructor after equals +// top enum +enum TopData { // first constructor after opening brace First // second constructor before separator - | Second; -// top class -class a:Documented { - // class method - function describe(x: a) -> word; + , Second } -// top instance -instance word:Documented { - // instance method - function describe(x: word) -> word { return x; } +// top trait +trait Documented { + // trait method + function describe(x: a) returns (word); +} +// top impl +impl Documented { + // impl method + function describe(x: word) returns (word) { return x; } } // top contract contract C { @@ -213,18 +216,19 @@ contract C { value: word; // contract alias type LocalAlias = word; - // contract data - data LocalData = + // contract enum + enum LocalData { // local first constructor LocalFirst - | // local second constructor after separator - LocalSecond; + , // local second constructor after separator + LocalSecond + } // contract constructor constructor() {} // contract fallback - fallback() -> () {} + fallback() {} // contract function - function get() -> word { return value; } + function get() returns (word) { return value; } } // top function function top() {} @@ -241,9 +245,9 @@ function top() {} " top export", " top pragma", " top alias", - " top data", - " top class", - " top instance", + " top enum", + " top trait", + " top impl", " top contract", " top function", ]; @@ -263,7 +267,7 @@ function top() {} assert_eq!(top_adt.ctors_with_comments(&db).len(), 2); assert_comment_texts( top_adt.ctor_leading_comments(&db, 0).expect("first ctor"), - &[" first constructor after equals"], + &[" first constructor after opening brace"], ); assert_comment_texts( top_adt.ctor_leading_comments(&db, 1).expect("second ctor"), @@ -281,7 +285,7 @@ function top() {} assert_eq!(class.methods_with_comments(&db).len(), 1); assert_comment_texts( class.method_leading_comments(&db, 0).expect("class method"), - &[" class method"], + &[" trait method"], ); let instance = module @@ -294,7 +298,7 @@ function top() {} .expect("instance"); assert_comment_texts( instance.methods(&db)[0].leading_comments(&db), - &[" instance method"], + &[" impl method"], ); let contract = module @@ -315,7 +319,7 @@ function top() {} let expected_contract_item_comments = [ " contract alias", - " contract data", + " contract enum", " contract constructor", " contract fallback", " contract function", @@ -363,13 +367,13 @@ fn item_comments_do_not_cross_blank_lines_trailing_code_or_bodies() { "item-comment-boundaries", r#" type Owner = word; // trailing top-level comment -data AfterTrailing; +enum AfterTrailing {} // separated top-level comment -class a:Boundary { +trait Boundary { // separated method comment - function method(x: a) -> word; + function method(x: a) returns (word); } contract C { first: word; // trailing field comment @@ -377,12 +381,13 @@ contract C { // separated field comment second: word; - data Nested = First // trailing constructor comment - | Second - // separated from the constructor name by a blank line after `|` - | + enum Nested { First // trailing constructor comment + , Second + // separated from the constructor name by a blank line after `,` + , - Third; + Third + } function body_owner() { // body-only comment } @@ -507,11 +512,11 @@ fn equivalent_type_and_predicate_refs_share_semantic_shapes_without_sharing_occu let (_, module) = parse_module( &db, "type-ref-shapes", - "class self:C {} + "trait C {} function a(x: word) {} function b(y: word) {} - forall t . t:C => function c(x: t) {} - forall t . t:C => function d(x: t) {}", + function c(x: t) where t:C {} + function d(x: t) where t:C {}", ); let a = top_function(&db, module, "a"); @@ -536,13 +541,15 @@ fn equivalent_type_and_predicate_refs_share_semantic_shapes_without_sharing_occu } #[test] -fn implicit_return_applies_to_function_definitions_but_not_lambdas() { +fn implicit_return_applies_only_to_named_function_tail_expressions() { let db = TestDb::default(); let (_, module) = parse_module( &db, "implicit-return", - "function id(x: word) -> word { x } - function make() { return lam (x: word) { x }; }", + "function id(x: word) returns (word) { x } + function sequence(x: word) returns (word) { let copy = x; copy } + function discarded(x: word) { x; } + function make() returns (function(word)) { return lam (x: word) { return x; }; }", ); let id = top_function(&db, module, "id"); @@ -550,6 +557,22 @@ fn implicit_return_applies_to_function_definitions_but_not_lambdas() { let id_stmt = id_body.stmts(&db).get(id_body.top_level_stmts(&db)[0]); assert!(matches!(&id_stmt.kind, StmtKind::Return(_))); + let sequence = top_function(&db, module, "sequence"); + let sequence_body = sequence.body(&db).expect("body"); + let sequence_stmts = sequence_body.top_level_stmts(&db); + assert_eq!(sequence_stmts.len(), 2); + assert!(matches!( + &sequence_body.stmts(&db).get(sequence_stmts[1]).kind, + StmtKind::Return(_) + )); + + let discarded = top_function(&db, module, "discarded"); + let discarded_body = discarded.body(&db).expect("body"); + let discarded_stmt = discarded_body + .stmts(&db) + .get(discarded_body.top_level_stmts(&db)[0]); + assert!(matches!(&discarded_stmt.kind, StmtKind::Expr(_))); + let make = top_function(&db, module, "make"); let make_body = make.body(&db).expect("body"); let lambda_body = make_body @@ -563,7 +586,147 @@ fn implicit_return_applies_to_function_definitions_but_not_lambdas() { let lambda_stmt = lambda_body .stmts(&db) .get(lambda_body.top_level_stmts(&db)[0]); - assert!(matches!(&lambda_stmt.kind, StmtKind::Expr(_))); + assert!(matches!(&lambda_stmt.kind, StmtKind::Return(_))); + + let (file, _) = parse_module( + &db, + "lambda-tail-expression", + "function invalid() returns (function(word)) { return lam (x: word) { x }; }", + ); + let diagnostics = diagnostics(&db, file); + assert!( + diagnostics.iter().any(|diagnostic| diagnostic + .message + .contains("expression statement requires trailing `;`")), + "missing lambda tail-expression diagnostic: {diagnostics:#?}" + ); +} + +#[test] +fn constructor_and_fallback_tail_expressions_require_semicolons() { + let db = TestDb::default(); + let (file, _) = parse_module( + &db, + "entry-tail-expression", + "contract C { + constructor() { (); } + fallback() { () } + }", + ); + let diagnostics = diagnostics(&db, file); + assert!( + diagnostics.iter().any(|diagnostic| diagnostic + .message + .contains("expression statement requires trailing `;`")), + "missing fallback tail-expression diagnostic: {diagnostics:#?}" + ); +} + +#[test] +fn named_parameters_are_typed_while_lambda_parameters_may_be_inferred() { + let db = TestDb::default(); + let (file, module) = parse_module( + &db, + "parameter-annotations", + "function apply(value: word) returns (word) { + let identity = lam (inferred) { return inferred; }; + return identity(value); + }", + ); + assert!(diagnostics(&db, file).is_empty()); + + let apply = top_function(&db, module, "apply"); + assert!(matches!( + apply.sig(&db).params.atom().as_slice(), + [FuncParam::Typed { .. }] + )); + let body = apply.body(&db).expect("body"); + let lambda_params = body + .exprs(&db) + .iter() + .find_map(|(_, expr)| match &expr.kind { + ExprKind::Lambda { params, .. } => Some(params.atom()), + _ => None, + }) + .expect("lambda expression"); + assert!(matches!( + lambda_params.as_slice(), + [FuncParam::Untyped { comptime: None, .. }] + )); + + // These two sources intentionally omit the annotation to assert the + // canonical named-parameter rejection rule. + for (name, source) in [ + ("untyped-named-parameter", "function invalid(value) {}"), + ( + "untyped-comptime-parameter", + "function invalid(comptime value) {}", + ), + ] { + let file = source_file(&db, name, source); + assert!(diagnostics(&db, file).iter().any(|diagnostic| { + diagnostic.message == "named function parameter requires an explicit type" + })); + } +} + +#[test] +fn omitted_named_return_is_explicit_unit_even_when_the_body_returns_a_value() { + let db = TestDb::default(); + let (file, module) = parse_module( + &db, + "omitted-return-is-unit", + "function noValue() {} + function valueInBody() { return 1; } + trait UnitMethod { function unit(value: T); }", + ); + assert!(diagnostics(&db, file).is_empty()); + + for name in ["noValue", "valueInBody"] { + let ret = top_function(&db, module, name) + .sig(&db) + .ret + .expect("omitted `returns` lowers to an explicit unit type"); + assert!(matches!(ret.kind(&db), TypeRefKind::Tuple { elems } if elems.atom().is_empty())); + } + + let trait_method = module + .items(&db) + .iter() + .find_map(|item| match item { + Item::ClassDef(class) => class.methods(&db).first().cloned(), + _ => None, + }) + .expect("trait method"); + let ret = trait_method + .ret + .expect("trait method omission lowers to unit"); + assert!(matches!(ret.kind(&db), TypeRefKind::Tuple { elems } if elems.atom().is_empty())); +} + +#[test] +fn core_bindings_and_assignments_reject_yul_colon_equals() { + let db = TestDb::default(); + // These are intentional legacy-rejection probes. `:=` remains valid only + // within an `assembly` block; canonical Core uses `=`. + // syntax-migration: preserve-literals-begin + for (name, source) in [ + ( + "colon-equals-binding", + "function invalid() { let value := 1; }", + ), + ( + "colon-equals-assignment", + "function invalid() { value := 1; }", + ), + ] { + let file = source_file(&db, name, source); + assert!( + !diagnostics(&db, file).is_empty(), + "Core `:=` unexpectedly accepted in {name}" + ); + } + // syntax-migration: preserve-literals-end } #[test] @@ -630,15 +793,15 @@ function good() {}"; } #[test] -fn arrow_types_preserve_source_arity_and_explicit_tuple_domains() { +fn function_types_preserve_source_arity_and_explicit_tuple_domains() { let db = TestDb::default(); let (_, module) = parse_module( &db, - "arrow-types", - "type F = word -> word -> bool; - type G = (word, bool) -> uint; - type H = ((word, bool)) -> uint; - type I = () -> uint;", + "function-types", + "type F = function(word) returns (function(word) returns (bool)); + type G = function(word, bool) returns (uint); + type H = function((word, bool)) returns (uint); + type I = function() returns (uint);", ); let aliases = module .items(&db) @@ -651,14 +814,14 @@ fn arrow_types_preserve_source_arity_and_explicit_tuple_domains() { let f = aliases[0].ty(&db); let TypeRefKind::Fn { params, ret } = f.kind(&db) else { - panic!("F should be an arrow type"); + panic!("F should be a function type"); }; assert_eq!(params.atom().len(), 1); assert!(matches!(ret.kind(&db), TypeRefKind::Fn { .. })); let g = aliases[1].ty(&db); let TypeRefKind::Fn { params, .. } = g.kind(&db) else { - panic!("G should be an arrow type"); + panic!("G should be a function type"); }; assert_eq!(params.atom().len(), 2); assert!( @@ -670,7 +833,7 @@ fn arrow_types_preserve_source_arity_and_explicit_tuple_domains() { let h = aliases[2].ty(&db); let TypeRefKind::Fn { params, .. } = h.kind(&db) else { - panic!("H should be an arrow type"); + panic!("H should be a function type"); }; assert_eq!(params.atom().len(), 1); assert!(matches!( @@ -680,7 +843,7 @@ fn arrow_types_preserve_source_arity_and_explicit_tuple_domains() { let i = aliases[3].ty(&db); let TypeRefKind::Fn { params, .. } = i.kind(&db) else { - panic!("I should be an arrow type"); + panic!("I should be a function type"); }; assert!(params.atom().is_empty()); } @@ -688,9 +851,9 @@ fn arrow_types_preserve_source_arity_and_explicit_tuple_domains() { #[test] fn type_and_predicate_argument_list_spans_are_precise() { let db = TestDb::default(); - let src = "class self:C(arg) {} -type T = Map(word, bool); -forall t . t:C(word) => function f(x: t) {}"; + let src = "trait C {} +type T = Map; +function f(x: t) where t:C {}"; let (_, module) = parse_module(&db, "precise-type-spans", src); let alias = module @@ -705,21 +868,21 @@ forall t . t:C(word) => function f(x: t) {}"; panic!("alias target should be named"); }; let args_abs = args.span(&db).resolve_to_absolute(&db); - let expected_args_start = src.find("(word, bool)").expect("type args") as u32; + let expected_args_start = src.find("").expect("type args") as u32; assert_eq!(args_abs.start().as_u32(), expected_args_start); assert_eq!( args_abs.end().as_u32(), - expected_args_start + "(word, bool)".len() as u32 + expected_args_start + "".len() as u32 ); let function = top_function(&db, module, "f"); let pred = function.sig(&db).preds[0].kind(&db); let pred_args_abs = pred.args.span(&db).resolve_to_absolute(&db); - let expected_pred_start = src.find("(word) =>").expect("predicate args") as u32; + let expected_pred_start = src.find("").expect("predicate args") as u32; assert_eq!(pred_args_abs.start().as_u32(), expected_pred_start); assert_eq!( pred_args_abs.end().as_u32(), - expected_pred_start + "(word)".len() as u32 + expected_pred_start + "".len() as u32 ); } @@ -729,7 +892,7 @@ fn ternary_expression_lowers_to_conditional_expression() { let (_, module) = parse_module( &db, "ternary", - "function f(x: bool) -> word { return x ? 1 : 0; }", + "function f(x: bool) returns (word) { return x ? 1 : 0; }", ); let function = top_function(&db, module, "f"); let body = function.body(&db).expect("body"); @@ -743,6 +906,46 @@ fn ternary_expression_lowers_to_conditional_expression() { )); } +#[test] +fn ternary_expression_is_right_associative_and_allows_a_nested_then_arm() { + let db = TestDb::default(); + let (file, module) = parse_module( + &db, + "nested-ternary", + "function right(x: bool, y: bool) returns (word) { + return x ? 1 : y ? 2 : 3; + } + function nestedThen(x: bool, y: bool) returns (word) { + return x ? y ? 1 : 2 : 3; + }", + ); + assert!(diagnostics(&db, file).is_empty()); + + let conditional_parts = |name| { + let function = top_function(&db, module, name); + let body = function.body(&db).expect("body"); + let stmt = body.stmts(&db).get(body.top_level_stmts(&db)[0]); + let StmtKind::Return(Some(expr_id)) = &stmt.kind else { + panic!("expected return with expression"); + }; + let ExprKind::If { + then_expr, + else_expr, + .. + } = &body.exprs(&db).get(*expr_id).kind + else { + panic!("expected outer conditional expression"); + }; + ( + matches!(&body.exprs(&db).get(*then_expr).kind, ExprKind::If { .. }), + matches!(&body.exprs(&db).get(*else_expr).kind, ExprKind::If { .. }), + ) + }; + + assert_eq!(conditional_parts("right"), (false, true)); + assert_eq!(conditional_parts("nestedThen"), (true, false)); +} + #[test] fn array_literals_lower_with_empty_nested_and_postfix_index_forms() { let db = TestDb::default(); @@ -750,7 +953,7 @@ fn array_literals_lower_with_empty_nested_and_postfix_index_forms() { &db, "array-literals", r#" -function f(a: word, b: word) -> word { +function f(a: word, b: word) returns (word) { let empty = []; let nested = [[a], [b]]; return [a, b][0]; @@ -852,8 +1055,8 @@ fn compound_assignments_lower_through_binary_operator_calls() { #[test] fn derive_attributes_lower_qualified_targets_and_precise_spans() { let db = TestDb::default(); - let src = "#[derive(Eq, core.Show)] data Top(a) = Top(a);\n\ -contract C { #[derive(pkg.codec.Encode)] data Local; }"; + let src = "#[derive(Eq, core.Show)] enum Top { Top(a) }\n\ +contract C { #[derive(pkg.codec.Encode)] enum Local {} }"; let (file, module) = parse_module(&db, "derive-attributes", src); let diagnostics = diagnostics(&db, file); assert!( @@ -926,16 +1129,17 @@ contract C { #[derive(pkg.codec.Encode)] data Local; }"; #[test] fn invalid_derive_attributes_diagnose_and_keep_following_declarations() { let db = TestDb::default(); + // syntax-migration: preserve-next-literal let src = r#" -#[derive()] data Empty; -#[derive(Eq,)] data Malformed; +#[derive()] enum Empty {} +#[derive(Eq,)] enum Malformed {} #[derive(Eq)] function kept() {} -data After; +enum After {} contract C { #[derive(Eq)] field: word; #[derive(Eq)] function nested() {} - #[derive()] data EmptyLocal; - data AfterLocal; + #[derive()] enum EmptyLocal {} + enum AfterLocal {} } "#; let (file, module) = parse_module(&db, "invalid-derive-attributes", src); @@ -948,19 +1152,19 @@ contract C { messages .iter() .filter(|message| { - message.as_str() == "derive attribute requires at least one class path" + message.as_str() == "derive attribute requires at least one trait path" }) .count(), 2 ); assert!(messages.iter().any(|message| { - message == "malformed derive attribute; expected `#[derive(Class, ...)]`" + message == "malformed derive attribute; expected `#[derive(Trait, ...)]`" })); assert_eq!( messages .iter() .filter(|message| { - message.as_str() == "derive attribute is only allowed on data declarations" + message.as_str() == "derive attribute is only allowed on enum declarations" }) .count(), 3 @@ -998,7 +1202,7 @@ contract C { #[test] fn unclosed_derive_attribute_recovers_at_the_next_declaration() { let db = TestDb::default(); - let src = "#[derive(Eq)\ndata Recovered;\nfunction after() {}"; + let src = "#[derive(Eq)\nenum Recovered {}\nfunction after() {}"; let (file, module) = parse_module(&db, "unclosed-derive-attribute", src); assert!(!diagnostics(&db, file).is_empty()); assert_eq!( @@ -1017,10 +1221,10 @@ fn recovery_before_derive_preserves_top_level_and_contract_local_attributes() { let db = TestDb::default(); let src = r#" @ stray -#[derive(Eq)] data Top; +#[derive(Eq)] enum Top {} contract C { @ stray - #[derive(Ord)] data Local; + #[derive(Ord)] enum Local {} } "#; let (file, module) = parse_module(&db, "recovery-before-derive", src); @@ -1071,7 +1275,7 @@ contract C { #[test] fn derive_remains_an_ordinary_identifier_outside_attributes() { let db = TestDb::default(); - let src = "data derive; function derive() -> derive { return derive; }"; + let src = "enum derive {} function derive() returns (derive) { return derive; }"; let (file, module) = parse_module(&db, "derive-soft-keyword", src); assert!(diagnostics(&db, file).is_empty()); assert!(module.items(&db).iter().any(|item| { @@ -1083,11 +1287,12 @@ fn derive_remains_an_ordinary_identifier_outside_attributes() { #[test] fn unclosed_derive_does_not_consume_later_declarations_or_contract_fields() { let db = TestDb::default(); + // syntax-migration: preserve-next-literal let src = r#" #[derive(Eq) function kept() {} ] -data After; +enum After {} contract C { #[derive(Eq) slot: word; @@ -1116,7 +1321,7 @@ contract C { #[test] fn derive_targets_reject_reserved_identifiers() { let db = TestDb::default(); - let src = "#[derive(fallback)] data Kept;"; + let src = "#[derive(fallback)] enum Kept {}"; let (file, module) = parse_module(&db, "derive-reserved-target", src); assert!(!diagnostics(&db, file).is_empty()); module @@ -1126,5 +1331,5 @@ fn derive_targets_reject_reserved_identifiers() { Item::AdtDef(adt) => Some(*adt), _ => None, }) - .expect("data declaration survives malformed attribute"); + .expect("enum declaration survives malformed attribute"); } diff --git a/crates/parser/tests/nameres.rs b/crates/parser/tests/nameres.rs index 6ef712a0..bf4d6a5f 100644 --- a/crates/parser/tests/nameres.rs +++ b/crates/parser/tests/nameres.rs @@ -36,7 +36,7 @@ impl hir::Db for TestDb { impl solcore_parser::Db for TestDb {} fn source_file(db: &TestDb, name: &str, src: &str) -> SourceFile { - let url = format!("memory:///{name}.solc").parse().expect("valid url"); + let url = format!("memory:///{name}.sol").parse().expect("valid url"); SourceFile::new(db, url, Some(src.to_owned())) } @@ -216,9 +216,9 @@ fn derive_targets_resolve_in_source_order_for_top_level_and_contract_adts() { let db = TestDb::default(); let module = parse_module( &db, - "class a:Eq {}\n\ - #[derive(Eq, Eq)] data Top;\n\ - contract C { #[derive(Eq)] data Local; }", + "trait Eq {}\n\ + #[derive(Eq, Eq)] enum Top {}\n\ + contract C { #[derive(Eq)] enum Local {} }", ); let resolution = resolve_module(&db, module); assert!(resolution.diagnostics.is_empty()); @@ -249,7 +249,7 @@ fn derive_targets_report_unknown_and_wrong_kind_names() { let db = TestDb::default(); let module = parse_module( &db, - "data NotAClass; #[derive(Missing, NotAClass)] data Target;", + "enum NotAClass {} #[derive(Missing, NotAClass)] enum Target {}", ); let resolution = resolve_module(&db, module); let undefined = resolution @@ -273,7 +273,7 @@ fn derive_targets_report_unknown_and_wrong_kind_names() { #[test] fn qualified_derive_target_uses_the_exact_imported_class_path() { let db = TestDb::default(); - let module = parse_module(&db, "class a:Eq {} #[derive(pkg.Eq)] data Target;"); + let module = parse_module(&db, "trait Eq {} #[derive(pkg.Eq)] enum Target {}"); let class = module .items(&db) .iter() @@ -301,38 +301,39 @@ fn parse_recovery_suppression_policy_silences_name_lookup_cascades() { let cases = [ ( "body_expr_error", - "function f() -> word { + "function f() returns (word) { let x = ; return missing; }", ), ( "lost_function_signature", - "lost(x: word) -> word { return 0; } - function caller() -> word { return lost(0); }", + // syntax-migration: preserve-next-literal + "lost(x: word) returns (word) { return 0; } + function caller() returns (word) { return lost(0); }", ), ( "broken_import", "impoort util; - function caller() -> word { return missing; }", + function caller() returns (word) { return missing; }", ), ( "broken_type_annotation", "typeish Alias = word; - function caller(x: Alias) -> word { return 0; }", + function caller(x: Alias) returns (word) { return 0; }", ), ( "top_level_item_error", "function first() {} unknown nonsense tokens function second() {} - function caller() -> word { return missing; }", + function caller() returns (word) { return missing; }", ), ( "broken_contract_member", "contract C { broken : - function get() -> word { return broken; } + function get() returns (word) { return broken; } }", ), ]; @@ -377,18 +378,18 @@ fn undefined_name_kind_distinguishes_bare_terms_from_path_lookups() { let (file, module) = parse_and_module( &db, "undefined_name_kinds", - "data Local = Present; - function bare() -> word { return missing; } - function qualified() -> word { return math.value(); } - function ctorExpr() -> word { return Option.Some(0); } - function ctorPat(x: word) -> word { - match x { - | Option.Some(y) => return y; - | _ => return 0; + "enum Local { Present } + function bare() returns (word) { return missing; } + function qualified() returns (word) { return math.value(); } + function ctorExpr() returns (word) { return Option.Some(0); } + function ctorPat(x: word) returns (word) { + match (x) { + case Option.Some(y) { return y; } + case _ { return 0; } } } - function valueMember(x: word) -> word { return x.absent; } - function member() -> word { return Local.absent; }", + function valueMember(x: word) returns (word) { return x.absent; } + function member() returns (word) { return Local.absent; }", ); assert!(parse_diagnostics(&db, file).is_empty()); @@ -437,8 +438,8 @@ fn missing_resolved_module_member_has_qualified_lookup_context() { let (file, module) = parse_and_module( &db, "missing_module_member", - "data Local = Present; - function missing() -> word { + "enum Local { Present } + function missing() returns (word) { let fromModule = math.value(); return Local.absent; }", @@ -485,11 +486,11 @@ fn missing_constructor_on_resolved_type_is_not_an_import_context() { let (file, module) = parse_and_module( &db, "missing_local_constructor", - "data Option = None; - function missing(value: Option) -> word { - match value { - | Option.Some => return 1; - | _ => return 0; + "enum Option { None } + function missing(value: Option) returns (word) { + match (value) { + case Option.Some { return 1; } + case _ { return 0; } } }", ); @@ -563,12 +564,12 @@ fn field_ufcs_resolves_a_unique_local_class_method() { let db = TestDb::default(); let module = parse_module( &db, - "forall self . class self:Combiner { - function combine(x: self, y: word) -> word; + "trait Combiner { + function combine(x: self, y: word) returns (word); } contract C { value: word; - function viaUfcs(y: word) -> word { return value.combine(y); } + function viaUfcs(y: word) returns (word) { return value.combine(y); } }", ); let resolution = resolve_module(&db, module); @@ -602,8 +603,8 @@ fn field_ufcs_resolves_a_unique_imported_class_method() { let db = TestDb::default(); let provider = parse_module( &db, - "forall self . class self:RemoteOps { - function touch(x: self) -> word; + "trait RemoteOps { + function touch(x: self) returns (word); }", ); let class = top_class_id(&db, provider, "RemoteOps"); @@ -611,7 +612,7 @@ fn field_ufcs_resolves_a_unique_imported_class_method() { &db, "contract C { value: word; - function viaImport() -> word { return value.touch(); } + function viaImport() returns (word) { return value.touch(); } }", ); let imports = ClassMethodImports { @@ -654,21 +655,21 @@ fn ufcs_reports_undefined_name_when_visible_methods_conflict() { let db = TestDb::default(); let provider = parse_module( &db, - "forall self . class self:RemoteOps { - function collide(x: self) -> word; + "trait RemoteOps { + function collide(x: self) returns (word); }", ); let remote_class = top_class_id(&db, provider, "RemoteOps"); let module = parse_module( &db, - "forall self . class self:LocalOps { - function collide(x: self) -> word; + "trait LocalOps { + function collide(x: self) returns (word); } contract C { value: word; - function ambiguous() -> word { return value.collide(); } - function ambiguousParameter(value: word) -> word { return value.collide(); } - function missing() -> word { return value.absent(); } + function ambiguous() returns (word) { return value.collide(); } + function ambiguousParameter(value: word) returns (word) { return value.collide(); } + function missing() returns (word) { return value.absent(); } }", ); let imports = ClassMethodImports { @@ -720,8 +721,7 @@ fn ufcs_reports_undefined_name_when_visible_methods_conflict() { .expect("parameter body map"); assert!(ident_resolutions(&db, parameter_body, parameter_map) .into_iter() - .any(|(name, resolution)| name == "value" - && matches!(resolution, Resolution::Param(_)))); + .any(|(name, resolution)| name == "value" && matches!(resolution, Resolution::Param(_)))); assert!( field_resolutions(&db, parameter_body, parameter_map) .into_iter() @@ -747,26 +747,26 @@ fn value_ufcs_resolves_parameters_and_locals_while_preserving_qualified_calls() let db = TestDb::default(); let module = parse_module( &db, - "forall self . class self:Combiner { - function combine(x: self, y: word) -> word; + "trait Combiner { + function combine(x: self, y: word) returns (word); } contract C { value: word; Combiner: word; - function qualified(y: word) -> word { + function qualified(y: word) returns (word) { return Combiner.combine(value, y); } - function sameNameQualifier(y: word) -> word { + function sameNameQualifier(y: word) returns (word) { return Combiner.combine(Combiner, y); } - function parameter(value: word, y: word) -> word { + function parameter(value: word, y: word) returns (word) { return value.combine(y); } - function local(value: word, y: word) -> word { + function local(value: word, y: word) returns (word) { let receiver = value; return receiver.combine(y); } - function arbitrary(y: word) -> word { + function arbitrary(y: word) returns (word) { return (value + y).combine(y); } }", @@ -844,8 +844,7 @@ fn value_ufcs_resolves_parameters_and_locals_while_preserving_qualified_calls() .expect("parameter body map"); assert!(ident_resolutions(&db, parameter_body, parameter_map) .into_iter() - .any(|(name, resolution)| name == "value" - && matches!(resolution, Resolution::Param(_)))); + .any(|(name, resolution)| name == "value" && matches!(resolution, Resolution::Param(_)))); assert!( field_resolutions(&db, parameter_body, parameter_map) .into_iter() @@ -892,7 +891,7 @@ fn let_initializer_resolves_before_binder_and_then_shadows() { let db = TestDb::default(); let module = parse_module( &db, - "function f(x: word) -> word { + "function f(x: word) returns (word) { let x = x; return x; }", @@ -917,7 +916,7 @@ fn explicit_blocks_scope_locals_but_for_body_lets_leak() { let db = TestDb::default(); let module = parse_module( &db, - "function f(x: word) -> word { + "function f(x: word) returns (word) { { let x = x; } @@ -947,11 +946,11 @@ fn contract_fields_beat_top_level_functions_and_params_shadow_fields() { let db = TestDb::default(); let module = parse_module( &db, - "function balance() -> word { return 0; } + "function balance() returns (word) { return 0; } contract C { balance: word; - function f() -> word { return balance; } - function g(balance: word) -> word { return balance; } + function f() returns (word) { return balance; } + function g(balance: word) returns (word) { return balance; } }", ); assert!(diagnostic_codes(&db, module).is_empty()); @@ -976,9 +975,9 @@ fn unqualified_call_callee_prefers_contract_function_over_same_name_field() { &db, "contract C { balance: word; - function balance() -> word { return 7; } - function call() -> word { return balance(); } - function bare() -> word { return balance; } + function balance() returns (word) { return 7; } + function call() returns (word) { return balance(); } + function bare() returns (word) { return balance; } }", ); assert!(diagnostic_codes(&db, module).is_empty()); @@ -1015,13 +1014,13 @@ fn qualified_ctor_class_method_and_dot_ctor_resolve_as_expected() { let db = TestDb::default(); let module = parse_module( &db, - "data Option = None | Some(word); - data Foo = Foo(word); - forall self . class self:Show { function show(x: self) -> word; } - function good(x: word) -> Option { return Option.Some(x); } - function classCall(x: word) -> word { return Show.show(x); } - function dot(x: word) -> Option { return .Some(x); } - function sameName(x: word) -> Foo { return Foo(x); }", + "enum Option { None, Some(word) } + enum Foo { Foo(word) } + trait Show { function show(x: self) returns (word); } + function good(x: word) returns (Option) { return Option.Some(x); } + function classCall(x: word) returns (word) { return Show.show(x); } + function dot(x: word) returns (Option) { return .Some(x); } + function sameName(x: word) returns (Foo) { return Foo(x); }", ); let codes = diagnostic_codes(&db, module); assert!(codes.is_empty()); @@ -1055,20 +1054,20 @@ fn self_qualified_contract_methods_do_not_shadow_same_named_local_adt_constructo &db, r#" contract Option { - data Option(a) = None | Some(a); + enum Option { None, Some(a) } - function some(x : word) -> Option(word) { + function some(x: word) returns (Option) { return Option.Some(x); } - function none() -> Option(word) { + function none() returns (Option) { return Option.None; } - function read(o : Option(word)) -> word { - match o { - | Option.Some(x) => return x; - | Option.None => return 0; + function read(o: Option) returns (word) { + match (o) { + case Option.Some(x) { return x; } + case Option.None { return 0; } } } } @@ -1108,7 +1107,7 @@ fn definite_same_name_constructor_beats_unknown_wildcard_import() { let db = TestDb::default(); let module = parse_module( &db, - "data Unit = Unit; function make() -> Unit { return Unit; }", + "enum Unit { Unit } function make() returns (Unit) { return Unit; }", ); let function = top_function(&db, module, "make"); let body = function.body(&db).expect("body"); diff --git a/crates/parser/tests/properties.rs b/crates/parser/tests/properties.rs index e71e1c95..fb58a3e0 100644 --- a/crates/parser/tests/properties.rs +++ b/crates/parser/tests/properties.rs @@ -25,15 +25,15 @@ impl hir::Db for TestDb { impl solcore_parser::Db for TestDb {} const CORPUS_SEEDS: &[&str] = &[ - include_str!("fixtures/ok/no_diagnostics.solc"), - include_str!("fixtures/ok/contract_modifiers_constructor_fallback.solc"), - include_str!("fixtures/ok/match_arm_block.solc"), - include_str!("fixtures/corpus/fail/test/diagnostics/parse-error.solc"), + include_str!("fixtures/ok/no_diagnostics.sol"), + include_str!("fixtures/ok/contract_modifiers_constructor_fallback.sol"), + include_str!("fixtures/ok/match_arm_block.sol"), + include_str!("fixtures/corpus/fail/test/diagnostics/parse-error.sol"), ]; fn parse_without_large_test_stack(source: String) -> Vec { let db = TestDb::default(); - let url = "memory:///property.solc".parse().expect("valid test URL"); + let url = "memory:///property.sol".parse().expect("valid test URL"); let file = SourceFile::new(&db, url, Some(source)); let _ = parse_file_to_hir(&db, file).module(&db); parse_diagnostics(&db, file) @@ -71,10 +71,10 @@ proptest! { } #[test] -fn right_nested_else_if_chain_uses_the_default_stack() { +fn right_nested_ternary_chain_uses_the_default_stack() { let depth = 96; - let mut source = "function main() -> word { return ".to_owned(); - source.push_str(&"if true then 0 else ".repeat(depth)); + let mut source = "function main() returns (word) { return ".to_owned(); + source.push_str(&"true ? 0 : ".repeat(depth)); source.push_str("0; }"); let diagnostics = parse_without_large_test_stack(source); assert!( diff --git a/crates/parser/tests/reserved_words.rs b/crates/parser/tests/reserved_words.rs new file mode 100644 index 00000000..19676530 --- /dev/null +++ b/crates/parser/tests/reserved_words.rs @@ -0,0 +1,83 @@ +use hir::{diag::AnyDiagnostic, input::SourceFile}; +use solcore_parser::{parse_diagnostics, parse_file_to_hir}; + +#[salsa::db] +#[derive(Default, Clone)] +struct TestDb { + storage: salsa::Storage, +} + +#[salsa::db] +impl salsa::Database for TestDb {} + +#[salsa::db] +impl hir::Db for TestDb { + fn def_location_table<'db>( + &'db self, + file: SourceFile, + ) -> &'db hir::anchor::DefLocationTable<'db> { + parse_file_to_hir(self, file).def_locations(self) + } +} + +#[salsa::db] +impl solcore_parser::Db for TestDb {} + +fn source_file(db: &TestDb, name: &str, source: &str) -> SourceFile { + let url = format!("memory:///{name}.sol").parse().expect("valid URL"); + SourceFile::new(db, url, Some(source.to_owned())) +} + +fn diagnostics(db: &TestDb, file: SourceFile) -> Vec { + parse_diagnostics(db, file).to_vec() +} + +#[test] +fn booleans_remain_valid_values_and_patterns_and_fallback_remains_an_entry_point() { + let db = TestDb::default(); + let file = source_file( + &db, + "reserved-positive", + r#" +function flip(value: bool) returns (bool) { + match (value) { + case true { return false; } + case false { return true; } + } +} + +contract C { + fallback() payable {} +} +"#, + ); + + assert!(diagnostics(&db, file).is_empty()); +} + +#[test] +fn reserved_values_and_entry_point_name_are_rejected_as_identifiers() { + let cases = [ + ("function-true", "function true() {}"), + ("function-false", "function false() {}"), + ( + "ordinary-function-fallback", + "contract C { function fallback() {} }", + ), + ("let-true", "function f() { let true = false; }"), + ("field-false", "contract C { false: word; }"), + ("parameter-fallback", "function f(fallback: word) {}"), + ("type-true", "function f(value: true) {}"), + ("import-false", "import {false} from std;"), + ("enum-fallback", "enum fallback { Value }"), + ]; + + for (name, source) in cases { + let db = TestDb::default(); + let file = source_file(&db, name, source); + assert!( + !diagnostics(&db, file).is_empty(), + "reserved identifier case `{name}` parsed without diagnostics" + ); + } +} diff --git a/crates/sonatina/tests/e2e.rs b/crates/sonatina/tests/e2e.rs index 7e29dc40..f2eaeb3c 100644 --- a/crates/sonatina/tests/e2e.rs +++ b/crates/sonatina/tests/e2e.rs @@ -31,7 +31,7 @@ type CompiledFixture = (Vec<(OptLevel, Vec)>, E2eExecution); #[dir_test( dir: "$CARGO_MANIFEST_DIR/../../tests/e2e", - glob: "**/main.solc" + glob: "**/main.sol" )] fn sonatina_evm_e2e(fixture: Fixture<&str>) { if !e2e_enabled() { @@ -245,7 +245,7 @@ fn resolve_fixture_directives( } Item::InstanceDef(instance) => { for function in instance.methods(db) { - reject_non_dispatch_directives(db, *function, "instance method")?; + reject_non_dispatch_directives(db, *function, "impl method")?; } } Item::ContractDef(contract) => { diff --git a/crates/sonatina/tests/lowering.rs b/crates/sonatina/tests/lowering.rs index 5bdfe4bc..c5a56697 100644 --- a/crates/sonatina/tests/lowering.rs +++ b/crates/sonatina/tests/lowering.rs @@ -49,7 +49,7 @@ define_frontend_test_db!(SourceTestDb, hir_ty); fn test_span<'db>(db: &'db TestDb) -> Span<'db> { let file = SourceFile::new( db, - "memory:///sonatina_lowering.solc" + "memory:///sonatina_lowering.sol" .parse() .expect("valid URL"), Some(String::new()), @@ -385,7 +385,7 @@ fn source_main_lowers_through_hull_to_verified_ir() { let (_, ir) = lower_source( r#" contract SimpleMain { - function main() -> word { + function main() returns (word) { return 42; } } @@ -403,18 +403,17 @@ fn source_bool_product_sum_and_branches_lower_to_verified_ir() { let (_, ir) = lower_source( r#" contract AggregateContract { - data Choice = Left(word, word) | Right(word); + enum Choice {Left(word, word) , Right(word)} - function runtime_flag() -> bool { + function runtime_flag() returns (bool) { let raw : word; assembly { raw := callvalue() } - match raw { - | 0 => return false; - | _ => return true; - } + match (raw) { + case 0 { return false; } +default { return true; }} } - function choose(flag : bool, x : word, y : word) -> Choice { + function choose(flag : bool, x : word, y : word) returns (Choice) { if (flag) { return Choice.Left(x, y); } else { @@ -422,14 +421,13 @@ contract AggregateContract { } } - function unwrap(value : Choice) -> word { - match value { - | Choice.Left(x, y) => return x; - | Choice.Right(x) => return x; - } + function unwrap(value : Choice) returns (word) { + match (value) { + case Choice.Left(x, y) { return x; } +case Choice.Right(x) { return x; }} } - function main() -> word { + function main() returns (word) { return unwrap(choose(runtime_flag(), 1, 42)); } } @@ -450,7 +448,7 @@ fn contract_object_data_symbols_and_inline_evm_lower_to_verified_ir() { let (_, ir) = lower_source( r#" contract MemoryContract { - function main() -> word { + function main() returns (word) { let result : word; assembly { mstore(0, 42) @@ -476,7 +474,7 @@ fn memoryguard_reserves_aligned_literal_space_through_the_unified_allocator() { let (_, ir) = lower_source( r#" contract MemoryGuardContract { - function main() -> word { + function main() returns (word) { let guarded : word; assembly { mstore(0x40, memoryguard(128)) @@ -550,12 +548,12 @@ fn contract_storage_load_and_store_lower_to_snapshotted_verified_ir() { contract StorageContract { value: word; - function update(next: word) -> word { + function update(next: word) returns (word) { value = next; return value; } - function main() -> word { + function main() returns (word) { return update(42); } } @@ -571,10 +569,10 @@ contract StorageContract { fn source_bit_not_lowers_to_verified_evm_not() { let (_, ir) = lower_source( r#" -import std.{*}; +import * from std; contract BitNotContract { - public function main() -> word { + function main() public returns (word) { let value:word; assembly { value := callvalue() } return ~value; @@ -591,7 +589,7 @@ fn inline_yul_for_init_binding_remains_in_loop_scope() { let (_, ir) = lower_source( r#" contract LoopContract { - function main() -> word { + function main() returns (word) { let result : word; assembly { result := 0 @@ -614,7 +612,7 @@ fn inline_yul_functions_lower_arguments_multi_returns_leave_and_recursion() { let (_, ir) = lower_source( r#" contract InlineYulFunctions { - function main() -> word { + function main() returns (word) { let left : word; let right : word; let result : word; @@ -664,7 +662,7 @@ fn inline_yul_named_returns_preserve_zero_defaults_and_position() { let (_, ir) = lower_source( r#" contract InlineYulNamedReturns { - function main() -> word { + function main() returns (word) { let x : word; let y : word; let z : word; @@ -760,7 +758,7 @@ fn inline_yul_functions_support_forward_calls_and_mutual_recursion() { let (_, ir) = lower_source( r#" contract InlineYulMutualRecursion { - function main() -> word { + function main() returns (word) { let result : word; assembly { result := even(6) @@ -803,7 +801,7 @@ fn inline_yul_call_arguments_evaluate_right_to_left_without_reordering_parameter let (_, ir) = lower_source( r#" contract InlineYulArgumentOrder { - function main() -> word { + function main() returns (word) { let result : word; assembly { function left() -> value { @@ -862,7 +860,7 @@ fn inline_yul_function_names_are_isolated_between_assembly_blocks() { let (_, ir) = lower_source( r#" contract InlineYulFunctionScopes { - function main() -> word { + function main() returns (word) { let result : word; assembly { function value() -> result { result := 1 } @@ -1049,26 +1047,26 @@ fn inline_yul_functions_do_not_inherit_outer_loop_targets() { fn polymorphic_yul_terminators_end_value_returning_functions() { let (_, ir) = lower_source( r#" -forall a . function viaStop() -> a { +function viaStop() returns (a) { assembly { stop() } } -forall a . function viaInvalid() -> a { +function viaInvalid() returns (a) { assembly { invalid() } } -forall a . function viaSelfdestruct(beneficiary : word) -> a { +function viaSelfdestruct(beneficiary : word) returns (a) { assembly { selfdestruct(beneficiary) } } -forall a . function viaRevert() -> a { +function viaRevert() returns (a) { assembly { revert(0, 0) } } -function useWord(value : word) -> () {} +function useWord(value : word) returns () {} contract Terminators { - public function main() -> () { + function main() public returns () { useWord(viaStop()); useWord(viaInvalid()); useWord(viaSelfdestruct(0)); @@ -1090,9 +1088,9 @@ contract Terminators { fn literal_revert_preserves_its_payload() { let (_, ir) = lower_source_with_file_url_imports( r#" -import std.{*}; +import * from std; -function main() -> () { +function main() returns () { revertLit("regression"); } "#, diff --git a/crates/specialize/src/evaluate/erasure.rs b/crates/specialize/src/evaluate/erasure.rs index f77120e8..0c74d593 100644 --- a/crates/specialize/src/evaluate/erasure.rs +++ b/crates/specialize/src/evaluate/erasure.rs @@ -148,7 +148,7 @@ fn is_runtime_string_location<'db>( if name == "storage" && is_canonical_std_def_named(db, def, "storage") { // Every storage reference has a one-word runtime representation. Its // payload is a layout tag and may recursively contain the source-only - // `string` tag (for example storage(array(string))). Do not treat that + // `string` tag (for example `storage>`). Do not treat that // nested tag as a runtime comptime-string value. return true; } diff --git a/crates/specialize/src/ir.rs b/crates/specialize/src/ir.rs index 687d083d..57adfe9d 100644 --- a/crates/specialize/src/ir.rs +++ b/crates/specialize/src/ir.rs @@ -126,7 +126,7 @@ pub enum MonoIntrinsic { KeccakLit, KeccakWordLit, /// Runtime materialization of a compile-time string literal into - /// `memory(string)`. This marker is deliberately not foldable: Hull + /// `memory`. This marker is deliberately not foldable: Hull /// replaces it with a call to a generated allocator. MemStringFromLit, /// Runtime revert carrying the bytes of a compile-time string literal. @@ -439,7 +439,7 @@ pub enum MonoExprKind<'db> { base: Box>, index: Box>, }, - /// Checked read from a `memory(DynArray(t))` value. + /// Checked read from a `memory>` value. MemoryArrayIndex { base: Box>, index: Box>, diff --git a/crates/specialize/src/specialize/body.rs b/crates/specialize/src/specialize/body.rs index 1fb1fcd7..42d8c277 100644 --- a/crates/specialize/src/specialize/body.rs +++ b/crates/specialize/src/specialize/body.rs @@ -1910,11 +1910,11 @@ impl<'a, 'db> BodyCtx<'a, 'db> { }; Some(MonoExpr { span, - ty: self.driver.mono_ty(result_ty, "class call result", span)?, + ty: self.driver.mono_ty(result_ty, "trait call result", span)?, kind: MonoExprKind::Call { callee: MonoId { name, - ty: self.driver.mono_ty(callee_ty, "class call callee", span)?, + ty: self.driver.mono_ty(callee_ty, "trait call callee", span)?, span, }, args, @@ -1965,13 +1965,13 @@ impl<'a, 'db> BodyCtx<'a, 'db> { span, ty: self .driver - .mono_ty(result_ty, "contract field class call result", span)?, + .mono_ty(result_ty, "contract field trait call result", span)?, kind: MonoExprKind::Call { callee: MonoId { name, ty: self .driver - .mono_ty(callee_ty, "contract field class call callee", span)?, + .mono_ty(callee_ty, "contract field trait call callee", span)?, span, }, args, diff --git a/crates/specialize/src/specialize/diagnostics.rs b/crates/specialize/src/specialize/diagnostics.rs index 006a94a2..2f62f1f9 100644 --- a/crates/specialize/src/specialize/diagnostics.rs +++ b/crates/specialize/src/specialize/diagnostics.rs @@ -121,8 +121,8 @@ impl SpecializeDiagnosticKind<'_> { Self::TypeSizeExceeded { .. } => "specialization type size limit reached here", Self::MissingBody { .. } => "function body required here", Self::MissingResolution { .. } => "name resolution required here", - Self::MissingEvidence { .. } => "class evidence required here", - Self::UnsupportedEvidence { .. } => "unsupported class evidence here", + Self::MissingEvidence { .. } => "trait evidence required here", + Self::UnsupportedEvidence { .. } => "unsupported trait evidence here", Self::UnresolvedExternal { .. } => "external function required here", Self::ComptimeEvaluationFailed { .. } => "comptime evaluation failed here", Self::ComptimeFuelExhausted { .. } => "comptime fuel limit reached here", diff --git a/crates/specialize/tests/fixtures/derived_abi_evidence_replay/abi.sol b/crates/specialize/tests/fixtures/derived_abi_evidence_replay/abi.sol new file mode 100644 index 00000000..40db776d --- /dev/null +++ b/crates/specialize/tests/fixtures/derived_abi_evidence_replay/abi.sol @@ -0,0 +1,30 @@ +pragma no-patterson-condition; +pragma no-bounded-variable-condition; +pragma no-coverage-condition; + +export { + Generic, + ABIDeriving, + Proxy(*), + ABIAttribs, + ABIDecode, + WordReader, + ABIDecoder(*) +}; + +enum Proxy { Proxy } +enum ABIDecoder { ABIDecoder(reader) } + +trait Generic { + function from(x: a) returns (rep) ; + function to(x: rep) returns (a) ; +} +trait ABIDeriving {} +trait ABIAttribs { + function headSize(ty: Proxy) returns (word) ; + function isStatic(ty: Proxy) returns (bool) ; +} +trait ABIDecode { + function decode(ptr: decoder, headOffset: word) returns (decoded) ; +} +trait WordReader {} diff --git a/crates/specialize/tests/fixtures/derived_abi_evidence_replay/abi.solc b/crates/specialize/tests/fixtures/derived_abi_evidence_replay/abi.solc deleted file mode 100644 index 99788bd8..00000000 --- a/crates/specialize/tests/fixtures/derived_abi_evidence_replay/abi.solc +++ /dev/null @@ -1,30 +0,0 @@ -pragma no-patterson-condition; -pragma no-bounded-variable-condition; -pragma no-coverage-condition; - -export { - Generic, - ABIDeriving, - Proxy(*), - ABIAttribs, - ABIDecode, - WordReader, - ABIDecoder(*) -}; - -data Proxy(t) = Proxy; -data ABIDecoder(ty, reader) = ABIDecoder(reader); - -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; -} -forall self . class self:ABIDeriving {} -forall self . class self:ABIAttribs { - function headSize(ty:Proxy(self)) -> word; - function isStatic(ty:Proxy(self)) -> bool; -} -forall decoder decoded . class decoder:ABIDecode(decoded) { - function decode(ptr:decoder, headOffset:word) -> decoded; -} -forall reader . class reader:WordReader {} diff --git a/crates/specialize/tests/fixtures/derived_abi_evidence_replay/competitor.sol b/crates/specialize/tests/fixtures/derived_abi_evidence_replay/competitor.sol new file mode 100644 index 00000000..4a8d0264 --- /dev/null +++ b/crates/specialize/tests/fixtures/derived_abi_evidence_replay/competitor.sol @@ -0,0 +1,14 @@ +import * from abi; +import {Leaf} from reexport; + +export { keepCompetitorReachable }; + +// This orphan is reachable from the entry module but is not visible in the +// module that defines Box. A derived wrapper must replay definition-side +// evidence rather than scanning every reachable environment. +impl ABIAttribs { + function headSize(ty: Proxy) returns (word) { return 64; } + function isStatic(ty: Proxy) returns (bool) { return true; } +} + +function keepCompetitorReachable() returns (word) { return 0; } diff --git a/crates/specialize/tests/fixtures/derived_abi_evidence_replay/competitor.solc b/crates/specialize/tests/fixtures/derived_abi_evidence_replay/competitor.solc deleted file mode 100644 index 06a4a691..00000000 --- a/crates/specialize/tests/fixtures/derived_abi_evidence_replay/competitor.solc +++ /dev/null @@ -1,14 +0,0 @@ -import abi.{*}; -import reexport.{Leaf}; - -export { keepCompetitorReachable }; - -// This orphan is reachable from the entry module but is not visible in the -// module that defines Box. A derived wrapper must replay definition-side -// evidence rather than scanning every reachable environment. -instance Leaf:ABIAttribs { - function headSize(ty:Proxy(Leaf)) -> word { return 64; } - function isStatic(ty:Proxy(Leaf)) -> bool { return true; } -} - -function keepCompetitorReachable() -> word { return 0; } diff --git a/crates/specialize/tests/fixtures/derived_abi_evidence_replay/main.sol b/crates/specialize/tests/fixtures/derived_abi_evidence_replay/main.sol new file mode 100644 index 00000000..4046f5bb --- /dev/null +++ b/crates/specialize/tests/fixtures/derived_abi_evidence_replay/main.sol @@ -0,0 +1,7 @@ +import * from abi; +import {Box} from types; +import {keepCompetitorReachable} from competitor; + +function main(p: Proxy) returns (word) { + return ABIAttribs.headSize(p); +} diff --git a/crates/specialize/tests/fixtures/derived_abi_evidence_replay/main.solc b/crates/specialize/tests/fixtures/derived_abi_evidence_replay/main.solc deleted file mode 100644 index 578990a1..00000000 --- a/crates/specialize/tests/fixtures/derived_abi_evidence_replay/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -import abi.{*}; -import types.{Box}; -import competitor.{keepCompetitorReachable}; - -function main(p:Proxy(Box)) -> word { - return ABIAttribs.headSize(p); -} diff --git a/crates/specialize/tests/fixtures/derived_abi_evidence_replay/reexport.solc b/crates/specialize/tests/fixtures/derived_abi_evidence_replay/reexport.sol similarity index 100% rename from crates/specialize/tests/fixtures/derived_abi_evidence_replay/reexport.solc rename to crates/specialize/tests/fixtures/derived_abi_evidence_replay/reexport.sol diff --git a/crates/specialize/tests/fixtures/derived_abi_evidence_replay/types.sol b/crates/specialize/tests/fixtures/derived_abi_evidence_replay/types.sol new file mode 100644 index 00000000..df01e7fd --- /dev/null +++ b/crates/specialize/tests/fixtures/derived_abi_evidence_replay/types.sol @@ -0,0 +1,23 @@ +import * from abi; + +export { Leaf(*), Box(*) }; + +pragma no-generic-instance-for Leaf; + +enum Leaf { Leaf(word) } +enum Box { Box(Leaf) } + +impl ABIAttribs { + // Keep the definition-side method observable through specialization. The + // competing orphan remains pure, so retaining the derived wrapper also + // proves that evidence was replayed from this module rather than re-solved + // against every reachable instance. + function headSize(ty: Proxy) returns (word) { + assembly { sstore(0, 32) } + return 32; + } + function isStatic(ty: Proxy) returns (bool) { + assembly { sstore(1, 1) } + return true; + } +} diff --git a/crates/specialize/tests/fixtures/derived_abi_evidence_replay/types.solc b/crates/specialize/tests/fixtures/derived_abi_evidence_replay/types.solc deleted file mode 100644 index 5ba51c21..00000000 --- a/crates/specialize/tests/fixtures/derived_abi_evidence_replay/types.solc +++ /dev/null @@ -1,23 +0,0 @@ -import abi.{*}; - -export { Leaf(*), Box(*) }; - -pragma no-generic-instance-for Leaf; - -data Leaf = Leaf(word); -data Box = Box(Leaf); - -instance Leaf:ABIAttribs { - // Keep the definition-side method observable through specialization. The - // competing orphan remains pure, so retaining the derived wrapper also - // proves that evidence was replayed from this module rather than re-solved - // against every reachable instance. - function headSize(ty:Proxy(Leaf)) -> word { - assembly { sstore(0, 32) } - return 32; - } - function isStatic(ty:Proxy(Leaf)) -> bool { - assembly { sstore(1, 1) } - return true; - } -} diff --git a/crates/specialize/tests/fixtures/derived_storage_evidence_replay/main.sol b/crates/specialize/tests/fixtures/derived_storage_evidence_replay/main.sol new file mode 100644 index 00000000..1091096f --- /dev/null +++ b/crates/specialize/tests/fixtures/derived_storage_evidence_replay/main.sol @@ -0,0 +1,8 @@ +import * from storage_support; +import {Box} from types; + +function main(r: storage>, v: Box) returns (Box) { + let slots = StorageSize.size(@Box); + CanStore.store(r, v); + return CanStore.load(r); +} diff --git a/crates/specialize/tests/fixtures/derived_storage_evidence_replay/main.solc b/crates/specialize/tests/fixtures/derived_storage_evidence_replay/main.solc deleted file mode 100644 index 241db9f8..00000000 --- a/crates/specialize/tests/fixtures/derived_storage_evidence_replay/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -import storage_support.{*}; -import types.{Box}; - -function main(r:storage(Box(word)), v:Box(word)) -> Box(word) { - let slots = StorageSize.size(Proxy:Proxy(Box(word))); - CanStore.store(r, v); - return CanStore.load(r); -} diff --git a/crates/specialize/tests/fixtures/derived_storage_evidence_replay/storage_support.sol b/crates/specialize/tests/fixtures/derived_storage_evidence_replay/storage_support.sol new file mode 100644 index 00000000..c115db56 --- /dev/null +++ b/crates/specialize/tests/fixtures/derived_storage_evidence_replay/storage_support.sol @@ -0,0 +1,54 @@ +pragma no-patterson-condition; +pragma no-bounded-variable-condition; +pragma no-coverage-condition; + +export { + Generic, + StorageDeriving, + Proxy(*), + storage(*), + StorageSize, + CanStore +}; + +enum Proxy { Proxy } +enum storage { storage(word) } + +trait Generic { + function from(x: a) returns (rep) ; + function to(x: rep) returns (a) ; +} +trait StorageDeriving {} +trait StorageSize { + function size(x: Proxy) returns (word) ; +} +trait CanStore { + function store(r: slot, v: value) ; + function load(r: slot) returns (value) ; +} + +impl StorageSize { + function size(x: Proxy) returns (word) { + assembly { sstore(0, 1) } + return 1; + } +} + +impl CanStore, word> { + function store(r: storage, v: word) { + match (r) { +case storage(slot) { +assembly { sstore(slot, v) } +} +} + } + function load(r: storage) returns (word) { + match (r) { +case storage(slot) { +let result:word; + assembly { result := sload(slot) } + return result; +} +} + } +} diff --git a/crates/specialize/tests/fixtures/derived_storage_evidence_replay/storage_support.solc b/crates/specialize/tests/fixtures/derived_storage_evidence_replay/storage_support.solc deleted file mode 100644 index 7a391eaa..00000000 --- a/crates/specialize/tests/fixtures/derived_storage_evidence_replay/storage_support.solc +++ /dev/null @@ -1,51 +0,0 @@ -pragma no-patterson-condition; -pragma no-bounded-variable-condition; -pragma no-coverage-condition; - -export { - Generic, - StorageDeriving, - Proxy(*), - storage(*), - StorageSize, - CanStore -}; - -data Proxy(t) = Proxy; -data storage(t) = storage(word); - -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; -} -forall self . class self:StorageDeriving {} -forall self . class self:StorageSize { - function size(x:Proxy(self)) -> word; -} -forall slot value . class slot:CanStore(value) { - function store(r:slot, v:value) -> (); - function load(r:slot) -> value; -} - -instance word:StorageSize { - function size(x:Proxy(word)) -> word { - assembly { sstore(0, 1) } - return 1; - } -} - -instance storage(word):CanStore(word) { - function store(r:storage(word), v:word) -> () { - match r { - | storage(slot) => assembly { sstore(slot, v) } - } - } - function load(r:storage(word)) -> word { - match r { - | storage(slot) => - let result:word; - assembly { result := sload(slot) } - return result; - } - } -} diff --git a/crates/specialize/tests/fixtures/derived_storage_evidence_replay/types.sol b/crates/specialize/tests/fixtures/derived_storage_evidence_replay/types.sol new file mode 100644 index 00000000..b000e6cb --- /dev/null +++ b/crates/specialize/tests/fixtures/derived_storage_evidence_replay/types.sol @@ -0,0 +1,5 @@ +import * from storage_support; + +export { Box(*) }; + +enum Box { Box(a) } diff --git a/crates/specialize/tests/fixtures/derived_storage_evidence_replay/types.solc b/crates/specialize/tests/fixtures/derived_storage_evidence_replay/types.solc deleted file mode 100644 index c8260a6c..00000000 --- a/crates/specialize/tests/fixtures/derived_storage_evidence_replay/types.solc +++ /dev/null @@ -1,5 +0,0 @@ -import storage_support.{*}; - -export { Box(*) }; - -data Box(a) = Box(a); diff --git a/crates/specialize/tests/fixtures/storage_field_definition_evidence/api.solc b/crates/specialize/tests/fixtures/storage_field_definition_evidence/api.sol similarity index 100% rename from crates/specialize/tests/fixtures/storage_field_definition_evidence/api.solc rename to crates/specialize/tests/fixtures/storage_field_definition_evidence/api.sol diff --git a/crates/specialize/tests/fixtures/storage_field_definition_evidence/competitor.sol b/crates/specialize/tests/fixtures/storage_field_definition_evidence/competitor.sol new file mode 100644 index 00000000..c6a1ec98 --- /dev/null +++ b/crates/specialize/tests/fixtures/storage_field_definition_evidence/competitor.sol @@ -0,0 +1,17 @@ +import {storage, uint256, CanStore} from api; + +export { keepCompetitorReachable }; + +impl CanStore, uint256> { + function store(r: storage, v: uint256) { + assembly { sstore(99, 99) } + } + + function load(r: storage) returns (uint256) { + let result:word; + assembly { result := sload(99) } + return uint256(99); + } +} + +function keepCompetitorReachable() returns (word) { return 0; } diff --git a/crates/specialize/tests/fixtures/storage_field_definition_evidence/competitor.solc b/crates/specialize/tests/fixtures/storage_field_definition_evidence/competitor.solc deleted file mode 100644 index be86bb9e..00000000 --- a/crates/specialize/tests/fixtures/storage_field_definition_evidence/competitor.solc +++ /dev/null @@ -1,17 +0,0 @@ -import api.{storage, uint256, CanStore}; - -export { keepCompetitorReachable }; - -instance storage(uint256):CanStore(uint256) { - function store(r:storage(uint256), v:uint256) -> () { - assembly { sstore(99, 99) } - } - - function load(r:storage(uint256)) -> uint256 { - let result:word; - assembly { result := sload(99) } - return uint256(99); - } -} - -function keepCompetitorReachable() -> word { return 0; } diff --git a/crates/specialize/tests/fixtures/storage_field_definition_evidence/lib.sol b/crates/specialize/tests/fixtures/storage_field_definition_evidence/lib.sol new file mode 100644 index 00000000..9e5af42b --- /dev/null +++ b/crates/specialize/tests/fixtures/storage_field_definition_evidence/lib.sol @@ -0,0 +1,13 @@ +import * from std; + +export { keepLibReachable }; + +function keepLibReachable() returns (word) { return 0; } + +contract C { + value : uint256; + + function main() returns (uint256) { + return value; + } +} diff --git a/crates/specialize/tests/fixtures/storage_field_definition_evidence/lib.solc b/crates/specialize/tests/fixtures/storage_field_definition_evidence/lib.solc deleted file mode 100644 index 86e8d750..00000000 --- a/crates/specialize/tests/fixtures/storage_field_definition_evidence/lib.solc +++ /dev/null @@ -1,13 +0,0 @@ -import std.{*}; - -export { keepLibReachable }; - -function keepLibReachable() -> word { return 0; } - -contract C { - value : uint256; - - function main() -> uint256 { - return value; - } -} diff --git a/crates/specialize/tests/fixtures/storage_field_definition_evidence/main.sol b/crates/specialize/tests/fixtures/storage_field_definition_evidence/main.sol new file mode 100644 index 00000000..751e3ab2 --- /dev/null +++ b/crates/specialize/tests/fixtures/storage_field_definition_evidence/main.sol @@ -0,0 +1,6 @@ +import {keepLibReachable} from lib; +import {keepCompetitorReachable} from competitor; + +// This module intentionally has no local contract. The reachable contract +// main in lib is still a specialization root, while this module's trait env +// sees both the definition-side and competing CanStore instances. diff --git a/crates/specialize/tests/fixtures/storage_field_definition_evidence/main.solc b/crates/specialize/tests/fixtures/storage_field_definition_evidence/main.solc deleted file mode 100644 index b2e2f14f..00000000 --- a/crates/specialize/tests/fixtures/storage_field_definition_evidence/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -import lib.{keepLibReachable}; -import competitor.{keepCompetitorReachable}; - -// This module intentionally has no local contract. The reachable contract -// main in lib is still a specialization root, while this module's trait env -// sees both the definition-side and competing CanStore instances. diff --git a/crates/specialize/tests/specialize.rs b/crates/specialize/tests/specialize.rs index 94818afa..094b0ea8 100644 --- a/crates/specialize/tests/specialize.rs +++ b/crates/specialize/tests/specialize.rs @@ -104,7 +104,7 @@ impl nameres::Db for TestDb { impl hir_ty::Db for TestDb {} fn source_file(db: &TestDb, name: &str, src: &str) -> SourceFile { - let url = format!("memory:///{name}.solc").parse().expect("valid URL"); + let url = format!("memory:///{name}.sol").parse().expect("valid URL"); SourceFile::new(db, url, Some(src.to_owned())) } @@ -162,7 +162,7 @@ fn specialize_src_with_std_and_db_options( db, [main_root.as_path(), std_root.as_path()], )); - let main_path = main_root.join("main.solc"); + let main_path = main_root.join("main.sol"); let key = module_key_for_path(LibraryId::Main, &main_root, &main_path).expect("file under main root"); let file = source_file_at_path(db, &main_path, src); @@ -187,42 +187,41 @@ fn specialize_src_with_fake_calldata_array_std( BTreeMap::new(), )); - let std_path = std_root.join("std.solc"); - let main_path = main_root.join("main.solc"); + let std_path = std_root.join("std.sol"); + let main_path = main_root.join("main.sol"); let std_file = source_file_at_path( db, &std_path, r#" export { calldata(*), array(*), uint256(*), Encoded(*), Decoded(*), Typedef, RValueIdxAccess }; -data calldata(t) = calldata(word); -data array(t) = array(word); -data uint256 = uint256(word); -data Encoded = Encoded(word); -data Decoded = Decoded(word); +enum calldata {calldata(word)} +enum array {array(word)} +enum uint256 {uint256(word)} +enum Encoded {Encoded(word)} +enum Decoded {Decoded(word)} -forall abs rep . class abs:Typedef(rep) { - function abs(x:rep) -> abs; - function rep(x:abs) -> rep; +trait Typedef { + function abs(x:rep) returns (abs) ; + function rep(x:abs) returns (rep) ; } -forall t . default instance t:Typedef(t) { - function abs(x:t) -> t { return x; } - function rep(x:t) -> t { return x; } +default impl Typedef { + function abs(x:t) returns (t) { return x; } + function rep(x:t) returns (t) { return x; } } -instance uint256:Typedef(word) { - function abs(x:word) -> uint256 { return uint256(x); } - function rep(x:uint256) -> word { return 0; } +impl Typedef { + function abs(x:word) returns (uint256) { return uint256(x); } + function rep(x:uint256) returns (word) { return 0; } } -forall col_idx val . class col_idx:RValueIdxAccess(val) { - function lookup(xi:col_idx) -> val; +trait RValueIdxAccess { + function lookup(xi:col_idx) returns (val) ; } -forall i . i:Typedef(word) => -instance (calldata(array(Encoded)), i):RValueIdxAccess(Decoded) { - function lookup(xi:(calldata(array(Encoded)), i)) -> Decoded { +impl RValueIdxAccess<(calldata>, i),Decoded> where i: Typedef { + function lookup(xi:(calldata>, i)) returns (Decoded) { let value:word; assembly { value := calldataload(0) } return Decoded(value); @@ -261,7 +260,7 @@ fn function_names(output: &SpecializeOutput<'_>) -> Vec { fn specializes_large_linear_body_with_indexed_frontend_lookups() { use std::fmt::Write as _; - let mut source = "function main() -> word {\n let value0 : word = 0;\n".to_owned(); + let mut source = "function main() returns (word) {\n let value0 : word = 0;\n".to_owned(); for index in 1..2_000 { writeln!( &mut source, @@ -282,9 +281,9 @@ fn specializes_large_linear_body_with_indexed_frontend_lookups() { fn calldata_array_index_specializes_to_rvalue_lookup_call() { let (db, output) = specialize_src_with_fake_calldata_array_std( r#" -import std.{*}; +import * from std; -function main(xs:calldata(array(Encoded)), i:uint256) -> Decoded { +function main(xs:calldata>, i:uint256) returns (Decoded) { return xs[i]; } "#, @@ -396,11 +395,11 @@ fn naming_matches_reference_mangling() { fn specialized_name_hash_is_independent_of_absolute_module_root() { let src = r#" contract C { - public function main() -> word { return 42; } + function main() public returns (word) { return 42; } } "#; - let left = specialize_source_at_root(Path::new("/workspace-a/project"), "src/main.solc", src); - let right = specialize_source_at_root(Path::new("/workspace-b/project"), "src/main.solc", src); + let left = specialize_source_at_root(Path::new("/workspace-a/project"), "src/main.sol", src); + let right = specialize_source_at_root(Path::new("/workspace-b/project"), "src/main.sol", src); assert_eq!(left.diagnostics, Vec::new()); assert_eq!(right.diagnostics, Vec::new()); @@ -411,10 +410,10 @@ contract C { fn deduplicates_identical_instantiations() { let (_db, output) = specialize_src( r#" -forall a . function id(x:a) -> a { return x; } +function id(x:a) returns (a) { return x; } contract C { - public function main(x:word) -> word { + function main(x:word) public returns (word) { let a = id(x); let b = id(a); return b; @@ -438,30 +437,30 @@ contract C { fn evidence_replay_resolves_instance_and_superclass_methods() { let (_db, output) = specialize_src( r#" -data Bool = True | False; +enum Bool {True , False} -forall a . class a:Eq { - function eq(x:a, y:a) -> Bool; +trait Eq { + function eq(x:a, y:a) returns (Bool) ; } -forall a . a:Eq => class a:Ord { - function lt(x:a, y:a) -> Bool; +trait Ord where a: Eq { + function lt(x:a, y:a) returns (Bool) ; } -instance word:Eq { - function eq(x:word, y:word) -> Bool { return primEqWord(x, y); } +impl Eq { + function eq(x:word, y:word) returns (Bool) { return primEqWord(x, y); } } -instance word:Ord { - function lt(x:word, y:word) -> Bool { return Bool.False; } +impl Ord { + function lt(x:word, y:word) returns (Bool) { return Bool.False; } } -forall a . a:Ord => function same(x:a) -> Bool { +function same(x:a) returns (Bool) where a: Ord { return Eq.eq(x, x); } contract C { - public function main(x:word) -> Bool { + function main(x:word) public returns (Bool) { return same(x); } } @@ -488,18 +487,18 @@ contract C { fn evidence_replay_preserves_class_method_local_forall_binders() { let (_db, output) = specialize_src( r#" -forall b . class b:IsA { - forall a . function ais(x : a, witness : b) -> a; +trait IsA { + function ais(x : a, witness : b) returns (a) ; } -instance word:IsA { - forall a . function ais(x : a, witness : word) -> a { +impl IsA { + function ais(x : a, witness : word) returns (a) { return x; } } contract C { - public function main(x : word) -> word { + function main(x : word) public returns (word) { return IsA.ais(x, 0); } } @@ -520,23 +519,23 @@ contract C { fn field_ufcs_prepends_receiver_and_resolves_instance_method() { let (db, _, output) = specialize_src_with_std_and_db( r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; -forall a . class a:Combiner { - function combine(x:a, y:uint256) -> uint256; +trait Combiner { + function combine(x:a, y:uint256) returns (uint256) ; } -instance storage(array(uint256)):Combiner { - function combine(x:storage(array(uint256)), y:uint256) -> uint256 { return y; } +impl Combiner>> { + function combine(x:storage>, y:uint256) returns (uint256) { return y; } } contract C { - value:array(uint256); + value:array; constructor() {} - public function viaUfcs(y:uint256) -> uint256 { + function viaUfcs(y:uint256) public returns (uint256) { return value.combine(y); } } @@ -611,24 +610,24 @@ contract C { fn local_and_parameter_ufcs_prepend_receivers_and_share_instance_method() { let (db, output) = specialize_src( r#" -forall a . class a:Combiner { - function combine(x:a, y:word) -> word; +trait Combiner { + function combine(x:a, y:word) returns (word) ; } -instance word:Combiner { - function combine(x:word, y:word) -> word { return y; } +impl Combiner { + function combine(x:word, y:word) returns (word) { return y; } } -function viaParam(paramReceiver:word, paramArg:word) -> word { +function viaParam(paramReceiver:word, paramArg:word) returns (word) { return paramReceiver.combine(paramArg); } -function viaLocal(seed:word, localArg:word) -> word { +function viaLocal(seed:word, localArg:word) returns (word) { let localReceiver:word = seed; return localReceiver.combine(localArg); } -function main(x:word, y:word) -> word { +function main(x:word, y:word) returns (word) { return viaParam(viaLocal(x, y), y); } "#, @@ -712,20 +711,20 @@ fn evidence_replay_resolves_imported_instance_methods() { BTreeMap::new(), )); db.module_fs_snapshot = Some(module_fs_snapshot_for_roots(db, [main_root.as_path()])); - let lib_path = main_root.join("lib.solc"); - let main_path = main_root.join("main.solc"); + let lib_path = main_root.join("lib.sol"); + let main_path = main_root.join("main.sol"); let lib_file = source_file_at_path( db, &lib_path, r#" export { Boxed }; -forall a . class a:Boxed { - function id(x:a) -> a; +trait Boxed { + function id(x:a) returns (a) ; } -instance word:Boxed { - function id(x:word) -> word { return x; } +impl Boxed { + function id(x:word) returns (word) { return x; } } "#, ); @@ -733,10 +732,10 @@ instance word:Boxed { db, &main_path, r#" -import lib.{Boxed}; +import {Boxed} from lib; contract C { - public function main(x:word) -> word { + function main(x:word) public returns (word) { return Boxed.id(x); } } @@ -774,53 +773,53 @@ fn same_named_classes_in_different_modules_get_distinct_method_symbols() { let modules = [ ( - "left.solc", + "left.sol", r#" export { left }; -forall a . class a:Pick { - function choose(x:a) -> word; +trait Pick { + function choose(x:a) returns (word) ; } -instance word:Pick { - function choose(x:word) -> word { +impl Pick { + function choose(x:word) returns (word) { let y : word; assembly { y := sload(x) } return y; } } -function left(x:word) -> word { return Pick.choose(x); } +function left(x:word) returns (word) { return Pick.choose(x); } "#, ), ( - "right.solc", + "right.sol", r#" export { right }; -forall a . class a:Pick { - function choose(x:a) -> word; +trait Pick { + function choose(x:a) returns (word) ; } -instance word:Pick { - function choose(x:word) -> word { +impl Pick { + function choose(x:word) returns (word) { let y : word; assembly { y := sload(x) } return x; } } -function right(x:word) -> word { return Pick.choose(x); } +function right(x:word) returns (word) { return Pick.choose(x); } "#, ), ( - "main.solc", + "main.sol", r#" -import left.{left}; -import right.{right}; +import {left} from left; +import {right} from right; contract C { - public function main(x:word) -> word { + function main(x:word) public returns (word) { let unused = right(x); return left(x); } @@ -835,7 +834,7 @@ contract C { let file = source_file_at_path(db, &path, src); let key = module_key_for_path(LibraryId::Main, &main_root, &path).unwrap(); db.insert_module_file(key, file); - if name == "main.solc" { + if name == "main.sol" { main_file = Some(file); } } @@ -865,43 +864,43 @@ fn same_named_adts_in_different_modules_get_distinct_generic_symbols() { let modules = [ ( - "common.solc", + "common.sol", r#" export { id }; -forall a . function id(x:a) -> a { return x; } +function id(x:a) returns (a) { return x; } "#, ), ( - "left.solc", + "left.sol", r#" -import common.{id}; +import {id} from common; export { left }; -data Foo = Foo(word); -function left(x:word) -> word { +enum Foo {Foo(word)} +function left(x:word) returns (word) { let value : Foo = id(Foo(x)); - match value { | Foo(result) => return result; } + match (value) { case Foo(result) { return result; }} } "#, ), ( - "right.solc", + "right.sol", r#" -import common.{id}; +import {id} from common; export { right }; -data Foo = Foo(word); -function right(x:word) -> word { +enum Foo {Foo(word)} +function right(x:word) returns (word) { let value : Foo = id(Foo(x)); - match value { | Foo(result) => return result; } + match (value) { case Foo(result) { return result; }} } "#, ), ( - "main.solc", + "main.sol", r#" -import left.{left}; -import right.{right}; +import {left} from left; +import {right} from right; contract C { - public function main(x:word) -> word { + function main(x:word) public returns (word) { let unused = right(x); return left(x); } @@ -916,7 +915,7 @@ contract C { let file = source_file_at_path(db, &path, src); let key = module_key_for_path(LibraryId::Main, &main_root, &path).unwrap(); db.insert_module_file(key, file); - if name == "main.solc" { + if name == "main.sol" { main_file = Some(file); } } @@ -944,8 +943,8 @@ fn derived_generic_specialization_uses_the_imported_adt_definition_module() { BTreeMap::new(), )); db.module_fs_snapshot = Some(module_fs_snapshot_for_roots(db, [main_root.as_path()])); - let lib_path = main_root.join("lib.solc"); - let main_path = main_root.join("main.solc"); + let lib_path = main_root.join("lib.sol"); + let main_path = main_root.join("main.sol"); let lib_file = source_file_at_path( db, &lib_path, @@ -955,14 +954,14 @@ pragma no-bounded-variable-condition; export { Box(*), exercise }; -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; +trait Generic { + function from(x:a) returns (rep) ; + function to(x:rep) returns (a) ; } -data Box = Box(word, bool); +enum Box {Box(word, bool)} -function exercise(x:Box) -> Box { +function exercise(x:Box) returns (Box) { let rep : (word, bool) = Generic.from(x); return Generic.to(rep); } @@ -972,10 +971,10 @@ function exercise(x:Box) -> Box { db, &main_path, r#" -import lib.{*}; +import * from lib; contract C { - function main(x:Box) -> Box { return exercise(x); } + function main(x:Box) returns (Box) { return exercise(x); } } "#, ); @@ -1007,26 +1006,26 @@ contract C { fn invokable_invoke_replays_call_site_evidence() { let (_db, output) = specialize_src( r#" -forall a b c . c : invokable(a, b) => function app(f : c, x : a) -> b { +function app(f : c, x : a) returns (b) where c : invokable { return invokable.invoke(f, x); } -data t_id = t_id; +enum t_id {t_id} -function impure(x : word) -> word { +function impure(x : word) returns (word) { let y : word; assembly { y := sload(x) } return y; } -instance t_id : invokable(word, word) { - function invoke(self : t_id, x : word) -> word { +impl invokable { + function invoke(self : t_id, x : word) returns (word) { return impure(x); } } contract C { - public function main(x : word) -> word { + function main(x : word) public returns (word) { return app(t_id, x); } } @@ -1057,42 +1056,39 @@ contract C { fn mptc_phantom_extras_recovered_before_naming_and_body_lowering() { let (_db, output) = specialize_src( r#" -data Foo = Foo(word); +enum Foo {Foo(word)} -forall self rep. -class self:Encoder(rep) { - function encode(x:self, hint:word) -> rep; +trait Encoder { + function encode(x:self, hint:word) returns (rep) ; } -forall rep r. -class rep:Sink(r) { - function sink(x:rep) -> r; +trait Sink { + function sink(x:rep) returns (r) ; } -instance Foo:Encoder(word) { - function encode(x:Foo, hint:word) -> word { +impl Encoder { + function encode(x:Foo, hint:word) returns (word) { let y : word; assembly { y := sload(hint) } - match x { | Foo(v) => return v; } + match (x) { case Foo(v) { return v; }} } } -instance word:Sink(word) { - function sink(x:word) -> word { +impl Sink { + function sink(x:word) returns (word) { let y : word; assembly { y := sload(x) } return x; } } -forall a rep . a:Encoder(rep), rep:Sink(word) => -function f(x:a) -> word { +function f(x:a) returns (word) where a: Encoder, rep: Sink { let r : rep = Encoder.encode(x, 0); return Sink.sink(r); } contract C { - public function main(x : word) -> word { + function main(x : word) public returns (word) { return f(Foo(x)); } } @@ -1125,33 +1121,31 @@ contract C { fn instance_method_names_include_the_complete_class_head() { let (_db, output) = specialize_src( r#" -data Box = Box(word); +enum Box {Box(word)} -forall self rep. -class self:Convert(rep) { - function toRep(x:self) -> rep; - function fromRep(x:rep) -> self; +trait Convert { + function toRep(x:self) returns (rep) ; + function fromRep(x:rep) returns (self) ; } -instance Box:Convert(word) { - function toRep(x:Box) -> word { - match x { | Box(w) => return w; } +impl Convert { + function toRep(x:Box) returns (word) { + match (x) { case Box(w) { return w; }} } - function fromRep(x:word) -> Box { + function fromRep(x:word) returns (Box) { return Box(x); } } -forall a rep . a:Convert(rep) => -function roundtrip(x:a) -> a { +function roundtrip(x:a) returns (a) where a: Convert { let r : rep = Convert.toRep(x); return Convert.fromRep(r); } contract C { - public function main(x:word) -> word { + function main(x:word) public returns (word) { let b : Box = roundtrip(Box(x)); - match b { | Box(w) => return w; } + match (b) { case Box(w) { return w; }} } } "#, @@ -1179,13 +1173,13 @@ contract C { fn ensure_closed_failure_aborts_that_specialization() { let (_db, output) = specialize_src( r#" -forall a . function leak() -> a { +function leak() returns (a) { let y : a; return y; } contract C { - public function main() -> () { + function main() public returns () { let x = leak(); return (); } @@ -1213,11 +1207,11 @@ contract C { #[test] fn generated_contract_dispatch_uses_explicit_std_dispatch_import() { let source = r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract C { - public function answer() -> uint256 { return uint256(1); } + function answer() public returns (uint256) { return uint256(1); } } "#; let output = specialize_src_with_std(source); @@ -1250,11 +1244,11 @@ contract C { fn generated_contract_dispatch_rejects_public_comptime_params_before_runtime_rooting() { let output = specialize_src_with_std( r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract C { - public function answer(comptime x: word) -> word { + function answer(comptime x: word) public returns (word) { return x; } } @@ -1296,11 +1290,11 @@ contract C { #[test] fn generated_contract_dispatch_keeps_the_original_source_file() { let src = r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract C { - public function answer() -> uint256 { + function answer() public returns (uint256) { return uint256(1); } } @@ -1365,12 +1359,12 @@ contract C { #[test] fn already_prepared_input_keeps_std_dispatch_origin() { let src = r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract C { - payable constructor(seed: uint256) { let saved = seed; } - public function answer() -> uint256 { return uint256(1); } + constructor(seed: uint256) payable { let saved = seed; } + function answer() public returns (uint256) { return uint256(1); } } "#; let (db, file, _) = specialize_src_with_std_and_db(src); @@ -1404,8 +1398,8 @@ contract C { fn source_names_are_qualified_across_contracts() { let (_db, output) = specialize_src( r#" -contract A { public function main() -> word { return 1; } } -contract B { public function main() -> word { return 2; } } +contract A { function main() public returns (word) { return 1; } } +contract B { function main() public returns (word) { return 2; } } "#, ); @@ -1448,13 +1442,13 @@ contract B { public function main() -> word { return 2; } } fn dispatch_abi_shape_is_preserved_in_std_dispatch_mono_ir() { let output = specialize_src_with_std( r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract PayableTest { constructor() {} - public payable function deposit() -> uint256 { return uint256(1); } - payable fallback() -> () {} + function deposit() public payable returns (uint256) { return uint256(1); } + fallback() payable {} } "#, ); @@ -1515,11 +1509,11 @@ contract PayableTest { fn tuple_dispatch_uses_the_canonical_abi_selector() { let output = specialize_src_with_std( r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract TupleSelector { - public function pack(point: (uint256, uint256), tag: uint256) -> uint256 { + function pack(point: (uint256, uint256), tag: uint256) public returns (uint256) { return tag; } } @@ -1555,33 +1549,33 @@ contract TupleSelector { fn dispatch_selector_patch_uses_identity_safe_method_markers() { let output = specialize_src_with_std( r#" -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; -data XDispatchNameTy_D_veryLongX = Wrapped(uint256); +enum XDispatchNameTy_D_veryLongX {Wrapped(uint256)} contract C { - public function putOpt(k: uint256, v: uint256) -> () { return (); } - public function putOptPair(k: uint256, a: uint256, b: uint256) -> () { return (); } - public function clearOpt(k: uint256) -> () { return (); } - public function clearOptPair(k: uint256) -> () { return (); } - public function foo(k: uint256) -> () { return (); } - public function foo_bar(k: uint256, v: uint256) -> () { return (); } - public function f(x: XDispatchNameTy_D_veryLongX) -> uint256 { return 7; } + function putOpt(k: uint256, v: uint256) public returns () { return (); } + function putOptPair(k: uint256, a: uint256, b: uint256) public returns () { return (); } + function clearOpt(k: uint256) public returns () { return (); } + function clearOptPair(k: uint256) public returns () { return (); } + function foo(k: uint256) public returns () { return (); } + function foo_bar(k: uint256, v: uint256) public returns () { return (); } + function f(x: XDispatchNameTy_D_veryLongX) public returns (uint256) { return 7; } } contract D { - public function veryLong(k: uint256) -> uint256 { return k; } + function veryLong(k: uint256) public returns (uint256) { return k; } } contract A { - public function B_C(k: uint256) -> uint256 { return k; } + function B_C(k: uint256) public returns (uint256) { return k; } } contract A_B { - public function C(k: uint256) -> uint256 { return k; } + function C(k: uint256) public returns (uint256) { return k; } } "#, ); @@ -1669,12 +1663,12 @@ contract A_B { fn constructor_overlay_roots_three_argument_deployment_main() { let output = specialize_src_with_std( r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract C { constructor(x : uint256, y : uint256, z : uint256) { let saved = x; } - function main() -> () { return (); } + function main() returns () { return (); } } "#, ); @@ -1713,7 +1707,7 @@ contract C { fn specializes_reference_constructor_and_dispatch_collision_regressions() { let repo = repo_root(); let corpus = repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch"); - for fixture in ["miniERC20.solc", "weth9.solc"] { + for fixture in ["miniERC20.sol", "weth9.sol"] { let output = specialize_fixture(&corpus.join(fixture)); assert_eq!(output.diagnostics, Vec::new(), "{fixture}"); } @@ -1723,15 +1717,15 @@ fn specializes_reference_constructor_and_dispatch_collision_regressions() { fn mono_ir_carries_frontend_desugar_hook_plan() { let repo = repo_root(); let storage = specialize_fixture( - &repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage.solc"), + &repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/dispatch/storage.sol"), ); let lambda = specialize_fixture( - &repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SimpleLambda.solc"), + &repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/cases/SimpleLambda.sol"), ); let (_if_db, if_output) = specialize_src( r#" contract C { - public function main() -> word { + function main() public returns (word) { if (true) { return 1; } else { return 0; } } } @@ -1784,11 +1778,10 @@ fn tuple_syntax_specializes_through_product_constructors() { let (_db, output) = specialize_src( r#" contract C { - public function main(x:word, y:word, z:word) -> pair(word, pair(word, word)) { + function main(x:word, y:word, z:word) public returns (pair>) { let t = (x, y, z); - match t { - | (a, b, c) => return (a, b, c); - } + match (t) { + case (a, b, c) { return (a, b, c); }} } } "#, @@ -1831,25 +1824,25 @@ fn specializes_p7_cited_regression_corpus() { let repo = repo_root(); let corpus = repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples"); for fixture in [ - "cases/app.solc", - "cases/mptc-chain-phantom.solc", - "cases/mptc-both-templates.solc", - "dispatch/nonpayable_ctor.solc", - "dispatch/storage.solc", - "cases/SimpleLambda.solc", - "dispatch/specialise_sum_of_product.solc", + "cases/app.sol", + "cases/mptc-chain-phantom.sol", + "cases/mptc-both-templates.sol", + "dispatch/nonpayable_ctor.sol", + "dispatch/storage.sol", + "cases/SimpleLambda.sol", + "dispatch/specialise_sum_of_product.sol", ] { let output = specialize_fixture(&corpus.join(fixture)); assert_eq!(output.diagnostics, Vec::new(), "{fixture}"); } - let basic = specialize_fixture(&corpus.join("dispatch/basic.solc")); - assert_eq!(basic.diagnostics, Vec::new(), "dispatch/basic.solc"); + let basic = specialize_fixture(&corpus.join("dispatch/basic.sol")); + assert_eq!(basic.diagnostics, Vec::new(), "dispatch/basic.sol"); assert!( !basic.module.items.iter().any(|item| match item { MonoItem::Function(function) => function.body.iter().any(stmt_has_closure_dispatch), _ => false, }), - "dispatch/basic.solc retained closure dispatch" + "dispatch/basic.sol retained closure dispatch" ); let basic_contract = basic .module @@ -1874,7 +1867,7 @@ fn specializes_p7_cited_regression_corpus() { "{:?}", basic_contract.entries ); - let payable = specialize_fixture(&corpus.join("dispatch/payable.solc")); + let payable = specialize_fixture(&corpus.join("dispatch/payable.sol")); let payable_contract = payable .module .items @@ -1907,7 +1900,7 @@ fn specializes_p7_cited_regression_corpus() { fn folds_direct_function_compose_closure_fixture() { let repo = repo_root(); let output = specialize_fixture( - &repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/spec/06comp.solc"), + &repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/spec/06comp.sol"), ); assert_eq!(output.diagnostics, Vec::new()); @@ -1915,24 +1908,23 @@ fn folds_direct_function_compose_closure_fixture() { } const OPERATOR_CUSTOM_UINT_ADD: &str = r#" -import std.{*}; +import * from std; -data uint = u(word); +enum uint {u(word)} -instance uint:Add { - function add(x:uint, y:uint) -> uint { +impl Add { + function add(x:uint, y:uint) returns (uint) { return uint.u(42); } } -function unwrap(x:uint) -> word { - match x { - | uint.u(w) => return w; - } +function unwrap(x:uint) returns (word) { + match (x) { + case uint.u(w) { return w; }} } contract C { - public function main() -> word { + function main() public returns (word) { let a:uint = uint.u(1); let b:uint = uint.u(2); let c:uint = a + b; @@ -1942,26 +1934,24 @@ contract C { "#; const OPERATOR_METERS_ADD: &str = r#" -import std.{*}; +import * from std; -data meters = meters(word); +enum meters {meters(word)} -instance meters:Add { - function add(x:meters, y:meters) -> meters { - match x, y { - | meters(xw), meters(yw) => return meters(addWord(xw, yw)); - } +impl Add { + function add(x:meters, y:meters) returns (meters) { + match (x, y) { + case (meters(xw), meters(yw)) { return meters(addWord(xw, yw)); }} } } -function unwrap(x:meters) -> word { - match x { - | meters(w) => return w; - } +function unwrap(x:meters) returns (word) { + match (x) { + case meters(w) { return w; }} } contract C { - public function main() -> word { + function main() public returns (word) { let a:meters = meters(1); let b:meters = meters(2); let c:meters = a + b; @@ -1971,28 +1961,26 @@ contract C { "#; const OPERATOR_METERS_ORD: &str = r#" -import std.{*}; +import * from std; -data meters = meters(word); +enum meters {meters(word)} -instance meters:Eq { - function eq(x:meters, y:meters) -> bool { - match x, y { - | meters(xw), meters(yw) => return eqWord(xw, yw); - } +impl Eq { + function eq(x:meters, y:meters) returns (bool) { + match (x, y) { + case (meters(xw), meters(yw)) { return eqWord(xw, yw); }} } } -instance meters:Ord { - function gt(x:meters, y:meters) -> bool { - match x, y { - | meters(xw), meters(yw) => return gtWord(xw, yw); - } +impl Ord { + function gt(x:meters, y:meters) returns (bool) { + match (x, y) { + case (meters(xw), meters(yw)) { return gtWord(xw, yw); }} } } contract C { - public function main() -> word { + function main() public returns (word) { let a:meters = meters(1); let b:meters = meters(2); if (a < b) { @@ -2005,59 +1993,59 @@ contract C { "#; const OPERATOR_CUSTOM_MUL: &str = r#" -import std.{*}; +import * from std; -data Weird = Weird(word); +enum Weird {Weird(word)} -instance Weird:Mul { - function mul(x:Weird, y:Weird) -> Weird { +impl Mul { + function mul(x:Weird, y:Weird) returns (Weird) { return Weird(99); } } contract C { - public function main() -> word { + function main() public returns (word) { let result : Weird = Weird(2) * Weird(3); - match result { | Weird(value) => return value; } + match (result) { case Weird(value) { return value; }} } } "#; const OPERATOR_CUSTOM_EQ: &str = r#" -import std.{*}; +import * from std; -data Weird = Weird(word); +enum Weird {Weird(word)} -instance Weird:Eq { - function eq(x:Weird, y:Weird) -> bool { +impl Eq { + function eq(x:Weird, y:Weird) returns (bool) { return false; } } contract C { - public function main() -> word { + function main() public returns (word) { if (Weird(1) == Weird(1)) { return 0; } else { return 99; } } } "#; const OPERATOR_VISIBLE_BOOL_FUNCTIONS: &str = r#" -function and(x:bool, y:bool) -> bool { return false; } -function or(x:bool, y:bool) -> bool { return false; } -function not(x:bool) -> bool { return true; } +function and(x:bool, y:bool) returns (bool) { return false; } +function or(x:bool, y:bool) returns (bool) { return false; } +function not(x:bool) returns (bool) { return true; } contract C { - public function main() -> word { + function main() public returns (word) { if ((true && true) || !true) { return 0; } else { return 99; } } } "#; const OPERATOR_WORD_ADD: &str = r#" -import std.{*}; +import * from std; contract C { - public function main() -> word { + function main() public returns (word) { return 1 + 2; } } @@ -2102,15 +2090,15 @@ fn every_audited_operator_uses_its_selected_semantics() { ] { let src = format!( r#" -import std.{{*}}; -data Weird = Weird(word); -instance Weird:{class} {{ - function {method}(x:Weird, y:Weird) -> Weird {{ return Weird({expected}); }} +import * from std; +enum Weird {{ Weird(word) }} +impl {class} {{ + function {method}(x: Weird, y: Weird) returns (Weird) {{ return Weird({expected}); }} }} contract C {{ - public function main() -> word {{ - let result : Weird = Weird(8) {operator} Weird(3); - match result {{ | Weird(value) => return value; }} + function main() public returns (word) {{ + let result: Weird = Weird(8) {operator} Weird(3); + match (result) {{ case Weird(value) {{ return value; }} }} }} }} "# @@ -2126,13 +2114,13 @@ contract C {{ let not_eq = specialize_src_with_std( r#" -import std.{*}; -data Weird = Weird(word); -instance Weird:Eq { - function eq(x:Weird, y:Weird) -> bool { return true; } +import * from std; +enum Weird {Weird(word)} +impl Eq { + function eq(x:Weird, y:Weird) returns (bool) { return true; } } contract C { - public function main() -> word { + function main() public returns (word) { if (Weird(1) != Weird(2)) { return 0; } else { return 96; } } } @@ -2144,19 +2132,19 @@ contract C { for (label, definition, expression, expected) in [ ( "And", - "function and(x:bool, y:bool) -> bool { return false; }", + "function and(x:bool, y:bool) returns (bool) { return false; }", "true && true", "0", ), ( "Or", - "function or(x:bool, y:bool) -> bool { return false; }", + "function or(x:bool, y:bool) returns (bool) { return false; }", "false || true", "0", ), ( "Not", - "function not(x:bool) -> bool { return true; }", + "function not(x:bool) returns (bool) { return true; }", "!true", "97", ), @@ -2165,7 +2153,7 @@ contract C { r#" {definition} contract C {{ - public function main() -> word {{ + function main() public returns (word) {{ if ({expression}) {{ return 97; }} else {{ return 0; }} }} }} @@ -2185,10 +2173,10 @@ contract C {{ fn comptime_obligations_are_carried_into_mono_side_table() { let (_db, output) = specialize_src( r#" -function need(comptime x : word) -> word { return x; } +function need(comptime x : word) returns (word) { return x; } contract C { - public function main(x : word) -> comptime word { + function main(x : word) public returns (comptime) { return need(x); } } @@ -2224,15 +2212,15 @@ contract C { fn derived_generic_evidence_generates_from_body() { let (_db, output) = specialize_src( r#" -data Pair = Pair(word, word); +enum Pair {Pair(word, word)} -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; +trait Generic { + function from(x:a) returns (rep) ; + function to(x:rep) returns (a) ; } contract C { - public function main(x:Pair) -> pair(word, word) { + function main(x:Pair) public returns (pair) { return Generic.from(x); } } @@ -2254,23 +2242,23 @@ fn derived_class_wrapper_converts_exact_self_arguments_and_returns() { pragma no-patterson-condition; pragma no-bounded-variable-condition; -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; +trait Generic { + function from(x:a) returns (rep) ; + function to(x:rep) returns (a) ; } -forall a . class a:CloneLike { - function clone(x:a) -> a; +trait CloneLike { + function clone(x:a) returns (a) ; } -instance word:CloneLike { - function clone(x:word) -> word { return x; } +impl CloneLike { + function clone(x:word) returns (word) { return x; } } #[derive(CloneLike)] -data Box = Box(word); +enum Box {Box(word)} -function main(x:Box) -> Box { +function main(x:Box) returns (Box) { return CloneLike.clone(x); } "#, @@ -2332,23 +2320,23 @@ fn derived_class_wrapper_keeps_method_binders_distinct_from_self() { pragma no-patterson-condition; pragma no-bounded-variable-condition; -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; +trait Generic { + function from(x:a) returns (rep) ; + function to(x:rep) returns (a) ; } -forall self . class self:Choose { - forall x . function choose(value:x, witness:self) -> x; +trait Choose { + function choose(value:x, witness:self) returns (x) ; } -instance word:Choose { - forall x . function choose(value:x, witness:word) -> x { return value; } +impl Choose { + function choose(value:x, witness:word) returns (x) { return value; } } #[derive(Choose)] -data Box = Box(word); +enum Box {Box(word)} -function main(value:Box, witness:Box) -> Box { +function main(value:Box, witness:Box) returns (Box) { return Choose.choose(value, witness); } "#, @@ -2409,28 +2397,28 @@ pragma no-patterson-condition; pragma no-bounded-variable-condition; pragma no-generic-instance-for Box; -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; +trait Generic { + function from(x:a) returns (rep) ; + function to(x:rep) returns (a) ; } -forall a . class a:CloneLike { - function clone(x:a) -> a; +trait CloneLike { + function clone(x:a) returns (a) ; } -instance word:CloneLike { - function clone(x:word) -> word { return x; } +impl CloneLike { + function clone(x:word) returns (word) { return x; } } #[derive(CloneLike)] -data Box = Box(bool); +enum Box {Box(bool)} -instance Box:Generic(word) { - function from(x:Box) -> word { return 7; } - function to(x:word) -> Box { return Box(false); } +impl Generic { + function from(x:Box) returns (word) { return 7; } + function to(x:word) returns (Box) { return Box(false); } } -function main(x:Box) -> Box { +function main(x:Box) returns (Box) { return CloneLike.clone(x); } "#, @@ -2467,8 +2455,8 @@ fn derived_class_wrapper_uses_the_imported_definition_environment() { BTreeMap::new(), )); db.module_fs_snapshot = Some(module_fs_snapshot_for_roots(db, [main_root.as_path()])); - let lib_path = main_root.join("lib.solc"); - let main_path = main_root.join("main.solc"); + let lib_path = main_root.join("lib.sol"); + let main_path = main_root.join("main.sol"); let lib_file = source_file_at_path( db, &lib_path, @@ -2478,23 +2466,23 @@ pragma no-bounded-variable-condition; export { Box(*), cloneBox }; -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; +trait Generic { + function from(x:a) returns (rep) ; + function to(x:rep) returns (a) ; } -forall a . class a:CloneLike { - function clone(x:a) -> a; +trait CloneLike { + function clone(x:a) returns (a) ; } -instance word:CloneLike { - function clone(x:word) -> word { return x; } +impl CloneLike { + function clone(x:word) returns (word) { return x; } } #[derive(CloneLike)] -data Box = Box(word); +enum Box {Box(word)} -function cloneBox(x:Box) -> Box { +function cloneBox(x:Box) returns (Box) { return CloneLike.clone(x); } "#, @@ -2505,16 +2493,16 @@ function cloneBox(x:Box) -> Box { r#" import lib; -forall a . class a:CloneLike { - function clone(x:a) -> a; +trait CloneLike { + function clone(x:a) returns (a) ; } -instance word:CloneLike { - function clone(x:word) -> word { return x; } +impl CloneLike { + function clone(x:word) returns (word) { return x; } } contract C { - function main(x:lib.Box) -> lib.Box { + function main(x:lib.Box) returns (lib.Box) { return lib.cloneBox(x); } } @@ -2559,87 +2547,86 @@ fn derived_class_and_instance_specializations_are_proof_aware_across_modules() { let modules = [ ( - "lib.solc", + "lib.sol", r#" pragma no-patterson-condition; pragma no-bounded-variable-condition; export { Pick, Wrap(*) }; -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; +trait Generic { + function from(x:a) returns (rep) ; + function to(x:rep) returns (a) ; } -forall a . class a:Pick { - function pick(x:a) -> word; +trait Pick { + function pick(x:a) returns (word) ; } -forall a b . a:Pick, b:Pick => -instance (a,b):Pick { - function pick(x:(a,b)) -> word { - match x { - | (left, right) => - let left_value = Pick.pick(left); +impl Pick<(a,b)> where a: Pick, b: Pick { + function pick(x:(a,b)) returns (word) { + match (x) { + case (left, right) { +let left_value = Pick.pick(left); let right_value = Pick.pick(right); let result : word; assembly { result := add(mul(left_value, 10), right_value) } return result; - } + }} } } #[derive(Pick)] -data Wrap(a) = Wrap(a, a); +enum Wrap {Wrap(a, a)} "#, ), ( - "left.solc", + "left.sol", r#" -import lib.{*}; +import * from lib; export { left }; -instance word:Pick { - function pick(x:word) -> word { +impl Pick { + function pick(x:word) returns (word) { let result : word; assembly { result := sload(x) } return result; } } -function left(x:Wrap(word)) -> word { +function left(x:Wrap) returns (word) { return Pick.pick(x); } "#, ), ( - "right.solc", + "right.sol", r#" -import lib.{*}; +import * from lib; export { right }; -instance word:Pick { - function pick(x:word) -> word { +impl Pick { + function pick(x:word) returns (word) { let result : word; assembly { result := sload(add(x, 1)) } return result; } } -function right(x:Wrap(word)) -> word { +function right(x:Wrap) returns (word) { return Pick.pick(x); } "#, ), ( - "main.solc", + "main.sol", r#" -import lib.{*}; -import left.{left}; -import right.{right}; +import * from lib; +import {left} from left; +import {right} from right; contract C { - function main(x:Wrap(word), y:Wrap(word)) -> (word, word) { + function main(x:Wrap, y:Wrap) returns ((word, word)) { return (left(x), right(y)); } } @@ -2656,10 +2643,10 @@ contract C { files.insert(name, file); } - let main_file = files["main.solc"]; - let lib_file = files["lib.solc"]; - let left_file = files["left.solc"]; - let right_file = files["right.solc"]; + let main_file = files["main.sol"]; + let lib_file = files["lib.sol"]; + let left_file = files["left.sol"]; + let right_file = files["right.sol"]; let module = parse_file_to_hir(db, main_file).module(db); let output = specialize_module(db, module, SpecializeOptions::default()); @@ -2764,23 +2751,23 @@ fn derived_class_wrapper_preserves_adt_arguments_and_reuses_proofs() { pragma no-patterson-condition; pragma no-bounded-variable-condition; -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; +trait Generic { + function from(x:a) returns (rep) ; + function to(x:rep) returns (a) ; } -forall a . class a:CloneLike { - function clone(x:a) -> a; +trait CloneLike { + function clone(x:a) returns (a) ; } -instance word:CloneLike { - function clone(x:word) -> word { return x; } +impl CloneLike { + function clone(x:word) returns (word) { return x; } } #[derive(CloneLike)] -data Wrap(a) = Wrap(a); +enum Wrap {Wrap(a)} -function main(x:Wrap(word)) -> Wrap(word) { +function main(x:Wrap) returns (Wrap) { let first = CloneLike.clone(x); return CloneLike.clone(first); } @@ -2830,16 +2817,16 @@ function main(x:Wrap(word)) -> Wrap(word) { fn derived_class_wrapper_reuses_its_reservation_for_recursive_adts() { let output = specialize_src_with_std( r#" -import std.{*}; -import std.Generic.{*}; +import * from std; +import * from std.Generic; pragma no-patterson-condition; pragma no-bounded-variable-condition; #[derive(Eq)] -data List = Nil | Cons(word, List); +enum List {Nil , Cons(word, List)} -function main(x:List) -> bool { +function main(x:List) returns (bool) { return Eq.eq(x, x); } "#, @@ -2871,23 +2858,23 @@ fn derived_class_wrapper_rejects_nested_self_without_emitting_unchecked_ir() { pragma no-patterson-condition; pragma no-bounded-variable-condition; -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; +trait Generic { + function from(x:a) returns (rep) ; + function to(x:rep) returns (a) ; } -forall a . class a:NestedSelf { - function inspect(x:a, nested:(a, word)) -> bool; +trait NestedSelf { + function inspect(x:a, nested:(a, word)) returns (bool) ; } -instance word:NestedSelf { - function inspect(x:word, nested:(word, word)) -> bool { return true; } +impl NestedSelf { + function inspect(x:word, nested:(word, word)) returns (bool) { return true; } } #[derive(NestedSelf)] -data Box = Box(word); +enum Box {Box(word)} -function main(x:Box) -> bool { +function main(x:Box) returns (bool) { return NestedSelf.inspect(x, (x, 0)); } "#, @@ -2909,16 +2896,16 @@ function main(x:Box) -> bool { fn derived_class_wrapper_uses_absurd_for_an_empty_adt() { let output = specialize_src_with_std( r#" -import std.{*}; +import * from std; -forall a . class a:Make { - function make() -> a; +trait Make { + function make() returns (a) ; } #[derive(Make)] -data Never; +enum Never {} -function main() -> Never { +function main() returns (Never) { return Make.make(); } "#, @@ -2957,22 +2944,26 @@ function main() -> Never { fn generic_abi_decoder_evidence_specializes_for_internal_sum_adt() { let output = specialize_src_with_std( r#" -import std.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.Generic; +import * from std.ABIGeneric; -data Choice = Left(uint256) | Right(address); +enum Choice {Left(uint256) , Right(address)} contract C { - function main() -> word { + function main() returns (word) { let buf = allocate_zeroed_memory(64); let rdr : MemoryWordReader = MemoryWordReader(buf); - let dec : ABIDecoder(Choice, MemoryWordReader) = - ABIDecoder(rdr) : ABIDecoder(Choice, MemoryWordReader); + let dec : ABIDecoder = + ABIDecoder(rdr) ; let value : Choice = decode(dec, 0); - match value { - | Choice.Left(x) => return Typedef.rep(x); - | Choice.Right(_) => return 0; + match (value) { + case Choice.Left(x) { + return Typedef.rep(x); + } + case Choice.Right(_) { + return 0; + } } } } @@ -2997,10 +2988,10 @@ contract C { fn snapshot_small_specialized_module() { let (db, output) = specialize_src( r#" -forall a . function id(x:a) -> a { return x; } +function id(x:a) returns (a) { return x; } contract C { - public function main(x:word) -> word { + function main(x:word) public returns (word) { return id(x); } } @@ -3035,9 +3026,9 @@ fn specializes_curated_typecheck_parity_corpus_files() { let repo = repo_root(); let corpus = repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples"); for fixture in [ - "spec/00answer.solc", - "spec/06comp.solc", - "cases/super-class.solc", + "spec/00answer.sol", + "spec/06comp.sol", + "cases/super-class.sol", ] { let output = specialize_fixture(&corpus.join(fixture)); assert_eq!(output.diagnostics, Vec::new(), "{fixture}"); @@ -3049,18 +3040,18 @@ fn specializes_comptime_evaluation_corpus_verdicts() { let repo = repo_root(); let corpus = repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples"); let passing = [ - "comptime/ct_asm_mem.solc", - "comptime/ct_chain_ok.solc", - "comptime/ct_let_ok.solc", - "comptime/ct_overloaded_ok.solc", - "comptime/ct_param_ok.solc", - "comptime/integer-basic.solc", - "comptime/integer-fib.solc", - "comptime/integer-lit-pat.solc", - "comptime/match_labels.solc", - "comptime/Plus.solc", - "comptime/string-lit-keccak.solc", - "comptime/string-lit-len.solc", + "comptime/ct_asm_mem.sol", + "comptime/ct_chain_ok.sol", + "comptime/ct_let_ok.sol", + "comptime/ct_overloaded_ok.sol", + "comptime/ct_param_ok.sol", + "comptime/integer-basic.sol", + "comptime/integer-fib.sol", + "comptime/integer-lit-pat.sol", + "comptime/match_labels.sol", + "comptime/Plus.sol", + "comptime/string-lit-keccak.sol", + "comptime/string-lit-len.sol", ]; for fixture in passing { let output = specialize_fixture(&corpus.join(fixture)); @@ -3072,7 +3063,7 @@ fn specializes_comptime_evaluation_corpus_verdicts() { fn folds_recursive_comptime_integer_function() { let (_db, output) = specialize_src( r#" -function fib(comptime n : integer) -> comptime integer { +function fib(comptime n : integer) returns (comptime) { if (integerLt(n, 2)) { return n; } else { @@ -3081,7 +3072,7 @@ function fib(comptime n : integer) -> comptime integer { } contract C { - public function main() -> word { + function main() public returns (word) { return wordFromInteger(fib(10)); } } @@ -3106,7 +3097,7 @@ contract C { fn folds_comptime_yul_mstore_mload_subset() { let (_db, output) = specialize_src( r#" -function storeLoad(x : word) -> word { +function storeLoad(x : word) returns (word) { let r : word; assembly { mstore(0, x) @@ -3116,8 +3107,8 @@ function storeLoad(x : word) -> word { } contract C { - public function main() -> word { - let res : comptime word = storeLoad(42); + function main() public returns (word) { + let res : comptime = storeLoad(42); return res; } } @@ -3133,7 +3124,7 @@ fn assembly_substitution_does_not_reuse_values_after_an_in_block_write() { let (db, output) = specialize_src( r#" contract C { - public function main(x: word) -> word { + function main(x: word) public returns (word) { let a: word = 1; assembly { a := add(a, x) @@ -3179,7 +3170,7 @@ fn assembly_substitution_does_not_capture_same_named_function_parameters() { let (db, output) = specialize_src( r#" contract C { - public function main() -> word { + function main() public returns (word) { let x : word = 1; let observed : word = 0; assembly { @@ -3246,12 +3237,12 @@ contract C { fn does_not_fold_user_function_shadowing_std_literal_intrinsic() { let (_db, output) = specialize_src( r#" -function keccakLit(a:string) -> word { +function keccakLit(a:string) returns (word) { return 0; } contract C { - public function main() -> word { + function main() public returns (word) { return keccakLit("abc"); } } @@ -3266,7 +3257,7 @@ contract C { fn folds_resolved_std_string_keccak_literal_intrinsic() { let repo = repo_root(); let fixture = repo.join( - "crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-keccak.solc", + "crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/string-lit-keccak.sol", ); let output = specialize_fixture(&fixture); @@ -3284,14 +3275,14 @@ fn folds_resolved_std_string_keccak_literal_intrinsic() { fn clones_and_deduplicates_folded_comptime_string_arguments() { let (db, _, output) = specialize_src_with_std_and_db( r#" -import std.{*}; +import * from std; -function consume(s:string, x:word) -> word { +function consume(s:string, x:word) returns (word) { return addWord(strlenLit(s), x); } contract C { - public function main(x:word) -> word { + function main(x:word) public returns (word) { return addWord(consume("abcd", x), consume(concatLit("ab", "cd"), x)); } } @@ -3332,18 +3323,18 @@ contract C { fn user_str_instance_clone_leaves_only_a_literal_materializer_call() { let output = specialize_src_with_std( r#" -import std.{*}; +import * from std; -data Wrapped = Wrapped(memory(string)); +enum Wrapped {Wrapped(memory)} -instance Wrapped:Str { - function fromString(s:string) -> Wrapped { +impl Str { + function fromString(s:string) returns (Wrapped) { return Wrapped(Str.fromString(s)); } } contract C { - public function main(x:word) -> word { + function main(x:word) public returns (word) { let wrapped:Wrapped = "abcd"; let source = "abcd"; let explicit:Wrapped = Str.fromString(source); @@ -3376,10 +3367,10 @@ contract C { fn require_accepts_a_string_literal_via_the_std_error_str_instance() { let output = specialize_src_with_std( r#" -import std.{*}; +import * from std; contract C { - public function main(cond:bool) -> () { + function main(cond:bool) public returns () { require(cond, "boom"); return (); } @@ -3405,13 +3396,13 @@ contract C { fn materializes_a_string_literal_through_a_memory_string_alias() { let output = specialize_src_with_std( r#" -import std.{*}; +import * from std; -type Text = memory(string); +type Text = memory; type Source = string; contract C { - public function main() -> word { + function main() public returns (word) { let implicit:Text = "x"; let source:Source = "y"; let explicit:Text = Str.fromString(source); @@ -3439,24 +3430,24 @@ contract C { fn string_clone_worklist_evaluates_clones_that_spawn_clones() { let output = specialize_src_with_std( r#" -import std.{*}; +import * from std; -function inner(s:string, x:word) -> word { +function inner(s:string, x:word) returns (word) { return addWord(strlenLit(s), x); } -function touch(x:word) -> () { +function touch(x:word) returns () { assembly { sstore(0, x) } } -function outer(s:string, x:word) -> word { +function outer(s:string, x:word) returns (word) { let result:word = inner(s, x); touch(x); return result; } contract C { - public function main(x:word) -> word { + function main(x:word) public returns (word) { return outer("abcd", x); } } @@ -3492,16 +3483,16 @@ contract C { fn recursive_string_clone_creation_consumes_global_fuel() { let output = specialize_src_with_std_options( r#" -import std.{*}; +import * from std; -function grow(s:string, x:word) -> word { +function grow(s:string, x:word) returns (word) { let result:word = grow(concatLit(s, "x"), x); assembly { sstore(0, x) } return result; } contract C { - public function main(x:word) -> word { + function main(x:word) public returns (word) { return grow("", x); } } @@ -3541,13 +3532,13 @@ contract C { fn desugars_memory_and_storage_array_literals_to_runtime_builders() { let output = specialize_src_with_std( r#" -import std.{*}; +import * from std; contract ArrayLit { - xs : array(uint256); + xs : array; - function main() -> uint256 { - let m : memory(DynArray(uint256)) = [1, 2, 3]; + function main() returns (uint256) { + let m : memory> = [1, 2, 3]; xs = [10, 20, 30]; return m[uint256(1)] + xs[uint256(2)]; } @@ -3593,13 +3584,13 @@ contract ArrayLit { fn routes_whole_storage_array_assignment_through_assign_instance() { let output = specialize_src_with_std( r#" -import std.{*}; +import * from std; contract ArrayCopy { - dst : array(uint256); - src : array(uint256); + dst : array; + src : array; - function main() -> () { + function main() returns () { dst = src; return (); } @@ -3627,23 +3618,23 @@ contract ArrayCopy { fn contract_fields_lower_through_storage_classes_and_prefix_offsets() { let (db, main_file, output) = specialize_src_with_std_and_db( r#" -import std.{*}; +import * from std; contract FieldAccess { first : uint256; second : uint256; third : uint256; - values : array(uint256); - balances : mapping(uint256, uint256); + values : array; + balances : mapping(uint256 => uint256); - function readFirst() -> uint256 { return first; } - function writeSecond(v:uint256) -> () { second = v; return (); } - function bumpThird(v:uint256) -> () { third += v; return (); } - function replaceValues() -> () { values = [uint256(4), uint256(5)]; return (); } - function readValue(k:uint256) -> uint256 { return values[k]; } - function readBalance(k:uint256) -> uint256 { return balances[k]; } + function readFirst() returns (uint256) { return first; } + function writeSecond(v:uint256) returns () { second = v; return (); } + function bumpThird(v:uint256) returns () { third += v; return (); } + function replaceValues() returns () { values = [uint256(4), uint256(5)]; return (); } + function readValue(k:uint256) returns (uint256) { return values[k]; } + function readBalance(k:uint256) returns (uint256) { return balances[k]; } - function main() -> uint256 { + function main() returns (uint256) { writeSecond(uint256(1)); bumpThird(uint256(2)); replaceValues(); @@ -3757,11 +3748,11 @@ contract FieldAccess { fn partial_contract_field_support_does_not_fall_back_to_legacy_slots() { let read = specialize_src_with_std( r#" -import std.{Proxy, storage, StorageSize}; +import {Proxy, storage, StorageSize} from std; contract C { value : word; - function main() -> word { return value; } + function main() returns (word) { return value; } } "#, ); @@ -3777,11 +3768,11 @@ contract C { let write = specialize_src_with_std( r#" -import std.{Proxy, storage, StorageSize, CanStore}; +import {Proxy, storage, StorageSize, CanStore} from std; contract C { value : word; - function main() -> word { + function main() returns (word) { value = value; return value; } @@ -3803,30 +3794,30 @@ contract C { fn array_indexes_preserve_non_identity_typedef_representations() { let (db, main_file, output) = specialize_src_with_std_and_db( r#" -import std.{*}; +import * from std; -data Shifted = Shifted(word); -instance Shifted:Typedef(word) { - function rep(x:Shifted) -> word { - match x { | Shifted(w) => return w + 100; } +enum Shifted {Shifted(word)} +impl Typedef { + function rep(x:Shifted) returns (word) { + match (x) { case Shifted(w) { return w + 100; }} } - function abs(w:word) -> Shifted { return Shifted(w - 100); } + function abs(w:word) returns (Shifted) { return Shifted(w - 100); } } -data Second = Second(word); -instance Second:Typedef(word) { - function rep(x:Second) -> word { - match x { | Second(w) => return w + 1; } +enum Second {Second(word)} +impl Typedef { + function rep(x:Second) returns (word) { + match (x) { case Second(w) { return w + 1; }} } - function abs(w:word) -> Second { return Second(w - 1); } + function abs(w:word) returns (Second) { return Second(w - 1); } } contract ReprArray { - xs : array(uint256); + xs : array; seed : word; - function main() -> word { - let m : memory(DynArray(Shifted)) = [Shifted(3), Shifted(4)]; + function main() returns (word) { + let m : memory> = [Shifted(3), Shifted(4)]; xs = [10, 20]; let idx : Second = Second(seed); let picked : Shifted = m[idx]; @@ -3878,13 +3869,13 @@ contract ReprArray { fn public_dynamic_array_return_reaches_abi_encoder() { let output = specialize_src_with_std( r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract PublicArray { constructor() {} - public function values() -> memory(DynArray(uint256)) { + function values() public returns (memory>) { return [1, 2, 3]; } } @@ -3911,14 +3902,14 @@ contract PublicArray { fn bool_and_nested_dynamic_storage_arrays_resolve_storage_conversions() { let output = specialize_src_with_std( r#" -import std.{*}; +import * from std; contract CollectionArray { - flags : array(bool); - grid : array(array(uint256)); - names : array(string); + flags : array; + grid : array>; + names : array; - function main() -> uint256 { + function main() returns (uint256) { Array.setLength(flags, uint256(0)); ArrayPush.push(flags, true); let flag : bool = flags[uint256(0)]; @@ -3926,15 +3917,15 @@ contract CollectionArray { Array.setLength(grid, uint256(1)); ArrayPush.push(grid[uint256(0)], uint256(7)); grid[uint256(0)][uint256(0)] = uint256(9); - let row : storage(array(uint256)) = grid[uint256(0)]; + let row : storage> = grid[uint256(0)]; ArrayPush.push(row, uint256(11)); - let s : memory(string) = "hello"; + let s : memory = "hello"; ArrayPush.push(names, s); names[uint256(0)] = s; - let loaded : memory(string) = names[uint256(0)]; + let loaded : memory = names[uint256(0)]; - if flag { return row[uint256(1)] + Length.length(names); } + if (flag) { return row[uint256(1)] + Length.length(names); } return uint256(0); } } @@ -3961,12 +3952,12 @@ contract CollectionArray { fn nested_bool_array_write_composes_storage_refs_without_intermediate_copy() { let output = specialize_src_with_std( r#" -import std.{*}; +import * from std; contract NestedBool { - grid : array(array(bool)); + grid : array>; - function main(v:bool) -> () { + function main(v:bool) returns () { grid[uint256(0)][uint256(0)] = v; return (); } @@ -4007,10 +3998,10 @@ contract NestedBool { fn folds_resolved_std_word_keccak_literal_intrinsic() { let output = specialize_src_with_std( r#" -import std.{*}; +import * from std; contract C { - public function main() -> word { + function main() public returns (word) { return keccakWordLit(0); } } @@ -4031,10 +4022,10 @@ contract C { fn folds_erc7201_namespace_to_a_single_constant() { let output = specialize_src_with_std( r#" -import std.{*}; +import * from std; contract C { - public function main() -> bytes32 { + function main() public returns (bytes32) { return erc7201("example.main"); } } @@ -4057,14 +4048,14 @@ contract C { fn does_not_fold_user_addword_shadowing_builtin_wrapper_name() { let (_db, output) = specialize_src( r#" -function addWord(x: word, y: word) -> word { +function addWord(x: word, y: word) returns (word) { let r : word; assembly { r := sload(0) } return r; } contract C { - public function main() -> word { + function main() public returns (word) { return addWord(1, 2); } } @@ -4079,7 +4070,7 @@ contract C { fn assignment_lhs_root_is_not_substituted() { let repo = repo_root(); let fixture = - repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Plus.solc"); + repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/comptime/Plus.sol"); let output = specialize_fixture(&fixture); assert_eq!(output.diagnostics, Vec::new()); @@ -4090,12 +4081,12 @@ fn assignment_lhs_root_is_not_substituted() { fn compound_assignment_invalidates_lhs_root() { let (_db, output) = specialize_src( r#" -forall t . class t:Add { - function add(l:t, r:t) -> t; +trait Add { + function add(l:t, r:t) returns (t) ; } -instance word:Add { - function add(l:word, r:word) -> word { +impl Add { + function add(l:word, r:word) returns (word) { let result : word; assembly { result := sload(0) } return result; @@ -4103,7 +4094,7 @@ instance word:Add { } contract C { - public function main() -> word { + function main() public returns (word) { let x : word = 1; x += 2; return x; @@ -4121,7 +4112,7 @@ fn unknown_if_invalidates_assignments_from_both_branches() { let (_db, output) = specialize_src( r#" contract C { - public function main(c: bool) -> word { + function main(c: bool) public returns (word) { let x : word = 1; if (c) { } else { @@ -4142,7 +4133,7 @@ fn if_statement_specializes_through_pre_typeck_match_view() { let (_db, output) = specialize_src( r#" contract C { - public function main(c: bool) -> word { + function main(c: bool) public returns (word) { let x : word = 1; if (c) { x = 2; @@ -4186,8 +4177,8 @@ fn if_expression_specializes_through_pre_typeck_match_view() { let (_db, output) = specialize_src( r#" contract C { - public function main(c: bool) -> word { - let x : word = if (c) then 2 else 3; + function main(c: bool) public returns (word) { + let x : word = ((c) ? 2 : 3); return x; } } @@ -4226,7 +4217,7 @@ fn bool_constructors_specialize_through_pre_typeck_unit_sum_view() { let (_true_db, true_output) = specialize_src( r#" contract C { - public function main() -> bool { + function main() public returns (bool) { return true; } } @@ -4235,7 +4226,7 @@ contract C { let (_false_db, false_output) = specialize_src( r#" contract C { - public function main() -> bool { + function main() public returns (bool) { return false; } } @@ -4259,11 +4250,10 @@ fn unknown_match_pattern_binders_shadow_outer_constants() { let (_db, output) = specialize_src( r#" contract C { - public function main(n: word) -> word { + function main(n: word) public returns (word) { let x : word = 1; - match n { - | x => return x; - } + match (n) { + case x { return x; }} } } "#, @@ -4281,9 +4271,9 @@ fn folds_qualified_constructor_matches_before_wildcard_defaults() { let repo = repo_root(); let corpus = repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/spec"); for (fixture, expected) in [ - ("037dwarves.solc", "5"), - ("038food0.solc", "42"), - ("039food.solc", "42"), + ("037dwarves.sol", "5"), + ("038food0.sol", "42"), + ("039food.sol", "42"), ] { let output = specialize_fixture(&corpus.join(fixture)); assert_eq!(output.diagnostics, Vec::new(), "{fixture}"); @@ -5081,7 +5071,7 @@ fn collect_module_fs_snapshot( }; for entry in entries.flatten() { let path = entry.path(); - if path.extension().and_then(|extension| extension.to_str()) == Some("solc") { + if path.extension().and_then(|extension| extension.to_str()) == Some("sol") { if path.is_file() { existing_files.insert(path.clone()); } @@ -5182,17 +5172,21 @@ fn repo_root() -> PathBuf { fn constructor_fold_is_not_confused_by_underscored_names() { let (_db, output) = specialize_src( r#" -data D = Suf | Pre_Suf; +enum D {Suf , Pre_Suf} -function pick(d:D) -> word { - match d { - | D.Suf => return 1; - | D.Pre_Suf => return 2; - }; +function pick(d:D) returns (word) { + match (d) { + case D.Suf { + return 1; + } + case D.Pre_Suf { + return 2; + } + } } contract C { - function main() -> word { + function main() returns (word) { return pick(D.Pre_Suf); } } @@ -5212,17 +5206,21 @@ contract C { fn for_loop_post_assignments_are_not_folded_to_preloop_constants() { let (_db, output) = specialize_src( r#" -data Flag = On | Off; +enum Flag {On , Off} -function isOn(f: Flag) -> bool { - match f { - | Flag.On => return true; - | Flag.Off => return false; - }; +function isOn(f: Flag) returns (bool) { + match (f) { + case Flag.On { + return true; + } + case Flag.Off { + return false; + } + } } contract C { - function main() -> word { + function main() returns (word) { let f : Flag = Flag.On; for (; isOn(f); f = Flag.Off) { } @@ -5257,8 +5255,8 @@ contract C { fn non_contract_main_survives_dead_function_elimination_after_name_mangling() { let (_db, output) = specialize_src( r#" -function answer() -> word { return 42; } -function main() -> word { return answer(); } +function answer() returns (word) { return 42; } +function main() returns (word) { return answer(); } "#, ); @@ -5284,12 +5282,12 @@ fn evaluator_fuel_bounds_total_inline_fanout_work() { let module = parse_module( db, r#" -function g2() -> word { return 1; } -function g1() -> word { return g2() + g2(); } -function g0() -> word { return g1() + g1(); } +function g2() returns (word) { return 1; } +function g1() returns (word) { return g2() + g2(); } +function g0() returns (word) { return g1() + g1(); } contract C { - function main() -> word { return g0(); } + function main() returns (word) { return g0(); } } "#, ); @@ -5316,7 +5314,7 @@ contract C { fn default_fuel_handles_the_e136_basic_dispatch_surface() { solcore_test_utils::run_in_large_stack(|| { let source = - include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/dispatch/basic.solc"); + include_str!("../../parser/tests/fixtures/corpus/ok/test/examples/dispatch/basic.sol"); let output = specialize_src_with_std(source); assert!(output.diagnostics.is_empty(), "{:?}", output.diagnostics); @@ -5359,15 +5357,15 @@ fn default_fuel_handles_the_e136_basic_dispatch_surface() { fn dead_function_elimination_traces_calls_inside_residual_lambdas() { let (_db, output) = specialize_src( r#" -data Box(f) = Box(f); +enum Box {Box(f)} -function target(x : word) -> word { +function target(x : word) returns (word) { let result : word; assembly { result := add(x, 1) } return result; } -function main() -> Box(word -> word) { +function main() returns (Box) { return Box(lam (x : word) -> word { return target(x); }); } "#, @@ -5387,15 +5385,15 @@ function main() -> Box(word -> word) { fn dead_function_elimination_keeps_function_values_nested_in_constructors() { let (_db, output) = specialize_src( r#" -data Box(f) = Box(f); +enum Box {Box(f)} -function target(x : word) -> word { +function target(x : word) returns (word) { let result : word; assembly { result := add(x, 1) } return result; } -function main() -> Box(word -> word) { +function main() returns (Box) { return Box(target); } "#, @@ -5433,15 +5431,15 @@ function main() -> Box(word -> word) { fn user_path_suffix_does_not_grant_std_dispatch_inlining() { let output = specialize_source_at_root( Path::new("/main"), - "mystd/dispatch.solc", + "mystd/dispatch.sol", r#" -function clobber(value : word) -> () { +function clobber(value : word) returns () { let observed : word; assembly { observed := callvalue() } return (); } -function main() -> word { +function main() returns (word) { clobber(0); return 7; } @@ -5473,19 +5471,19 @@ fn std_dispatch_statement_inlining_preserves_lexical_scope() { db, [main_root.as_path(), std_root.as_path()], )); - let path = std_root.join("dispatch.solc"); + let path = std_root.join("dispatch.sol"); let key = module_key_for_path(LibraryId::Std, &std_root, &path).expect("std dispatch key"); let file = source_file_at_path( db, &path, r#" -function clobber() -> () { +function clobber() returns () { let x : word = 1; assembly { mstore(x, x) } return (); } -function main(x : word) -> word { +function main(x : word) returns (word) { clobber(); return x; } @@ -5528,20 +5526,20 @@ function main(x : word) -> word { fn class_method_values_resolve_to_the_specialized_instance_method() { let (_db, output) = specialize_src( r#" -forall t . class t:Pick { - function pick(x : t) -> t; +trait Pick { + function pick(x : t) returns (t) ; } -instance word:Pick { - function pick(x : word) -> word { +impl Pick { + function pick(x : word) returns (word) { let result : word; assembly { result := add(x, 1) } return result; } } -function main(x : word) -> word { - let f : word -> word = Pick.pick; +function main(x : word) returns (word) { + let f : function(word) returns (word) = Pick.pick; return f(x); } "#, @@ -5574,45 +5572,45 @@ pragma no-patterson-condition; pragma no-bounded-variable-condition; pragma no-coverage-condition; -data Proxy(t) = Proxy; -data ABIDecoder(ty, reader) = ABIDecoder(reader); -data Reader = Reader; +enum Proxy {Proxy} +enum ABIDecoder {ABIDecoder(reader)} +enum Reader {Reader} -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; +trait Generic { + function from(x:a) returns (rep) ; + function to(x:rep) returns (a) ; } -forall self . class self:ABIDeriving {} -forall self . class self:ABIAttribs { - function headSize(ty:Proxy(self)) -> word; - function isStatic(ty:Proxy(self)) -> bool; +trait ABIDeriving {} +trait ABIAttribs { + function headSize(ty:Proxy) returns (word) ; + function isStatic(ty:Proxy) returns (bool) ; } -forall decoder decoded . class decoder:ABIDecode(decoded) { - function decode(ptr:decoder, headOffset:word) -> decoded; +trait ABIDecode { + function decode(ptr:decoder, headOffset:word) returns (decoded) ; } -forall reader . class reader:WordReader {} +trait WordReader {} -instance word:ABIAttribs { - function headSize(ty:Proxy(word)) -> word { +impl ABIAttribs { + function headSize(ty:Proxy) returns (word) { assembly { sstore(0, 32) } return 32; } - function isStatic(ty:Proxy(word)) -> bool { + function isStatic(ty:Proxy) returns (bool) { assembly { sstore(1, 1) } return true; } } -instance Reader:WordReader {} -instance ABIDecoder(word, Reader):ABIDecode(word) { - function decode(ptr:ABIDecoder(word, Reader), headOffset:word) -> word { +impl WordReader {} +impl ABIDecode,word> { + function decode(ptr:ABIDecoder, headOffset:word) returns (word) { return headOffset; } } -data Box(a) = Box(a); +enum Box {Box(a)} -function main(ptr:ABIDecoder(Box(word), Reader), headOffset:word) -> Box(word) { - let p:Proxy(Box(word)); +function main(ptr:ABIDecoder, Reader>, headOffset:word) returns (Box) { + let p:Proxy>; let first = ABIAttribs.headSize(p); let second = ABIAttribs.headSize(p); let static = ABIAttribs.isStatic(p); @@ -5745,7 +5743,7 @@ function main(ptr:ABIDecoder(Box(word), Reader), headOffset:word) -> Box(word) { #[test] fn derived_abi_wrappers_replay_definition_side_evidence() { let fixture = - repo_root().join("crates/specialize/tests/fixtures/derived_abi_evidence_replay/main.solc"); + repo_root().join("crates/specialize/tests/fixtures/derived_abi_evidence_replay/main.sol"); let output = specialize_fixture(&fixture); assert_eq!(output.diagnostics, Vec::new(), "{:#?}", output.diagnostics); @@ -5764,21 +5762,21 @@ fn derived_abi_wrappers_replay_definition_side_evidence() { fn direct_adt_abi_specializations_keep_sum_representations_separate() { let (db, _, output) = specialize_src_with_std_and_db( r#" -import std.{*}; -import std.dispatch.{*}; -import std.Generic.{*}; -import std.ABIGeneric.{*}; +import * from std; +import * from std.dispatch; +import * from std.Generic; +import * from std.ABIGeneric; -data D2 = L(uint256) | R(memory(bytes)); -data D3 = X(uint256) | Y(uint256) | Z(memory(bytes)); -data S2 = P(uint256) | Q(uint256); +enum D2 {L(uint256) , R(memory)} +enum D3 {X(uint256) , Y(uint256) , Z(memory)} +enum S2 {P(uint256) , Q(uint256)} contract Sums { constructor() {} - public function makeD2(b:memory(bytes)) -> D2 { return D2.R(b); } - public function makeD3(b:memory(bytes)) -> D3 { return D3.Z(b); } - public function makeS2(n:uint256) -> S2 { return S2.P(n); } - public function roundtripD3(value:D3) -> D3 { return value; } + function makeD2(b:memory) public returns (D2) { return D2.R(b); } + function makeD3(b:memory) public returns (D3) { return D3.Z(b); } + function makeS2(n:uint256) public returns (S2) { return S2.P(n); } + function roundtripD3(value:D3) public returns (D3) { return value; } } "#, ); @@ -6000,49 +5998,48 @@ pragma no-patterson-condition; pragma no-bounded-variable-condition; pragma no-coverage-condition; -data Proxy(t) = Proxy; -data storage(t) = storage(word); +enum Proxy {Proxy} +enum storage {storage(word)} -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; +trait Generic { + function from(x:a) returns (rep) ; + function to(x:rep) returns (a) ; } -forall self . class self:StorageDeriving {} -forall self . class self:StorageSize { - function size(x:Proxy(self)) -> word; +trait StorageDeriving {} +trait StorageSize { + function size(x:Proxy) returns (word) ; } -forall slot value . class slot:CanStore(value) { - function store(r:slot, v:value) -> (); - function load(r:slot) -> value; +trait CanStore { + function store(r:slot, v:value) returns () ; + function load(r:slot) returns (value) ; } -instance word:StorageSize { - function size(x:Proxy(word)) -> word { +impl StorageSize { + function size(x:Proxy) returns (word) { assembly { sstore(0, 1) } return 1; } } -instance storage(word):CanStore(word) { - function store(r:storage(word), v:word) -> () { - match r { - | storage(slot) => assembly { sstore(slot, v) } - } +impl CanStore,word> { + function store(r:storage, v:word) returns () { + match (r) { + case storage(slot) { assembly { sstore(slot, v) } }} } - function load(r:storage(word)) -> word { - match r { - | storage(slot) => - let result:word; + function load(r:storage) returns (word) { + match (r) { + case storage(slot) { +let result:word; assembly { result := sload(slot) } return result; - } + }} } } -data Box(a) = Box(a); +enum Box {Box(a)} -function main(r:storage(Box(word)), v:Box(word)) -> Box(word) { - let first = StorageSize.size(Proxy:Proxy(Box(word))); - let second = StorageSize.size(Proxy:Proxy(Box(word))); +function main(r:storage>, v:Box) returns (Box) { + let first = StorageSize.size(@Box); + let second = StorageSize.size(@Box); CanStore.store(r, v); return CanStore.load(r); } @@ -6145,27 +6142,27 @@ pragma no-patterson-condition; pragma no-bounded-variable-condition; pragma no-coverage-condition; -data storage(t) = storage(word); -data mapping(k, v) = mapping(word); +enum storage {storage(word)} +enum mapping {mapping(word)} -forall a rep . class a:Generic(rep) { - function from(x:a) -> rep; - function to(x:rep) -> a; +trait Generic { + function from(x:a) returns (rep) ; + function to(x:rep) returns (a) ; } -forall self . class self:StorageDeriving {} -forall self . class self:StorageSize {} -forall slot value . class slot:CanStore(value) { - function store(r:slot, v:value) -> (); - function load(r:slot) -> value; +trait StorageDeriving {} +trait StorageSize {} +trait CanStore { + function store(r:slot, v:value) returns () ; + function load(r:slot) returns (value) ; } -instance word:StorageSize {} -forall k v . instance mapping(k, v):StorageSize {} -forall k v . instance storage(mapping(k, v)):CanStore(storage(mapping(k, v))) {} +impl StorageSize {} +impl StorageSize v)> {} +impl CanStore v)>,storage v)>> {} -data Wrapper = Wrapper(mapping(word, word)); +enum Wrapper {Wrapper(mapping(word => word))} -function main(r:storage(Wrapper)) -> Wrapper { +function main(r:storage) returns (Wrapper) { return CanStore.load(r); } "#, @@ -6185,7 +6182,7 @@ function main(r:storage(Wrapper)) -> Wrapper { #[test] fn derived_storage_wrappers_replay_definition_side_evidence() { let fixture = repo_root() - .join("crates/specialize/tests/fixtures/derived_storage_evidence_replay/main.solc"); + .join("crates/specialize/tests/fixtures/derived_storage_evidence_replay/main.sol"); let output = specialize_fixture(&fixture); assert_eq!(output.diagnostics, Vec::new(), "{:#?}", output.diagnostics); @@ -6209,7 +6206,7 @@ fn derived_storage_wrappers_replay_definition_side_evidence() { #[test] fn contract_field_calls_use_definition_module_evidence() { let fixture = repo_root() - .join("crates/specialize/tests/fixtures/storage_field_definition_evidence/main.solc"); + .join("crates/specialize/tests/fixtures/storage_field_definition_evidence/main.sol"); let output = specialize_fixture(&fixture); assert_eq!(output.diagnostics, Vec::new(), "{:#?}", output.diagnostics); diff --git a/crates/test-utils/src/e2e/vector.rs b/crates/test-utils/src/e2e/vector.rs index 05806323..c4018df9 100644 --- a/crates/test-utils/src/e2e/vector.rs +++ b/crates/test-utils/src/e2e/vector.rs @@ -81,7 +81,7 @@ impl RawE2eConstructor { } } -/// Loads `main.json` next to a `main.solc` fixture when it exists. +/// Loads `main.json` next to a `main.sol` fixture when it exists. pub fn load_raw_e2e_vector(source_path: &Path) -> Result, E2eFailure> { let vector_path = source_path.with_extension("json"); let source = match fs::read_to_string(&vector_path) { diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index e6cee3b0..f344de89 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -264,8 +264,8 @@ where ); } - let entry_path = root.join("main.solc"); - module_key_for_path(LibraryId::Main, root, &entry_path).expect("fixture main.solc key") + let entry_path = root.join("main.sol"); + module_key_for_path(LibraryId::Main, root, &entry_path).expect("fixture main.sol key") } pub fn load_main_source(db: &mut Db, source: &str) -> ModuleKey @@ -403,7 +403,7 @@ pub fn render_diagnostics(db: &dyn hir::Db, diagnostics: &[Diagnostic]) -> Strin pub fn assert_diagnostics_snapshot(fixture_root: &Path, rendered: &str) { let mut settings = insta::Settings::new(); settings.set_snapshot_path(fixture_root); - settings.set_input_file(fixture_root.join("main.solc")); + settings.set_input_file(fixture_root.join("main.sol")); settings.set_prepend_module_to_snapshot(false); settings.bind(|| { insta::assert_snapshot!("diagnostics", rendered); @@ -450,7 +450,7 @@ fn collect_module_fs_snapshot( }; for entry in entries.flatten() { let path = entry.path(); - if path.extension().and_then(|extension| extension.to_str()) == Some("solc") { + if path.extension().and_then(|extension| extension.to_str()) == Some("sol") { if path.is_file() { existing_files.insert(path.clone()); } @@ -480,7 +480,7 @@ fn load_library_files( let path = entry.expect("fixture entry").path(); if path.is_dir() { load_library_files(db, library.clone(), root, &path, url_style); - } else if path.extension().and_then(|ext| ext.to_str()) == Some("solc") { + } else if path.extension().and_then(|ext| ext.to_str()) == Some("sol") { let key = module_key_for_path(library.clone(), root, &path).expect("module key"); let file = source_file_for_path(db, &key, &path, url_style); db.insert_module_file(key, file); @@ -518,7 +518,7 @@ fn fixture_url(key: &ModuleKey) -> Url { LibraryId::External(name) => format!("external/{name}"), }; let path = key.logical_path.join("/"); - format!("memory:///{library}/{path}.solc") + format!("memory:///{library}/{path}.sol") .parse() .expect("fixture memory URL") } diff --git a/crates/uitest/tests/diagnostics.rs b/crates/uitest/tests/diagnostics.rs index 9a1bda5b..29e7e91d 100644 --- a/crates/uitest/tests/diagnostics.rs +++ b/crates/uitest/tests/diagnostics.rs @@ -17,14 +17,14 @@ define_frontend_test_db!(TestDb, hir_ty); #[dir_test( dir: "$CARGO_MANIFEST_DIR/tests/fixtures/parse", - glob: "**/main.solc" + glob: "**/main.sol" )] fn parse_fail_diagnostics(fixture: Fixture<&str>) { let path = fixture.path().to_owned(); let source = fixture.content().to_string(); run_in_large_stack(move || { let db = TestDb::default(); - let diagnostics = parse_diagnostics_for_source(&db, "main.solc", &source); + let diagnostics = parse_diagnostics_for_source(&db, "main.sol", &source); assert_failure_snapshot( &db, Path::new(&path).parent().expect("case dir"), @@ -35,7 +35,7 @@ fn parse_fail_diagnostics(fixture: Fixture<&str>) { #[dir_test( dir: "$CARGO_MANIFEST_DIR/tests/fixtures/nameres", - glob: "**/main.solc" + glob: "**/main.sol" )] fn nameres_fail_diagnostics(fixture: Fixture<&str>) { run_fixture_case(fixture, |db, entry| nameres_diagnostics(db, &entry)); @@ -43,7 +43,7 @@ fn nameres_fail_diagnostics(fixture: Fixture<&str>) { #[dir_test( dir: "$CARGO_MANIFEST_DIR/tests/fixtures/typeck", - glob: "**/main.solc" + glob: "**/main.sol" )] fn typeck_fail_diagnostics(fixture: Fixture<&str>) { run_fixture_case_with_dependencies(fixture, full_frontend_diagnostics); @@ -51,7 +51,7 @@ fn typeck_fail_diagnostics(fixture: Fixture<&str>) { #[dir_test( dir: "$CARGO_MANIFEST_DIR/tests/fixtures/solver", - glob: "**/main.solc" + glob: "**/main.sol" )] fn solver_fail_diagnostics(fixture: Fixture<&str>) { run_fixture_case_with_dependencies(fixture, full_frontend_diagnostics); @@ -59,7 +59,7 @@ fn solver_fail_diagnostics(fixture: Fixture<&str>) { #[dir_test( dir: "$CARGO_MANIFEST_DIR/tests/fixtures/comptime", - glob: "**/main.solc" + glob: "**/main.sol" )] fn comptime_fail_diagnostics(fixture: Fixture<&str>) { run_fixture_case(fixture, specialize_diagnostics); @@ -67,7 +67,7 @@ fn comptime_fail_diagnostics(fixture: Fixture<&str>) { #[dir_test( dir: "$CARGO_MANIFEST_DIR/tests/fixtures/specialize", - glob: "**/main.solc" + glob: "**/main.sol" )] fn specialize_fail_diagnostics(fixture: Fixture<&str>) { run_fixture_case(fixture, specialize_diagnostics); @@ -75,7 +75,7 @@ fn specialize_fail_diagnostics(fixture: Fixture<&str>) { #[dir_test( dir: "$CARGO_MANIFEST_DIR/tests/fixtures/hull", - glob: "**/main.solc" + glob: "**/main.sol" )] fn hull_fail_diagnostics(fixture: Fixture<&str>) { run_fixture_case_with_dependencies(fixture, hull_diagnostics); diff --git a/crates/uitest/tests/fixtures/comptime/ct_asm_ret/diagnostics.snap b/crates/uitest/tests/fixtures/comptime/ct_asm_ret/diagnostics.snap index 620ac301..c225c3b4 100644 --- a/crates/uitest/tests/fixtures/comptime/ct_asm_ret/diagnostics.snap +++ b/crates/uitest/tests/fixtures/comptime/ct_asm_ret/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/comptime/ct_asm_ret/main.solc +input_file: crates/uitest/tests/fixtures/comptime/ct_asm_ret/main.sol --- error[SC0409]: comptime evaluation failed: function annotated '-> comptime' returns a runtime expression - --> /main/main.solc:12:5 + --> /main/main.sol:12:5 | 11 | } 12 | return v; diff --git a/crates/uitest/tests/fixtures/comptime/ct_asm_ret/main.sol b/crates/uitest/tests/fixtures/comptime/ct_asm_ret/main.sol new file mode 100644 index 00000000..3ba34ae7 --- /dev/null +++ b/crates/uitest/tests/fixtures/comptime/ct_asm_ret/main.sol @@ -0,0 +1,17 @@ +/* Negative: function annotated '-> comptime word' but body reads from + storage via sload — storage is mutable state, never comptime. + The verifier must reject this. +*/ + +contract ComptimeAsmRet { + function loadFromStorage() returns (comptime) { + let v : word; + assembly { + v := sload(0) + } + return v; + } + function main() returns (word) { + return loadFromStorage(); + } +} diff --git a/crates/uitest/tests/fixtures/comptime/ct_asm_ret/main.solc b/crates/uitest/tests/fixtures/comptime/ct_asm_ret/main.solc deleted file mode 100644 index b0d3893b..00000000 --- a/crates/uitest/tests/fixtures/comptime/ct_asm_ret/main.solc +++ /dev/null @@ -1,17 +0,0 @@ -/* Negative: function annotated '-> comptime word' but body reads from - storage via sload — storage is mutable state, never comptime. - The verifier must reject this. -*/ - -contract ComptimeAsmRet { - function loadFromStorage() -> comptime word { - let v : word; - assembly { - v := sload(0) - } - return v; - } - function main() -> word { - return loadFromStorage(); - } -} diff --git a/crates/uitest/tests/fixtures/comptime/ct_let_runtime/diagnostics.snap b/crates/uitest/tests/fixtures/comptime/ct_let_runtime/diagnostics.snap index 4515e9bc..01d0a3b8 100644 --- a/crates/uitest/tests/fixtures/comptime/ct_let_runtime/diagnostics.snap +++ b/crates/uitest/tests/fixtures/comptime/ct_let_runtime/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/comptime/ct_let_runtime/main.solc +input_file: crates/uitest/tests/fixtures/comptime/ct_let_runtime/main.sol --- error[SC0409]: comptime evaluation failed: comptime let 'y' is bound to a runtime expression - --> /main/main.solc:18:5 + --> /main/main.sol:18:5 | -17 | function main() -> word { -18 | let y : comptime word = sloadWord(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comptime evaluation failed here +17 | function main() returns (word) { +18 | let y : comptime = sloadWord(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comptime evaluation failed here 19 | return y; | diff --git a/crates/uitest/tests/fixtures/comptime/ct_let_runtime/main.sol b/crates/uitest/tests/fixtures/comptime/ct_let_runtime/main.sol new file mode 100644 index 00000000..16c9def3 --- /dev/null +++ b/crates/uitest/tests/fixtures/comptime/ct_let_runtime/main.sol @@ -0,0 +1,21 @@ +/* Negative: comptime let bound to a runtime expression — must fail. + sloadWord reads from storage (sload); storage is mutable state, + so its result is runtime. Binding it with 'let y : comptime word' + must be rejected by the verifier. +*/ +import std; + +function sloadWord() returns (word) { + let v : word; + assembly { + v := sload(0) + } + return v; +} + +contract ComptimeLetRuntime { + function main() returns (word) { + let y : comptime = sloadWord(); + return y; + } +} diff --git a/crates/uitest/tests/fixtures/comptime/ct_let_runtime/main.solc b/crates/uitest/tests/fixtures/comptime/ct_let_runtime/main.solc deleted file mode 100644 index 2db7a7d6..00000000 --- a/crates/uitest/tests/fixtures/comptime/ct_let_runtime/main.solc +++ /dev/null @@ -1,21 +0,0 @@ -/* Negative: comptime let bound to a runtime expression — must fail. - sloadWord reads from storage (sload); storage is mutable state, - so its result is runtime. Binding it with 'let y : comptime word' - must be rejected by the verifier. -*/ -import std; - -function sloadWord() -> word { - let v : word; - assembly { - v := sload(0) - } - return v; -} - -contract ComptimeLetRuntime { - function main() -> word { - let y : comptime word = sloadWord(); - return y; - } -} diff --git a/crates/uitest/tests/fixtures/comptime/ct_overloaded_bad/diagnostics.snap b/crates/uitest/tests/fixtures/comptime/ct_overloaded_bad/diagnostics.snap index b4e1636e..0ac3209a 100644 --- a/crates/uitest/tests/fixtures/comptime/ct_overloaded_bad/diagnostics.snap +++ b/crates/uitest/tests/fixtures/comptime/ct_overloaded_bad/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/comptime/ct_overloaded_bad/main.solc +input_file: crates/uitest/tests/fixtures/comptime/ct_overloaded_bad/main.sol --- error[SC0409]: comptime evaluation failed: function annotated '-> comptime' returns a runtime expression - --> /main/main.solc:18:5 + --> /main/main.sol:18:5 | 17 | } 18 | return base + x * factor; @@ -14,30 +14,30 @@ error[SC0409]: comptime evaluation failed: function annotated '-> comptime' retu --- error[SC0406]: missing evidence: add - --> /main/main.solc:18:12 + --> /main/main.sol:18:12 | 17 | } 18 | return base + x * factor; - | ^^^^^^^^^^^^^^^^^ class evidence required here + | ^^^^^^^^^^^^^^^^^ trait evidence required here 19 | } | --- error[SC0406]: missing evidence: mul - --> /main/main.solc:18:19 + --> /main/main.sol:18:19 | 17 | } 18 | return base + x * factor; - | ^^^^^^^^^^ class evidence required here + | ^^^^^^^^^^ trait evidence required here 19 | } | --- error[SC0409]: comptime evaluation failed: comptime let 'a' is bound to a runtime expression - --> /main/main.solc:24:5 + --> /main/main.sol:24:5 | -23 | function main() -> word { -24 | let a : comptime word = Scale.scale(3, 10); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comptime evaluation failed here +23 | function main() returns (word) { +24 | let a : comptime = Scale.scale(3, 10); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comptime evaluation failed here 25 | return a; | diff --git a/crates/uitest/tests/fixtures/comptime/ct_overloaded_bad/main.sol b/crates/uitest/tests/fixtures/comptime/ct_overloaded_bad/main.sol new file mode 100644 index 00000000..19af3db5 --- /dev/null +++ b/crates/uitest/tests/fixtures/comptime/ct_overloaded_bad/main.sol @@ -0,0 +1,27 @@ +/* Negative: Scale instance whose 'scale' reads from storage — not comptime. + Despite the comptime annotations on the method signature, the word + instance body uses sload (mutable storage state), making the result + a runtime value. The verifier must reject the comptime let binding. +*/ +import std; + +trait Scale { + function scale(comptime factor: word, comptime x: a) returns (comptime) ; +} + +impl Scale { + function scale(comptime factor: word, comptime x: word) returns (comptime) { + let base : word; + assembly { + base := sload(0) + } + return base + x * factor; + } +} + +contract ComptimeOverloadedBad { + function main() returns (word) { + let a : comptime = Scale.scale(3, 10); + return a; + } +} diff --git a/crates/uitest/tests/fixtures/comptime/ct_overloaded_bad/main.solc b/crates/uitest/tests/fixtures/comptime/ct_overloaded_bad/main.solc deleted file mode 100644 index 68042e0a..00000000 --- a/crates/uitest/tests/fixtures/comptime/ct_overloaded_bad/main.solc +++ /dev/null @@ -1,27 +0,0 @@ -/* Negative: Scale instance whose 'scale' reads from storage — not comptime. - Despite the comptime annotations on the method signature, the word - instance body uses sload (mutable storage state), making the result - a runtime value. The verifier must reject the comptime let binding. -*/ -import std; - -forall a. class a : Scale { - function scale(comptime factor : word, comptime x : a) -> comptime a; -} - -instance word : Scale { - function scale(comptime factor : word, comptime x : word) -> comptime word { - let base : word; - assembly { - base := sload(0) - } - return base + x * factor; - } -} - -contract ComptimeOverloadedBad { - function main() -> word { - let a : comptime word = Scale.scale(3, 10); - return a; - } -} diff --git a/crates/uitest/tests/fixtures/comptime/ct_param_poly_runtime/diagnostics.snap b/crates/uitest/tests/fixtures/comptime/ct_param_poly_runtime/diagnostics.snap index 8669f4db..d8889107 100644 --- a/crates/uitest/tests/fixtures/comptime/ct_param_poly_runtime/diagnostics.snap +++ b/crates/uitest/tests/fixtures/comptime/ct_param_poly_runtime/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/comptime/ct_param_poly_runtime/main.solc +input_file: crates/uitest/tests/fixtures/comptime/ct_param_poly_runtime/main.sol --- error[SC0409]: comptime evaluation failed: runtime value passed to comptime parameter 'x' of 'unwrap' - --> /main/main.solc:20:10 + --> /main/main.sol:20:10 | -19 | forall t. t:Wrap => function process(z : t) -> word { +19 | function process(z: t) returns (word) where t: Wrap { 20 | return Wrap.unwrap(z); | ^^^^^^^^^^^^^^ comptime evaluation failed here 21 | } diff --git a/crates/uitest/tests/fixtures/comptime/ct_param_poly_runtime/main.sol b/crates/uitest/tests/fixtures/comptime/ct_param_poly_runtime/main.sol new file mode 100644 index 00000000..66341e77 --- /dev/null +++ b/crates/uitest/tests/fixtures/comptime/ct_param_poly_runtime/main.sol @@ -0,0 +1,27 @@ +/* Negative: comptime violation in a polymorphic (generic) function. + Before specialisation the concrete type of 'z' is unknown, so this + cannot be resolved by inlining. The SAIL-level check catches the + violation: 'z' is a non-comptime parameter and cannot satisfy the + comptime contract of 'unwrap'. +*/ +import std; + +trait Wrap { + function unwrap(comptime x: t) returns (comptime) ; +} + +impl Wrap { + function unwrap(comptime x: word) returns (comptime) { + return x; + } +} + +function process(z: t) returns (word) where t: Wrap { + return Wrap.unwrap(z); +} + +contract ComptimeParamPolyRuntime { + function main() returns (word) { + return process(42); + } +} diff --git a/crates/uitest/tests/fixtures/comptime/ct_param_poly_runtime/main.solc b/crates/uitest/tests/fixtures/comptime/ct_param_poly_runtime/main.solc deleted file mode 100644 index e67a24c1..00000000 --- a/crates/uitest/tests/fixtures/comptime/ct_param_poly_runtime/main.solc +++ /dev/null @@ -1,27 +0,0 @@ -/* Negative: comptime violation in a polymorphic (generic) function. - Before specialisation the concrete type of 'z' is unknown, so this - cannot be resolved by inlining. The SAIL-level check catches the - violation: 'z' is a non-comptime parameter and cannot satisfy the - comptime contract of 'unwrap'. -*/ -import std; - -forall t. class t : Wrap { - function unwrap(comptime x : t) -> comptime word; -} - -instance word : Wrap { - function unwrap(comptime x : word) -> comptime word { - return x; - } -} - -forall t. t:Wrap => function process(z : t) -> word { - return Wrap.unwrap(z); -} - -contract ComptimeParamPolyRuntime { - function main() -> word { - return process(42); - } -} diff --git a/crates/uitest/tests/fixtures/comptime/ct_param_runtime/diagnostics.snap b/crates/uitest/tests/fixtures/comptime/ct_param_runtime/diagnostics.snap index c002bef0..df0bff94 100644 --- a/crates/uitest/tests/fixtures/comptime/ct_param_runtime/diagnostics.snap +++ b/crates/uitest/tests/fixtures/comptime/ct_param_runtime/diagnostics.snap @@ -1,22 +1,22 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/comptime/ct_param_runtime/main.solc +input_file: crates/uitest/tests/fixtures/comptime/ct_param_runtime/main.sol --- error[SC0406]: missing evidence: add - --> /main/main.solc:11:12 + --> /main/main.sol:11:12 | -10 | function double(comptime x : word) -> comptime word { +10 | function double(comptime x: word) returns (comptime) { 11 | return x + x; - | ^^^^^ class evidence required here + | ^^^^^ trait evidence required here 12 | } | --- error[SC0409]: comptime evaluation failed: runtime value passed to comptime parameter 'x' of 'double' - --> /main/main.solc:14:12 + --> /main/main.sol:14:12 | -13 | function process(value : word) -> word { +13 | function process(value: word) returns (word) { 14 | return double(value); | ^^^^^^^^^^^^^ comptime evaluation failed here 15 | } diff --git a/crates/uitest/tests/fixtures/comptime/ct_param_runtime/main.sol b/crates/uitest/tests/fixtures/comptime/ct_param_runtime/main.sol new file mode 100644 index 00000000..d9dd45f3 --- /dev/null +++ b/crates/uitest/tests/fixtures/comptime/ct_param_runtime/main.sol @@ -0,0 +1,19 @@ +/* Negative: non-comptime function parameter passed to a comptime parameter. + Caught by the SAIL-level check: 'process' CAN be called with an argument + not known at compile time, which would violate the comptime requirement + of 'double'. The SAIL check rejects this on the parameter type alone, + before looking at specific call sites. +*/ +import std; + +contract ComptimeParamRuntime { + function double(comptime x: word) returns (comptime) { + return x + x; + } + function process(value: word) returns (word) { + return double(value); + } + function main() returns (word) { + return process(21); + } +} diff --git a/crates/uitest/tests/fixtures/comptime/ct_param_runtime/main.solc b/crates/uitest/tests/fixtures/comptime/ct_param_runtime/main.solc deleted file mode 100644 index 496cb2a7..00000000 --- a/crates/uitest/tests/fixtures/comptime/ct_param_runtime/main.solc +++ /dev/null @@ -1,19 +0,0 @@ -/* Negative: non-comptime function parameter passed to a comptime parameter. - Caught by the SAIL-level check: 'process' CAN be called with an argument - not known at compile time, which would violate the comptime requirement - of 'double'. The SAIL check rejects this on the parameter type alone, - before looking at specific call sites. -*/ -import std; - -contract ComptimeParamRuntime { - function double(comptime x : word) -> comptime word { - return x + x; - } - function process(value : word) -> word { - return double(value); - } - function main() -> word { - return process(21); - } -} diff --git a/crates/uitest/tests/fixtures/comptime/ct_runtime_arg/diagnostics.snap b/crates/uitest/tests/fixtures/comptime/ct_runtime_arg/diagnostics.snap index ea6836e5..6083a724 100644 --- a/crates/uitest/tests/fixtures/comptime/ct_runtime_arg/diagnostics.snap +++ b/crates/uitest/tests/fixtures/comptime/ct_runtime_arg/diagnostics.snap @@ -1,22 +1,22 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/comptime/ct_runtime_arg/main.solc +input_file: crates/uitest/tests/fixtures/comptime/ct_runtime_arg/main.sol --- error[SC0406]: missing evidence: add - --> /main/main.solc:17:12 + --> /main/main.sol:17:12 | -16 | function double(comptime x : word) -> comptime word { +16 | function double(comptime x: word) returns (comptime) { 17 | return x + x; - | ^^^^^ class evidence required here + | ^^^^^ trait evidence required here 18 | } | --- error[SC0409]: comptime evaluation failed: runtime value passed to comptime parameter 'x' of 'double' - --> /main/main.solc:20:12 + --> /main/main.sol:20:12 | -19 | function main() -> word { +19 | function main() returns (word) { 20 | return double(sloadWord()); | ^^^^^^^^^^^^^^^^^^^ comptime evaluation failed here 21 | } diff --git a/crates/uitest/tests/fixtures/comptime/ct_runtime_arg/main.sol b/crates/uitest/tests/fixtures/comptime/ct_runtime_arg/main.sol new file mode 100644 index 00000000..d40acb84 --- /dev/null +++ b/crates/uitest/tests/fixtures/comptime/ct_runtime_arg/main.sol @@ -0,0 +1,22 @@ +/* Negative: runtime value passed to a comptime parameter — must fail. + sloadWord uses sload; storage is mutable state, so its result is + a runtime value; passing it to double's comptime param is an error. +*/ +import std; + +function sloadWord() returns (word) { + let v : word; + assembly { + v := sload(0) + } + return v; +} + +contract ComptimeRuntimeArg { + function double(comptime x: word) returns (comptime) { + return x + x; + } + function main() returns (word) { + return double(sloadWord()); + } +} diff --git a/crates/uitest/tests/fixtures/comptime/ct_runtime_arg/main.solc b/crates/uitest/tests/fixtures/comptime/ct_runtime_arg/main.solc deleted file mode 100644 index ed9e0132..00000000 --- a/crates/uitest/tests/fixtures/comptime/ct_runtime_arg/main.solc +++ /dev/null @@ -1,22 +0,0 @@ -/* Negative: runtime value passed to a comptime parameter — must fail. - sloadWord uses sload; storage is mutable state, so its result is - a runtime value; passing it to double's comptime param is an error. -*/ -import std; - -function sloadWord() -> word { - let v : word; - assembly { - v := sload(0) - } - return v; -} - -contract ComptimeRuntimeArg { - function double(comptime x : word) -> comptime word { - return x + x; - } - function main() -> word { - return double(sloadWord()); - } -} diff --git a/crates/uitest/tests/fixtures/comptime/ergo_ct_fuel_infinite/diagnostics.snap b/crates/uitest/tests/fixtures/comptime/ergo_ct_fuel_infinite/diagnostics.snap index 7609fcf5..15d43fb5 100644 --- a/crates/uitest/tests/fixtures/comptime/ergo_ct_fuel_infinite/diagnostics.snap +++ b/crates/uitest/tests/fixtures/comptime/ergo_ct_fuel_infinite/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/comptime/ergo_ct_fuel_infinite/main.solc +input_file: crates/uitest/tests/fixtures/comptime/ergo_ct_fuel_infinite/main.sol --- error[SC0410]: comptime evaluation fuel exhausted in spin at 128 unfold steps - --> /main/main.solc:6:10 + --> /main/main.sol:6:10 | -5 | function spin(comptime n : integer) -> comptime integer { +5 | function spin(comptime n: integer) returns (comptime) { 6 | return spin(integerAdd(n, 1)); | ^^^^^^^^^^^^^^^^^^^^^^ comptime fuel limit reached here 7 | } diff --git a/crates/uitest/tests/fixtures/comptime/ergo_ct_fuel_infinite/main.sol b/crates/uitest/tests/fixtures/comptime/ergo_ct_fuel_infinite/main.sol new file mode 100644 index 00000000..1b79d188 --- /dev/null +++ b/crates/uitest/tests/fixtures/comptime/ergo_ct_fuel_infinite/main.sol @@ -0,0 +1,13 @@ +// Non-terminating comptime recursion: inline-depth exhaustion must stop the +// recursive evaluator before the larger total-work fuel budget is consumed. +import std; + +function spin(comptime n: integer) returns (comptime) { + return spin(integerAdd(n, 1)); +} + +contract CtFuelInfinite { + function main() returns (word) { + return wordFromInteger(spin(0)); + } +} diff --git a/crates/uitest/tests/fixtures/comptime/ergo_ct_fuel_infinite/main.solc b/crates/uitest/tests/fixtures/comptime/ergo_ct_fuel_infinite/main.solc deleted file mode 100644 index 273b9d4e..00000000 --- a/crates/uitest/tests/fixtures/comptime/ergo_ct_fuel_infinite/main.solc +++ /dev/null @@ -1,13 +0,0 @@ -// Non-terminating comptime recursion: inline-depth exhaustion must stop the -// recursive evaluator before the larger total-work fuel budget is consumed. -import std; - -function spin(comptime n : integer) -> comptime integer { - return spin(integerAdd(n, 1)); -} - -contract CtFuelInfinite { - function main() -> word { - return wordFromInteger(spin(0)); - } -} diff --git a/crates/uitest/tests/fixtures/comptime/ergo_ct_let_runtime_param/diagnostics.snap b/crates/uitest/tests/fixtures/comptime/ergo_ct_let_runtime_param/diagnostics.snap index 63b683ab..470f6051 100644 --- a/crates/uitest/tests/fixtures/comptime/ergo_ct_let_runtime_param/diagnostics.snap +++ b/crates/uitest/tests/fixtures/comptime/ergo_ct_let_runtime_param/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/comptime/ergo_ct_let_runtime_param/main.solc +input_file: crates/uitest/tests/fixtures/comptime/ergo_ct_let_runtime_param/main.sol --- error[SC0401]: cannot specialize expression: type is not concrete - --> /main/main.solc:7:33 + --> /main/main.sol:7:34 | -6 | function scale(k : word) -> word { -7 | let c : comptime word = k + 1; - | ^ type must be concrete here +6 | function scale(k: word) returns (word) { +7 | let c : comptime = k + 1; + | ^ type must be concrete here 8 | return c; | = note: this can happen when a constructor or expression leaves a type parameter unresolved diff --git a/crates/uitest/tests/fixtures/comptime/ergo_ct_let_runtime_param/main.sol b/crates/uitest/tests/fixtures/comptime/ergo_ct_let_runtime_param/main.sol new file mode 100644 index 00000000..5f131aa8 --- /dev/null +++ b/crates/uitest/tests/fixtures/comptime/ergo_ct_let_runtime_param/main.sol @@ -0,0 +1,17 @@ +// comptime let bound to a runtime function parameter: must fail comptime +// evaluation. The interesting question is span quality + cascade volume. +import std; + +contract CtLetRuntimeParam { + function scale(k: word) returns (word) { + let c : comptime = k + 1; + return c; + } + function main() returns (word) { + let v : word; + assembly { + v := sload(0) + } + return scale(v); + } +} diff --git a/crates/uitest/tests/fixtures/comptime/ergo_ct_let_runtime_param/main.solc b/crates/uitest/tests/fixtures/comptime/ergo_ct_let_runtime_param/main.solc deleted file mode 100644 index a97563fb..00000000 --- a/crates/uitest/tests/fixtures/comptime/ergo_ct_let_runtime_param/main.solc +++ /dev/null @@ -1,17 +0,0 @@ -// comptime let bound to a runtime function parameter: must fail comptime -// evaluation. The interesting question is span quality + cascade volume. -import std; - -contract CtLetRuntimeParam { - function scale(k : word) -> word { - let c : comptime word = k + 1; - return c; - } - function main() -> word { - let v : word; - assembly { - v := sload(0) - } - return scale(v); - } -} diff --git a/crates/uitest/tests/fixtures/hull/assembly_assign_no_return/diagnostics.snap b/crates/uitest/tests/fixtures/hull/assembly_assign_no_return/diagnostics.snap index c02a1784..e86c925c 100644 --- a/crates/uitest/tests/fixtures/hull/assembly_assign_no_return/diagnostics.snap +++ b/crates/uitest/tests/fixtures/hull/assembly_assign_no_return/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/hull/assembly_assign_no_return/main.solc +input_file: crates/uitest/tests/fixtures/hull/assembly_assign_no_return/main.sol --- error[SC0445]: inline assembly assignment returns 0 values, expected 1 - --> /main/main.solc:6:12 + --> /main/main.sol:6:12 | 5 | assembly { 6 | x := mstore(1, 1) diff --git a/crates/uitest/tests/fixtures/hull/assembly_assign_no_return/main.sol b/crates/uitest/tests/fixtures/hull/assembly_assign_no_return/main.sol new file mode 100644 index 00000000..05809a5b --- /dev/null +++ b/crates/uitest/tests/fixtures/hull/assembly_assign_no_return/main.sol @@ -0,0 +1,10 @@ +// mstore does not return a value, so it cannot be assigned. +contract Test { + function main() public returns (word) { + let x : word; + assembly { + x := mstore(1, 1) + } + return x; + } +} diff --git a/crates/uitest/tests/fixtures/hull/assembly_assign_no_return/main.solc b/crates/uitest/tests/fixtures/hull/assembly_assign_no_return/main.solc deleted file mode 100644 index 445bb347..00000000 --- a/crates/uitest/tests/fixtures/hull/assembly_assign_no_return/main.solc +++ /dev/null @@ -1,10 +0,0 @@ -// mstore does not return a value, so it cannot be assigned. -contract Test { - public function main() -> word { - let x : word; - assembly { - x := mstore(1, 1) - } - return x; - } -} diff --git a/crates/uitest/tests/fixtures/hull/assembly_assign_non_word/diagnostics.snap b/crates/uitest/tests/fixtures/hull/assembly_assign_non_word/diagnostics.snap index 5e3479f2..9ea4d003 100644 --- a/crates/uitest/tests/fixtures/hull/assembly_assign_non_word/diagnostics.snap +++ b/crates/uitest/tests/fixtures/hull/assembly_assign_non_word/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/hull/assembly_assign_non_word/main.solc +input_file: crates/uitest/tests/fixtures/hull/assembly_assign_non_word/main.sol --- error[SC0434]: Hull type mismatch: expected (unit + unit), got word - --> /main/main.solc:7:9 + --> /main/main.sol:7:9 | -6 | public function main() -> word { +6 | function main() public returns (word) { 7 | let b : bool = false; | ^ type mismatch 8 | assembly { b := add(1, 1) } @@ -14,10 +14,10 @@ error[SC0434]: Hull type mismatch: expected (unit + unit), got word --- error[SC0448]: inline assembly assignment to `b` requires word type, got (unit + unit) - --> /main/main.solc:8:16 + --> /main/main.sol:8:16 | 7 | let b : bool = false; 8 | assembly { b := add(1, 1) } | ^^^^^^^^^^^^^^ assembly assignment must be word -9 | if b { return 1; } else { return 0; } +9 | if ( b ) { return 1; } else { return 0; } | diff --git a/crates/uitest/tests/fixtures/hull/assembly_assign_non_word/main.sol b/crates/uitest/tests/fixtures/hull/assembly_assign_non_word/main.sol new file mode 100644 index 00000000..d77b3f7f --- /dev/null +++ b/crates/uitest/tests/fixtures/hull/assembly_assign_non_word/main.sol @@ -0,0 +1,11 @@ +// An assembly assignment writes a raw scalar word, so its LHS must have type +// 'word'. Assigning to a non-word local (here a 'bool', whose runtime layout +// is a tagged inl/inr pair) would corrupt that layout, so the type checker +// must reject this program. +contract AsmBool { + function main() public returns (word) { + let b : bool = false; + assembly { b := add(1, 1) } + if ( b ) { return 1; } else { return 0; } + } +} diff --git a/crates/uitest/tests/fixtures/hull/assembly_assign_non_word/main.solc b/crates/uitest/tests/fixtures/hull/assembly_assign_non_word/main.solc deleted file mode 100644 index be96a1bb..00000000 --- a/crates/uitest/tests/fixtures/hull/assembly_assign_non_word/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -// An assembly assignment writes a raw scalar word, so its LHS must have type -// 'word'. Assigning to a non-word local (here a 'bool', whose runtime layout -// is a tagged inl/inr pair) would corrupt that layout, so the type checker -// must reject this program. -contract AsmBool { - public function main() -> word { - let b : bool = false; - assembly { b := add(1, 1) } - if b { return 1; } else { return 0; } - } -} diff --git a/crates/uitest/tests/fixtures/hull/assembly_multi_return_arity/diagnostics.snap b/crates/uitest/tests/fixtures/hull/assembly_multi_return_arity/diagnostics.snap index 90148e27..b5eff43d 100644 --- a/crates/uitest/tests/fixtures/hull/assembly_multi_return_arity/diagnostics.snap +++ b/crates/uitest/tests/fixtures/hull/assembly_multi_return_arity/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/hull/assembly_multi_return_arity/main.solc +input_file: crates/uitest/tests/fixtures/hull/assembly_multi_return_arity/main.sol --- error[SC0445]: inline assembly assignment returns 2 values, expected 3 - --> /main/main.solc:11:18 + --> /main/main.sol:11:18 | 10 | } 11 | x, y, z := pair() diff --git a/crates/uitest/tests/fixtures/hull/assembly_multi_return_arity/main.sol b/crates/uitest/tests/fixtures/hull/assembly_multi_return_arity/main.sol new file mode 100644 index 00000000..c83ab153 --- /dev/null +++ b/crates/uitest/tests/fixtures/hull/assembly_multi_return_arity/main.sol @@ -0,0 +1,15 @@ +contract YulMultiRetBad { + function main() public returns (word) { + let x : word; + let y : word; + let z : word; + assembly { + function pair() -> a, b { + a := 1 + b := 2 + } + x, y, z := pair() + } + return x; + } +} diff --git a/crates/uitest/tests/fixtures/hull/assembly_multi_return_arity/main.solc b/crates/uitest/tests/fixtures/hull/assembly_multi_return_arity/main.solc deleted file mode 100644 index 02263c08..00000000 --- a/crates/uitest/tests/fixtures/hull/assembly_multi_return_arity/main.solc +++ /dev/null @@ -1,15 +0,0 @@ -contract YulMultiRetBad { - public function main() -> word { - let x : word; - let y : word; - let z : word; - assembly { - function pair() -> a, b { - a := 1 - b := 2 - } - x, y, z := pair() - } - return x; - } -} diff --git a/crates/uitest/tests/fixtures/hull/ergo_hull_multi_error/diagnostics.snap b/crates/uitest/tests/fixtures/hull/ergo_hull_multi_error/diagnostics.snap index b6fbf316..8505051a 100644 --- a/crates/uitest/tests/fixtures/hull/ergo_hull_multi_error/diagnostics.snap +++ b/crates/uitest/tests/fixtures/hull/ergo_hull_multi_error/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/hull/ergo_hull_multi_error/main.solc +input_file: crates/uitest/tests/fixtures/hull/ergo_hull_multi_error/main.sol --- error[SC0421]: cannot lower literal `"oops"` to Hull - --> /main/main.solc:5:12 + --> /main/main.sol:5:12 | -4 | public function main() -> string { +4 | function main() public returns (string) { 5 | return "oops"; | ^^^^^^ unsupported literal 6 | } @@ -14,9 +14,9 @@ error[SC0421]: cannot lower literal `"oops"` to Hull --- error[SC0421]: cannot lower literal `"also bad"` to Hull - --> /main/main.solc:11:12 + --> /main/main.sol:11:12 | -10 | public function main() -> string { +10 | function main() public returns (string) { 11 | return "also bad"; | ^^^^^^^^^^ unsupported literal 12 | } diff --git a/crates/uitest/tests/fixtures/hull/ergo_hull_multi_error/main.sol b/crates/uitest/tests/fixtures/hull/ergo_hull_multi_error/main.sol new file mode 100644 index 00000000..e749ac19 --- /dev/null +++ b/crates/uitest/tests/fixtures/hull/ergo_hull_multi_error/main.sol @@ -0,0 +1,13 @@ +// Two independent Hull-level problems in separate contracts: +// string literals are not representable in Hull. +contract First { + function main() public returns (string) { + return "oops"; + } +} + +contract Second { + function main() public returns (string) { + return "also bad"; + } +} diff --git a/crates/uitest/tests/fixtures/hull/ergo_hull_multi_error/main.solc b/crates/uitest/tests/fixtures/hull/ergo_hull_multi_error/main.solc deleted file mode 100644 index 4bfcf091..00000000 --- a/crates/uitest/tests/fixtures/hull/ergo_hull_multi_error/main.solc +++ /dev/null @@ -1,13 +0,0 @@ -// Two independent Hull-level problems in separate contracts: -// string literals are not representable in Hull. -contract First { - public function main() -> string { - return "oops"; - } -} - -contract Second { - public function main() -> string { - return "also bad"; - } -} diff --git a/crates/uitest/tests/fixtures/hull/ergo_hull_string_return/diagnostics.snap b/crates/uitest/tests/fixtures/hull/ergo_hull_string_return/diagnostics.snap index 80c67d29..631ab476 100644 --- a/crates/uitest/tests/fixtures/hull/ergo_hull_string_return/diagnostics.snap +++ b/crates/uitest/tests/fixtures/hull/ergo_hull_string_return/diagnostics.snap @@ -1,17 +1,17 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/hull/ergo_hull_string_return/main.solc +input_file: crates/uitest/tests/fixtures/hull/ergo_hull_string_return/main.sol --- error[SC0411]: runtime lowering cannot represent `string` in return type of `main` - --> /main/main.solc:4:3 + --> /main/main.sol:5:3 | -3 | contract Answer { -4 | / public function main() { -5 | | return "42"; -6 | | } +4 | contract Answer { +5 | / function main() returns (string) { +6 | | return helper(); +7 | | } | |___^ not representable at runtime -7 | } +8 | } | = note: `integer`, `string`, and `comptime` values must be eliminated before runtime lowering = note: help: evaluate the value at comptime or change it to a runtime-representable type diff --git a/crates/uitest/tests/fixtures/hull/ergo_hull_string_return/main.sol b/crates/uitest/tests/fixtures/hull/ergo_hull_string_return/main.sol new file mode 100644 index 00000000..932cf2e4 --- /dev/null +++ b/crates/uitest/tests/fixtures/hull/ergo_hull_string_return/main.sol @@ -0,0 +1,12 @@ +// A runtime function whose result type is not representable in Hull. +import {string} from std; + +contract Answer { + function main() returns (string) { + return helper(); + } +} + +function helper() returns (string) { + return "42"; +} diff --git a/crates/uitest/tests/fixtures/hull/ergo_hull_string_return/main.solc b/crates/uitest/tests/fixtures/hull/ergo_hull_string_return/main.solc deleted file mode 100644 index a7804492..00000000 --- a/crates/uitest/tests/fixtures/hull/ergo_hull_string_return/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -// Mirrors reference corpus test/examples/cases/string-const.solc: -// a public function returning a string constant. -contract Answer { - public function main() { - return "42"; - } -} diff --git a/crates/uitest/tests/fixtures/hull/ergo_hull_word_match_no_default/diagnostics.snap b/crates/uitest/tests/fixtures/hull/ergo_hull_word_match_no_default/diagnostics.snap index c9f9caa1..99cce396 100644 --- a/crates/uitest/tests/fixtures/hull/ergo_hull_word_match_no_default/diagnostics.snap +++ b/crates/uitest/tests/fixtures/hull/ergo_hull_word_match_no_default/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/hull/ergo_hull_word_match_no_default/main.solc +input_file: crates/uitest/tests/fixtures/hull/ergo_hull_word_match_no_default/main.sol --- error[SC0302]: non-exhaustive pattern match - --> /main/main.solc:6:11 + --> /main/main.sol:6:12 | -5 | public function name(d : uint256) -> uint256 { -6 | match d { - | ^ match is not exhaustive -7 | | 0 => return 100; +5 | function name(d: uint256) public returns (uint256) { +6 | match (d) { + | ^ match is not exhaustive +7 | case 0 { | = note: missing case: _ = note: help: add a default or catch-all arm that covers the remaining values diff --git a/crates/uitest/tests/fixtures/hull/ergo_hull_word_match_no_default/main.sol b/crates/uitest/tests/fixtures/hull/ergo_hull_word_match_no_default/main.sol new file mode 100644 index 00000000..238a62e6 --- /dev/null +++ b/crates/uitest/tests/fixtures/hull/ergo_hull_word_match_no_default/main.sol @@ -0,0 +1,15 @@ +import * from std; +import * from std.dispatch; + +contract Digits { + function name(d: uint256) public returns (uint256) { + match (d) { +case 0 { +return 100; +} +case 1 { +return 101; +} +} + } +} diff --git a/crates/uitest/tests/fixtures/hull/ergo_hull_word_match_no_default/main.solc b/crates/uitest/tests/fixtures/hull/ergo_hull_word_match_no_default/main.solc deleted file mode 100644 index 17e323e9..00000000 --- a/crates/uitest/tests/fixtures/hull/ergo_hull_word_match_no_default/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract Digits { - public function name(d : uint256) -> uint256 { - match d { - | 0 => return 100; - | 1 => return 101; - } - } -} diff --git a/crates/uitest/tests/fixtures/hull/non_exhaustive_match/diagnostics.snap b/crates/uitest/tests/fixtures/hull/non_exhaustive_match/diagnostics.snap index 968ab0d3..8bb14641 100644 --- a/crates/uitest/tests/fixtures/hull/non_exhaustive_match/diagnostics.snap +++ b/crates/uitest/tests/fixtures/hull/non_exhaustive_match/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/hull/non_exhaustive_match/main.solc +input_file: crates/uitest/tests/fixtures/hull/non_exhaustive_match/main.sol --- error[SC0302]: non-exhaustive pattern match - --> /main/main.solc:11:9 + --> /main/main.sol:11:10 | -10 | function onlyA(b : B) -> word { -11 | match b { - | ^ match is not exhaustive -12 | | B.A => return 1; +10 | function onlyA(b: B) returns (word) { +11 | match (b) { + | ^ match is not exhaustive +12 | case B.A { | = note: missing case: _ = note: help: add a default or catch-all arm that covers the remaining values diff --git a/crates/uitest/tests/fixtures/hull/non_exhaustive_match/main.sol b/crates/uitest/tests/fixtures/hull/non_exhaustive_match/main.sol new file mode 100644 index 00000000..3b55153e --- /dev/null +++ b/crates/uitest/tests/fixtures/hull/non_exhaustive_match/main.sol @@ -0,0 +1,24 @@ +enum B { A, C } + +function choose(x: bool) returns (B) { + if (x) { + return B.A; + } + return B.C; +} + +function onlyA(b: B) returns (word) { + match (b) { +case B.A { +return 1; +} +} +} + +contract C { + function main() public returns (word) { + let x: bool; + assembly { x := calldataload(0) } + return onlyA(choose(x)); + } +} diff --git a/crates/uitest/tests/fixtures/hull/non_exhaustive_match/main.solc b/crates/uitest/tests/fixtures/hull/non_exhaustive_match/main.solc deleted file mode 100644 index 130d7c8d..00000000 --- a/crates/uitest/tests/fixtures/hull/non_exhaustive_match/main.solc +++ /dev/null @@ -1,22 +0,0 @@ -data B = A | C; - -function choose(x : bool) -> B { - if (x) { - return B.A; - } - return B.C; -} - -function onlyA(b : B) -> word { - match b { - | B.A => return 1; - } -} - -contract C { - public function main() -> word { - let x: bool; - assembly { x := calldataload(0) } - return onlyA(choose(x)); - } -} diff --git a/crates/uitest/tests/fixtures/hull/ok_dispatch_storage/diagnostics.snap b/crates/uitest/tests/fixtures/hull/ok_dispatch_storage/diagnostics.snap index eb8caeec..d0b9ef69 100644 --- a/crates/uitest/tests/fixtures/hull/ok_dispatch_storage/diagnostics.snap +++ b/crates/uitest/tests/fixtures/hull/ok_dispatch_storage/diagnostics.snap @@ -1,6 +1,6 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/hull/ok_dispatch_storage/main.solc +input_file: crates/uitest/tests/fixtures/hull/ok_dispatch_storage/main.sol --- no diagnostics diff --git a/crates/uitest/tests/fixtures/hull/ok_dispatch_storage/main.sol b/crates/uitest/tests/fixtures/hull/ok_dispatch_storage/main.sol new file mode 100644 index 00000000..f7ff51e2 --- /dev/null +++ b/crates/uitest/tests/fixtures/hull/ok_dispatch_storage/main.sol @@ -0,0 +1,17 @@ +import * from std; +import * from std.dispatch; + +// Storage support for a `memory(bytes)` contract field: assigning to the +// field copies the byte array into storage, reading it back loads it into +// fresh memory. Exercises StorageSize / CanStore for memory(bytes). +contract C { + content: bytes; + + function set(value: memory) public { + content = value; + } + + function get() public returns (memory) { + return content; + } +} diff --git a/crates/uitest/tests/fixtures/hull/ok_dispatch_storage/main.solc b/crates/uitest/tests/fixtures/hull/ok_dispatch_storage/main.solc deleted file mode 100644 index a1a53781..00000000 --- a/crates/uitest/tests/fixtures/hull/ok_dispatch_storage/main.solc +++ /dev/null @@ -1,17 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// Storage support for a `memory(bytes)` contract field: assigning to the -// field copies the byte array into storage, reading it back loads it into -// fresh memory. Exercises StorageSize / CanStore for memory(bytes). -contract C { - content: bytes; - - public function set(value: memory(bytes)) -> () { - content = value; - } - - public function get() -> memory(bytes) { - return content; - } -} diff --git a/crates/uitest/tests/fixtures/hull/ok_fallback_unit/diagnostics.snap b/crates/uitest/tests/fixtures/hull/ok_fallback_unit/diagnostics.snap new file mode 100644 index 00000000..541e546e --- /dev/null +++ b/crates/uitest/tests/fixtures/hull/ok_fallback_unit/diagnostics.snap @@ -0,0 +1,6 @@ +--- +source: crates/test-utils/src/lib.rs +expression: rendered +input_file: crates/uitest/tests/fixtures/hull/ok_fallback_unit/main.sol +--- +no diagnostics diff --git a/crates/uitest/tests/fixtures/hull/ok_fallback_unit/main.sol b/crates/uitest/tests/fixtures/hull/ok_fallback_unit/main.sol new file mode 100644 index 00000000..8fda5aef --- /dev/null +++ b/crates/uitest/tests/fixtures/hull/ok_fallback_unit/main.sol @@ -0,0 +1,8 @@ +import * from std; +import * from std.dispatch; + +contract C { + fallback() { + return; + } +} diff --git a/crates/uitest/tests/fixtures/hull/ok_guarded_runtime_recursion/diagnostics.snap b/crates/uitest/tests/fixtures/hull/ok_guarded_runtime_recursion/diagnostics.snap index 2b82fff6..72702a57 100644 --- a/crates/uitest/tests/fixtures/hull/ok_guarded_runtime_recursion/diagnostics.snap +++ b/crates/uitest/tests/fixtures/hull/ok_guarded_runtime_recursion/diagnostics.snap @@ -1,6 +1,6 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/hull/ok_guarded_runtime_recursion/main.solc +input_file: crates/uitest/tests/fixtures/hull/ok_guarded_runtime_recursion/main.sol --- no diagnostics diff --git a/crates/uitest/tests/fixtures/hull/ok_guarded_runtime_recursion/main.sol b/crates/uitest/tests/fixtures/hull/ok_guarded_runtime_recursion/main.sol new file mode 100644 index 00000000..56451306 --- /dev/null +++ b/crates/uitest/tests/fixtures/hull/ok_guarded_runtime_recursion/main.sol @@ -0,0 +1,15 @@ +import * from std; + +function countdown(n: word) returns (word) { + if (n == 0) { + return 0; + } else { + return countdown(n - 1); + } +} + +contract Counter { + function main() public returns (word) { + return countdown(3); + } +} diff --git a/crates/uitest/tests/fixtures/hull/ok_guarded_runtime_recursion/main.solc b/crates/uitest/tests/fixtures/hull/ok_guarded_runtime_recursion/main.solc deleted file mode 100644 index 67ecd180..00000000 --- a/crates/uitest/tests/fixtures/hull/ok_guarded_runtime_recursion/main.solc +++ /dev/null @@ -1,15 +0,0 @@ -import std.{*}; - -function countdown(n: word) -> word { - if (n == 0) { - return 0; - } else { - return countdown(n - 1); - } -} - -contract Counter { - public function main() -> word { - return countdown(3); - } -} diff --git a/crates/uitest/tests/fixtures/hull/unsupported_public_fallback_return/diagnostics.snap b/crates/uitest/tests/fixtures/hull/unsupported_public_fallback_return/diagnostics.snap deleted file mode 100644 index f342931f..00000000 --- a/crates/uitest/tests/fixtures/hull/unsupported_public_fallback_return/diagnostics.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/test-utils/src/lib.rs -expression: rendered -input_file: crates/uitest/tests/fixtures/hull/unsupported_public_fallback_return/main.solc ---- -error[SC0231]: fallback ABI must be unit -> unit - --> /main/main.solc:5:3 - | -4 | contract C { -5 | / fallback() -> word { -6 | | return 1; -7 | | } - | |___^ unsupported fallback ABI -8 | } - | diff --git a/crates/uitest/tests/fixtures/hull/unsupported_public_fallback_return/main.solc b/crates/uitest/tests/fixtures/hull/unsupported_public_fallback_return/main.solc deleted file mode 100644 index 161fc5ec..00000000 --- a/crates/uitest/tests/fixtures/hull/unsupported_public_fallback_return/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract C { - fallback() -> word { - return 1; - } -} diff --git a/crates/uitest/tests/fixtures/nameres/ambiguous/a.solc b/crates/uitest/tests/fixtures/nameres/ambiguous/a.sol similarity index 100% rename from crates/uitest/tests/fixtures/nameres/ambiguous/a.solc rename to crates/uitest/tests/fixtures/nameres/ambiguous/a.sol diff --git a/crates/uitest/tests/fixtures/nameres/ambiguous/b.solc b/crates/uitest/tests/fixtures/nameres/ambiguous/b.sol similarity index 100% rename from crates/uitest/tests/fixtures/nameres/ambiguous/b.solc rename to crates/uitest/tests/fixtures/nameres/ambiguous/b.sol diff --git a/crates/uitest/tests/fixtures/nameres/ambiguous/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/ambiguous/diagnostics.snap index 1f2714f1..75482771 100644 --- a/crates/uitest/tests/fixtures/nameres/ambiguous/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/ambiguous/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/ambiguous/main.solc +input_file: crates/uitest/tests/fixtures/nameres/ambiguous/main.sol --- error[SC0120]: ambiguous selected import `value` in term namespace - --> /main/main.solc:1:1 + --> /main/main.sol:1:1 | -1 | import a.{value}; - | ^^^^^^^^^^^^^^^^^ ambiguous selected import in term namespace -2 | import b.{value}; +1 | import {value} from a; + | ^^^^^^^^^^^^^^^^^^^^^^ ambiguous selected import in term namespace +2 | import {value} from b; | = note: `value` is imported from a, b in term namespace = note: use an explicit module qualifier or narrow the selected imports diff --git a/crates/uitest/tests/fixtures/nameres/ambiguous/main.sol b/crates/uitest/tests/fixtures/nameres/ambiguous/main.sol new file mode 100644 index 00000000..285a6509 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ambiguous/main.sol @@ -0,0 +1,2 @@ +import {value} from a; +import {value} from b; diff --git a/crates/uitest/tests/fixtures/nameres/ambiguous/main.solc b/crates/uitest/tests/fixtures/nameres/ambiguous/main.solc deleted file mode 100644 index 02ca1356..00000000 --- a/crates/uitest/tests/fixtures/nameres/ambiguous/main.solc +++ /dev/null @@ -1,2 +0,0 @@ -import a.{value}; -import b.{value}; diff --git a/crates/uitest/tests/fixtures/nameres/clean_undefined_name/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/clean_undefined_name/diagnostics.snap index 2c73e8b1..d4c87dd8 100644 --- a/crates/uitest/tests/fixtures/nameres/clean_undefined_name/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/clean_undefined_name/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/clean_undefined_name/main.solc +input_file: crates/uitest/tests/fixtures/nameres/clean_undefined_name/main.sol --- error[SC0101]: undefined name: missing - --> /main/main.solc:1:36 + --> /main/main.sol:1:43 | -1 | function caller() -> word { return missing; } - | ^^^^^^^ unknown name +1 | function caller() returns (word) { return missing; } + | ^^^^^^^ unknown name diff --git a/crates/uitest/tests/fixtures/nameres/clean_undefined_name/main.sol b/crates/uitest/tests/fixtures/nameres/clean_undefined_name/main.sol new file mode 100644 index 00000000..21f048b4 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/clean_undefined_name/main.sol @@ -0,0 +1 @@ +function caller() returns (word) { return missing; } diff --git a/crates/uitest/tests/fixtures/nameres/clean_undefined_name/main.solc b/crates/uitest/tests/fixtures/nameres/clean_undefined_name/main.solc deleted file mode 100644 index 524bf823..00000000 --- a/crates/uitest/tests/fixtures/nameres/clean_undefined_name/main.solc +++ /dev/null @@ -1 +0,0 @@ -function caller() -> word { return missing; } diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/a.sol b/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/a.sol new file mode 100644 index 00000000..38437949 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/a.sol @@ -0,0 +1,3 @@ +enum T { A } + +export { T }; diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/a.solc b/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/a.solc deleted file mode 100644 index 94fae05c..00000000 --- a/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/a.solc +++ /dev/null @@ -1,3 +0,0 @@ -data T = A; - -export { T }; diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/b.sol b/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/b.sol new file mode 100644 index 00000000..89b0bcc5 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/b.sol @@ -0,0 +1,5 @@ +function T() returns (word) { + return 0; +} + +export { T }; diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/b.solc b/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/b.solc deleted file mode 100644 index 4f6e6ca6..00000000 --- a/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/b.solc +++ /dev/null @@ -1,5 +0,0 @@ -function T() -> word { - return 0; -} - -export { T }; diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/diagnostics.snap index c808d4fc..d9ddeba7 100644 --- a/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/main.solc +input_file: crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/main.sol --- error[SC0111]: duplicate exported item name `T` - --> /main/main.solc:2:11 + --> /main/main.sol:2:11 | 1 | export a.{T}; 2 | export b.{T}; diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/main.sol b/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/main.sol new file mode 100644 index 00000000..765499d0 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/main.sol @@ -0,0 +1,6 @@ +export a.{T}; +export b.{T}; + +function main() returns (word) { + return 0; +} diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/main.solc b/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/main.solc deleted file mode 100644 index 5e3a31ab..00000000 --- a/crates/uitest/tests/fixtures/nameres/duplicate_export_cross_namespace/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -export a.{T}; -export b.{T}; - -function main() -> word { - return 0; -} diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_local_declarations/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/duplicate_local_declarations/diagnostics.snap index 717a6f6a..bf62276c 100644 --- a/crates/uitest/tests/fixtures/nameres/duplicate_local_declarations/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/duplicate_local_declarations/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/duplicate_local_declarations/main.solc +input_file: crates/uitest/tests/fixtures/nameres/duplicate_local_declarations/main.sol --- error[SC0108]: duplicate declaration `Foo` in type namespace - --> /main/main.solc:2:6 + --> /main/main.sol:2:6 | -1 | data Foo = Foo; +1 | enum Foo { Foo } | --- previous declaration 2 | type Foo = word; | ^^^ duplicate declaration @@ -15,7 +15,7 @@ error[SC0108]: duplicate declaration `Foo` in type namespace --- error[SC0108]: duplicate declaration `dup` in term namespace - --> /main/main.solc:5:10 + --> /main/main.sol:5:10 | 3 | 4 | function dup() {} diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_local_declarations/main.sol b/crates/uitest/tests/fixtures/nameres/duplicate_local_declarations/main.sol new file mode 100644 index 00000000..3db86d09 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/duplicate_local_declarations/main.sol @@ -0,0 +1,5 @@ +enum Foo { Foo } +type Foo = word; + +function dup() {} +function dup() {} diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_local_declarations/main.solc b/crates/uitest/tests/fixtures/nameres/duplicate_local_declarations/main.solc deleted file mode 100644 index 53873613..00000000 --- a/crates/uitest/tests/fixtures/nameres/duplicate_local_declarations/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Foo = Foo; -type Foo = word; - -function dup() {} -function dup() {} diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_qualifier/baz/bar.solc b/crates/uitest/tests/fixtures/nameres/duplicate_qualifier/baz/bar.sol similarity index 100% rename from crates/uitest/tests/fixtures/nameres/duplicate_qualifier/baz/bar.solc rename to crates/uitest/tests/fixtures/nameres/duplicate_qualifier/baz/bar.sol diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_qualifier/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/duplicate_qualifier/diagnostics.snap index d15f7081..07f7a211 100644 --- a/crates/uitest/tests/fixtures/nameres/duplicate_qualifier/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/duplicate_qualifier/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/duplicate_qualifier/main.solc +input_file: crates/uitest/tests/fixtures/nameres/duplicate_qualifier/main.sol --- error[SC0116]: duplicate import qualifier `bar` - --> /main/main.solc:2:12 + --> /main/main.sol:2:12 | 1 | import foo.bar; | --- first qualifier with this name diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_qualifier/foo/bar.solc b/crates/uitest/tests/fixtures/nameres/duplicate_qualifier/foo/bar.sol similarity index 100% rename from crates/uitest/tests/fixtures/nameres/duplicate_qualifier/foo/bar.solc rename to crates/uitest/tests/fixtures/nameres/duplicate_qualifier/foo/bar.sol diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_qualifier/main.solc b/crates/uitest/tests/fixtures/nameres/duplicate_qualifier/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/nameres/duplicate_qualifier/main.solc rename to crates/uitest/tests/fixtures/nameres/duplicate_qualifier/main.sol diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_selector/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/duplicate_selector/diagnostics.snap index 6b574b36..0979c8c8 100644 --- a/crates/uitest/tests/fixtures/nameres/duplicate_selector/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/duplicate_selector/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/duplicate_selector/main.solc +input_file: crates/uitest/tests/fixtures/nameres/duplicate_selector/main.sol --- error[SC0117]: duplicate name `value` in selective import - --> /main/main.solc:1:21 + --> /main/main.sol:1:16 | -1 | import util.{value, value}; - | ----- ^^^^^ duplicate selected import - | | - | first selected import with this name +1 | import {value, value} from util; + | ----- ^^^^^ duplicate selected import + | | + | first selected import with this name | = note: list each selected or hidden name only once diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_selector/main.sol b/crates/uitest/tests/fixtures/nameres/duplicate_selector/main.sol new file mode 100644 index 00000000..c6939c45 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/duplicate_selector/main.sol @@ -0,0 +1 @@ +import {value, value} from util; diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_selector/main.solc b/crates/uitest/tests/fixtures/nameres/duplicate_selector/main.solc deleted file mode 100644 index 286b8c29..00000000 --- a/crates/uitest/tests/fixtures/nameres/duplicate_selector/main.solc +++ /dev/null @@ -1 +0,0 @@ -import util.{value, value}; diff --git a/crates/uitest/tests/fixtures/nameres/duplicate_selector/util.solc b/crates/uitest/tests/fixtures/nameres/duplicate_selector/util.sol similarity index 100% rename from crates/uitest/tests/fixtures/nameres/duplicate_selector/util.solc rename to crates/uitest/tests/fixtures/nameres/duplicate_selector/util.sol diff --git a/crates/uitest/tests/fixtures/nameres/ergo_dup_data_class/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/ergo_dup_data_class/diagnostics.snap index 513e43eb..4b72e043 100644 --- a/crates/uitest/tests/fixtures/nameres/ergo_dup_data_class/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/ergo_dup_data_class/diagnostics.snap @@ -1,30 +1,30 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/ergo_dup_data_class/main.solc +input_file: crates/uitest/tests/fixtures/nameres/ergo_dup_data_class/main.sol --- error[SC0108]: duplicate declaration `Shape` in type namespace - --> /main/main.solc:3:6 + --> /main/main.sol:3:6 | -1 | data Shape = Circle(word); +1 | enum Shape { Circle(word) } | ----- previous declaration 2 | -3 | data Shape = Square(word); +3 | enum Shape { Square(word) } | ^^^^^ duplicate declaration 4 | | --- error[SC0108]: duplicate declaration `Render` in type namespace - --> /main/main.solc:9:20 + --> /main/main.sol:9:7 | 4 | - 5 | forall a . class a:Render { - | ------ previous declaration - 6 | function render(x: a) -> word; + 5 | trait Render { + | ------ previous declaration + 6 | function render(x: a) returns (word) ; 7 | } 8 | - 9 | forall a . class a:Render { - | ^^^^^^ duplicate declaration -10 | function paint(x: a) -> word; + 9 | trait Render { + | ^^^^^^ duplicate declaration +10 | function paint(x: a) returns (word) ; | diff --git a/crates/uitest/tests/fixtures/nameres/ergo_dup_data_class/main.sol b/crates/uitest/tests/fixtures/nameres/ergo_dup_data_class/main.sol new file mode 100644 index 00000000..4a12014e --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_dup_data_class/main.sol @@ -0,0 +1,11 @@ +enum Shape { Circle(word) } + +enum Shape { Square(word) } + +trait Render { + function render(x: a) returns (word) ; +} + +trait Render { + function paint(x: a) returns (word) ; +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_dup_data_class/main.solc b/crates/uitest/tests/fixtures/nameres/ergo_dup_data_class/main.solc deleted file mode 100644 index 4e727aa2..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_dup_data_class/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -data Shape = Circle(word); - -data Shape = Square(word); - -forall a . class a:Render { - function render(x: a) -> word; -} - -forall a . class a:Render { - function paint(x: a) -> word; -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_dup_function/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/ergo_dup_function/diagnostics.snap index e6f5d824..5e9d4b0d 100644 --- a/crates/uitest/tests/fixtures/nameres/ergo_dup_function/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/ergo_dup_function/diagnostics.snap @@ -1,18 +1,18 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/ergo_dup_function/main.solc +input_file: crates/uitest/tests/fixtures/nameres/ergo_dup_function/main.sol --- error[SC0108]: duplicate declaration `twice` in term namespace - --> /main/main.solc:9:10 + --> /main/main.sol:9:10 | - 1 | function twice(x: word) -> word { + 1 | function twice(x: word) returns (word) { | ----- previous declaration 2 | return x; 3 | } ... 8 | - 9 | function twice(x: word) -> word { + 9 | function twice(x: word) returns (word) { | ^^^^^ duplicate declaration 10 | return x; | diff --git a/crates/uitest/tests/fixtures/nameres/ergo_dup_function/main.sol b/crates/uitest/tests/fixtures/nameres/ergo_dup_function/main.sol new file mode 100644 index 00000000..e46ad4a7 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_dup_function/main.sol @@ -0,0 +1,11 @@ +function twice(x: word) returns (word) { + return x; +} + +function helper(y: word) returns (word) { + return y; +} + +function twice(x: word) returns (word) { + return x; +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_dup_function/main.solc b/crates/uitest/tests/fixtures/nameres/ergo_dup_function/main.solc deleted file mode 100644 index 5f708955..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_dup_function/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -function twice(x: word) -> word { - return x; -} - -function helper(y: word) -> word { - return y; -} - -function twice(x: word) -> word { - return x; -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/diagnostics.snap index e4ee7599..ff89f69d 100644 --- a/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/main.solc +input_file: crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/main.sol --- error[SC0109]: import helprs: file not found - --> /main/main.solc:1:8 + --> /main/main.sol:1:27 | -1 | import helprs.{helperValue}; - | ^^^^^^ module reference +1 | import {helperValue} from helprs; + | ^^^^^^ module reference 2 | -3 | function main() -> word { +3 | function main() returns (word) { | = help: check the module path or add the missing source file = help: did you mean `helpers`? diff --git a/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/helpers.sol b/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/helpers.sol new file mode 100644 index 00000000..057993ab --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/helpers.sol @@ -0,0 +1,5 @@ +export { helperValue }; + +function helperValue(x: word) returns (word) { + return x; +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/helpers.solc b/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/helpers.solc deleted file mode 100644 index e497c9f4..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/helpers.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { helperValue }; - -function helperValue(x: word) -> word { - return x; -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/main.sol b/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/main.sol new file mode 100644 index 00000000..dca48485 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/main.sol @@ -0,0 +1,5 @@ +import {helperValue} from helprs; + +function main() returns (word) { + return helperValue(1); +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/main.solc b/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/main.solc deleted file mode 100644 index 7af60146..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_import_module_typo/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import helprs.{helperValue}; - -function main() -> word { - return helperValue(1); -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/diagnostics.snap index 6f7470d1..1f45b263 100644 --- a/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/main.solc +input_file: crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/main.sol --- error[SC0110]: unknown import item `valu` - --> /main/main.solc:1:14 + --> /main/main.sol:1:9 | -1 | import util.{valu}; - | ^^^^ unknown import item +1 | import {valu} from util; + | ^^^^ unknown import item 2 | -3 | function main() -> word { +3 | function main() returns (word) { | = note: `valu` is not exported by module `util` = help: did you mean `value`? diff --git a/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/main.sol b/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/main.sol new file mode 100644 index 00000000..f57c8e4e --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/main.sol @@ -0,0 +1,5 @@ +import {valu} from util; + +function main() returns (word) { + return valu(1); +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/main.solc b/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/main.solc deleted file mode 100644 index 1dae0969..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import util.{valu}; - -function main() -> word { - return valu(1); -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/util.sol b/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/util.sol new file mode 100644 index 00000000..9e614c51 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/util.sol @@ -0,0 +1,5 @@ +export { value }; + +function value(x: word) returns (word) { + return x; +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/util.solc b/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/util.solc deleted file mode 100644 index e88bc4d3..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_import_symbol_typo/util.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { value }; - -function value(x: word) -> word { - return x; -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/diagnostics.snap index 5e268843..726d56d6 100644 --- a/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/diagnostics.snap @@ -1,20 +1,20 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/ergo_private_qualified/main.solc +input_file: crates/uitest/tests/fixtures/nameres/ergo_private_qualified/main.sol --- error[SC0101]: undefined name: secret - --> /main/main.solc:4:16 + --> /main/main.sol:4:16 | -3 | function main() -> word { +3 | function main() returns (word) { 4 | return vault.secret(1); | ^^^^^^ unknown name 5 | } | - ::: /main/vault.solc:6 + ::: /main/vault.sol:6 | 6 | -7 | function secret(x: word) -> word { +7 | function secret(x: word) returns (word) { | ------ private item declared here 8 | return x; | diff --git a/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/main.sol b/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/main.sol new file mode 100644 index 00000000..76988d72 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/main.sol @@ -0,0 +1,5 @@ +import vault; + +function main() returns (word) { + return vault.secret(1); +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/main.solc b/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/main.solc deleted file mode 100644 index b5fb1d84..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import vault; - -function main() -> word { - return vault.secret(1); -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/vault.sol b/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/vault.sol new file mode 100644 index 00000000..36917988 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/vault.sol @@ -0,0 +1,9 @@ +export { opened }; + +function opened(x: word) returns (word) { + return secret(x); +} + +function secret(x: word) returns (word) { + return x; +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/vault.solc b/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/vault.solc deleted file mode 100644 index 6911fc2e..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_private_qualified/vault.solc +++ /dev/null @@ -1,9 +0,0 @@ -export { opened }; - -function opened(x: word) -> word { - return secret(x); -} - -function secret(x: word) -> word { - return x; -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_typo_did_you_mean/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/ergo_typo_did_you_mean/diagnostics.snap index 2101a5ce..a5182982 100644 --- a/crates/uitest/tests/fixtures/nameres/ergo_typo_did_you_mean/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/ergo_typo_did_you_mean/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/ergo_typo_did_you_mean/main.solc +input_file: crates/uitest/tests/fixtures/nameres/ergo_typo_did_you_mean/main.sol --- error[SC0101]: undefined name: computeVale - --> /main/main.solc:6:10 + --> /main/main.sol:6:10 | -5 | function main() -> word { +5 | function main() returns (word) { 6 | return computeVale(1); | ^^^^^^^^^^^ unknown name 7 | } diff --git a/crates/uitest/tests/fixtures/nameres/ergo_typo_did_you_mean/main.sol b/crates/uitest/tests/fixtures/nameres/ergo_typo_did_you_mean/main.sol new file mode 100644 index 00000000..b3c9eccc --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_typo_did_you_mean/main.sol @@ -0,0 +1,7 @@ +function computeValue(x: word) returns (word) { + return x; +} + +function main() returns (word) { + return computeVale(1); +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_typo_did_you_mean/main.solc b/crates/uitest/tests/fixtures/nameres/ergo_typo_did_you_mean/main.solc deleted file mode 100644 index 0f561585..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_typo_did_you_mean/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -function computeValue(x: word) -> word { - return x; -} - -function main() -> word { - return computeVale(1); -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_undef_class/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/ergo_undef_class/diagnostics.snap index c26208be..996f5a3f 100644 --- a/crates/uitest/tests/fixtures/nameres/ergo_undef_class/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/ergo_undef_class/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/ergo_undef_class/main.solc +input_file: crates/uitest/tests/fixtures/nameres/ergo_undef_class/main.sol --- -error[SC0105]: undefined class: NoSuchClass - --> /main/main.solc:1:17 +error[SC0105]: undefined trait: NoSuchClass + --> /main/main.sol:1:6 | -1 | instance word : NoSuchClass { - | ^^^^^^^^^^^ undefined class -2 | function frob(x: word) -> word { +1 | impl NoSuchClass { + | ^^^^^^^^^^^ undefined trait +2 | function frob(x: word) returns (word) { 3 | return x; | diff --git a/crates/uitest/tests/fixtures/nameres/ergo_undef_class/main.sol b/crates/uitest/tests/fixtures/nameres/ergo_undef_class/main.sol new file mode 100644 index 00000000..78d226c5 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_undef_class/main.sol @@ -0,0 +1,5 @@ +impl NoSuchClass { + function frob(x: word) returns (word) { + return x; + } +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_undef_class/main.solc b/crates/uitest/tests/fixtures/nameres/ergo_undef_class/main.solc deleted file mode 100644 index 3cd65960..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_undef_class/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -instance word : NoSuchClass { - function frob(x: word) -> word { - return x; - } -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_undef_constructor/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/ergo_undef_constructor/diagnostics.snap index b2de4860..4c50a215 100644 --- a/crates/uitest/tests/fixtures/nameres/ergo_undef_constructor/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/ergo_undef_constructor/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/ergo_undef_constructor/main.solc +input_file: crates/uitest/tests/fixtures/nameres/ergo_undef_constructor/main.sol --- error[SC0101]: undefined name: Option.Nope - --> /main/main.solc:5:12 + --> /main/main.sol:5:13 | -4 | match o { -5 | | Option.Nope => return 0; - | ^^^^ unknown name -6 | | Option.Some(v) => return v; +4 | match (o) { +5 | case Option.Nope { + | ^^^^ unknown name +6 | return 0; | = help: did you mean `Option.None`? diff --git a/crates/uitest/tests/fixtures/nameres/ergo_undef_constructor/main.sol b/crates/uitest/tests/fixtures/nameres/ergo_undef_constructor/main.sol new file mode 100644 index 00000000..72f4282d --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_undef_constructor/main.sol @@ -0,0 +1,12 @@ +enum Option { None, Some(word) } + +function unwrap(o: Option) returns (word) { + match (o) { +case Option.Nope { +return 0; +} +case Option.Some(v) { +return v; +} +} +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_undef_constructor/main.solc b/crates/uitest/tests/fixtures/nameres/ergo_undef_constructor/main.solc deleted file mode 100644 index ab92497d..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_undef_constructor/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -data Option = None | Some(word); - -function unwrap(o: Option) -> word { - match o { - | Option.Nope => return 0; - | Option.Some(v) => return v; - } -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_undef_type/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/ergo_undef_type/diagnostics.snap index 96364ff4..5e4759f7 100644 --- a/crates/uitest/tests/fixtures/nameres/ergo_undef_type/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/ergo_undef_type/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/ergo_undef_type/main.solc +input_file: crates/uitest/tests/fixtures/nameres/ergo_undef_type/main.sol --- error[SC0103]: undefined type constructor: MissingType - --> /main/main.solc:1:20 + --> /main/main.sol:1:20 | -1 | function takeIt(x: MissingType) -> word { +1 | function takeIt(x: MissingType) returns (word) { | ^^^^^^^^^^^ undefined type constructor 2 | return 0; 3 | } diff --git a/crates/uitest/tests/fixtures/nameres/ergo_undef_type/main.sol b/crates/uitest/tests/fixtures/nameres/ergo_undef_type/main.sol new file mode 100644 index 00000000..e73ef31b --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_undef_type/main.sol @@ -0,0 +1,3 @@ +function takeIt(x: MissingType) returns (word) { + return 0; +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_undef_type/main.solc b/crates/uitest/tests/fixtures/nameres/ergo_undef_type/main.solc deleted file mode 100644 index c74efea5..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_undef_type/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -function takeIt(x: MissingType) -> word { - return 0; -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_undef_variable/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/ergo_undef_variable/diagnostics.snap index 38ecf1f1..a1515d76 100644 --- a/crates/uitest/tests/fixtures/nameres/ergo_undef_variable/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/ergo_undef_variable/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/ergo_undef_variable/main.solc +input_file: crates/uitest/tests/fixtures/nameres/ergo_undef_variable/main.sol --- error[SC0101]: undefined name: missingVar - --> /main/main.solc:2:14 + --> /main/main.sol:2:14 | -1 | function addOne(x: word) -> word { +1 | function addOne(x: word) returns (word) { 2 | return x + missingVar; | ^^^^^^^^^^ unknown name 3 | } diff --git a/crates/uitest/tests/fixtures/nameres/ergo_undef_variable/main.sol b/crates/uitest/tests/fixtures/nameres/ergo_undef_variable/main.sol new file mode 100644 index 00000000..996c4b93 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_undef_variable/main.sol @@ -0,0 +1,3 @@ +function addOne(x: word) returns (word) { + return x + missingVar; +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_undef_variable/main.solc b/crates/uitest/tests/fixtures/nameres/ergo_undef_variable/main.solc deleted file mode 100644 index eaa7a911..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_undef_variable/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -function addOne(x: word) -> word { - return x + missingVar; -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_unqual_ctor_sc0106/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/ergo_unqual_ctor_sc0106/diagnostics.snap index a5d17840..a9227220 100644 --- a/crates/uitest/tests/fixtures/nameres/ergo_unqual_ctor_sc0106/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/ergo_unqual_ctor_sc0106/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/ergo_unqual_ctor_sc0106/main.solc +input_file: crates/uitest/tests/fixtures/nameres/ergo_unqual_ctor_sc0106/main.sol --- error[SC0106]: unqualified constructor: On - --> /main/main.solc:12:15 + --> /main/main.sol:16:15 | -11 | function main() -> word { -12 | return isOn(On); +15 | function main() returns (word) { +16 | return isOn(On); | ^^ constructor must be qualified -13 | } +17 | } | = help: use `Light.On` diff --git a/crates/uitest/tests/fixtures/nameres/ergo_unqual_ctor_sc0106/main.sol b/crates/uitest/tests/fixtures/nameres/ergo_unqual_ctor_sc0106/main.sol new file mode 100644 index 00000000..99a7f088 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_unqual_ctor_sc0106/main.sol @@ -0,0 +1,17 @@ +enum Light { On, Off } +enum Power { Plugged, Battery } + +function isOn(l: Light) returns (word) { + match (l) { +case Light.On { +return 1; +} +case Light.Off { +return 0; +} +} +} + +function main() returns (word) { + return isOn(On); +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_unqual_ctor_sc0106/main.solc b/crates/uitest/tests/fixtures/nameres/ergo_unqual_ctor_sc0106/main.solc deleted file mode 100644 index deca5936..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_unqual_ctor_sc0106/main.solc +++ /dev/null @@ -1,13 +0,0 @@ -data Light = On | Off; -data Power = Plugged | Battery; - -function isOn(l: Light) -> word { - match l { - | Light.On => return 1; - | Light.Off => return 0; - } -} - -function main() -> word { - return isOn(On); -} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_value_as_type/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/ergo_value_as_type/diagnostics.snap index 8ef102cf..5bf1c59e 100644 --- a/crates/uitest/tests/fixtures/nameres/ergo_value_as_type/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/ergo_value_as_type/diagnostics.snap @@ -1,17 +1,17 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/ergo_value_as_type/main.solc +input_file: crates/uitest/tests/fixtures/nameres/ergo_value_as_type/main.sol --- error[SC0103]: undefined type constructor: MkPair - --> /main/main.solc:3:19 + --> /main/main.sol:3:19 | -1 | data Pair = MkPair(word, word); +1 | enum Pair { MkPair(word, word) } | ------ constructor declared here 2 | -3 | function first(p: MkPair) -> word { +3 | function first(p: MkPair) returns (word) { | ^^^^^^ undefined type constructor -4 | match p { +4 | match (p) { | = note: `MkPair` is a constructor of type `Pair` = help: use `Pair` as the type name diff --git a/crates/uitest/tests/fixtures/nameres/ergo_value_as_type/main.sol b/crates/uitest/tests/fixtures/nameres/ergo_value_as_type/main.sol new file mode 100644 index 00000000..a62a6bc2 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/ergo_value_as_type/main.sol @@ -0,0 +1,9 @@ +enum Pair { MkPair(word, word) } + +function first(p: MkPair) returns (word) { + match (p) { +case Pair.MkPair(a, b) { +return a; +} +} +} diff --git a/crates/uitest/tests/fixtures/nameres/ergo_value_as_type/main.solc b/crates/uitest/tests/fixtures/nameres/ergo_value_as_type/main.solc deleted file mode 100644 index ff8e3173..00000000 --- a/crates/uitest/tests/fixtures/nameres/ergo_value_as_type/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -data Pair = MkPair(word, word); - -function first(p: MkPair) -> word { - match p { - | Pair.MkPair(a, b) => return a; - } -} diff --git a/crates/uitest/tests/fixtures/nameres/glob_shadow_local/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/glob_shadow_local/diagnostics.snap index 483e1d4f..2768226e 100644 --- a/crates/uitest/tests/fixtures/nameres/glob_shadow_local/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/glob_shadow_local/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/glob_shadow_local/main.solc +input_file: crates/uitest/tests/fixtures/nameres/glob_shadow_local/main.sol --- error[SC0108]: duplicate declaration `value` in term namespace - --> /main/main.solc:3:10 + --> /main/main.sol:3:10 | -1 | import lib.{*}; - | --------------- previous declaration +1 | import * from lib; + | ------------------ previous declaration 2 | -3 | function value(x: word) -> word { +3 | function value(x: word) returns (word) { | ^^^^^ duplicate declaration 4 | return x; | diff --git a/crates/uitest/tests/fixtures/nameres/glob_shadow_local/lib.sol b/crates/uitest/tests/fixtures/nameres/glob_shadow_local/lib.sol new file mode 100644 index 00000000..36dd500c --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/glob_shadow_local/lib.sol @@ -0,0 +1,5 @@ +function value(x: word) returns (word) { + return x; +} + +export { value }; diff --git a/crates/uitest/tests/fixtures/nameres/glob_shadow_local/lib.solc b/crates/uitest/tests/fixtures/nameres/glob_shadow_local/lib.solc deleted file mode 100644 index 0d203179..00000000 --- a/crates/uitest/tests/fixtures/nameres/glob_shadow_local/lib.solc +++ /dev/null @@ -1,5 +0,0 @@ -function value(x: word) -> word { - return x; -} - -export { value }; diff --git a/crates/uitest/tests/fixtures/nameres/glob_shadow_local/main.sol b/crates/uitest/tests/fixtures/nameres/glob_shadow_local/main.sol new file mode 100644 index 00000000..c963b6e0 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/glob_shadow_local/main.sol @@ -0,0 +1,5 @@ +import * from lib; + +function value(x: word) returns (word) { + return x; +} diff --git a/crates/uitest/tests/fixtures/nameres/glob_shadow_local/main.solc b/crates/uitest/tests/fixtures/nameres/glob_shadow_local/main.solc deleted file mode 100644 index 269e54e6..00000000 --- a/crates/uitest/tests/fixtures/nameres/glob_shadow_local/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import lib.{*}; - -function value(x: word) -> word { - return x; -} diff --git a/crates/uitest/tests/fixtures/nameres/hidden_ctor/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/hidden_ctor/diagnostics.snap index d6a48725..db0475fb 100644 --- a/crates/uitest/tests/fixtures/nameres/hidden_ctor/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/hidden_ctor/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/hidden_ctor/main.solc +input_file: crates/uitest/tests/fixtures/nameres/hidden_ctor/main.sol --- error[SC0101]: undefined name: Err - --> /main/main.solc:4:16 + --> /main/main.sol:4:16 | -3 | function main() -> Token { +3 | function main() returns (Token) { 4 | return Token.Err(0); | ^^^ unknown name 5 | } diff --git a/crates/uitest/tests/fixtures/nameres/hidden_ctor/lib.sol b/crates/uitest/tests/fixtures/nameres/hidden_ctor/lib.sol new file mode 100644 index 00000000..cec692ef --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/hidden_ctor/lib.sol @@ -0,0 +1,7 @@ +export { Token(Ok), mkErr }; + +enum Token { Ok(word), Err(word) } + +function mkErr(x: word) returns (Token) { + return Token.Err(x); +} diff --git a/crates/uitest/tests/fixtures/nameres/hidden_ctor/lib.solc b/crates/uitest/tests/fixtures/nameres/hidden_ctor/lib.solc deleted file mode 100644 index 597b0300..00000000 --- a/crates/uitest/tests/fixtures/nameres/hidden_ctor/lib.solc +++ /dev/null @@ -1,7 +0,0 @@ -export { Token(Ok), mkErr }; - -data Token = Ok(word) | Err(word); - -function mkErr(x: word) -> Token { - return Token.Err(x); -} diff --git a/crates/uitest/tests/fixtures/nameres/hidden_ctor/main.sol b/crates/uitest/tests/fixtures/nameres/hidden_ctor/main.sol new file mode 100644 index 00000000..fb35bb00 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/hidden_ctor/main.sol @@ -0,0 +1,5 @@ +import {Token} from lib; + +function main() returns (Token) { + return Token.Err(0); +} diff --git a/crates/uitest/tests/fixtures/nameres/hidden_ctor/main.solc b/crates/uitest/tests/fixtures/nameres/hidden_ctor/main.solc deleted file mode 100644 index 02d84415..00000000 --- a/crates/uitest/tests/fixtures/nameres/hidden_ctor/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import lib.{Token}; - -function main() -> Token { - return Token.Err(0); -} diff --git a/crates/uitest/tests/fixtures/nameres/missing/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/missing/diagnostics.snap index 7a2e91a2..8d580222 100644 --- a/crates/uitest/tests/fixtures/nameres/missing/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/missing/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/missing/main.solc +input_file: crates/uitest/tests/fixtures/nameres/missing/main.sol --- error[SC0109]: import missing: file not found - --> /main/main.solc:1:8 + --> /main/main.sol:1:21 | -1 | import missing.{value}; - | ^^^^^^^ module reference +1 | import {value} from missing; + | ^^^^^^^ module reference | = help: check the module path or add the missing source file diff --git a/crates/uitest/tests/fixtures/nameres/missing/main.sol b/crates/uitest/tests/fixtures/nameres/missing/main.sol new file mode 100644 index 00000000..7babefc7 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/missing/main.sol @@ -0,0 +1 @@ +import {value} from missing; diff --git a/crates/uitest/tests/fixtures/nameres/missing/main.solc b/crates/uitest/tests/fixtures/nameres/missing/main.solc deleted file mode 100644 index 80f575d9..00000000 --- a/crates/uitest/tests/fixtures/nameres/missing/main.solc +++ /dev/null @@ -1 +0,0 @@ -import missing.{value}; diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/a.sol b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/a.sol new file mode 100644 index 00000000..38437949 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/a.sol @@ -0,0 +1,3 @@ +enum T { A } + +export { T }; diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/a.solc b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/a.solc deleted file mode 100644 index 94fae05c..00000000 --- a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/a.solc +++ /dev/null @@ -1,3 +0,0 @@ -data T = A; - -export { T }; diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/b.sol b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/b.sol new file mode 100644 index 00000000..89b0bcc5 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/b.sol @@ -0,0 +1,5 @@ +function T() returns (word) { + return 0; +} + +export { T }; diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/b.solc b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/b.solc deleted file mode 100644 index 4f6e6ca6..00000000 --- a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/b.solc +++ /dev/null @@ -1,5 +0,0 @@ -function T() -> word { - return 0; -} - -export { T }; diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/diagnostics.snap index 18cbe552..347c0981 100644 --- a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/main.solc +input_file: crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/main.sol --- error[SC0120]: ambiguous selected import `T` across term/type namespaces - --> /main/main.solc:1:1 + --> /main/main.sol:1:1 | -1 | import a.{T}; - | ^^^^^^^^^^^^^ ambiguous selected import across term/type namespaces -2 | import b.{T}; +1 | import {T} from a; + | ^^^^^^^^^^^^^^^^^^ ambiguous selected import across term/type namespaces +2 | import {T} from b; 3 | | = note: `T` is imported from a, b across term/type namespaces diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/main.sol b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/main.sol new file mode 100644 index 00000000..32ab0288 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/main.sol @@ -0,0 +1,6 @@ +import {T} from a; +import {T} from b; + +function main() returns (word) { + return 0; +} diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/main.solc b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/main.solc deleted file mode 100644 index a25f626a..00000000 --- a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_cross_namespace/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -import a.{T}; -import b.{T}; - -function main() -> word { - return 0; -} diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/a.sol b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/a.sol new file mode 100644 index 00000000..699035c4 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/a.sol @@ -0,0 +1,7 @@ +enum T { A } + +function T() returns (word) { + return 0; +} + +export { T }; diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/a.solc b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/a.solc deleted file mode 100644 index 7621b143..00000000 --- a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/a.solc +++ /dev/null @@ -1,7 +0,0 @@ -data T = A; - -function T() -> word { - return 0; -} - -export { T }; diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/b.sol b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/b.sol new file mode 100644 index 00000000..699035c4 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/b.sol @@ -0,0 +1,7 @@ +enum T { A } + +function T() returns (word) { + return 0; +} + +export { T }; diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/b.solc b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/b.solc deleted file mode 100644 index 7621b143..00000000 --- a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/b.solc +++ /dev/null @@ -1,7 +0,0 @@ -data T = A; - -function T() -> word { - return 0; -} - -export { T }; diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/diagnostics.snap index c07d3f72..2bdbaf2b 100644 --- a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/main.solc +input_file: crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/main.sol --- error[SC0120]: ambiguous selected import `T` in term namespace - --> /main/main.solc:1:1 + --> /main/main.sol:1:1 | -1 | import a.{T}; - | ^^^^^^^^^^^^^ ambiguous selected import in term namespace -2 | import b.{T}; +1 | import {T} from a; + | ^^^^^^^^^^^^^^^^^^ ambiguous selected import in term namespace +2 | import {T} from b; 3 | | = note: `T` is imported from a, b in term namespace @@ -16,11 +16,11 @@ error[SC0120]: ambiguous selected import `T` in term namespace --- error[SC0120]: ambiguous selected import `T` in type namespace - --> /main/main.solc:1:1 + --> /main/main.sol:1:1 | -1 | import a.{T}; - | ^^^^^^^^^^^^^ ambiguous selected import in type namespace -2 | import b.{T}; +1 | import {T} from a; + | ^^^^^^^^^^^^^^^^^^ ambiguous selected import in type namespace +2 | import {T} from b; 3 | | = note: `T` is imported from a, b in type namespace diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/main.sol b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/main.sol new file mode 100644 index 00000000..32ab0288 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/main.sol @@ -0,0 +1,6 @@ +import {T} from a; +import {T} from b; + +function main() returns (word) { + return 0; +} diff --git a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/main.solc b/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/main.solc deleted file mode 100644 index a25f626a..00000000 --- a/crates/uitest/tests/fixtures/nameres/selected_import_ambiguity_namespace_identity/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -import a.{T}; -import b.{T}; - -function main() -> word { - return 0; -} diff --git a/crates/uitest/tests/fixtures/nameres/string_type_annotation/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/string_type_annotation/diagnostics.snap index d9f07ec1..fffe3fd1 100644 --- a/crates/uitest/tests/fixtures/nameres/string_type_annotation/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/string_type_annotation/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/string_type_annotation/main.solc +input_file: crates/uitest/tests/fixtures/nameres/string_type_annotation/main.sol --- error[SC0103]: undefined type constructor: string - --> /main/main.solc:1:17 + --> /main/main.sol:1:23 | -1 | function f() -> string { - | ^^^^^^ undefined type constructor +1 | function f() returns (string) { + | ^^^^^^ undefined type constructor 2 | return "ok"; 3 | } | diff --git a/crates/uitest/tests/fixtures/nameres/string_type_annotation/main.sol b/crates/uitest/tests/fixtures/nameres/string_type_annotation/main.sol new file mode 100644 index 00000000..8c0cfdbf --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/string_type_annotation/main.sol @@ -0,0 +1,3 @@ +function f() returns (string) { + return "ok"; +} diff --git a/crates/uitest/tests/fixtures/nameres/string_type_annotation/main.solc b/crates/uitest/tests/fixtures/nameres/string_type_annotation/main.solc deleted file mode 100644 index c80a1a93..00000000 --- a/crates/uitest/tests/fixtures/nameres/string_type_annotation/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -function f() -> string { - return "ok"; -} diff --git a/crates/uitest/tests/fixtures/nameres/undefined_name_namespaces/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/undefined_name_namespaces/diagnostics.snap index 2d9f2f07..139eabb4 100644 --- a/crates/uitest/tests/fixtures/nameres/undefined_name_namespaces/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/undefined_name_namespaces/diagnostics.snap @@ -1,32 +1,32 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/undefined_name_namespaces/main.solc +input_file: crates/uitest/tests/fixtures/nameres/undefined_name_namespaces/main.sol --- -error[SC0105]: undefined class: MissingClass - --> /main/main.solc:1:14 +error[SC0103]: undefined type constructor: MissingTy + --> /main/main.sol:1:18 | -1 | forall a . a:MissingClass => function f(x: MissingTy) -> word { - | ^^^^^^^^^^^^ undefined class +1 | function f(x: MissingTy) returns (word) where a: MissingClass { + | ^^^^^^^^^ undefined type constructor 2 | return missingName; 3 | } | --- -error[SC0103]: undefined type constructor: MissingTy - --> /main/main.solc:1:44 +error[SC0105]: undefined trait: MissingClass + --> /main/main.sol:1:53 | -1 | forall a . a:MissingClass => function f(x: MissingTy) -> word { - | ^^^^^^^^^ undefined type constructor +1 | function f(x: MissingTy) returns (word) where a: MissingClass { + | ^^^^^^^^^^^^ undefined trait 2 | return missingName; 3 | } | --- error[SC0101]: undefined name: missingName - --> /main/main.solc:2:10 + --> /main/main.sol:2:10 | -1 | forall a . a:MissingClass => function f(x: MissingTy) -> word { +1 | function f(x: MissingTy) returns (word) where a: MissingClass { 2 | return missingName; | ^^^^^^^^^^^ unknown name 3 | } diff --git a/crates/uitest/tests/fixtures/nameres/undefined_name_namespaces/main.sol b/crates/uitest/tests/fixtures/nameres/undefined_name_namespaces/main.sol new file mode 100644 index 00000000..f2b3c1fc --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/undefined_name_namespaces/main.sol @@ -0,0 +1,3 @@ +function f(x: MissingTy) returns (word) where a: MissingClass { + return missingName; +} diff --git a/crates/uitest/tests/fixtures/nameres/undefined_name_namespaces/main.solc b/crates/uitest/tests/fixtures/nameres/undefined_name_namespaces/main.solc deleted file mode 100644 index 806ba090..00000000 --- a/crates/uitest/tests/fixtures/nameres/undefined_name_namespaces/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -forall a . a:MissingClass => function f(x: MissingTy) -> word { - return missingName; -} diff --git a/crates/uitest/tests/fixtures/nameres/unknown_import/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/unknown_import/diagnostics.snap index 4aa9e360..cff08b03 100644 --- a/crates/uitest/tests/fixtures/nameres/unknown_import/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/unknown_import/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/unknown_import/main.solc +input_file: crates/uitest/tests/fixtures/nameres/unknown_import/main.sol --- error[SC0110]: unknown import item `missing` - --> /main/main.solc:1:14 + --> /main/main.sol:1:9 | -1 | import util.{missing}; - | ^^^^^^^ unknown import item +1 | import {missing} from util; + | ^^^^^^^ unknown import item | = note: `missing` is not exported by module `util` = help: check the imported module's exported names diff --git a/crates/uitest/tests/fixtures/nameres/unknown_import/main.sol b/crates/uitest/tests/fixtures/nameres/unknown_import/main.sol new file mode 100644 index 00000000..e08ec7b1 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/unknown_import/main.sol @@ -0,0 +1 @@ +import {missing} from util; diff --git a/crates/uitest/tests/fixtures/nameres/unknown_import/main.solc b/crates/uitest/tests/fixtures/nameres/unknown_import/main.solc deleted file mode 100644 index 38d0deaf..00000000 --- a/crates/uitest/tests/fixtures/nameres/unknown_import/main.solc +++ /dev/null @@ -1 +0,0 @@ -import util.{missing}; diff --git a/crates/uitest/tests/fixtures/nameres/unknown_import/util.solc b/crates/uitest/tests/fixtures/nameres/unknown_import/util.sol similarity index 100% rename from crates/uitest/tests/fixtures/nameres/unknown_import/util.solc rename to crates/uitest/tests/fixtures/nameres/unknown_import/util.sol diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_constructor_all_forms/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/unqualified_constructor_all_forms/diagnostics.snap index fcc05e96..7643b962 100644 --- a/crates/uitest/tests/fixtures/nameres/unqualified_constructor_all_forms/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/unqualified_constructor_all_forms/diagnostics.snap @@ -1,58 +1,58 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/unqualified_constructor_all_forms/main.solc +input_file: crates/uitest/tests/fixtures/nameres/unqualified_constructor_all_forms/main.sol --- error[SC0106]: unqualified constructor: Some - --> /main/main.solc:4:47 + --> /main/main.sol:4:54 | 3 | -4 | function exprCall(x: word) -> Option { return Some(x); } - | ^^^^ constructor must be qualified -5 | function exprBare(f: flag) -> flag { return on; } +4 | function exprCall(x: word) returns (Option) { return Some(x); } + | ^^^^ constructor must be qualified +5 | function exprBare(f: flag) returns (flag) { return on; } | = help: use `Option.Some` --- error[SC0106]: unqualified constructor: on - --> /main/main.solc:5:45 + --> /main/main.sol:5:52 | -4 | function exprCall(x: word) -> Option { return Some(x); } -5 | function exprBare(f: flag) -> flag { return on; } - | ^^ constructor must be qualified +4 | function exprCall(x: word) returns (Option) { return Some(x); } +5 | function exprBare(f: flag) returns (flag) { return on; } + | ^^ constructor must be qualified 6 | | = help: use `flag.on` --- error[SC0106]: unqualified constructor: off - --> /main/main.solc:9:5 + --> /main/main.sol:9:6 | - 8 | match f { - 9 | | off => return 0; - | ^^^ constructor must be qualified -10 | | on => return 1; + 8 | match (f) { + 9 | case off { + | ^^^ constructor must be qualified +10 | return 0; | = help: use `flag.off` --- error[SC0106]: unqualified constructor: on - --> /main/main.solc:10:5 + --> /main/main.sol:12:6 | - 9 | | off => return 0; -10 | | on => return 1; - | ^^ constructor must be qualified -11 | } +11 | } +12 | case on { + | ^^ constructor must be qualified +13 | return 1; | = help: use `flag.on` --- error[SC0106]: unqualified constructor: None - --> /main/main.solc:16:5 + --> /main/main.sol:20:6 | -15 | match o { -16 | | None => return 0; - | ^^^^ constructor must be qualified -17 | | _ => return 1; +19 | match (o) { +20 | case None { + | ^^^^ constructor must be qualified +21 | return 0; | = help: use `Option.None` diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_constructor_all_forms/main.sol b/crates/uitest/tests/fixtures/nameres/unqualified_constructor_all_forms/main.sol new file mode 100644 index 00000000..e2b714a2 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/unqualified_constructor_all_forms/main.sol @@ -0,0 +1,27 @@ +enum Option { None, Some(word) } +enum flag { off, on } + +function exprCall(x: word) returns (Option) { return Some(x); } +function exprBare(f: flag) returns (flag) { return on; } + +function patLower(f: flag) returns (word) { + match (f) { +case off { +return 0; +} +case on { +return 1; +} +} +} + +function patUpper(o: Option) returns (word) { + match (o) { +case None { +return 0; +} +default { +return 1; +} +} +} diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_constructor_all_forms/main.solc b/crates/uitest/tests/fixtures/nameres/unqualified_constructor_all_forms/main.solc deleted file mode 100644 index ca9e2a06..00000000 --- a/crates/uitest/tests/fixtures/nameres/unqualified_constructor_all_forms/main.solc +++ /dev/null @@ -1,19 +0,0 @@ -data Option = None | Some(word); -data flag = off | on; - -function exprCall(x: word) -> Option { return Some(x); } -function exprBare(f: flag) -> flag { return on; } - -function patLower(f: flag) -> word { - match f { - | off => return 0; - | on => return 1; - } -} - -function patUpper(o: Option) -> word { - match o { - | None => return 0; - | _ => return 1; - } -} diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_expr/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_expr/diagnostics.snap index d18cfee0..59a48aee 100644 --- a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_expr/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_expr/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/unqualified_ctor_expr/main.solc +input_file: crates/uitest/tests/fixtures/nameres/unqualified_ctor_expr/main.sol --- error[SC0106]: unqualified constructor: on - --> /main/main.solc:11:15 + --> /main/main.sol:15:15 | -10 | function main() -> word { -11 | return pick(on); +14 | function main() returns (word) { +15 | return pick(on); | ^^ constructor must be qualified -12 | } +16 | } | = help: use `flag.on` diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_expr/main.sol b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_expr/main.sol new file mode 100644 index 00000000..e0943254 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_expr/main.sol @@ -0,0 +1,16 @@ +enum flag { off, on } + +function pick(f: flag) returns (word) { + match (f) { +case flag.off { +return 0; +} +case flag.on { +return 1; +} +} +} + +function main() returns (word) { + return pick(on); +} diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_expr/main.solc b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_expr/main.solc deleted file mode 100644 index 01a76d27..00000000 --- a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_expr/main.solc +++ /dev/null @@ -1,12 +0,0 @@ -data flag = off | on; - -function pick(f: flag) -> word { - match f { - | flag.off => return 0; - | flag.on => return 1; - } -} - -function main() -> word { - return pick(on); -} diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/diagnostics.snap index dc58f592..00b70136 100644 --- a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/main.solc +input_file: crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/main.sol --- error[SC0106]: unqualified constructor: Ok - --> /main/main.solc:4:10 + --> /main/main.sol:4:10 | -3 | function mk(x: word) -> Token { +3 | function mk(x: word) returns (Token) { 4 | return Ok(x); | ^^ constructor must be qualified 5 | } @@ -15,11 +15,11 @@ error[SC0106]: unqualified constructor: Ok --- error[SC0106]: unqualified constructor: Ok - --> /main/main.solc:9:5 + --> /main/main.sol:9:6 | - 8 | match t { - 9 | | Ok(v) => return v; - | ^^ constructor must be qualified -10 | | Token.Err(v) => return v; + 8 | match (t) { + 9 | case Ok(v) { + | ^^ constructor must be qualified +10 | return v; | = help: use Type.Constructor form diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/lib.sol b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/lib.sol new file mode 100644 index 00000000..30f19027 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/lib.sol @@ -0,0 +1,3 @@ +export { Token(Ok, Err) }; + +enum Token { Ok(word), Err(word) } diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/lib.solc b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/lib.solc deleted file mode 100644 index f5a73f55..00000000 --- a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/lib.solc +++ /dev/null @@ -1,3 +0,0 @@ -export { Token(Ok, Err) }; - -data Token = Ok(word) | Err(word); diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/main.sol b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/main.sol new file mode 100644 index 00000000..f204532b --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/main.sol @@ -0,0 +1,16 @@ +import {Token} from lib; + +function mk(x: word) returns (Token) { + return Ok(x); +} + +function classify(t: Token) returns (word) { + match (t) { +case Ok(v) { +return v; +} +case Token.Err(v) { +return v; +} +} +} diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/main.solc b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/main.solc deleted file mode 100644 index 961a91bd..00000000 --- a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_imported/main.solc +++ /dev/null @@ -1,12 +0,0 @@ -import lib.{Token}; - -function mk(x: word) -> Token { - return Ok(x); -} - -function classify(t: Token) -> word { - match t { - | Ok(v) => return v; - | Token.Err(v) => return v; - } -} diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern/diagnostics.snap index 72d6f481..1c1a64f4 100644 --- a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern/diagnostics.snap @@ -1,25 +1,25 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern/main.solc +input_file: crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern/main.sol --- error[SC0106]: unqualified constructor: off - --> /main/main.solc:5:5 + --> /main/main.sol:5:6 | -4 | match f { -5 | | off => return 0; - | ^^^ constructor must be qualified -6 | | on => return 1; +4 | match (f) { +5 | case off { + | ^^^ constructor must be qualified +6 | return 0; | = help: use `flag.off` --- error[SC0106]: unqualified constructor: on - --> /main/main.solc:6:5 + --> /main/main.sol:8:6 | -5 | | off => return 0; -6 | | on => return 1; - | ^^ constructor must be qualified -7 | } +7 | } +8 | case on { + | ^^ constructor must be qualified +9 | return 1; | = help: use `flag.on` diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern/main.sol b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern/main.sol new file mode 100644 index 00000000..352d9547 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern/main.sol @@ -0,0 +1,16 @@ +enum flag { off, on } + +function pick(f: flag) returns (word) { + match (f) { +case off { +return 0; +} +case on { +return 1; +} +} +} + +function main() returns (word) { + return pick(flag.on); +} diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern/main.solc b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern/main.solc deleted file mode 100644 index bfef502d..00000000 --- a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern/main.solc +++ /dev/null @@ -1,12 +0,0 @@ -data flag = off | on; - -function pick(f: flag) -> word { - match f { - | off => return 0; - | on => return 1; - } -} - -function main() -> word { - return pick(flag.on); -} diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern_direction/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern_direction/diagnostics.snap index 13ca30bc..d3ea5ed5 100644 --- a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern_direction/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern_direction/diagnostics.snap @@ -1,25 +1,25 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern_direction/main.solc +input_file: crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern_direction/main.sol --- error[SC0106]: unqualified constructor: north - --> /main/main.solc:5:5 + --> /main/main.sol:5:6 | -4 | match d { -5 | | north => return 1; - | ^^^^^ constructor must be qualified -6 | | south => return 2; +4 | match (d) { +5 | case north { + | ^^^^^ constructor must be qualified +6 | return 1; | = help: use `direction.north` --- error[SC0106]: unqualified constructor: south - --> /main/main.solc:6:5 + --> /main/main.sol:8:6 | -5 | | north => return 1; -6 | | south => return 2; - | ^^^^^ constructor must be qualified -7 | } +7 | } +8 | case south { + | ^^^^^ constructor must be qualified +9 | return 2; | = help: use `direction.south` diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern_direction/main.sol b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern_direction/main.sol new file mode 100644 index 00000000..f4e83e89 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern_direction/main.sol @@ -0,0 +1,16 @@ +enum direction { north, south } + +function pick(d: direction) returns (word) { + match (d) { +case north { +return 1; +} +case south { +return 2; +} +} +} + +function main() returns (word) { + return pick(direction.south); +} diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern_direction/main.solc b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern_direction/main.solc deleted file mode 100644 index 14613022..00000000 --- a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_pattern_direction/main.solc +++ /dev/null @@ -1,12 +0,0 @@ -data direction = north | south; - -function pick(d: direction) -> word { - match d { - | north => return 1; - | south => return 2; - } -} - -function main() -> word { - return pick(direction.south); -} diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/diagnostics.snap index 3c8755a5..6c166319 100644 --- a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/diagnostics.snap @@ -1,25 +1,25 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/main.solc +input_file: crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/main.sol --- error[SC0106]: unqualified constructor: wrapper - --> /main/main.solc:5:5 + --> /main/main.sol:5:6 | -4 | match u { -5 | | wrapper(w) => return w; - | ^^^^^^^ constructor must be qualified -6 | } +4 | match (u) { +5 | case wrapper(w) { + | ^^^^^^^ constructor must be qualified +6 | return w; | = help: use Type.Constructor form --- error[SC0106]: unqualified constructor: wrapper - --> /main/main.solc:10:17 + --> /main/main.sol:12:17 | - 9 | function main() -> word { -10 | return unwrap(wrapper(3)); +11 | function main() returns (word) { +12 | return unwrap(wrapper(3)); | ^^^^^^^ constructor must be qualified -11 | } +13 | } | = help: use Type.Constructor form diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/lib.sol b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/lib.sol new file mode 100644 index 00000000..b547863a --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/lib.sol @@ -0,0 +1,3 @@ +export { wrapper(wrapper) }; + +enum wrapper { wrapper(word) } diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/lib.solc b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/lib.solc deleted file mode 100644 index fdfd1361..00000000 --- a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/lib.solc +++ /dev/null @@ -1,3 +0,0 @@ -export { wrapper(wrapper) }; - -data wrapper = wrapper(word); diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/main.sol b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/main.sol new file mode 100644 index 00000000..5224176b --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/main.sol @@ -0,0 +1,13 @@ +import lib; + +function unwrap(u: lib.wrapper) returns (word) { + match (u) { +case wrapper(w) { +return w; +} +} +} + +function main() returns (word) { + return unwrap(wrapper(3)); +} diff --git a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/main.solc b/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/main.solc deleted file mode 100644 index 483b0690..00000000 --- a/crates/uitest/tests/fixtures/nameres/unqualified_ctor_plain_import/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -import lib; - -function unwrap(u: lib.wrapper) -> word { - match u { - | wrapper(w) => return w; - } -} - -function main() -> word { - return unwrap(wrapper(3)); -} diff --git a/crates/uitest/tests/fixtures/nameres/unresolved_qualified/diagnostics.snap b/crates/uitest/tests/fixtures/nameres/unresolved_qualified/diagnostics.snap index 06d5aecc..b63de61b 100644 --- a/crates/uitest/tests/fixtures/nameres/unresolved_qualified/diagnostics.snap +++ b/crates/uitest/tests/fixtures/nameres/unresolved_qualified/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/nameres/unresolved_qualified/main.solc +input_file: crates/uitest/tests/fixtures/nameres/unresolved_qualified/main.sol --- error[SC0101]: undefined name: missing - --> /main/main.solc:4:15 + --> /main/main.sol:4:15 | -3 | function main() -> word { +3 | function main() returns (word) { 4 | return util.missing(); | ^^^^^^^ unknown name 5 | } diff --git a/crates/uitest/tests/fixtures/nameres/unresolved_qualified/main.sol b/crates/uitest/tests/fixtures/nameres/unresolved_qualified/main.sol new file mode 100644 index 00000000..11ba6564 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/unresolved_qualified/main.sol @@ -0,0 +1,5 @@ +import util; + +function main() returns (word) { + return util.missing(); +} diff --git a/crates/uitest/tests/fixtures/nameres/unresolved_qualified/main.solc b/crates/uitest/tests/fixtures/nameres/unresolved_qualified/main.solc deleted file mode 100644 index 8090a9fa..00000000 --- a/crates/uitest/tests/fixtures/nameres/unresolved_qualified/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import util; - -function main() -> word { - return util.missing(); -} diff --git a/crates/uitest/tests/fixtures/nameres/unresolved_qualified/util.sol b/crates/uitest/tests/fixtures/nameres/unresolved_qualified/util.sol new file mode 100644 index 00000000..41eb3ad7 --- /dev/null +++ b/crates/uitest/tests/fixtures/nameres/unresolved_qualified/util.sol @@ -0,0 +1,5 @@ +export { value }; + +function value() returns (word) { + return 1; +} diff --git a/crates/uitest/tests/fixtures/nameres/unresolved_qualified/util.solc b/crates/uitest/tests/fixtures/nameres/unresolved_qualified/util.solc deleted file mode 100644 index 816f96ee..00000000 --- a/crates/uitest/tests/fixtures/nameres/unresolved_qualified/util.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { value }; - -function value() -> word { - return 1; -} diff --git a/crates/uitest/tests/fixtures/parse/assembly_trailing_semicolon/diagnostics.snap b/crates/uitest/tests/fixtures/parse/assembly_trailing_semicolon/diagnostics.snap index 7598c808..f787881a 100644 --- a/crates/uitest/tests/fixtures/parse/assembly_trailing_semicolon/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/assembly_trailing_semicolon/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/assembly_trailing_semicolon/main.solc +input_file: crates/uitest/tests/fixtures/parse/assembly_trailing_semicolon/main.sol --- error[SC0001]: parse error: unexpected `;` - --> /main/main.solc:4:4 + --> /main/main.sol:4:4 | 3 | mstore(0, 0) 4 | }; diff --git a/crates/uitest/tests/fixtures/parse/assembly_trailing_semicolon/main.solc b/crates/uitest/tests/fixtures/parse/assembly_trailing_semicolon/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/assembly_trailing_semicolon/main.solc rename to crates/uitest/tests/fixtures/parse/assembly_trailing_semicolon/main.sol diff --git a/crates/uitest/tests/fixtures/parse/assignment_missing_semicolon/diagnostics.snap b/crates/uitest/tests/fixtures/parse/assignment_missing_semicolon/diagnostics.snap index 68f1df72..f68456e0 100644 --- a/crates/uitest/tests/fixtures/parse/assignment_missing_semicolon/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/assignment_missing_semicolon/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/assignment_missing_semicolon/main.solc +input_file: crates/uitest/tests/fixtures/parse/assignment_missing_semicolon/main.sol --- error[SC0001]: assignment statement requires trailing `;` - --> /main/main.solc:2:3 + --> /main/main.sol:2:3 | 1 | function bad() { 2 | x = 1 diff --git a/crates/uitest/tests/fixtures/parse/assignment_missing_semicolon/main.solc b/crates/uitest/tests/fixtures/parse/assignment_missing_semicolon/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/assignment_missing_semicolon/main.solc rename to crates/uitest/tests/fixtures/parse/assignment_missing_semicolon/main.sol diff --git a/crates/uitest/tests/fixtures/parse/body_independent_errors/diagnostics.snap b/crates/uitest/tests/fixtures/parse/body_independent_errors/diagnostics.snap index 8b952327..669981a0 100644 --- a/crates/uitest/tests/fixtures/parse/body_independent_errors/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/body_independent_errors/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/body_independent_errors/main.solc +input_file: crates/uitest/tests/fixtures/parse/body_independent_errors/main.sol --- error[SC0001]: invalid token `§` - --> /main/main.solc:2:1 + --> /main/main.sol:2:1 | -1 | function main() -> word { +1 | function main() returns (word) { 2 | § | ^ invalid token 3 | let broken = ; @@ -14,11 +14,11 @@ error[SC0001]: invalid token `§` --- error[SC0001]: parse error: unexpected `;` - --> /main/main.solc:3:14 + --> /main/main.sol:3:14 | 2 | § 3 | let broken = ; | ^ unexpected token 4 | return 0; | - = note: expecting expression after `=` + = note: expecting `&&`, `&`, `(`, `.`, `?`, `[`, `^`, `|`, or `||` diff --git a/crates/uitest/tests/fixtures/parse/body_independent_errors/main.sol b/crates/uitest/tests/fixtures/parse/body_independent_errors/main.sol new file mode 100644 index 00000000..38461834 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/body_independent_errors/main.sol @@ -0,0 +1,5 @@ +function main() returns (word) { +§ +let broken = ; +return 0; +} diff --git a/crates/uitest/tests/fixtures/parse/body_independent_errors/main.solc b/crates/uitest/tests/fixtures/parse/body_independent_errors/main.solc deleted file mode 100644 index 98c12f9c..00000000 --- a/crates/uitest/tests/fixtures/parse/body_independent_errors/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -function main() -> word { -§ -let broken = ; -return 0; -} diff --git a/crates/uitest/tests/fixtures/parse/body_invalid_token/diagnostics.snap b/crates/uitest/tests/fixtures/parse/body_invalid_token/diagnostics.snap index c884a545..34df822e 100644 --- a/crates/uitest/tests/fixtures/parse/body_invalid_token/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/body_invalid_token/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/body_invalid_token/main.solc +input_file: crates/uitest/tests/fixtures/parse/body_invalid_token/main.sol --- error[SC0001]: invalid token `§` - --> /main/main.solc:1:34 + --> /main/main.sol:1:41 | -1 | function main() -> word { return §; } - | ^ invalid token +1 | function main() returns (word) { return §; } + | ^ invalid token diff --git a/crates/uitest/tests/fixtures/parse/body_invalid_token/main.sol b/crates/uitest/tests/fixtures/parse/body_invalid_token/main.sol new file mode 100644 index 00000000..a18154fc --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/body_invalid_token/main.sol @@ -0,0 +1 @@ +function main() returns (word) { return §; } diff --git a/crates/uitest/tests/fixtures/parse/body_invalid_token/main.solc b/crates/uitest/tests/fixtures/parse/body_invalid_token/main.solc deleted file mode 100644 index efed509d..00000000 --- a/crates/uitest/tests/fixtures/parse/body_invalid_token/main.solc +++ /dev/null @@ -1 +0,0 @@ -function main() -> word { return §; } diff --git a/crates/uitest/tests/fixtures/parse/bom_only_file/diagnostics.snap b/crates/uitest/tests/fixtures/parse/bom_only_file/diagnostics.snap index d715561c..d1dd1eb9 100644 --- a/crates/uitest/tests/fixtures/parse/bom_only_file/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/bom_only_file/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/bom_only_file/main.solc +input_file: crates/uitest/tests/fixtures/parse/bom_only_file/main.sol --- error[SC0001]: invalid token `` - --> /main/main.solc:1:1 + --> /main/main.sol:1:1 | 1 |  | ^ invalid token diff --git a/crates/uitest/tests/fixtures/parse/bom_only_file/main.solc b/crates/uitest/tests/fixtures/parse/bom_only_file/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/bom_only_file/main.solc rename to crates/uitest/tests/fixtures/parse/bom_only_file/main.sol diff --git a/crates/uitest/tests/fixtures/parse/class_missing_body_brace/diagnostics.snap b/crates/uitest/tests/fixtures/parse/class_missing_body_brace/diagnostics.snap deleted file mode 100644 index 7030b07b..00000000 --- a/crates/uitest/tests/fixtures/parse/class_missing_body_brace/diagnostics.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: crates/test-utils/src/lib.rs -expression: rendered -input_file: crates/uitest/tests/fixtures/parse/class_missing_body_brace/main.solc ---- -error[SC0001]: parse error: unexpected end of input - --> /main/main.solc:1:13 - | -1 | class T: Eq - | ^ unexpected token - | - = note: expecting `(`, or `{` - = note: while parsing predicate diff --git a/crates/uitest/tests/fixtures/parse/class_missing_body_brace/main.solc b/crates/uitest/tests/fixtures/parse/class_missing_body_brace/main.solc deleted file mode 100644 index 8e27f54d..00000000 --- a/crates/uitest/tests/fixtures/parse/class_missing_body_brace/main.solc +++ /dev/null @@ -1 +0,0 @@ -class T: Eq diff --git a/crates/uitest/tests/fixtures/parse/data_trailing_pipe/diagnostics.snap b/crates/uitest/tests/fixtures/parse/data_trailing_pipe/diagnostics.snap index 783edf38..c990a88f 100644 --- a/crates/uitest/tests/fixtures/parse/data_trailing_pipe/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/data_trailing_pipe/diagnostics.snap @@ -1,12 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/data_trailing_pipe/main.solc +input_file: crates/uitest/tests/fixtures/parse/data_trailing_pipe/main.sol --- -error[SC0001]: parse error: unexpected `;` - --> /main/main.solc:1:28 +error[SC0001]: parse error: unexpected `|` + --> /main/main.sol:1:26 | -1 | data Option(T) = Some(T) | ; - | ^ unexpected token +1 | enum Option { Some(T) | } + | ^ unexpected token | - = note: while parsing data declaration + = note: expecting `,`, or `}` + = note: while parsing enum declaration diff --git a/crates/uitest/tests/fixtures/parse/data_trailing_pipe/main.sol b/crates/uitest/tests/fixtures/parse/data_trailing_pipe/main.sol new file mode 100644 index 00000000..36f75971 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/data_trailing_pipe/main.sol @@ -0,0 +1 @@ +enum Option { Some(T) | } diff --git a/crates/uitest/tests/fixtures/parse/data_trailing_pipe/main.solc b/crates/uitest/tests/fixtures/parse/data_trailing_pipe/main.solc deleted file mode 100644 index 6ff3e4c4..00000000 --- a/crates/uitest/tests/fixtures/parse/data_trailing_pipe/main.solc +++ /dev/null @@ -1 +0,0 @@ -data Option(T) = Some(T) | ; diff --git a/crates/uitest/tests/fixtures/parse/delimiter_nesting_limit/diagnostics.snap b/crates/uitest/tests/fixtures/parse/delimiter_nesting_limit/diagnostics.snap index f36cf5f5..1aeacbef 100644 --- a/crates/uitest/tests/fixtures/parse/delimiter_nesting_limit/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/delimiter_nesting_limit/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/delimiter_nesting_limit/main.solc +input_file: crates/uitest/tests/fixtures/parse/delimiter_nesting_limit/main.sol --- error[SC0001]: delimiter nesting exceeds the compiler limit of 128 - --> /main/main.solc:1:164 + --> /main/main.sol:1:172 | 1 | ...((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((... | ^ diff --git a/crates/uitest/tests/fixtures/parse/delimiter_nesting_limit/main.sol b/crates/uitest/tests/fixtures/parse/delimiter_nesting_limit/main.sol new file mode 100644 index 00000000..5ff7c981 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/delimiter_nesting_limit/main.sol @@ -0,0 +1 @@ +function f(x: word) returns (word) { return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((x)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))); } diff --git a/crates/uitest/tests/fixtures/parse/delimiter_nesting_limit/main.solc b/crates/uitest/tests/fixtures/parse/delimiter_nesting_limit/main.solc deleted file mode 100644 index 72be801d..00000000 --- a/crates/uitest/tests/fixtures/parse/delimiter_nesting_limit/main.solc +++ /dev/null @@ -1 +0,0 @@ -function f(x:word) -> word { return ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((x)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))); } diff --git a/crates/uitest/tests/fixtures/parse/ergo_assembly_unclosed_call/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_assembly_unclosed_call/diagnostics.snap index a2f7cb67..0d86945a 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_assembly_unclosed_call/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_assembly_unclosed_call/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_assembly_unclosed_call/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_assembly_unclosed_call/main.sol --- error[SC0001]: parse error: unexpected `(` - --> /main/main.solc:4:17 + --> /main/main.sol:4:17 | 3 | assembly { 4 | r := add(1, @@ -14,7 +14,7 @@ error[SC0001]: parse error: unexpected `(` --- error[SC0001]: parse error: unexpected `,` - --> /main/main.solc:4:19 + --> /main/main.sol:4:19 | 3 | assembly { 4 | r := add(1, diff --git a/crates/uitest/tests/fixtures/parse/ergo_assembly_unclosed_call/main.sol b/crates/uitest/tests/fixtures/parse/ergo_assembly_unclosed_call/main.sol new file mode 100644 index 00000000..2f8465ac --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_assembly_unclosed_call/main.sol @@ -0,0 +1,7 @@ +function f() returns (word) { + let r : word; + assembly { + r := add(1, + } + return r; +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_assembly_unclosed_call/main.solc b/crates/uitest/tests/fixtures/parse/ergo_assembly_unclosed_call/main.solc deleted file mode 100644 index 2a9b5ab2..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_assembly_unclosed_call/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -function f() -> word { - let r : word; - assembly { - r := add(1, - } - return r; -} diff --git a/crates/uitest/tests/fixtures/parse/ergo_contract_missing_name/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_contract_missing_name/diagnostics.snap index 0883f5c7..8632b0a2 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_contract_missing_name/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_contract_missing_name/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_contract_missing_name/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_contract_missing_name/main.sol --- error[SC0001]: parse error: unexpected `{` - --> /main/main.solc:1:10 + --> /main/main.sol:1:10 | 1 | contract { | ^ unexpected token -2 | function f() -> word { +2 | function f() returns (word) { 3 | return 1; | = note: expecting identifier diff --git a/crates/uitest/tests/fixtures/parse/ergo_contract_missing_name/main.sol b/crates/uitest/tests/fixtures/parse/ergo_contract_missing_name/main.sol new file mode 100644 index 00000000..806d8334 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_contract_missing_name/main.sol @@ -0,0 +1,5 @@ +contract { + function f() returns (word) { + return 1; + } +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_contract_missing_name/main.solc b/crates/uitest/tests/fixtures/parse/ergo_contract_missing_name/main.solc deleted file mode 100644 index 516bdf25..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_contract_missing_name/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -contract { - function f() -> word { - return 1; - } -} diff --git a/crates/uitest/tests/fixtures/parse/ergo_function_missing_params/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_function_missing_params/diagnostics.snap index c618f371..4923bd80 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_function_missing_params/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_function_missing_params/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_function_missing_params/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_function_missing_params/main.sol --- error[SC0001]: parse error: unexpected `->` - --> /main/main.solc:1:12 + --> /main/main.sol:1:12 | 1 | function f -> word { | ^^ unexpected token 2 | return 1; 3 | } | - = note: expecting `(` + = note: expecting `(`, or `<` = note: while parsing function signature diff --git a/crates/uitest/tests/fixtures/parse/ergo_function_missing_params/main.solc b/crates/uitest/tests/fixtures/parse/ergo_function_missing_params/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/ergo_function_missing_params/main.solc rename to crates/uitest/tests/fixtures/parse/ergo_function_missing_params/main.sol diff --git a/crates/uitest/tests/fixtures/parse/ergo_hull_empty_match/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_hull_empty_match/diagnostics.snap index d98676ba..20d51988 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_hull_empty_match/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_hull_empty_match/diagnostics.snap @@ -1,16 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_hull_empty_match/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_hull_empty_match/main.sol --- -error[SC0001]: match statement requires at least one arm - --> /main/main.solc:4:11 +error[SC0001]: match requires at least one `case` or `default` arm + --> /main/main.sol:4:3 | -3 | function impossible(b : B) -> word { -4 | match b { - | ___________^ +3 | function impossible(b: B) returns (word) { +4 | / match (b) { 5 | | } - | |___^ empty match arm list + | |___^ 6 | } | - = note: add a `| pattern =>` arm diff --git a/crates/uitest/tests/fixtures/parse/ergo_hull_empty_match/main.sol b/crates/uitest/tests/fixtures/parse/ergo_hull_empty_match/main.sol new file mode 100644 index 00000000..b026d22d --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_hull_empty_match/main.sol @@ -0,0 +1,12 @@ +enum B { A, C } + +function impossible(b: B) returns (word) { + match (b) { + } +} + +contract T { + function main(x: word) public returns (word) { + return impossible(B.A); + } +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_hull_empty_match/main.solc b/crates/uitest/tests/fixtures/parse/ergo_hull_empty_match/main.solc deleted file mode 100644 index 139fc1dc..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_hull_empty_match/main.solc +++ /dev/null @@ -1,12 +0,0 @@ -data B = A | C; - -function impossible(b : B) -> word { - match b { - } -} - -contract T { - public function main(x : word) -> word { - return impossible(B.A); - } -} diff --git a/crates/uitest/tests/fixtures/parse/ergo_hull_fallback_args/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_hull_fallback_args/diagnostics.snap index e4b23b12..9039605f 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_hull_fallback_args/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_hull_fallback_args/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_hull_fallback_args/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_hull_fallback_args/main.sol --- error[SC0001]: fallback function must not declare input parameters - --> /main/main.solc:9:13 + --> /main/main.sol:9:13 | 8 | - 9 | fallback(x: uint256) -> () { + 9 | fallback(x: uint256) { | ^^^^^^^^^^^^ 10 | revert("fallback-was-called"); | diff --git a/crates/uitest/tests/fixtures/parse/ergo_hull_fallback_args/main.sol b/crates/uitest/tests/fixtures/parse/ergo_hull_fallback_args/main.sol new file mode 100644 index 00000000..8f6aeaa8 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_hull_fallback_args/main.sol @@ -0,0 +1,12 @@ +// Mirrors reference corpus test/examples/cases/fallback-with-args.sol +// (expected failure there): fallback must take no arguments. +import * from std; +import * from std.dispatch; + +contract BadFallback { + constructor() {} + + fallback(x: uint256) { + revert("fallback-was-called"); + } +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_hull_fallback_args/main.solc b/crates/uitest/tests/fixtures/parse/ergo_hull_fallback_args/main.solc deleted file mode 100644 index 8928a4a7..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_hull_fallback_args/main.solc +++ /dev/null @@ -1,12 +0,0 @@ -// Mirrors reference corpus test/examples/cases/fallback-with-args.solc -// (expected failure there): fallback must take no arguments. -import std.{*}; -import std.dispatch.{*}; - -contract BadFallback { - constructor() {} - - fallback(x: uint256) -> () { - revert("fallback-was-called"); - } -} diff --git a/crates/uitest/tests/fixtures/parse/ergo_import_trailing_dot/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_import_trailing_dot/diagnostics.snap index 35577677..88d09442 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_import_trailing_dot/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_import_trailing_dot/diagnostics.snap @@ -1,15 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_import_trailing_dot/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_import_trailing_dot/main.sol --- error[SC0001]: parse error: unexpected `;` - --> /main/main.solc:1:12 + --> /main/main.sol:1:12 | 1 | import a.b.; | ^ unexpected token 2 | -3 | function f() -> word { +3 | function f() returns (word) { | - = note: expecting import selector after `.` = note: while parsing import declaration diff --git a/crates/uitest/tests/fixtures/parse/ergo_import_trailing_dot/main.sol b/crates/uitest/tests/fixtures/parse/ergo_import_trailing_dot/main.sol new file mode 100644 index 00000000..0a18d6ba --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_import_trailing_dot/main.sol @@ -0,0 +1,5 @@ +import a.b.; + +function f() returns (word) { + return 1; +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_import_trailing_dot/main.solc b/crates/uitest/tests/fixtures/parse/ergo_import_trailing_dot/main.solc deleted file mode 100644 index 81a5f777..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_import_trailing_dot/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import a.b.; - -function f() -> word { - return 1; -} diff --git a/crates/uitest/tests/fixtures/parse/ergo_invalid_token_unicode/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_invalid_token_unicode/diagnostics.snap index f5c2b1a3..079563ec 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_invalid_token_unicode/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_invalid_token_unicode/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_invalid_token_unicode/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_invalid_token_unicode/main.sol --- error[SC0001]: invalid token `§` - --> /main/main.solc:2:15 + --> /main/main.sol:2:15 | -1 | function f() -> word { +1 | function f() returns (word) { 2 | let x = 1 § 2; | ^ invalid token 3 | return x; diff --git a/crates/uitest/tests/fixtures/parse/ergo_invalid_token_unicode/main.sol b/crates/uitest/tests/fixtures/parse/ergo_invalid_token_unicode/main.sol new file mode 100644 index 00000000..c7a88ee1 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_invalid_token_unicode/main.sol @@ -0,0 +1,4 @@ +function f() returns (word) { + let x = 1 § 2; + return x; +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_invalid_token_unicode/main.solc b/crates/uitest/tests/fixtures/parse/ergo_invalid_token_unicode/main.solc deleted file mode 100644 index 6072f816..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_invalid_token_unicode/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -function f() -> word { - let x = 1 § 2; - return x; -} diff --git a/crates/uitest/tests/fixtures/parse/ergo_keyword_as_ident/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_keyword_as_ident/diagnostics.snap index f5a63638..383a64ad 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_keyword_as_ident/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_keyword_as_ident/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_keyword_as_ident/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_keyword_as_ident/main.sol --- error[SC0001]: parse error: unexpected `match` - --> /main/main.solc:1:10 + --> /main/main.sol:1:10 | -1 | function match(x : word) -> word { +1 | function match(x: word) returns (word) { | ^^^^^ unexpected token 2 | return x; 3 | } diff --git a/crates/uitest/tests/fixtures/parse/ergo_keyword_as_ident/main.sol b/crates/uitest/tests/fixtures/parse/ergo_keyword_as_ident/main.sol new file mode 100644 index 00000000..5cb9ee82 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_keyword_as_ident/main.sol @@ -0,0 +1,3 @@ +function match(x: word) returns (word) { + return x; +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_keyword_as_ident/main.solc b/crates/uitest/tests/fixtures/parse/ergo_keyword_as_ident/main.solc deleted file mode 100644 index de4dce67..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_keyword_as_ident/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -function match(x : word) -> word { - return x; -} diff --git a/crates/uitest/tests/fixtures/parse/ergo_lambda_missing_parens/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_lambda_missing_parens/diagnostics.snap index 812a438a..74b49c51 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_lambda_missing_parens/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_lambda_missing_parens/diagnostics.snap @@ -1,23 +1,23 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_lambda_missing_parens/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_lambda_missing_parens/main.sol --- -error[SC0001]: parse error: unexpected identifier `x` - --> /main/main.solc:2:17 +error[SC0001]: parse error: unexpected `;` + --> /main/main.sol:2:29 | -1 | function f() -> word { +1 | function f() returns (word) { 2 | let g = lam x { return x; }; - | ^ unexpected token + | ^ unexpected token 3 | return g(1); | - = note: expecting `(` + = note: expecting `&&`, `&`, `(`, `.`, `?`, `[`, `^`, `|`, or `||` --- error[SC0001]: parse error: unexpected `}` - --> /main/main.solc:2:31 + --> /main/main.sol:2:31 | -1 | function f() -> word { +1 | function f() returns (word) { 2 | let g = lam x { return x; }; | ^ unexpected token 3 | return g(1); diff --git a/crates/uitest/tests/fixtures/parse/ergo_lambda_missing_parens/main.sol b/crates/uitest/tests/fixtures/parse/ergo_lambda_missing_parens/main.sol new file mode 100644 index 00000000..367eb2a5 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_lambda_missing_parens/main.sol @@ -0,0 +1,4 @@ +function f() returns (word) { + let g = lam x { return x; }; + return g(1); +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_lambda_missing_parens/main.solc b/crates/uitest/tests/fixtures/parse/ergo_lambda_missing_parens/main.solc deleted file mode 100644 index cb9032ad..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_lambda_missing_parens/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -function f() -> word { - let g = lam x { return x; }; - return g(1); -} diff --git a/crates/uitest/tests/fixtures/parse/ergo_missing_semicolon_stmts/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_missing_semicolon_stmts/diagnostics.snap index 5e1dcbe7..2a7a245b 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_missing_semicolon_stmts/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_missing_semicolon_stmts/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_missing_semicolon_stmts/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_missing_semicolon_stmts/main.sol --- -error[SC0001]: parse error: unexpected `return` - --> /main/main.solc:3:5 +error[SC0001]: parse error: unexpected `;` + --> /main/main.sol:3:13 | 2 | let x = 1 3 | return x; - | ^^^^^^ unexpected token + | ^ unexpected token 4 | } | - = note: expecting `;` after let statement + = note: expecting `&&`, `&`, `(`, `.`, `?`, `[`, `^`, `|`, or `||` diff --git a/crates/uitest/tests/fixtures/parse/ergo_missing_semicolon_stmts/main.sol b/crates/uitest/tests/fixtures/parse/ergo_missing_semicolon_stmts/main.sol new file mode 100644 index 00000000..e8c0ca0e --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_missing_semicolon_stmts/main.sol @@ -0,0 +1,4 @@ +function f() returns (word) { + let x = 1 + return x; +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_missing_semicolon_stmts/main.solc b/crates/uitest/tests/fixtures/parse/ergo_missing_semicolon_stmts/main.solc deleted file mode 100644 index 1d87720c..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_missing_semicolon_stmts/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -function f() -> word { - let x = 1 - return x; -} diff --git a/crates/uitest/tests/fixtures/parse/ergo_pragma_missing_semi/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_pragma_missing_semi/diagnostics.snap index 5902cf8b..ac5f4d1f 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_pragma_missing_semi/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_pragma_missing_semi/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_pragma_missing_semi/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_pragma_missing_semi/main.sol --- error[SC0001]: parse error: unexpected `function` - --> /main/main.solc:3:1 + --> /main/main.sol:3:1 | 2 | -3 | function f() -> word { +3 | function f() returns (word) { | ^^^^^^^^ unexpected token 4 | return 1; | diff --git a/crates/uitest/tests/fixtures/parse/ergo_pragma_missing_semi/main.sol b/crates/uitest/tests/fixtures/parse/ergo_pragma_missing_semi/main.sol new file mode 100644 index 00000000..38b6a517 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_pragma_missing_semi/main.sol @@ -0,0 +1,5 @@ +pragma no-coverage-condition + +function f() returns (word) { + return 1; +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_pragma_missing_semi/main.solc b/crates/uitest/tests/fixtures/parse/ergo_pragma_missing_semi/main.solc deleted file mode 100644 index 037f935e..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_pragma_missing_semi/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -pragma no-coverage-condition - -function f() -> word { - return 1; -} diff --git a/crates/uitest/tests/fixtures/parse/ergo_stray_top_level_semi/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_stray_top_level_semi/diagnostics.snap index 04254226..7c1b1e8a 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_stray_top_level_semi/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_stray_top_level_semi/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_stray_top_level_semi/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_stray_top_level_semi/main.sol --- -error[SC0001]: could not parse top-level item near `;`; expected a declaration starting with `import`, `pragma`, `type`, `data`, `class`, `instance`, `contract`, or `function` - --> /main/main.solc:3:2 +error[SC0001]: could not parse top-level item near `;`; expected a declaration starting with `import`, `pragma`, `type`, `enum`, `trait`, `impl`, `contract`, or `function` + --> /main/main.sol:3:2 | 2 | return 1; 3 | }; diff --git a/crates/uitest/tests/fixtures/parse/ergo_stray_top_level_semi/main.sol b/crates/uitest/tests/fixtures/parse/ergo_stray_top_level_semi/main.sol new file mode 100644 index 00000000..00006570 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_stray_top_level_semi/main.sol @@ -0,0 +1,7 @@ +function f() returns (word) { + return 1; +}; + +function g() returns (word) { + return 2; +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_stray_top_level_semi/main.solc b/crates/uitest/tests/fixtures/parse/ergo_stray_top_level_semi/main.solc deleted file mode 100644 index 42538fb7..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_stray_top_level_semi/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -function f() -> word { - return 1; -}; - -function g() -> word { - return 2; -} diff --git a/crates/uitest/tests/fixtures/parse/ergo_two_errors_recovery/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_two_errors_recovery/diagnostics.snap index 05fc270b..2e7d3c1d 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_two_errors_recovery/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_two_errors_recovery/diagnostics.snap @@ -1,25 +1,25 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_two_errors_recovery/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_two_errors_recovery/main.sol --- error[SC0001]: parse error: unexpected `;` - --> /main/main.solc:2:13 + --> /main/main.sol:2:13 | -1 | function f() -> word { +1 | function f() returns (word) { 2 | let x = ; | ^ unexpected token 3 | return 0; | - = note: expecting expression after `=` + = note: expecting `&&`, `&`, `(`, `.`, `?`, `[`, `^`, `|`, or `||` --- error[SC0001]: parse error: unexpected `;` - --> /main/main.solc:12:14 + --> /main/main.sol:12:14 | -11 | function h() -> word { +11 | function h() returns (word) { 12 | return (1; | ^ unexpected token 13 | } | - = note: expecting `&&`, `&`, `(`, `)`, `,`, `.`, `:`, `?`, `[`, `^`, `|`, or `||` + = note: expecting `&&`, `&`, `(`, `)`, `,`, `.`, `?`, `[`, `^`, `|`, or `||` diff --git a/crates/uitest/tests/fixtures/parse/ergo_two_errors_recovery/main.sol b/crates/uitest/tests/fixtures/parse/ergo_two_errors_recovery/main.sol new file mode 100644 index 00000000..51b4909b --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_two_errors_recovery/main.sol @@ -0,0 +1,13 @@ +function f() returns (word) { + let x = ; + return 0; +} + +function g(y: word) returns (word) { + if ( y ) { return 1; } + return 0; +} + +function h() returns (word) { + return (1; +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_two_errors_recovery/main.solc b/crates/uitest/tests/fixtures/parse/ergo_two_errors_recovery/main.solc deleted file mode 100644 index 43ce3b01..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_two_errors_recovery/main.solc +++ /dev/null @@ -1,13 +0,0 @@ -function f() -> word { - let x = ; - return 0; -} - -function g(y : word) -> word { - if y { return 1; } - return 0; -} - -function h() -> word { - return (1; -} diff --git a/crates/uitest/tests/fixtures/parse/ergo_unclosed_brace_eof/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_unclosed_brace_eof/diagnostics.snap index 9fdd331b..e1056927 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_unclosed_brace_eof/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_unclosed_brace_eof/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_unclosed_brace_eof/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_unclosed_brace_eof/main.sol --- error[SC0001]: parse error: unexpected end of input - --> /main/main.solc:4:7 + --> /main/main.sol:4:7 | -2 | function f() -> word { +2 | function f() returns (word) { 3 | return 1; 4 | } | ^ unexpected token diff --git a/crates/uitest/tests/fixtures/parse/ergo_unclosed_brace_eof/main.sol b/crates/uitest/tests/fixtures/parse/ergo_unclosed_brace_eof/main.sol new file mode 100644 index 00000000..ff5c44b8 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_unclosed_brace_eof/main.sol @@ -0,0 +1,4 @@ +contract C { + function f() returns (word) { + return 1; + } diff --git a/crates/uitest/tests/fixtures/parse/ergo_unclosed_brace_eof/main.solc b/crates/uitest/tests/fixtures/parse/ergo_unclosed_brace_eof/main.solc deleted file mode 100644 index 878ca7a0..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_unclosed_brace_eof/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -contract C { - function f() -> word { - return 1; - } diff --git a/crates/uitest/tests/fixtures/parse/ergo_unterminated_block_comment/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_unterminated_block_comment/diagnostics.snap index 01321e01..fb0b1b97 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_unterminated_block_comment/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_unterminated_block_comment/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_unterminated_block_comment/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_unterminated_block_comment/main.sol --- error[SC0001]: unterminated block comment - --> /main/main.solc:4:1 + --> /main/main.sol:4:1 | 3 | } 4 | / /* this comment never ends -5 | | function g() -> word { +5 | | function g() returns (word) { 6 | | return 2; 7 | | } | |__^ comment starts here diff --git a/crates/uitest/tests/fixtures/parse/ergo_unterminated_block_comment/main.sol b/crates/uitest/tests/fixtures/parse/ergo_unterminated_block_comment/main.sol new file mode 100644 index 00000000..a0b611a1 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_unterminated_block_comment/main.sol @@ -0,0 +1,7 @@ +function f() returns (word) { + return 1; +} +/* this comment never ends +function g() returns (word) { + return 2; +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_unterminated_block_comment/main.solc b/crates/uitest/tests/fixtures/parse/ergo_unterminated_block_comment/main.solc deleted file mode 100644 index 194cb14c..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_unterminated_block_comment/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -function f() -> word { - return 1; -} -/* this comment never ends -function g() -> word { - return 2; -} diff --git a/crates/uitest/tests/fixtures/parse/ergo_unterminated_string/diagnostics.snap b/crates/uitest/tests/fixtures/parse/ergo_unterminated_string/diagnostics.snap index a7ab6aa1..9af9a886 100644 --- a/crates/uitest/tests/fixtures/parse/ergo_unterminated_string/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/ergo_unterminated_string/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/ergo_unterminated_string/main.solc +input_file: crates/uitest/tests/fixtures/parse/ergo_unterminated_string/main.sol --- error[SC0001]: unterminated string literal - --> /main/main.solc:2:13 + --> /main/main.sol:2:13 | -1 | function f() -> word { +1 | function f() returns (word) { 2 | let s = "hello; | _____________^ 3 | | return 1; diff --git a/crates/uitest/tests/fixtures/parse/ergo_unterminated_string/main.sol b/crates/uitest/tests/fixtures/parse/ergo_unterminated_string/main.sol new file mode 100644 index 00000000..bb3e97a9 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/ergo_unterminated_string/main.sol @@ -0,0 +1,4 @@ +function f() returns (word) { + let s = "hello; + return 1; +} diff --git a/crates/uitest/tests/fixtures/parse/ergo_unterminated_string/main.solc b/crates/uitest/tests/fixtures/parse/ergo_unterminated_string/main.solc deleted file mode 100644 index 6bfccb64..00000000 --- a/crates/uitest/tests/fixtures/parse/ergo_unterminated_string/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -function f() -> word { - let s = "hello; - return 1; -} diff --git a/crates/uitest/tests/fixtures/parse/excessive_conditional_nesting/diagnostics.snap b/crates/uitest/tests/fixtures/parse/excessive_conditional_nesting/diagnostics.snap index a44b7093..31f1a87f 100644 --- a/crates/uitest/tests/fixtures/parse/excessive_conditional_nesting/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/excessive_conditional_nesting/diagnostics.snap @@ -1,13 +1,131 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/excessive_conditional_nesting/main.solc +input_file: crates/uitest/tests/fixtures/parse/excessive_conditional_nesting/main.sol --- -error[SC0001]: conditional expression nesting exceeds the compiler limit of 128 - --> /main/main.solc:131:17 +error[SC0001]: expression nesting exceeds the compiler limit of 32 + --> /main/main.sol:34:18 + | +33 | true ? 0 : +34 | true ? 0 : + | ^^^^ +35 | true ? 0 : + | +--- + +error[SC0001]: expression nesting exceeds the compiler limit of 32 + --> /main/main.sol:34:27 + | +33 | true ? 0 : +34 | true ? 0 : + | ^ +35 | true ? 0 : + | +--- + +error[SC0001]: expression nesting exceeds the compiler limit of 32 + --> /main/main.sol:35:18 | -130 | if true then 0 else -131 | if true then 0 else - | ^^ -132 | if true then 0 else + 34 | true ? 0 : + 35 | / true ? 0 : + 36 | | true ? 0 : + 37 | | true ? 0 : + 38 | | true ? 0 : + 39 | | true ? 0 : + 40 | | true ? 0 : + 41 | | true ? 0 : + 42 | | true ? 0 : + 43 | | true ? 0 : + 44 | | true ? 0 : + 45 | | true ? 0 : + 46 | | true ? 0 : + 47 | | true ? 0 : + 48 | | true ? 0 : + 49 | | true ? 0 : + 50 | | true ? 0 : + 51 | | true ? 0 : + 52 | | true ? 0 : + 53 | | true ? 0 : + 54 | | true ? 0 : + 55 | | true ? 0 : + 56 | | true ? 0 : + 57 | | true ? 0 : + 58 | | true ? 0 : + 59 | | true ? 0 : + 60 | | true ? 0 : + 61 | | true ? 0 : + 62 | | true ? 0 : + 63 | | true ? 0 : + 64 | | true ? 0 : + 65 | | true ? 0 : + 66 | | true ? 0 : + 67 | | true ? 0 : + 68 | | true ? 0 : + 69 | | true ? 0 : + 70 | | true ? 0 : + 71 | | true ? 0 : + 72 | | true ? 0 : + 73 | | true ? 0 : + 74 | | true ? 0 : + 75 | | true ? 0 : + 76 | | true ? 0 : + 77 | | true ? 0 : + 78 | | true ? 0 : + 79 | | true ? 0 : + 80 | | true ? 0 : + 81 | | true ? 0 : + 82 | | true ? 0 : + 83 | | true ? 0 : + 84 | | true ? 0 : + 85 | | true ? 0 : + 86 | | true ? 0 : + 87 | | true ? 0 : + 88 | | true ? 0 : + 89 | | true ? 0 : + 90 | | true ? 0 : + 91 | | true ? 0 : + 92 | | true ? 0 : + 93 | | true ? 0 : + 94 | | true ? 0 : + 95 | | true ? 0 : + 96 | | true ? 0 : + 97 | | true ? 0 : + 98 | | true ? 0 : + 99 | | true ? 0 : +100 | | true ? 0 : +101 | | true ? 0 : +102 | | true ? 0 : +103 | | true ? 0 : +104 | | true ? 0 : +105 | | true ? 0 : +106 | | true ? 0 : +107 | | true ? 0 : +108 | | true ? 0 : +109 | | true ? 0 : +110 | | true ? 0 : +111 | | true ? 0 : +112 | | true ? 0 : +113 | | true ? 0 : +114 | | true ? 0 : +115 | | true ? 0 : +116 | | true ? 0 : +117 | | true ? 0 : +118 | | true ? 0 : +119 | | true ? 0 : +120 | | true ? 0 : +121 | | true ? 0 : +122 | | true ? 0 : +123 | | true ? 0 : +124 | | true ? 0 : +125 | | true ? 0 : +126 | | true ? 0 : +127 | | true ? 0 : +128 | | true ? 0 : +129 | | true ? 0 : +130 | | true ? 0 : +131 | | true ? 0 : +132 | | true ? 0 : +133 | | 0; + | |___^ +134 | } | diff --git a/crates/uitest/tests/fixtures/parse/excessive_conditional_nesting/main.sol b/crates/uitest/tests/fixtures/parse/excessive_conditional_nesting/main.sol new file mode 100644 index 00000000..fb685df4 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/excessive_conditional_nesting/main.sol @@ -0,0 +1,134 @@ +function main() returns (word) { + return + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + true ? 0 : + 0; +} diff --git a/crates/uitest/tests/fixtures/parse/excessive_conditional_nesting/main.solc b/crates/uitest/tests/fixtures/parse/excessive_conditional_nesting/main.solc deleted file mode 100644 index 309f09e9..00000000 --- a/crates/uitest/tests/fixtures/parse/excessive_conditional_nesting/main.solc +++ /dev/null @@ -1,134 +0,0 @@ -function main() -> word { - return - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - if true then 0 else - 0; -} diff --git a/crates/uitest/tests/fixtures/parse/excessive_expression_nesting/diagnostics.snap b/crates/uitest/tests/fixtures/parse/excessive_expression_nesting/diagnostics.snap index 0f9738c4..dc82095d 100644 --- a/crates/uitest/tests/fixtures/parse/excessive_expression_nesting/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/excessive_expression_nesting/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/excessive_expression_nesting/main.solc +input_file: crates/uitest/tests/fixtures/parse/excessive_expression_nesting/main.sol --- error[SC0001]: expression nesting exceeds the compiler limit of 32 - --> /main/main.solc:1:66 + --> /main/main.sol:1:73 | -1 | function main() -> word { return !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!true; } - | ^^^^^^^^^^^^ +1 | function main() returns (word) { return !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!true; } + | ^^^^^^^^^^^^ diff --git a/crates/uitest/tests/fixtures/parse/excessive_expression_nesting/main.sol b/crates/uitest/tests/fixtures/parse/excessive_expression_nesting/main.sol new file mode 100644 index 00000000..df75f702 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/excessive_expression_nesting/main.sol @@ -0,0 +1 @@ +function main() returns (word) { return !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!true; } diff --git a/crates/uitest/tests/fixtures/parse/excessive_expression_nesting/main.solc b/crates/uitest/tests/fixtures/parse/excessive_expression_nesting/main.solc deleted file mode 100644 index c000fcad..00000000 --- a/crates/uitest/tests/fixtures/parse/excessive_expression_nesting/main.solc +++ /dev/null @@ -1 +0,0 @@ -function main() -> word { return !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!true; } diff --git a/crates/uitest/tests/fixtures/parse/fallback_with_non_unit_return/diagnostics.snap b/crates/uitest/tests/fixtures/parse/fallback_with_non_unit_return/diagnostics.snap index e0a2d443..d8bdf585 100644 --- a/crates/uitest/tests/fixtures/parse/fallback_with_non_unit_return/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/fallback_with_non_unit_return/diagnostics.snap @@ -1,14 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/fallback_with_non_unit_return/main.solc +input_file: crates/uitest/tests/fixtures/parse/fallback_with_non_unit_return/main.sol --- -error[SC0001]: fallback function must return unit (`()`) - --> /main/main.solc:2:17 +error[SC0001]: parse error: unexpected identifier `returns` + --> /main/main.sol:2:14 | 1 | contract Bad { -2 | fallback() -> word {} - | ^^^^ +2 | fallback() returns (word) {} + | ^^^^^^^ unexpected token 3 | | + = note: expecting `payable`, `public`, or `{` = note: while parsing fallback definition diff --git a/crates/uitest/tests/fixtures/parse/fallback_with_non_unit_return/main.sol b/crates/uitest/tests/fixtures/parse/fallback_with_non_unit_return/main.sol new file mode 100644 index 00000000..cd30dedd --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/fallback_with_non_unit_return/main.sol @@ -0,0 +1,5 @@ +contract Bad { + fallback() returns (word) {} + + function after() {} +} diff --git a/crates/uitest/tests/fixtures/parse/fallback_with_non_unit_return/main.solc b/crates/uitest/tests/fixtures/parse/fallback_with_non_unit_return/main.solc deleted file mode 100644 index d10c3f58..00000000 --- a/crates/uitest/tests/fixtures/parse/fallback_with_non_unit_return/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -contract Bad { - fallback() -> word {} - - function after() {} -} diff --git a/crates/uitest/tests/fixtures/parse/fallback_with_params/diagnostics.snap b/crates/uitest/tests/fixtures/parse/fallback_with_params/diagnostics.snap index 797ceb94..4251346b 100644 --- a/crates/uitest/tests/fixtures/parse/fallback_with_params/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/fallback_with_params/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/fallback_with_params/main.solc +input_file: crates/uitest/tests/fixtures/parse/fallback_with_params/main.sol --- error[SC0001]: fallback function must not declare input parameters - --> /main/main.solc:2:11 + --> /main/main.sol:2:11 | 1 | contract Bad { 2 | fallback(x: word) {} diff --git a/crates/uitest/tests/fixtures/parse/fallback_with_params/main.solc b/crates/uitest/tests/fixtures/parse/fallback_with_params/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/fallback_with_params/main.solc rename to crates/uitest/tests/fixtures/parse/fallback_with_params/main.sol diff --git a/crates/uitest/tests/fixtures/parse/function_param_recovery/diagnostics.snap b/crates/uitest/tests/fixtures/parse/function_param_recovery/diagnostics.snap index 400cc596..70c779b2 100644 --- a/crates/uitest/tests/fixtures/parse/function_param_recovery/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/function_param_recovery/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/function_param_recovery/main.solc +input_file: crates/uitest/tests/fixtures/parse/function_param_recovery/main.sol --- error[SC0001]: parse error: unexpected `,` - --> /main/main.solc:1:16 + --> /main/main.sol:1:16 | 1 | function bad(x:, y: U) {} | ^ unexpected token diff --git a/crates/uitest/tests/fixtures/parse/function_param_recovery/main.solc b/crates/uitest/tests/fixtures/parse/function_param_recovery/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/function_param_recovery/main.solc rename to crates/uitest/tests/fixtures/parse/function_param_recovery/main.sol diff --git a/crates/uitest/tests/fixtures/parse/function_signature_missing_type/diagnostics.snap b/crates/uitest/tests/fixtures/parse/function_signature_missing_type/diagnostics.snap index 6b10bc21..6b306e2c 100644 --- a/crates/uitest/tests/fixtures/parse/function_signature_missing_type/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/function_signature_missing_type/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/function_signature_missing_type/main.solc +input_file: crates/uitest/tests/fixtures/parse/function_signature_missing_type/main.sol --- error[SC0001]: parse error: unexpected `)` - --> /main/main.solc:1:17 + --> /main/main.sol:1:17 | 1 | function bad(x: ) {} | ^ unexpected token diff --git a/crates/uitest/tests/fixtures/parse/function_signature_missing_type/main.solc b/crates/uitest/tests/fixtures/parse/function_signature_missing_type/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/function_signature_missing_type/main.solc rename to crates/uitest/tests/fixtures/parse/function_signature_missing_type/main.sol diff --git a/crates/uitest/tests/fixtures/parse/if_trailing_semicolon/diagnostics.snap b/crates/uitest/tests/fixtures/parse/if_trailing_semicolon/diagnostics.snap index 6e2e1be9..53c61a7a 100644 --- a/crates/uitest/tests/fixtures/parse/if_trailing_semicolon/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/if_trailing_semicolon/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/if_trailing_semicolon/main.solc +input_file: crates/uitest/tests/fixtures/parse/if_trailing_semicolon/main.sol --- error[SC0001]: parse error: unexpected `;` - --> /main/main.solc:4:4 + --> /main/main.sol:4:4 | 3 | return (); 4 | }; diff --git a/crates/uitest/tests/fixtures/parse/if_trailing_semicolon/main.sol b/crates/uitest/tests/fixtures/parse/if_trailing_semicolon/main.sol new file mode 100644 index 00000000..abf01766 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/if_trailing_semicolon/main.sol @@ -0,0 +1,5 @@ +function f() { + if ( true ) { + return (); + }; +} diff --git a/crates/uitest/tests/fixtures/parse/if_trailing_semicolon/main.solc b/crates/uitest/tests/fixtures/parse/if_trailing_semicolon/main.solc deleted file mode 100644 index 2ab5a6fd..00000000 --- a/crates/uitest/tests/fixtures/parse/if_trailing_semicolon/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -function f() { - if true { - return (); - }; -} diff --git a/crates/uitest/tests/fixtures/parse/impl_missing_head/diagnostics.snap b/crates/uitest/tests/fixtures/parse/impl_missing_head/diagnostics.snap new file mode 100644 index 00000000..abce0d12 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/impl_missing_head/diagnostics.snap @@ -0,0 +1,13 @@ +--- +source: crates/test-utils/src/lib.rs +expression: rendered +input_file: crates/uitest/tests/fixtures/parse/impl_missing_head/main.sol +--- +error[SC0001]: parse error: unexpected `{` + --> /main/main.sol:1:6 + | +1 | impl {} + | ^ unexpected token + | + = note: expecting `<` + = note: while parsing impl declaration diff --git a/crates/uitest/tests/fixtures/parse/impl_missing_head/main.sol b/crates/uitest/tests/fixtures/parse/impl_missing_head/main.sol new file mode 100644 index 00000000..21261d48 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/impl_missing_head/main.sol @@ -0,0 +1 @@ +impl {} diff --git a/crates/uitest/tests/fixtures/parse/import_ctor_group_syntax/diagnostics.snap b/crates/uitest/tests/fixtures/parse/import_ctor_group_syntax/diagnostics.snap index 7035f84e..835155f7 100644 --- a/crates/uitest/tests/fixtures/parse/import_ctor_group_syntax/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/import_ctor_group_syntax/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/import_ctor_group_syntax/main.solc +input_file: crates/uitest/tests/fixtures/parse/import_ctor_group_syntax/main.sol --- error[SC0001]: parse error: unexpected `(` - --> /main/main.solc:1:14 + --> /main/main.sol:1:10 | -1 | import lib.{D(C)}; - | ^ unexpected token +1 | import {D(C)} from lib; + | ^ unexpected token | = note: expecting `,`, `as`, or `}` = note: while parsing import declaration diff --git a/crates/uitest/tests/fixtures/parse/import_ctor_group_syntax/main.sol b/crates/uitest/tests/fixtures/parse/import_ctor_group_syntax/main.sol new file mode 100644 index 00000000..b1815643 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/import_ctor_group_syntax/main.sol @@ -0,0 +1 @@ +import {D(C)} from lib; diff --git a/crates/uitest/tests/fixtures/parse/import_ctor_group_syntax/main.solc b/crates/uitest/tests/fixtures/parse/import_ctor_group_syntax/main.solc deleted file mode 100644 index e9299abd..00000000 --- a/crates/uitest/tests/fixtures/parse/import_ctor_group_syntax/main.solc +++ /dev/null @@ -1 +0,0 @@ -import lib.{D(C)}; diff --git a/crates/uitest/tests/fixtures/parse/import_selector_unterminated/diagnostics.snap b/crates/uitest/tests/fixtures/parse/import_selector_unterminated/diagnostics.snap index 089c3525..efb404cd 100644 --- a/crates/uitest/tests/fixtures/parse/import_selector_unterminated/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/import_selector_unterminated/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/import_selector_unterminated/main.solc +input_file: crates/uitest/tests/fixtures/parse/import_selector_unterminated/main.sol --- error[SC0001]: parse error: unexpected end of input - --> /main/main.solc:1:14 + --> /main/main.sol:1:10 | -1 | import mod.{ - | ^ unexpected token +1 | import { + | ^ unexpected token | - = note: expecting `*`, or selector name + = note: expecting selector name = note: while parsing import declaration diff --git a/crates/uitest/tests/fixtures/parse/import_selector_unterminated/main.sol b/crates/uitest/tests/fixtures/parse/import_selector_unterminated/main.sol new file mode 100644 index 00000000..c762184a --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/import_selector_unterminated/main.sol @@ -0,0 +1 @@ +import { diff --git a/crates/uitest/tests/fixtures/parse/import_selector_unterminated/main.solc b/crates/uitest/tests/fixtures/parse/import_selector_unterminated/main.solc deleted file mode 100644 index f91674fe..00000000 --- a/crates/uitest/tests/fixtures/parse/import_selector_unterminated/main.solc +++ /dev/null @@ -1 +0,0 @@ -import mod.{ diff --git a/crates/uitest/tests/fixtures/parse/instance_missing_head/diagnostics.snap b/crates/uitest/tests/fixtures/parse/instance_missing_head/diagnostics.snap deleted file mode 100644 index 375100c3..00000000 --- a/crates/uitest/tests/fixtures/parse/instance_missing_head/diagnostics.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: crates/test-utils/src/lib.rs -expression: rendered -input_file: crates/uitest/tests/fixtures/parse/instance_missing_head/main.solc ---- -error[SC0001]: parse error: unexpected `{` - --> /main/main.solc:1:10 - | -1 | instance {} - | ^ unexpected token - | - = note: expecting `(`, `=>`, or predicate - = note: while parsing instance declaration diff --git a/crates/uitest/tests/fixtures/parse/instance_missing_head/main.solc b/crates/uitest/tests/fixtures/parse/instance_missing_head/main.solc deleted file mode 100644 index d45578e9..00000000 --- a/crates/uitest/tests/fixtures/parse/instance_missing_head/main.solc +++ /dev/null @@ -1 +0,0 @@ -instance {} diff --git a/crates/uitest/tests/fixtures/parse/invalid_token/diagnostics.snap b/crates/uitest/tests/fixtures/parse/invalid_token/diagnostics.snap index 4cdc756c..e922f996 100644 --- a/crates/uitest/tests/fixtures/parse/invalid_token/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/invalid_token/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/invalid_token/main.solc +input_file: crates/uitest/tests/fixtures/parse/invalid_token/main.sol --- error[SC0001]: invalid token `§` - --> /main/main.solc:1:1 + --> /main/main.sol:1:1 | 1 | § | ^ invalid token diff --git a/crates/uitest/tests/fixtures/parse/invalid_token/main.solc b/crates/uitest/tests/fixtures/parse/invalid_token/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/invalid_token/main.solc rename to crates/uitest/tests/fixtures/parse/invalid_token/main.sol diff --git a/crates/uitest/tests/fixtures/parse/keyword_comptime_identifier/diagnostics.snap b/crates/uitest/tests/fixtures/parse/keyword_comptime_identifier/diagnostics.snap index b37f29ac..7815869d 100644 --- a/crates/uitest/tests/fixtures/parse/keyword_comptime_identifier/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/keyword_comptime_identifier/diagnostics.snap @@ -1,12 +1,21 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/keyword_comptime_identifier/main.solc +input_file: crates/uitest/tests/fixtures/parse/keyword_comptime_identifier/main.sol --- error[SC0001]: `comptime` is a parameter modifier; expected parameter name - --> /main/main.solc:1:12 + --> /main/main.sol:1:12 | -1 | function f(comptime) -> word { return comptime; } +1 | function f(comptime) returns (word) { return comptime; } | ^^^^^^^^ | = note: while parsing function parameter +--- + +error[SC0001]: named function parameter requires an explicit type + --> /main/main.sol:1:12 + | +1 | function f(comptime) returns (word) { return comptime; } + | ^^^^^^^^ + | + = note: while parsing function signature diff --git a/crates/uitest/tests/fixtures/parse/keyword_comptime_identifier/main.sol b/crates/uitest/tests/fixtures/parse/keyword_comptime_identifier/main.sol new file mode 100644 index 00000000..e12b366c --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/keyword_comptime_identifier/main.sol @@ -0,0 +1 @@ +function f(comptime) returns (word) { return comptime; } diff --git a/crates/uitest/tests/fixtures/parse/keyword_comptime_identifier/main.solc b/crates/uitest/tests/fixtures/parse/keyword_comptime_identifier/main.solc deleted file mode 100644 index 72a73f39..00000000 --- a/crates/uitest/tests/fixtures/parse/keyword_comptime_identifier/main.solc +++ /dev/null @@ -1 +0,0 @@ -function f(comptime) -> word { return comptime; } diff --git a/crates/uitest/tests/fixtures/parse/match_arm_arity/diagnostics.snap b/crates/uitest/tests/fixtures/parse/match_arm_arity/diagnostics.snap new file mode 100644 index 00000000..a762dc58 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/match_arm_arity/diagnostics.snap @@ -0,0 +1,15 @@ +--- +source: crates/test-utils/src/lib.rs +expression: rendered +input_file: crates/uitest/tests/fixtures/parse/match_arm_arity/main.sol +--- +error[SC0001]: match has 2 scrutinees but this case has 1 patterns + --> /main/main.sol:5:1 + | +4 | match (x, y) { +5 | / case Nat.Zero { +6 | | return 0; +7 | | } + | |_^ +8 | case (Nat.Succ(a), Nat.Zero) { + | diff --git a/crates/uitest/tests/fixtures/parse/match_arm_arity/main.sol b/crates/uitest/tests/fixtures/parse/match_arm_arity/main.sol new file mode 100644 index 00000000..f2f6bc02 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/match_arm_arity/main.sol @@ -0,0 +1,21 @@ +enum Nat { Zero, Succ(Nat) } + +function pick(x: Nat, y: Nat) returns (word) { + match (x, y) { +case Nat.Zero { +return 0; +} +case (Nat.Succ(a), Nat.Zero) { +return 1; +} +case (Nat.Succ(a), Nat.Succ(b)) { +return 2; +} +} +} + +contract T { + function main() public returns (word) { + return pick(Nat.Zero, Nat.Zero); + } +} diff --git a/crates/uitest/tests/fixtures/parse/missing_data_semicolon/diagnostics.snap b/crates/uitest/tests/fixtures/parse/missing_data_semicolon/diagnostics.snap deleted file mode 100644 index 508d8d19..00000000 --- a/crates/uitest/tests/fixtures/parse/missing_data_semicolon/diagnostics.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: crates/test-utils/src/lib.rs -expression: rendered -input_file: crates/uitest/tests/fixtures/parse/missing_data_semicolon/main.solc ---- -error[SC0001]: parse error: unexpected end of input - --> /main/main.solc:1:12 - | -1 | data D = C - | ^ unexpected token - | - = note: expecting `(`, `;`, or `|` - = note: while parsing data declaration diff --git a/crates/uitest/tests/fixtures/parse/missing_data_semicolon/main.solc b/crates/uitest/tests/fixtures/parse/missing_data_semicolon/main.solc deleted file mode 100644 index 8e327275..00000000 --- a/crates/uitest/tests/fixtures/parse/missing_data_semicolon/main.solc +++ /dev/null @@ -1 +0,0 @@ -data D = C diff --git a/crates/uitest/tests/fixtures/parse/missing_semicolon/diagnostics.snap b/crates/uitest/tests/fixtures/parse/missing_semicolon/diagnostics.snap index f049be89..718c3124 100644 --- a/crates/uitest/tests/fixtures/parse/missing_semicolon/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/missing_semicolon/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/missing_semicolon/main.solc +input_file: crates/uitest/tests/fixtures/parse/missing_semicolon/main.sol --- error[SC0001]: parse error: unexpected end of input - --> /main/main.solc:1:18 + --> /main/main.sol:1:18 | 1 | import core.math | ^ unexpected token | - = note: expecting `.`, `;`, or `as` + = note: expecting `.`, or `;` = note: while parsing import declaration diff --git a/crates/uitest/tests/fixtures/parse/missing_semicolon/main.solc b/crates/uitest/tests/fixtures/parse/missing_semicolon/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/missing_semicolon/main.solc rename to crates/uitest/tests/fixtures/parse/missing_semicolon/main.sol diff --git a/crates/uitest/tests/fixtures/parse/multibyte_eof_string/diagnostics.snap b/crates/uitest/tests/fixtures/parse/multibyte_eof_string/diagnostics.snap index 060fd1e4..6ca23e3b 100644 --- a/crates/uitest/tests/fixtures/parse/multibyte_eof_string/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/multibyte_eof_string/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/multibyte_eof_string/main.solc +input_file: crates/uitest/tests/fixtures/parse/multibyte_eof_string/main.sol --- error[SC0001]: unterminated string literal - --> /main/main.solc:2:11 + --> /main/main.sol:2:11 | -1 | function f() -> word { +1 | function f() returns (word) { 2 | let s = "café | ^^^^^ string literal starts here | diff --git a/crates/uitest/tests/fixtures/parse/multibyte_eof_string/main.sol b/crates/uitest/tests/fixtures/parse/multibyte_eof_string/main.sol new file mode 100644 index 00000000..b9627b80 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/multibyte_eof_string/main.sol @@ -0,0 +1,2 @@ +function f() returns (word) { + let s = "café \ No newline at end of file diff --git a/crates/uitest/tests/fixtures/parse/multibyte_eof_string/main.solc b/crates/uitest/tests/fixtures/parse/multibyte_eof_string/main.solc deleted file mode 100644 index 5c5dcaf2..00000000 --- a/crates/uitest/tests/fixtures/parse/multibyte_eof_string/main.solc +++ /dev/null @@ -1,2 +0,0 @@ -function f() -> word { - let s = "café \ No newline at end of file diff --git a/crates/uitest/tests/fixtures/parse/multiple_emitted_errors/diagnostics.snap b/crates/uitest/tests/fixtures/parse/multiple_emitted_errors/diagnostics.snap index 02f8aa0f..477f9af0 100644 --- a/crates/uitest/tests/fixtures/parse/multiple_emitted_errors/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/multiple_emitted_errors/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/multiple_emitted_errors/main.solc +input_file: crates/uitest/tests/fixtures/parse/multiple_emitted_errors/main.sol --- error[SC0001]: invalid token `§` - --> /main/main.solc:1:1 + --> /main/main.sol:1:1 | 1 | § | ^ invalid token @@ -13,7 +13,7 @@ error[SC0001]: invalid token `§` --- error[SC0001]: invalid token `§` - --> /main/main.solc:2:1 + --> /main/main.sol:2:1 | 1 | § 2 | § diff --git a/crates/uitest/tests/fixtures/parse/multiple_emitted_errors/main.solc b/crates/uitest/tests/fixtures/parse/multiple_emitted_errors/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/multiple_emitted_errors/main.solc rename to crates/uitest/tests/fixtures/parse/multiple_emitted_errors/main.sol diff --git a/crates/uitest/tests/fixtures/parse/multiple_errors_continue/diagnostics.snap b/crates/uitest/tests/fixtures/parse/multiple_errors_continue/diagnostics.snap index 5b65fafd..fb35dd34 100644 --- a/crates/uitest/tests/fixtures/parse/multiple_errors_continue/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/multiple_errors_continue/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/multiple_errors_continue/main.solc +input_file: crates/uitest/tests/fixtures/parse/multiple_errors_continue/main.sol --- error[SC0001]: import declaration requires trailing `;` - --> /main/main.solc:2:1 + --> /main/main.sol:2:1 | 1 | import core.math 2 | function bad() { @@ -15,11 +15,11 @@ error[SC0001]: import declaration requires trailing `;` --- error[SC0001]: parse error: unexpected `;` - --> /main/main.solc:3:13 + --> /main/main.sol:3:13 | 2 | function bad() { 3 | let x = ; | ^ unexpected token 4 | return 1; | - = note: expecting expression after `=` + = note: expecting `&&`, `&`, `(`, `.`, `?`, `[`, `^`, `|`, or `||` diff --git a/crates/uitest/tests/fixtures/parse/multiple_errors_continue/main.solc b/crates/uitest/tests/fixtures/parse/multiple_errors_continue/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/multiple_errors_continue/main.solc rename to crates/uitest/tests/fixtures/parse/multiple_errors_continue/main.sol diff --git a/crates/uitest/tests/fixtures/parse/named_param_missing_type_contract/diagnostics.snap b/crates/uitest/tests/fixtures/parse/named_param_missing_type_contract/diagnostics.snap new file mode 100644 index 00000000..d6e04282 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/named_param_missing_type_contract/diagnostics.snap @@ -0,0 +1,14 @@ +--- +source: crates/test-utils/src/lib.rs +expression: rendered +input_file: crates/uitest/tests/fixtures/parse/named_param_missing_type_contract/main.sol +--- +error[SC0001]: named function parameter requires an explicit type + --> /main/main.sol:2:15 + | +1 | contract C { +2 | function id(x) public { + | ^ +3 | return x; + | + = note: while parsing function signature diff --git a/crates/uitest/tests/fixtures/parse/named_param_missing_type_contract/main.sol b/crates/uitest/tests/fixtures/parse/named_param_missing_type_contract/main.sol new file mode 100644 index 00000000..1ef672a2 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/named_param_missing_type_contract/main.sol @@ -0,0 +1,9 @@ +contract C { + function id(x) public { + return x; + } + + function main() returns (word) { + return 0; + } +} diff --git a/crates/uitest/tests/fixtures/parse/named_param_missing_type_top_level/diagnostics.snap b/crates/uitest/tests/fixtures/parse/named_param_missing_type_top_level/diagnostics.snap new file mode 100644 index 00000000..c03aa122 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/named_param_missing_type_top_level/diagnostics.snap @@ -0,0 +1,14 @@ +--- +source: crates/test-utils/src/lib.rs +expression: rendered +input_file: crates/uitest/tests/fixtures/parse/named_param_missing_type_top_level/main.sol +--- +error[SC0001]: named function parameter requires an explicit type + --> /main/main.sol:1:13 + | +1 | function id(x) { + | ^ +2 | return x; +3 | } + | + = note: while parsing function signature diff --git a/crates/uitest/tests/fixtures/parse/named_param_missing_type_top_level/main.sol b/crates/uitest/tests/fixtures/parse/named_param_missing_type_top_level/main.sol new file mode 100644 index 00000000..40d70dca --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/named_param_missing_type_top_level/main.sol @@ -0,0 +1,9 @@ +function id(x) { + return x; +} + +contract C { + function main() public returns (word) { + return id(42); + } +} diff --git a/crates/uitest/tests/fixtures/parse/named_params_missing_types/diagnostics.snap b/crates/uitest/tests/fixtures/parse/named_params_missing_types/diagnostics.snap new file mode 100644 index 00000000..f1d20139 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/named_params_missing_types/diagnostics.snap @@ -0,0 +1,25 @@ +--- +source: crates/test-utils/src/lib.rs +expression: rendered +input_file: crates/uitest/tests/fixtures/parse/named_params_missing_types/main.sol +--- +error[SC0001]: named function parameter requires an explicit type + --> /main/main.sol:2:20 + | +1 | contract C { +2 | function compose(f, g) public { + | ^ +3 | return lam (x) { + | + = note: while parsing function signature +--- + +error[SC0001]: named function parameter requires an explicit type + --> /main/main.sol:2:23 + | +1 | contract C { +2 | function compose(f, g) public { + | ^ +3 | return lam (x) { + | + = note: while parsing function signature diff --git a/crates/uitest/tests/fixtures/parse/named_params_missing_types/main.sol b/crates/uitest/tests/fixtures/parse/named_params_missing_types/main.sol new file mode 100644 index 00000000..ef0c6756 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/named_params_missing_types/main.sol @@ -0,0 +1,16 @@ +contract C { + function compose(f, g) public { + return lam (x) { + return f(g(x)); + }; + } + + function id(x: word) public returns (word) { + return x; + } + + function main() public returns (word) { + let f = compose(id, id); + return f(42); + } +} diff --git a/crates/uitest/tests/fixtures/parse/nullary_ctor_applied_pattern/diagnostics.snap b/crates/uitest/tests/fixtures/parse/nullary_ctor_applied_pattern/diagnostics.snap index 244258bb..93123fa5 100644 --- a/crates/uitest/tests/fixtures/parse/nullary_ctor_applied_pattern/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/nullary_ctor_applied_pattern/diagnostics.snap @@ -1,25 +1,25 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/nullary_ctor_applied_pattern/main.solc +input_file: crates/uitest/tests/fixtures/parse/nullary_ctor_applied_pattern/main.sol --- -error[SC0001]: parse error: unexpected `match` - --> /main/main.solc:4:3 +error[SC0001]: parse error: unexpected `)` + --> /main/main.sol:4:11 | -3 | function f(x: D) -> word { -4 | match x { - | ^^^^^ unexpected token -5 | | C() => return 1; +3 | function f(x: D) returns (word) { +4 | match (x) { + | ^ unexpected token +5 | case C() { | - = note: expecting `!`, `(`, `.`, `@`, `[`, `if`, `lam`, or `~` + = note: expecting `&&`, `&`, `(`, `.`, `?`, `[`, `^`, `|`, or `||` --- -error[SC0001]: parse error: unexpected `=>` - --> /main/main.solc:5:9 +error[SC0001]: parse error: unexpected `)` + --> /main/main.sol:5:8 | -4 | match x { -5 | | C() => return 1; - | ^^ unexpected token -6 | } +4 | match (x) { +5 | case C() { + | ^ unexpected token +6 | return 1; | - = note: expecting `%=`, `&&`, `&=`, `&`, `(`, `*=`, `+=`, `-=`, `.`, `/=`, `:`, `;`, `=`, `?`, `[`, `^=`, `^`, `|=`, `|`, `||`, `~=`, end of input, or statement + = note: expecting `(`, `.`, or `_` diff --git a/crates/uitest/tests/fixtures/parse/nullary_ctor_applied_pattern/main.sol b/crates/uitest/tests/fixtures/parse/nullary_ctor_applied_pattern/main.sol new file mode 100644 index 00000000..3dad6cef --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/nullary_ctor_applied_pattern/main.sol @@ -0,0 +1,9 @@ +enum D { C } + +function f(x: D) returns (word) { + match (x) { +case C() { +return 1; +} +} +} diff --git a/crates/uitest/tests/fixtures/parse/nullary_ctor_applied_pattern/main.solc b/crates/uitest/tests/fixtures/parse/nullary_ctor_applied_pattern/main.solc deleted file mode 100644 index cd6787a2..00000000 --- a/crates/uitest/tests/fixtures/parse/nullary_ctor_applied_pattern/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -data D = C; - -function f(x: D) -> word { - match x { - | C() => return 1; - } -} diff --git a/crates/uitest/tests/fixtures/parse/pragma_missing_name/diagnostics.snap b/crates/uitest/tests/fixtures/parse/pragma_missing_name/diagnostics.snap index a234f096..1fda092e 100644 --- a/crates/uitest/tests/fixtures/parse/pragma_missing_name/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/pragma_missing_name/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/pragma_missing_name/main.solc +input_file: crates/uitest/tests/fixtures/parse/pragma_missing_name/main.sol --- error[SC0001]: parse error: unexpected `;` - --> /main/main.solc:1:8 + --> /main/main.sol:1:8 | 1 | pragma ; | ^ unexpected token diff --git a/crates/uitest/tests/fixtures/parse/pragma_missing_name/main.solc b/crates/uitest/tests/fixtures/parse/pragma_missing_name/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/pragma_missing_name/main.solc rename to crates/uitest/tests/fixtures/parse/pragma_missing_name/main.sol diff --git a/crates/uitest/tests/fixtures/parse/public_constructor/diagnostics.snap b/crates/uitest/tests/fixtures/parse/public_constructor/diagnostics.snap index fd84c524..71723703 100644 --- a/crates/uitest/tests/fixtures/parse/public_constructor/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/public_constructor/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/public_constructor/main.solc +input_file: crates/uitest/tests/fixtures/parse/public_constructor/main.sol --- error[SC0001]: constructor is implicitly public; remove the 'public' keyword - --> /main/main.solc:2:3 + --> /main/main.sol:2:17 | 1 | contract Bad { -2 | public constructor() {} - | ^^^^^^ +2 | constructor() public {} + | ^^^^^^ 3 | | = note: while parsing constructor definition diff --git a/crates/uitest/tests/fixtures/parse/public_constructor/main.sol b/crates/uitest/tests/fixtures/parse/public_constructor/main.sol new file mode 100644 index 00000000..9bc248c4 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/public_constructor/main.sol @@ -0,0 +1,5 @@ +contract Bad { + constructor() public {} + + function after() {} +} diff --git a/crates/uitest/tests/fixtures/parse/public_constructor/main.solc b/crates/uitest/tests/fixtures/parse/public_constructor/main.solc deleted file mode 100644 index bc487a53..00000000 --- a/crates/uitest/tests/fixtures/parse/public_constructor/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -contract Bad { - public constructor() {} - - function after() {} -} diff --git a/crates/uitest/tests/fixtures/parse/public_fallback/diagnostics.snap b/crates/uitest/tests/fixtures/parse/public_fallback/diagnostics.snap index 7bbaa7c5..d1a56320 100644 --- a/crates/uitest/tests/fixtures/parse/public_fallback/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/public_fallback/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/public_fallback/main.solc +input_file: crates/uitest/tests/fixtures/parse/public_fallback/main.sol --- error[SC0001]: fallback is implicitly public; remove the 'public' keyword - --> /main/main.solc:2:3 + --> /main/main.sol:2:14 | 1 | contract Bad { -2 | public fallback() {} - | ^^^^^^ +2 | fallback() public {} + | ^^^^^^ 3 | | = note: while parsing fallback definition diff --git a/crates/uitest/tests/fixtures/parse/public_fallback/main.sol b/crates/uitest/tests/fixtures/parse/public_fallback/main.sol new file mode 100644 index 00000000..6ec041f2 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/public_fallback/main.sol @@ -0,0 +1,5 @@ +contract Bad { + fallback() public {} + + function after() {} +} diff --git a/crates/uitest/tests/fixtures/parse/public_fallback/main.solc b/crates/uitest/tests/fixtures/parse/public_fallback/main.solc deleted file mode 100644 index 5bc8b97e..00000000 --- a/crates/uitest/tests/fixtures/parse/public_fallback/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -contract Bad { - public fallback() {} - - function after() {} -} diff --git a/crates/uitest/tests/fixtures/parse/public_free_function/diagnostics.snap b/crates/uitest/tests/fixtures/parse/public_free_function/diagnostics.snap index 22f093d8..a52ce449 100644 --- a/crates/uitest/tests/fixtures/parse/public_free_function/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/public_free_function/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/public_free_function/main.solc +input_file: crates/uitest/tests/fixtures/parse/public_free_function/main.sol --- error[SC0001]: 'public' is only allowed on functions declared inside a contract - --> /main/main.solc:1:1 + --> /main/main.sol:1:16 | -1 | public function bad() {} - | ^^^^^^ +1 | function bad() public {} + | ^^^^^^ 2 | 3 | function after() {} | diff --git a/crates/uitest/tests/fixtures/parse/public_free_function/main.sol b/crates/uitest/tests/fixtures/parse/public_free_function/main.sol new file mode 100644 index 00000000..5dffb2f9 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/public_free_function/main.sol @@ -0,0 +1,3 @@ +function bad() public {} + +function after() {} diff --git a/crates/uitest/tests/fixtures/parse/public_free_function/main.solc b/crates/uitest/tests/fixtures/parse/public_free_function/main.solc deleted file mode 100644 index 5983ec5f..00000000 --- a/crates/uitest/tests/fixtures/parse/public_free_function/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -public function bad() {} - -function after() {} diff --git a/crates/uitest/tests/fixtures/parse/string_bad_escape/diagnostics.snap b/crates/uitest/tests/fixtures/parse/string_bad_escape/diagnostics.snap index faffbbb5..7db2f98a 100644 --- a/crates/uitest/tests/fixtures/parse/string_bad_escape/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/string_bad_escape/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/string_bad_escape/main.solc +input_file: crates/uitest/tests/fixtures/parse/string_bad_escape/main.sol --- error[SC0001]: invalid string escape `/q` - --> /main/main.solc:1:33 + --> /main/main.sol:1:40 | -1 | function f() -> string { return "a/q"; } - | ^^^^^ invalid escape sequence +1 | function f() returns (string) { return "a/q"; } + | ^^^^^ invalid escape sequence diff --git a/crates/uitest/tests/fixtures/parse/string_bad_escape/main.sol b/crates/uitest/tests/fixtures/parse/string_bad_escape/main.sol new file mode 100644 index 00000000..5de7e713 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/string_bad_escape/main.sol @@ -0,0 +1 @@ +function f() returns (string) { return "a\q"; } diff --git a/crates/uitest/tests/fixtures/parse/string_bad_escape/main.solc b/crates/uitest/tests/fixtures/parse/string_bad_escape/main.solc deleted file mode 100644 index e5178a88..00000000 --- a/crates/uitest/tests/fixtures/parse/string_bad_escape/main.solc +++ /dev/null @@ -1 +0,0 @@ -function f() -> string { return "a\q"; } diff --git a/crates/uitest/tests/fixtures/parse/top_level_recovery/diagnostics.snap b/crates/uitest/tests/fixtures/parse/top_level_recovery/diagnostics.snap index f66f4ded..30cc2f64 100644 --- a/crates/uitest/tests/fixtures/parse/top_level_recovery/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/top_level_recovery/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/top_level_recovery/main.solc +input_file: crates/uitest/tests/fixtures/parse/top_level_recovery/main.sol --- -error[SC0001]: could not parse top-level item near `unknown nonsense tokens`; expected a declaration starting with `import`, `pragma`, `type`, `data`, `class`, `instance`, `contract`, or `function` - --> /main/main.solc:2:1 +error[SC0001]: could not parse top-level item near `unknown nonsense tokens`; expected a declaration starting with `import`, `pragma`, `type`, `enum`, `trait`, `impl`, `contract`, or `function` + --> /main/main.sol:2:1 | 1 | function first() {} 2 | unknown nonsense tokens diff --git a/crates/uitest/tests/fixtures/parse/top_level_recovery/main.solc b/crates/uitest/tests/fixtures/parse/top_level_recovery/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/top_level_recovery/main.solc rename to crates/uitest/tests/fixtures/parse/top_level_recovery/main.sol diff --git a/crates/uitest/tests/fixtures/parse/trailing_call_comma/diagnostics.snap b/crates/uitest/tests/fixtures/parse/trailing_call_comma/diagnostics.snap index 4786f703..57b8b531 100644 --- a/crates/uitest/tests/fixtures/parse/trailing_call_comma/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/trailing_call_comma/diagnostics.snap @@ -1,23 +1,23 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/trailing_call_comma/main.solc +input_file: crates/uitest/tests/fixtures/parse/trailing_call_comma/main.sol --- -error[SC0001]: parse error: unexpected `return` - --> /main/main.solc:2:24 +error[SC0001]: parse error: unexpected `,` + --> /main/main.sol:2:41 | -1 | function g(x: word) -> word { return x; } -2 | function f() -> word { return g(1,); } - | ^^^^^^ unexpected token +1 | function g(x: word) returns (word) { return x; } +2 | function f() returns (word) { return g(1,); } + | ^ unexpected token | - = note: expecting `!`, `(`, `.`, `@`, `[`, `if`, `lam`, or `~` + = note: expecting `&&`, `&`, `(`, `.`, `?`, `[`, `^`, `|`, or `||` --- error[SC0001]: parse error: unexpected `)` - --> /main/main.solc:2:35 + --> /main/main.sol:2:42 | -1 | function g(x: word) -> word { return x; } -2 | function f() -> word { return g(1,); } - | ^ unexpected token +1 | function g(x: word) returns (word) { return x; } +2 | function f() returns (word) { return g(1,); } + | ^ unexpected token | - = note: expecting `!`, `(`, `.`, `@`, `[`, `if`, `lam`, or `~` + = note: expecting `!`, `(`, `.`, `@`, `[`, `lam`, or `~` diff --git a/crates/uitest/tests/fixtures/parse/trailing_call_comma/main.sol b/crates/uitest/tests/fixtures/parse/trailing_call_comma/main.sol new file mode 100644 index 00000000..9cf897f6 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/trailing_call_comma/main.sol @@ -0,0 +1,2 @@ +function g(x: word) returns (word) { return x; } +function f() returns (word) { return g(1,); } diff --git a/crates/uitest/tests/fixtures/parse/trailing_call_comma/main.solc b/crates/uitest/tests/fixtures/parse/trailing_call_comma/main.solc deleted file mode 100644 index 78e36bd3..00000000 --- a/crates/uitest/tests/fixtures/parse/trailing_call_comma/main.solc +++ /dev/null @@ -1,2 +0,0 @@ -function g(x: word) -> word { return x; } -function f() -> word { return g(1,); } diff --git a/crates/uitest/tests/fixtures/parse/trailing_constructor_comma/diagnostics.snap b/crates/uitest/tests/fixtures/parse/trailing_constructor_comma/diagnostics.snap index 0708b14f..cc363273 100644 --- a/crates/uitest/tests/fixtures/parse/trailing_constructor_comma/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/trailing_constructor_comma/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/trailing_constructor_comma/main.solc +input_file: crates/uitest/tests/fixtures/parse/trailing_constructor_comma/main.sol --- error[SC0001]: parse error: unexpected `)` - --> /main/main.solc:1:17 + --> /main/main.sol:1:17 | -1 | data D = C(word,); +1 | enum D { C(word,) } | ^ unexpected token | = note: expecting type - = note: while parsing data declaration + = note: while parsing enum declaration diff --git a/crates/uitest/tests/fixtures/parse/trailing_constructor_comma/main.sol b/crates/uitest/tests/fixtures/parse/trailing_constructor_comma/main.sol new file mode 100644 index 00000000..033dc120 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/trailing_constructor_comma/main.sol @@ -0,0 +1 @@ +enum D { C(word,) } diff --git a/crates/uitest/tests/fixtures/parse/trailing_constructor_comma/main.solc b/crates/uitest/tests/fixtures/parse/trailing_constructor_comma/main.solc deleted file mode 100644 index 3625006e..00000000 --- a/crates/uitest/tests/fixtures/parse/trailing_constructor_comma/main.solc +++ /dev/null @@ -1 +0,0 @@ -data D = C(word,); diff --git a/crates/uitest/tests/fixtures/parse/trailing_import_comma/diagnostics.snap b/crates/uitest/tests/fixtures/parse/trailing_import_comma/diagnostics.snap deleted file mode 100644 index ae369e1f..00000000 --- a/crates/uitest/tests/fixtures/parse/trailing_import_comma/diagnostics.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: crates/test-utils/src/lib.rs -expression: rendered -input_file: crates/uitest/tests/fixtures/parse/trailing_import_comma/main.solc ---- -error[SC0001]: parse error: unexpected `}` - --> /main/main.solc:1:16 - | -1 | import m.{a, b,}; - | ^ unexpected token - | - = note: expecting `*`, or selector name - = note: while parsing import declaration diff --git a/crates/uitest/tests/fixtures/parse/trailing_import_comma/main.solc b/crates/uitest/tests/fixtures/parse/trailing_import_comma/main.solc deleted file mode 100644 index f0bdad5e..00000000 --- a/crates/uitest/tests/fixtures/parse/trailing_import_comma/main.solc +++ /dev/null @@ -1 +0,0 @@ -import m.{a, b,}; diff --git a/crates/uitest/tests/fixtures/parse/trait_missing_body_brace/diagnostics.snap b/crates/uitest/tests/fixtures/parse/trait_missing_body_brace/diagnostics.snap new file mode 100644 index 00000000..e53ffa9b --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/trait_missing_body_brace/diagnostics.snap @@ -0,0 +1,13 @@ +--- +source: crates/test-utils/src/lib.rs +expression: rendered +input_file: crates/uitest/tests/fixtures/parse/trait_missing_body_brace/main.sol +--- +error[SC0001]: parse error: unexpected end of input + --> /main/main.sol:1:12 + | +1 | trait T + | ^ unexpected token + | + = note: expecting `{` + = note: while parsing trait declaration diff --git a/crates/uitest/tests/fixtures/parse/trait_missing_body_brace/main.sol b/crates/uitest/tests/fixtures/parse/trait_missing_body_brace/main.sol new file mode 100644 index 00000000..a48771e8 --- /dev/null +++ b/crates/uitest/tests/fixtures/parse/trait_missing_body_brace/main.sol @@ -0,0 +1 @@ +trait T diff --git a/crates/uitest/tests/fixtures/parse/type_alias_missing_equals/diagnostics.snap b/crates/uitest/tests/fixtures/parse/type_alias_missing_equals/diagnostics.snap index f13eb5c7..d95091ab 100644 --- a/crates/uitest/tests/fixtures/parse/type_alias_missing_equals/diagnostics.snap +++ b/crates/uitest/tests/fixtures/parse/type_alias_missing_equals/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/parse/type_alias_missing_equals/main.solc +input_file: crates/uitest/tests/fixtures/parse/type_alias_missing_equals/main.sol --- error[SC0001]: parse error: unexpected identifier `U` - --> /main/main.solc:1:13 + --> /main/main.sol:1:13 | 1 | type Amount U; | ^ unexpected token diff --git a/crates/uitest/tests/fixtures/parse/type_alias_missing_equals/main.solc b/crates/uitest/tests/fixtures/parse/type_alias_missing_equals/main.sol similarity index 100% rename from crates/uitest/tests/fixtures/parse/type_alias_missing_equals/main.solc rename to crates/uitest/tests/fixtures/parse/type_alias_missing_equals/main.sol diff --git a/crates/uitest/tests/fixtures/solver/bounded_variable_condition/diagnostics.snap b/crates/uitest/tests/fixtures/solver/bounded_variable_condition/diagnostics.snap index b51fe158..86ed9f22 100644 --- a/crates/uitest/tests/fixtures/solver/bounded_variable_condition/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/bounded_variable_condition/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/bounded_variable_condition/main.solc +input_file: crates/uitest/tests/fixtures/solver/bounded_variable_condition/main.sol --- error[SC0214]: Bounded variable condition fails! - --> /main/main.solc:5:31 + --> /main/main.sol:5:12 | -3 | forall a b . class a:Container(b) {} +3 | trait Container {} 4 | -5 | forall a c . c:Eq => instance Box(a):Container(a) {} - | ^^^^^^^^^^^^^^^^^^^ instance head is missing context variables +5 | impl Container, a> where c: Eq {} + | ^^^^^^^^^^^^^^^^^^^^ impl head is missing context variables diff --git a/crates/uitest/tests/fixtures/solver/bounded_variable_condition/main.sol b/crates/uitest/tests/fixtures/solver/bounded_variable_condition/main.sol new file mode 100644 index 00000000..61c97af8 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/bounded_variable_condition/main.sol @@ -0,0 +1,5 @@ +enum Box { Box(word) } +trait Eq {} +trait Container {} + +impl Container, a> where c: Eq {} diff --git a/crates/uitest/tests/fixtures/solver/bounded_variable_condition/main.solc b/crates/uitest/tests/fixtures/solver/bounded_variable_condition/main.solc deleted file mode 100644 index 43dc0a1f..00000000 --- a/crates/uitest/tests/fixtures/solver/bounded_variable_condition/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Box(a) = Box(word); -forall a . class a:Eq {} -forall a b . class a:Container(b) {} - -forall a c . c:Eq => instance Box(a):Container(a) {} diff --git a/crates/uitest/tests/fixtures/solver/coverage_condition/diagnostics.snap b/crates/uitest/tests/fixtures/solver/coverage_condition/diagnostics.snap index 2d0737c2..5aedac0d 100644 --- a/crates/uitest/tests/fixtures/solver/coverage_condition/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/coverage_condition/diagnostics.snap @@ -1,17 +1,17 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/coverage_condition/main.solc +input_file: crates/uitest/tests/fixtures/solver/coverage_condition/main.sol --- -error[SC0212]: Coverage condition fails for class: +error[SC0212]: Coverage condition fails for trait: MyClass - the type: - Box(a) + Box does not determine: b - --> /main/main.solc:4:23 + --> /main/main.sol:4:12 | -2 | forall a b . class a:MyClass(b) {} +2 | trait MyClass {} 3 | -4 | forall a b . instance Box(a):MyClass(b) {} - | ^^^^^^^^^^^^^^^^^ instance head does not determine these variables +4 | impl MyClass, b> {} + | ^^^^^^^^^^^^^^^^^^ impl head does not determine these variables diff --git a/crates/uitest/tests/fixtures/solver/coverage_condition/main.sol b/crates/uitest/tests/fixtures/solver/coverage_condition/main.sol new file mode 100644 index 00000000..27feb73d --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/coverage_condition/main.sol @@ -0,0 +1,4 @@ +enum Box { Box(word) } +trait MyClass {} + +impl MyClass, b> {} diff --git a/crates/uitest/tests/fixtures/solver/coverage_condition/main.solc b/crates/uitest/tests/fixtures/solver/coverage_condition/main.solc deleted file mode 100644 index 1f8d5cc4..00000000 --- a/crates/uitest/tests/fixtures/solver/coverage_condition/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -data Box(a) = Box(word); -forall a b . class a:MyClass(b) {} - -forall a b . instance Box(a):MyClass(b) {} diff --git a/crates/uitest/tests/fixtures/solver/coverage_condition_alias_expansion/diagnostics.snap b/crates/uitest/tests/fixtures/solver/coverage_condition_alias_expansion/diagnostics.snap index 48886b07..ef4ce899 100644 --- a/crates/uitest/tests/fixtures/solver/coverage_condition_alias_expansion/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/coverage_condition_alias_expansion/diagnostics.snap @@ -1,17 +1,17 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/coverage_condition_alias_expansion/main.solc +input_file: crates/uitest/tests/fixtures/solver/coverage_condition_alias_expansion/main.sol --- -error[SC0212]: Coverage condition fails for class: +error[SC0212]: Coverage condition fails for trait: MyClass - the type: word does not determine: a - --> /main/main.solc:4:21 + --> /main/main.sol:4:9 | -2 | forall a b . class a:MyClass(b) {} +2 | trait MyClass {} 3 | -4 | forall a . instance Phantom(a):MyClass(a) {} - | ^^^^^^^^^^^^^^^^^^^^^ instance head does not determine these variables +4 | impl MyClass, a> {} + | ^^^^^^^^^^^^^^^^^^^^^^ impl head does not determine these variables diff --git a/crates/uitest/tests/fixtures/solver/coverage_condition_alias_expansion/main.sol b/crates/uitest/tests/fixtures/solver/coverage_condition_alias_expansion/main.sol new file mode 100644 index 00000000..31ba7b74 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/coverage_condition_alias_expansion/main.sol @@ -0,0 +1,4 @@ +type Phantom(a) = word; +trait MyClass {} + +impl MyClass, a> {} diff --git a/crates/uitest/tests/fixtures/solver/coverage_condition_alias_expansion/main.solc b/crates/uitest/tests/fixtures/solver/coverage_condition_alias_expansion/main.solc deleted file mode 100644 index 0a9b5c14..00000000 --- a/crates/uitest/tests/fixtures/solver/coverage_condition_alias_expansion/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -type Phantom(a) = word; -forall a b . class a:MyClass(b) {} - -forall a . instance Phantom(a):MyClass(a) {} diff --git a/crates/uitest/tests/fixtures/solver/ergo_ambiguous_defaulting/diagnostics.snap b/crates/uitest/tests/fixtures/solver/ergo_ambiguous_defaulting/diagnostics.snap index 6e11392b..ea4e7a38 100644 --- a/crates/uitest/tests/fixtures/solver/ergo_ambiguous_defaulting/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/ergo_ambiguous_defaulting/diagnostics.snap @@ -1,17 +1,17 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/ergo_ambiguous_defaulting/main.solc +input_file: crates/uitest/tests/fixtures/solver/ergo_ambiguous_defaulting/main.sol --- error[SC0299]: ambiguous inferred type - --> /main/main.solc:28:22 + --> /main/main.sol:30:29 | -27 | -28 | function f() -> word { - | ______________________^ -29 | | return Conv.out(Conv.make(1)); -30 | | } +29 | +30 | function f() returns (word) { + | _____________________________^ +31 | | return Conv.out(Conv.make(1)); +32 | | } | |_^ ambiguous inferred type | - = note: forall _ . _ : Conv => () -> word - = help: add a type annotation or a matching instance to fix the ambiguous type variable + = note: <_> function() returns (word) where _: Conv + = help: add a type annotation or a matching impl to fix the ambiguous type variable diff --git a/crates/uitest/tests/fixtures/solver/ergo_ambiguous_defaulting/main.sol b/crates/uitest/tests/fixtures/solver/ergo_ambiguous_defaulting/main.sol new file mode 100644 index 00000000..f1a502c3 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/ergo_ambiguous_defaulting/main.sol @@ -0,0 +1,32 @@ +enum Wrap { Wrap(word) } + +trait Conv { + function make(x: word) returns (a) ; + function out(y: a) returns (word) ; +} + +impl Conv { + function make(x: word) returns (word) { + return x; + } + function out(y: word) returns (word) { + return y; + } +} + +impl Conv { + function make(x: word) returns (Wrap) { + return Wrap(x); + } + function out(y: Wrap) returns (word) { + match (y) { +case Wrap(w) { +return w; +} +} + } +} + +function f() returns (word) { + return Conv.out(Conv.make(1)); +} diff --git a/crates/uitest/tests/fixtures/solver/ergo_ambiguous_defaulting/main.solc b/crates/uitest/tests/fixtures/solver/ergo_ambiguous_defaulting/main.solc deleted file mode 100644 index 873fad24..00000000 --- a/crates/uitest/tests/fixtures/solver/ergo_ambiguous_defaulting/main.solc +++ /dev/null @@ -1,30 +0,0 @@ -data Wrap = Wrap(word); - -forall a . class a : Conv { - function make(x: word) -> a; - function out(y: a) -> word; -} - -instance word : Conv { - function make(x: word) -> word { - return x; - } - function out(y: word) -> word { - return y; - } -} - -instance Wrap : Conv { - function make(x: word) -> Wrap { - return Wrap(x); - } - function out(y: Wrap) -> word { - match y { - | Wrap(w) => return w; - } - } -} - -function f() -> word { - return Conv.out(Conv.make(1)); -} diff --git a/crates/uitest/tests/fixtures/solver/ergo_constraint_escape/diagnostics.snap b/crates/uitest/tests/fixtures/solver/ergo_constraint_escape/diagnostics.snap index 22116269..44bfb589 100644 --- a/crates/uitest/tests/fixtures/solver/ergo_constraint_escape/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/ergo_constraint_escape/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/ergo_constraint_escape/main.solc +input_file: crates/uitest/tests/fixtures/solver/ergo_constraint_escape/main.sol --- -error[SC0207]: cannot satisfy class constraint: a : Same - --> /main/main.solc:7:8 +error[SC0207]: cannot satisfy trait constraint: a: Same + --> /main/main.sol:7:12 | 6 | -7 | forall a . function f(x: a) -> Bool { - | ^ constraint originates here +7 | function f(x: a) returns (Bool) { + | ^ constraint originates here 8 | return Same.same(x, x); | - = note: no visible instance matches `a : Same` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `a: Same` + = help: add a matching impl or strengthen the surrounding type context diff --git a/crates/uitest/tests/fixtures/solver/ergo_constraint_escape/main.sol b/crates/uitest/tests/fixtures/solver/ergo_constraint_escape/main.sol new file mode 100644 index 00000000..4be339ab --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/ergo_constraint_escape/main.sol @@ -0,0 +1,9 @@ +enum Bool { True, False } + +trait Same { + function same(x: a, y: a) returns (Bool) ; +} + +function f(x: a) returns (Bool) { + return Same.same(x, x); +} diff --git a/crates/uitest/tests/fixtures/solver/ergo_constraint_escape/main.solc b/crates/uitest/tests/fixtures/solver/ergo_constraint_escape/main.solc deleted file mode 100644 index c0e7035f..00000000 --- a/crates/uitest/tests/fixtures/solver/ergo_constraint_escape/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -data Bool = True | False; - -forall a . class a : Same { - function same(x: a, y: a) -> Bool; -} - -forall a . function f(x: a) -> Bool { - return Same.same(x, x); -} diff --git a/crates/uitest/tests/fixtures/solver/ergo_contract_no_instance/diagnostics.snap b/crates/uitest/tests/fixtures/solver/ergo_contract_no_instance/diagnostics.snap index dc40d04e..8b5f836b 100644 --- a/crates/uitest/tests/fixtures/solver/ergo_contract_no_instance/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/ergo_contract_no_instance/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/ergo_contract_no_instance/main.solc +input_file: crates/uitest/tests/fixtures/solver/ergo_contract_no_instance/main.sol --- -error[SC0207]: cannot satisfy class constraint: word : Eq - --> /main/main.solc:9:12 +error[SC0207]: cannot satisfy trait constraint: word: Eq + --> /main/main.sol:9:12 | - 8 | function go(x: word) -> Bool { + 8 | function go(x: word) returns (Bool) { 9 | return Eq.eq(x, x); | ^^^^^^^^^^^ constraint originates here 10 | } | - = note: no visible instance matches `word : Eq` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `word: Eq` + = help: add a matching impl or strengthen the surrounding type context diff --git a/crates/uitest/tests/fixtures/solver/ergo_contract_no_instance/main.sol b/crates/uitest/tests/fixtures/solver/ergo_contract_no_instance/main.sol new file mode 100644 index 00000000..c4c4436c --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/ergo_contract_no_instance/main.sol @@ -0,0 +1,13 @@ +enum Bool { True, False } + +trait Eq { + function eq(x: a, y: a) returns (Bool) ; +} + +contract Check { + function go(x: word) returns (Bool) { + return Eq.eq(x, x); + } + + function main() {} +} diff --git a/crates/uitest/tests/fixtures/solver/ergo_contract_no_instance/main.solc b/crates/uitest/tests/fixtures/solver/ergo_contract_no_instance/main.solc deleted file mode 100644 index 5a2784cf..00000000 --- a/crates/uitest/tests/fixtures/solver/ergo_contract_no_instance/main.solc +++ /dev/null @@ -1,13 +0,0 @@ -data Bool = True | False; - -forall a . class a : Eq { - function eq(x: a, y: a) -> Bool; -} - -contract Check { - function go(x: word) -> Bool { - return Eq.eq(x, x); - } - - function main() -> () {} -} diff --git a/crates/uitest/tests/fixtures/solver/ergo_fuel_blowup/diagnostics.snap b/crates/uitest/tests/fixtures/solver/ergo_fuel_blowup/diagnostics.snap index 0354cfb1..565f04f5 100644 --- a/crates/uitest/tests/fixtures/solver/ergo_fuel_blowup/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/ergo_fuel_blowup/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/ergo_fuel_blowup/main.solc +input_file: crates/uitest/tests/fixtures/solver/ergo_fuel_blowup/main.sol --- -error[SC0209]: cannot solve class constraint `word : C`: solver exceeded its iteration bound - --> /main/main.solc:16:10 +error[SC0209]: cannot solve trait constraint `word: C`: solver exceeded its iteration bound + --> /main/main.sol:16:10 | -15 | function f() -> word { +15 | function f() returns (word) { 16 | return C.c(0); | ^^^^^^ constraint originates here 17 | } | - = help: simplify the instance chain or add a more direct instance + = help: simplify the impl chain or add a more direct impl diff --git a/crates/uitest/tests/fixtures/solver/ergo_fuel_blowup/main.sol b/crates/uitest/tests/fixtures/solver/ergo_fuel_blowup/main.sol new file mode 100644 index 00000000..59e1c010 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/ergo_fuel_blowup/main.sol @@ -0,0 +1,17 @@ +pragma no-patterson-condition ; + +enum Box { MkBox(a) } + +trait C { + function c(x: a) returns (word) ; +} + +impl C where Box: C { + function c(x: a) returns (word) { + return 1; + } +} + +function f() returns (word) { + return C.c(0); +} diff --git a/crates/uitest/tests/fixtures/solver/ergo_fuel_blowup/main.solc b/crates/uitest/tests/fixtures/solver/ergo_fuel_blowup/main.solc deleted file mode 100644 index cfb7df44..00000000 --- a/crates/uitest/tests/fixtures/solver/ergo_fuel_blowup/main.solc +++ /dev/null @@ -1,17 +0,0 @@ -pragma no-patterson-condition ; - -data Box(a) = MkBox(a); - -forall a . class a : C { - function c(x: a) -> word; -} - -forall a . Box(a) : C => instance a : C { - function c(x: a) -> word { - return 1; - } -} - -function f() -> word { - return C.c(0); -} diff --git a/crates/uitest/tests/fixtures/solver/ergo_inst_class_arity/diagnostics.snap b/crates/uitest/tests/fixtures/solver/ergo_inst_class_arity/diagnostics.snap index 5ee9a662..92fce73a 100644 --- a/crates/uitest/tests/fixtures/solver/ergo_inst_class_arity/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/ergo_inst_class_arity/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/ergo_inst_class_arity/main.solc +input_file: crates/uitest/tests/fixtures/solver/ergo_inst_class_arity/main.sol --- -error[SC0217]: class arity mismatch for `Rel`: expected 1, got 0 - --> /main/main.solc:5:10 +error[SC0217]: trait arity mismatch for `Rel`: expected 1, got 0 + --> /main/main.sol:5:6 | 4 | -5 | instance word : Rel { - | ^^^^^^^^^^ class predicate arity mismatch -6 | function rel(x: word, y: word) -> word { +5 | impl Rel { + | ^^^^^^^^^ trait predicate arity mismatch +6 | function rel(x: word, y: word) returns (word) { | diff --git a/crates/uitest/tests/fixtures/solver/ergo_inst_class_arity/main.sol b/crates/uitest/tests/fixtures/solver/ergo_inst_class_arity/main.sol new file mode 100644 index 00000000..f9340fc4 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/ergo_inst_class_arity/main.sol @@ -0,0 +1,9 @@ +trait Rel { + function rel(x: a, y: b) returns (word) ; +} + +impl Rel { + function rel(x: word, y: word) returns (word) { + return 1; + } +} diff --git a/crates/uitest/tests/fixtures/solver/ergo_inst_class_arity/main.solc b/crates/uitest/tests/fixtures/solver/ergo_inst_class_arity/main.solc deleted file mode 100644 index 8f6e8c9f..00000000 --- a/crates/uitest/tests/fixtures/solver/ergo_inst_class_arity/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -forall a b . class a : Rel(b) { - function rel(x: a, y: b) -> word; -} - -instance word : Rel { - function rel(x: word, y: word) -> word { - return 1; - } -} diff --git a/crates/uitest/tests/fixtures/solver/ergo_inst_method_sig_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/solver/ergo_inst_method_sig_mismatch/diagnostics.snap index 04f5de57..abb91d19 100644 --- a/crates/uitest/tests/fixtures/solver/ergo_inst_method_sig_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/ergo_inst_method_sig_mismatch/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/ergo_inst_method_sig_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/solver/ergo_inst_method_sig_mismatch/main.sol --- -error[SC0221]: invalid instance member signature for `size`: expected (Bool) -> word, got (Bool) -> Bool - --> /main/main.solc:8:3 +error[SC0221]: invalid impl member signature for `size`: expected function(Bool) returns (word), got function(Bool) returns (Bool) + --> /main/main.sol:8:3 | -7 | instance Bool : Sz { -8 | function size(x: Bool) -> Bool { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid instance method signature +7 | impl Sz { +8 | function size(x: Bool) returns (Bool) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid impl method signature 9 | return x; | - = note: the instance method must match the class method after substituting the instance head + = note: the impl method must match the trait method after substituting the impl head diff --git a/crates/uitest/tests/fixtures/solver/ergo_inst_method_sig_mismatch/main.sol b/crates/uitest/tests/fixtures/solver/ergo_inst_method_sig_mismatch/main.sol new file mode 100644 index 00000000..0c48e493 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/ergo_inst_method_sig_mismatch/main.sol @@ -0,0 +1,11 @@ +enum Bool { True, False } + +trait Sz { + function size(x: a) returns (word) ; +} + +impl Sz { + function size(x: Bool) returns (Bool) { + return x; + } +} diff --git a/crates/uitest/tests/fixtures/solver/ergo_inst_method_sig_mismatch/main.solc b/crates/uitest/tests/fixtures/solver/ergo_inst_method_sig_mismatch/main.solc deleted file mode 100644 index 0c2ea9e7..00000000 --- a/crates/uitest/tests/fixtures/solver/ergo_inst_method_sig_mismatch/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -data Bool = True | False; - -forall a . class a : Sz { - function size(x: a) -> word; -} - -instance Bool : Sz { - function size(x: Bool) -> Bool { - return x; - } -} diff --git a/crates/uitest/tests/fixtures/solver/ergo_inst_wrong_kind/diagnostics.snap b/crates/uitest/tests/fixtures/solver/ergo_inst_wrong_kind/diagnostics.snap index 59d222f7..d43aeb47 100644 --- a/crates/uitest/tests/fixtures/solver/ergo_inst_wrong_kind/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/ergo_inst_wrong_kind/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/ergo_inst_wrong_kind/main.solc +input_file: crates/uitest/tests/fixtures/solver/ergo_inst_wrong_kind/main.sol --- error[SC0299]: Invalid number of type arguments! - --> /main/main.solc:5:10 + --> /main/main.sol:5:8 | -3 | forall a . class a : C {} +3 | trait C {} 4 | -5 | instance Box : C {} - | ^^^ diagnostic reported here +5 | impl C {} + | ^^^ diagnostic reported here | = note: Type Box is expected to have 1 type arguments = note: but, type Box has 0 arguments diff --git a/crates/uitest/tests/fixtures/solver/ergo_inst_wrong_kind/main.sol b/crates/uitest/tests/fixtures/solver/ergo_inst_wrong_kind/main.sol new file mode 100644 index 00000000..b319980b --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/ergo_inst_wrong_kind/main.sol @@ -0,0 +1,5 @@ +enum Box { Box(a) } + +trait C {} + +impl C {} diff --git a/crates/uitest/tests/fixtures/solver/ergo_inst_wrong_kind/main.solc b/crates/uitest/tests/fixtures/solver/ergo_inst_wrong_kind/main.solc deleted file mode 100644 index a279b387..00000000 --- a/crates/uitest/tests/fixtures/solver/ergo_inst_wrong_kind/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Box(a) = Box(a); - -forall a . class a : C {} - -instance Box : C {} diff --git a/crates/uitest/tests/fixtures/solver/ergo_no_instance/diagnostics.snap b/crates/uitest/tests/fixtures/solver/ergo_no_instance/diagnostics.snap index 45c69a81..8c0353cb 100644 --- a/crates/uitest/tests/fixtures/solver/ergo_no_instance/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/ergo_no_instance/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/ergo_no_instance/main.solc +input_file: crates/uitest/tests/fixtures/solver/ergo_no_instance/main.sol --- -error[SC0207]: cannot satisfy class constraint: Bool : Eq - --> /main/main.solc:14:10 +error[SC0207]: cannot satisfy trait constraint: Bool: Eq + --> /main/main.sol:14:10 | -13 | function f() -> Bool { +13 | function f() returns (Bool) { 14 | return Eq.eq(Bool.True, Bool.False); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constraint originates here 15 | } | - = note: no visible instance matches `Bool : Eq` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `Bool: Eq` + = help: add a matching impl or strengthen the surrounding type context diff --git a/crates/uitest/tests/fixtures/solver/ergo_no_instance/main.sol b/crates/uitest/tests/fixtures/solver/ergo_no_instance/main.sol new file mode 100644 index 00000000..20bf66a8 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/ergo_no_instance/main.sol @@ -0,0 +1,15 @@ +enum Bool { True, False } + +trait Eq { + function eq(x: a, y: a) returns (Bool) ; +} + +impl Eq { + function eq(x: word, y: word) returns (Bool) { + return Bool.True; + } +} + +function f() returns (Bool) { + return Eq.eq(Bool.True, Bool.False); +} diff --git a/crates/uitest/tests/fixtures/solver/ergo_no_instance/main.solc b/crates/uitest/tests/fixtures/solver/ergo_no_instance/main.solc deleted file mode 100644 index 8cf56c6e..00000000 --- a/crates/uitest/tests/fixtures/solver/ergo_no_instance/main.solc +++ /dev/null @@ -1,15 +0,0 @@ -data Bool = True | False; - -forall a . class a : Eq { - function eq(x: a, y: a) -> Bool; -} - -instance word : Eq { - function eq(x: word, y: word) -> Bool { - return Bool.True; - } -} - -function f() -> Bool { - return Eq.eq(Bool.True, Bool.False); -} diff --git a/crates/uitest/tests/fixtures/solver/ergo_overlapping_instances/diagnostics.snap b/crates/uitest/tests/fixtures/solver/ergo_overlapping_instances/diagnostics.snap index 8aa65cbd..e6bbe085 100644 --- a/crates/uitest/tests/fixtures/solver/ergo_overlapping_instances/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/ergo_overlapping_instances/diagnostics.snap @@ -1,22 +1,22 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/ergo_overlapping_instances/main.solc +input_file: crates/uitest/tests/fixtures/solver/ergo_overlapping_instances/main.sol --- -error[SC0218]: Overlapping instances are not supported - instance: - word : C +error[SC0218]: Overlapping impls are not supported + impl: + word: C overlaps with: - word : C - --> /main/main.solc:11:10 + word: C + --> /main/main.sol:11:6 | 4 | - 5 | instance word : C { - | -------- previous overlapping instance - 6 | function c(x: word) -> word { + 5 | impl C { + | ------- previous overlapping impl + 6 | function c(x: word) returns (word) { ... 10 | -11 | instance word : C { - | ^^^^^^^^ overlapping instance -12 | function c(x: word) -> word { +11 | impl C { + | ^^^^^^^ overlapping impl +12 | function c(x: word) returns (word) { | diff --git a/crates/uitest/tests/fixtures/solver/ergo_overlapping_instances/main.sol b/crates/uitest/tests/fixtures/solver/ergo_overlapping_instances/main.sol new file mode 100644 index 00000000..8ed46eb3 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/ergo_overlapping_instances/main.sol @@ -0,0 +1,19 @@ +trait C { + function c(x: a) returns (word) ; +} + +impl C { + function c(x: word) returns (word) { + return 1; + } +} + +impl C { + function c(x: word) returns (word) { + return 2; + } +} + +function f() returns (word) { + return C.c(0); +} diff --git a/crates/uitest/tests/fixtures/solver/ergo_overlapping_instances/main.solc b/crates/uitest/tests/fixtures/solver/ergo_overlapping_instances/main.solc deleted file mode 100644 index 1896cddf..00000000 --- a/crates/uitest/tests/fixtures/solver/ergo_overlapping_instances/main.solc +++ /dev/null @@ -1,19 +0,0 @@ -forall a . class a : C { - function c(x: a) -> word; -} - -instance word : C { - function c(x: word) -> word { - return 1; - } -} - -instance word : C { - function c(x: word) -> word { - return 2; - } -} - -function f() -> word { - return C.c(0); -} diff --git a/crates/uitest/tests/fixtures/solver/ergo_patterson_violation/diagnostics.snap b/crates/uitest/tests/fixtures/solver/ergo_patterson_violation/diagnostics.snap index 6940aedd..9e0d562c 100644 --- a/crates/uitest/tests/fixtures/solver/ergo_patterson_violation/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/ergo_patterson_violation/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/ergo_patterson_violation/main.solc +input_file: crates/uitest/tests/fixtures/solver/ergo_patterson_violation/main.sol --- -error[SC0213]: instance `U : C1` does not satisfy the Patterson conditions - --> /main/main.solc:4:39 +error[SC0213]: impl `U: C1` does not satisfy the Patterson conditions + --> /main/main.sol:4:9 | -2 | forall a . class a : C2 {} +2 | trait C2 {} 3 | -4 | forall U . U : C1, U : C2 => instance U : C1 {} - | ^^^^^^ instance head violates Patterson condition +4 | impl C1 where U: C1, U: C2 {} + | ^^^^^ impl head violates Patterson condition | - = note: each instance context must be structurally smaller than the instance head - = help: remove the recursive context, add a more specific instance, or use the Patterson-condition pragma intentionally + = note: each impl context must be structurally smaller than the impl head + = help: remove the recursive context, add a more specific impl, or use the Patterson-condition pragma intentionally diff --git a/crates/uitest/tests/fixtures/solver/ergo_patterson_violation/main.sol b/crates/uitest/tests/fixtures/solver/ergo_patterson_violation/main.sol new file mode 100644 index 00000000..772f9d7f --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/ergo_patterson_violation/main.sol @@ -0,0 +1,4 @@ +trait C1 {} +trait C2 {} + +impl C1 where U: C1, U: C2 {} diff --git a/crates/uitest/tests/fixtures/solver/ergo_patterson_violation/main.solc b/crates/uitest/tests/fixtures/solver/ergo_patterson_violation/main.solc deleted file mode 100644 index b5689070..00000000 --- a/crates/uitest/tests/fixtures/solver/ergo_patterson_violation/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -forall a . class a : C1 {} -forall a . class a : C2 {} - -forall U . U : C1, U : C2 => instance U : C1 {} diff --git a/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/diagnostics.snap b/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/diagnostics.snap index d5f1cb92..6fcbeab5 100644 --- a/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/diagnostics.snap @@ -1,44 +1,44 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/main.solc +input_file: crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/main.sol --- -error[SC0212]: Coverage condition fails for class: +error[SC0212]: Coverage condition fails for trait: C - the type: - List(b) + List does not determine: a - --> /main/main.solc:7:23 + --> /main/main.sol:7:12 | 6 | -7 | forall a b . instance List(b) : C(a, List(a)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ instance head does not determine these variables -8 | forall x . x:C(word, word) => instance x:C(word, word) {} +7 | impl C, a, List> {} + | ^^^^^^^^^^^^^^^^^^^^^^ impl head does not determine these variables +8 | impl C where x: C {} | --- -error[SC0213]: instance `x : C(word, word)` does not satisfy the Patterson conditions - --> /main/main.solc:8:40 +error[SC0213]: impl `x: C` does not satisfy the Patterson conditions + --> /main/main.sol:8:9 | 6 | -7 | forall a b . instance List(b) : C(a, List(a)) {} -8 | forall x . x:C(word, word) => instance x:C(word, word) {} - | ^^^^^^^^^^^^^^^ instance head violates Patterson condition +7 | impl C, a, List> {} +8 | impl C where x: C {} + | ^^^^^^^^^^^^^^^^ impl head violates Patterson condition | - = note: each instance context must be structurally smaller than the instance head - = help: remove the recursive context, add a more specific instance, or use the Patterson-condition pragma intentionally + = note: each impl context must be structurally smaller than the impl head + = help: remove the recursive context, add a more specific impl, or use the Patterson-condition pragma intentionally --- -error[SC0218]: Overlapping instances are not supported - instance: - x : C(word, word) +error[SC0218]: Overlapping impls are not supported + impl: + x: C overlaps with: - List(_) : C(_, List(_)) - --> /main/main.solc:8:40 + List<_>: C<_, List<_>> + --> /main/main.sol:8:9 | 6 | -7 | forall a b . instance List(b) : C(a, List(a)) {} - | ----------------------- previous overlapping instance -8 | forall x . x:C(word, word) => instance x:C(word, word) {} - | ^^^^^^^^^^^^^^^ overlapping instance +7 | impl C, a, List> {} + | ---------------------- previous overlapping impl +8 | impl C where x: C {} + | ^^^^^^^^^^^^^^^^ overlapping impl diff --git a/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/main.sol b/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/main.sol new file mode 100644 index 00000000..c77fdb30 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/main.sol @@ -0,0 +1,8 @@ +import pragma_scope_lib; + +enum List { Nil, Cons(a, List) } + +trait C {} + +impl C, a, List> {} +impl C where x: C {} diff --git a/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/main.solc b/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/main.solc deleted file mode 100644 index ae9ca254..00000000 --- a/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -import pragma_scope_lib; - -data List(a) = Nil | Cons(a, List(a)); - -forall a b c . class a : C(b, c) {} - -forall a b . instance List(b) : C(a, List(a)) {} -forall x . x:C(word, word) => instance x:C(word, word) {} diff --git a/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/pragma_scope_lib.sol b/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/pragma_scope_lib.sol new file mode 100644 index 00000000..100259b8 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/pragma_scope_lib.sol @@ -0,0 +1,7 @@ +export { helper }; + +pragma no-patterson-condition C; + +function helper() returns (word) { + return 1; +} diff --git a/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/pragma_scope_lib.solc b/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/pragma_scope_lib.solc deleted file mode 100644 index 035f940a..00000000 --- a/crates/uitest/tests/fixtures/solver/imported_pragma_does_not_suppress_local/pragma_scope_lib.solc +++ /dev/null @@ -1,7 +0,0 @@ -export { helper }; - -pragma no-patterson-condition C; - -function helper() -> word { - return 1; -} diff --git a/crates/uitest/tests/fixtures/solver/instance_extra_method/diagnostics.snap b/crates/uitest/tests/fixtures/solver/instance_extra_method/diagnostics.snap index 14353455..dc8f92e4 100644 --- a/crates/uitest/tests/fixtures/solver/instance_extra_method/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/instance_extra_method/diagnostics.snap @@ -1,18 +1,18 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/instance_extra_method/main.solc +input_file: crates/uitest/tests/fixtures/solver/instance_extra_method/main.sol --- error[SC0202]: undefined name: C.g - --> /main/main.solc:7:12 + --> /main/main.sol:7:12 | -1 | forall a . class a : C { - | - class defined here -2 | function f(x: a) -> word; +1 | trait C { + | - trait defined here +2 | function f(x: a) returns (word) ; 3 | } ... -6 | function f(x: word) -> word { return x; } -7 | function g(x: word) -> word { return x; } +6 | function f(x: word) returns (word) { return x; } +7 | function g(x: word) returns (word) { return x; } | ^ unknown name 8 | } | diff --git a/crates/uitest/tests/fixtures/solver/instance_extra_method/main.sol b/crates/uitest/tests/fixtures/solver/instance_extra_method/main.sol new file mode 100644 index 00000000..38f86b23 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/instance_extra_method/main.sol @@ -0,0 +1,8 @@ +trait C { + function f(x: a) returns (word) ; +} + +impl C { + function f(x: word) returns (word) { return x; } + function g(x: word) returns (word) { return x; } +} diff --git a/crates/uitest/tests/fixtures/solver/instance_extra_method/main.solc b/crates/uitest/tests/fixtures/solver/instance_extra_method/main.solc deleted file mode 100644 index 20a5d184..00000000 --- a/crates/uitest/tests/fixtures/solver/instance_extra_method/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -forall a . class a : C { - function f(x: a) -> word; -} - -instance word : C { - function f(x: word) -> word { return x; } - function g(x: word) -> word { return x; } -} diff --git a/crates/uitest/tests/fixtures/solver/invalid_default_instance/diagnostics.snap b/crates/uitest/tests/fixtures/solver/invalid_default_instance/diagnostics.snap index 35ccb7bf..238950db 100644 --- a/crates/uitest/tests/fixtures/solver/invalid_default_instance/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/invalid_default_instance/diagnostics.snap @@ -1,11 +1,11 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/invalid_default_instance/main.solc +input_file: crates/uitest/tests/fixtures/solver/invalid_default_instance/main.sol --- -error[SC0219]: Cannot have a default instance whose main argument contains no type variable: word : C - --> /main/main.solc:2:18 +error[SC0219]: Cannot have a default impl whose main argument contains no type variable: word: C + --> /main/main.sol:2:14 | -1 | forall a . class a:C {} -2 | default instance word:C {} - | ^^^^^^ invalid default instance head +1 | trait C {} +2 | default impl C {} + | ^^^^^^^ invalid default impl head diff --git a/crates/uitest/tests/fixtures/solver/invalid_default_instance/main.sol b/crates/uitest/tests/fixtures/solver/invalid_default_instance/main.sol new file mode 100644 index 00000000..8f959b16 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/invalid_default_instance/main.sol @@ -0,0 +1,2 @@ +trait C {} +default impl C {} diff --git a/crates/uitest/tests/fixtures/solver/invalid_default_instance/main.solc b/crates/uitest/tests/fixtures/solver/invalid_default_instance/main.solc deleted file mode 100644 index 8113191f..00000000 --- a/crates/uitest/tests/fixtures/solver/invalid_default_instance/main.solc +++ /dev/null @@ -1,2 +0,0 @@ -forall a . class a:C {} -default instance word:C {} diff --git a/crates/uitest/tests/fixtures/solver/local_given_rigid_var_unsatisfied/diagnostics.snap b/crates/uitest/tests/fixtures/solver/local_given_rigid_var_unsatisfied/diagnostics.snap index 3921ecd6..280f1502 100644 --- a/crates/uitest/tests/fixtures/solver/local_given_rigid_var_unsatisfied/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/local_given_rigid_var_unsatisfied/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/local_given_rigid_var_unsatisfied/main.solc +input_file: crates/uitest/tests/fixtures/solver/local_given_rigid_var_unsatisfied/main.sol --- -error[SC0207]: cannot satisfy class constraint: word : C - --> /main/main.solc:6:10 +error[SC0207]: cannot satisfy trait constraint: word: C + --> /main/main.sol:6:10 | -5 | forall a . a:C => function bad() -> word { +5 | function bad() returns (word) where a: C { 6 | return C.c(1); | ^^^^^^ constraint originates here 7 | } | - = note: no visible instance matches `word : C` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `word: C` + = help: add a matching impl or strengthen the surrounding type context diff --git a/crates/uitest/tests/fixtures/solver/local_given_rigid_var_unsatisfied/main.sol b/crates/uitest/tests/fixtures/solver/local_given_rigid_var_unsatisfied/main.sol new file mode 100644 index 00000000..9ecfa84b --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/local_given_rigid_var_unsatisfied/main.sol @@ -0,0 +1,7 @@ +trait C { + function c(x: a) returns (word) ; +} + +function bad() returns (word) where a: C { + return C.c(1); +} diff --git a/crates/uitest/tests/fixtures/solver/local_given_rigid_var_unsatisfied/main.solc b/crates/uitest/tests/fixtures/solver/local_given_rigid_var_unsatisfied/main.solc deleted file mode 100644 index 2ce22b22..00000000 --- a/crates/uitest/tests/fixtures/solver/local_given_rigid_var_unsatisfied/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -forall a . class a:C { - function c(x:a) -> word; -} - -forall a . a:C => function bad() -> word { - return C.c(1); -} diff --git a/crates/uitest/tests/fixtures/solver/method_extra_forall/diagnostics.snap b/crates/uitest/tests/fixtures/solver/method_extra_forall/diagnostics.snap index 16d2e3cb..89808cdd 100644 --- a/crates/uitest/tests/fixtures/solver/method_extra_forall/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/method_extra_forall/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/method_extra_forall/main.solc +input_file: crates/uitest/tests/fixtures/solver/method_extra_forall/main.sol --- error[SC0299]: ambiguous inferred type - --> /main/main.solc:7:10 + --> /main/main.sol:7:6 | 6 | -7 | instance word : C { - | ^^^^^^^^ ambiguous inferred type -8 | forall b . b:D => function f(x: word) -> word { return x; } +7 | impl C { + | ^^^^^^^ ambiguous inferred type +8 | function f(x: word) returns (word) where b: D { return x; } | - = note: forall b. b : D => (word) -> word - = help: add a type annotation or a matching instance to fix the ambiguous type variable + = note: function(word) returns (word) where b: D + = help: add a type annotation or a matching impl to fix the ambiguous type variable diff --git a/crates/uitest/tests/fixtures/solver/method_extra_forall/main.sol b/crates/uitest/tests/fixtures/solver/method_extra_forall/main.sol new file mode 100644 index 00000000..526952f7 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/method_extra_forall/main.sol @@ -0,0 +1,9 @@ +trait C { + function f(x: a) returns (word) ; +} + +trait D {} + +impl C { + function f(x: word) returns (word) where b: D { return x; } +} diff --git a/crates/uitest/tests/fixtures/solver/method_extra_forall/main.solc b/crates/uitest/tests/fixtures/solver/method_extra_forall/main.solc deleted file mode 100644 index f64d3892..00000000 --- a/crates/uitest/tests/fixtures/solver/method_extra_forall/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -forall a . class a : C { - function f(x: a) -> word; -} - -forall b . class b : D {} - -instance word : C { - forall b . b:D => function f(x: word) -> word { return x; } -} diff --git a/crates/uitest/tests/fixtures/solver/non_ground_unique_answer/diagnostics.snap b/crates/uitest/tests/fixtures/solver/non_ground_unique_answer/diagnostics.snap index 74a91d29..b3c40b58 100644 --- a/crates/uitest/tests/fixtures/solver/non_ground_unique_answer/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/non_ground_unique_answer/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/non_ground_unique_answer/main.solc +input_file: crates/uitest/tests/fixtures/solver/non_ground_unique_answer/main.sol --- -error[SC0208]: ambiguous class constraint: word : Parent - --> /main/main.solc:12:10 +error[SC0208]: ambiguous trait constraint: word: Parent + --> /main/main.sol:12:10 | -11 | forall unused . function trigger() -> word { +11 | function trigger() returns (word) { 12 | return use(0); | ^^^^^^ ambiguous constraint here 13 | } | = note: the matching proof leaves existential type variables unresolved - = help: make the type more specific or remove overlapping instances + = help: make the type more specific or remove overlapping impls diff --git a/crates/uitest/tests/fixtures/solver/non_ground_unique_answer/main.sol b/crates/uitest/tests/fixtures/solver/non_ground_unique_answer/main.sol new file mode 100644 index 00000000..70c817ae --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/non_ground_unique_answer/main.sol @@ -0,0 +1,17 @@ +pragma no-coverage-condition; + +trait Parent {} +trait Child where a: Parent {} +impl Child {} + +function use(x: a) returns (a) where a: Parent { + return x; +} + +function trigger() returns (word) { + return use(0); +} + +function main() returns (word) { + return 0; +} diff --git a/crates/uitest/tests/fixtures/solver/non_ground_unique_answer/main.solc b/crates/uitest/tests/fixtures/solver/non_ground_unique_answer/main.solc deleted file mode 100644 index 85ad78a1..00000000 --- a/crates/uitest/tests/fixtures/solver/non_ground_unique_answer/main.solc +++ /dev/null @@ -1,17 +0,0 @@ -pragma no-coverage-condition; - -forall a . class a:Parent {} -forall a b . a:Parent => class a:Child(b) {} -forall b . instance word:Child(b) {} - -forall a . a:Parent => function use(x: a) -> a { - return x; -} - -forall unused . function trigger() -> word { - return use(0); -} - -function main() -> word { - return 0; -} diff --git a/crates/uitest/tests/fixtures/solver/noncallable_invokable_constraint/diagnostics.snap b/crates/uitest/tests/fixtures/solver/noncallable_invokable_constraint/diagnostics.snap index f7f0385c..66216b0c 100644 --- a/crates/uitest/tests/fixtures/solver/noncallable_invokable_constraint/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/noncallable_invokable_constraint/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/noncallable_invokable_constraint/main.solc +input_file: crates/uitest/tests/fixtures/solver/noncallable_invokable_constraint/main.sol --- error[SC0206]: non-callable value of type word - --> /main/main.solc:3:10 + --> /main/main.sol:3:10 | 2 | let x : word = 1; 3 | return x(); diff --git a/crates/uitest/tests/fixtures/solver/noncallable_invokable_constraint/main.sol b/crates/uitest/tests/fixtures/solver/noncallable_invokable_constraint/main.sol new file mode 100644 index 00000000..bb19a5e0 --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/noncallable_invokable_constraint/main.sol @@ -0,0 +1,4 @@ +function f() returns (word) { + let x : word = 1; + return x(); +} diff --git a/crates/uitest/tests/fixtures/solver/noncallable_invokable_constraint/main.solc b/crates/uitest/tests/fixtures/solver/noncallable_invokable_constraint/main.solc deleted file mode 100644 index 0f223160..00000000 --- a/crates/uitest/tests/fixtures/solver/noncallable_invokable_constraint/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -function f() -> word { - let x : word = 1; - return x(); -} diff --git a/crates/uitest/tests/fixtures/solver/patterson_condition/diagnostics.snap b/crates/uitest/tests/fixtures/solver/patterson_condition/diagnostics.snap index a911d31f..ab4862e3 100644 --- a/crates/uitest/tests/fixtures/solver/patterson_condition/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/patterson_condition/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/patterson_condition/main.solc +input_file: crates/uitest/tests/fixtures/solver/patterson_condition/main.sol --- -error[SC0213]: instance `U : C1` does not satisfy the Patterson conditions - --> /main/main.solc:4:35 +error[SC0213]: impl `U: C1` does not satisfy the Patterson conditions + --> /main/main.sol:4:9 | -2 | forall a . class a:C2 {} +2 | trait C2 {} 3 | -4 | forall U . U:C1, U:C2 => instance U:C1 {} - | ^^^^ instance head violates Patterson condition +4 | impl C1 where U: C1, U: C2 {} + | ^^^^^ impl head violates Patterson condition | - = note: each instance context must be structurally smaller than the instance head - = help: remove the recursive context, add a more specific instance, or use the Patterson-condition pragma intentionally + = note: each impl context must be structurally smaller than the impl head + = help: remove the recursive context, add a more specific impl, or use the Patterson-condition pragma intentionally diff --git a/crates/uitest/tests/fixtures/solver/patterson_condition/main.sol b/crates/uitest/tests/fixtures/solver/patterson_condition/main.sol new file mode 100644 index 00000000..772f9d7f --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/patterson_condition/main.sol @@ -0,0 +1,4 @@ +trait C1 {} +trait C2 {} + +impl C1 where U: C1, U: C2 {} diff --git a/crates/uitest/tests/fixtures/solver/patterson_condition/main.solc b/crates/uitest/tests/fixtures/solver/patterson_condition/main.solc deleted file mode 100644 index df603eb1..00000000 --- a/crates/uitest/tests/fixtures/solver/patterson_condition/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -forall a . class a:C1 {} -forall a . class a:C2 {} - -forall U . U:C1, U:C2 => instance U:C1 {} diff --git a/crates/uitest/tests/fixtures/solver/poly_int_defaulting/diagnostics.snap b/crates/uitest/tests/fixtures/solver/poly_int_defaulting/diagnostics.snap index 263334a8..26a0e202 100644 --- a/crates/uitest/tests/fixtures/solver/poly_int_defaulting/diagnostics.snap +++ b/crates/uitest/tests/fixtures/solver/poly_int_defaulting/diagnostics.snap @@ -1,18 +1,18 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/solver/poly_int_defaulting/main.solc +input_file: crates/uitest/tests/fixtures/solver/poly_int_defaulting/main.sol --- error[SC0299]: ambiguous inferred type - --> /main/main.solc:5:22 + --> /main/main.sol:5:29 | 4 | -5 | function f() -> word { - | ______________________^ +5 | function f() returns (word) { + | _____________________________^ 6 | | let y = poly(7); 7 | | return 0; 8 | | } | |_^ ambiguous inferred type | - = note: forall _ . _ : Int => () -> word - = help: add a type annotation or a matching instance to fix the ambiguous type variable + = note: <_> function() returns (word) where _: Int + = help: add a type annotation or a matching impl to fix the ambiguous type variable diff --git a/crates/uitest/tests/fixtures/solver/poly_int_defaulting/main.sol b/crates/uitest/tests/fixtures/solver/poly_int_defaulting/main.sol new file mode 100644 index 00000000..5d1bee2b --- /dev/null +++ b/crates/uitest/tests/fixtures/solver/poly_int_defaulting/main.sol @@ -0,0 +1,8 @@ +function poly(x: a) returns (a) where a: Int { + return x; +} + +function f() returns (word) { + let y = poly(7); + return 0; +} diff --git a/crates/uitest/tests/fixtures/solver/poly_int_defaulting/main.solc b/crates/uitest/tests/fixtures/solver/poly_int_defaulting/main.solc deleted file mode 100644 index 1051a71e..00000000 --- a/crates/uitest/tests/fixtures/solver/poly_int_defaulting/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -forall a . a:Int => function poly(x:a) -> a { - return x; -} - -function f() -> word { - let y = poly(7); - return 0; -} diff --git a/crates/uitest/tests/fixtures/specialize/comptime_evaluation_failed/diagnostics.snap b/crates/uitest/tests/fixtures/specialize/comptime_evaluation_failed/diagnostics.snap index 86ea0dfc..fbe0227c 100644 --- a/crates/uitest/tests/fixtures/specialize/comptime_evaluation_failed/diagnostics.snap +++ b/crates/uitest/tests/fixtures/specialize/comptime_evaluation_failed/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/specialize/comptime_evaluation_failed/main.solc +input_file: crates/uitest/tests/fixtures/specialize/comptime_evaluation_failed/main.sol --- error[SC0409]: comptime evaluation failed: comptime let 'y' is bound to a runtime expression - --> /main/main.solc:11:5 + --> /main/main.sol:11:5 | -10 | public function main() -> word { -11 | let y : comptime word = sloadWord(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comptime evaluation failed here +10 | function main() public returns (word) { +11 | let y : comptime = sloadWord(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ comptime evaluation failed here 12 | return y; | diff --git a/crates/uitest/tests/fixtures/specialize/comptime_evaluation_failed/main.sol b/crates/uitest/tests/fixtures/specialize/comptime_evaluation_failed/main.sol new file mode 100644 index 00000000..d705ec2f --- /dev/null +++ b/crates/uitest/tests/fixtures/specialize/comptime_evaluation_failed/main.sol @@ -0,0 +1,14 @@ +function sloadWord() returns (word) { + let v : word; + assembly { + v := sload(0) + } + return v; +} + +contract C { + function main() public returns (word) { + let y : comptime = sloadWord(); + return y; + } +} diff --git a/crates/uitest/tests/fixtures/specialize/comptime_evaluation_failed/main.solc b/crates/uitest/tests/fixtures/specialize/comptime_evaluation_failed/main.solc deleted file mode 100644 index f3d76258..00000000 --- a/crates/uitest/tests/fixtures/specialize/comptime_evaluation_failed/main.solc +++ /dev/null @@ -1,14 +0,0 @@ -function sloadWord() -> word { - let v : word; - assembly { - v := sload(0) - } - return v; -} - -contract C { - public function main() -> word { - let y : comptime word = sloadWord(); - return y; - } -} diff --git a/crates/uitest/tests/fixtures/specialize/comptime_return_evaluation_failed/diagnostics.snap b/crates/uitest/tests/fixtures/specialize/comptime_return_evaluation_failed/diagnostics.snap index 62e09b7e..a010de32 100644 --- a/crates/uitest/tests/fixtures/specialize/comptime_return_evaluation_failed/diagnostics.snap +++ b/crates/uitest/tests/fixtures/specialize/comptime_return_evaluation_failed/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/specialize/comptime_return_evaluation_failed/main.solc +input_file: crates/uitest/tests/fixtures/specialize/comptime_return_evaluation_failed/main.sol --- error[SC0409]: comptime evaluation failed: function annotated '-> comptime' returns a runtime expression - --> /main/main.solc:10:3 + --> /main/main.sol:10:3 | - 9 | function leak(comptime x: word) -> comptime word { + 9 | function leak(comptime x: word) returns (comptime) { 10 | return sloadWord(); | ^^^^^^^^^^^^^^^^^^^ comptime evaluation failed here 11 | } diff --git a/crates/uitest/tests/fixtures/specialize/comptime_return_evaluation_failed/main.sol b/crates/uitest/tests/fixtures/specialize/comptime_return_evaluation_failed/main.sol new file mode 100644 index 00000000..a20c08b7 --- /dev/null +++ b/crates/uitest/tests/fixtures/specialize/comptime_return_evaluation_failed/main.sol @@ -0,0 +1,17 @@ +function sloadWord() returns (word) { + let v : word; + assembly { + v := sload(0) + } + return v; +} + +function leak(comptime x: word) returns (comptime) { + return sloadWord(); +} + +contract C { + function main() public returns (word) { + return leak(1); + } +} diff --git a/crates/uitest/tests/fixtures/specialize/comptime_return_evaluation_failed/main.solc b/crates/uitest/tests/fixtures/specialize/comptime_return_evaluation_failed/main.solc deleted file mode 100644 index 0adcb263..00000000 --- a/crates/uitest/tests/fixtures/specialize/comptime_return_evaluation_failed/main.solc +++ /dev/null @@ -1,17 +0,0 @@ -function sloadWord() -> word { - let v : word; - assembly { - v := sload(0) - } - return v; -} - -function leak(comptime x: word) -> comptime word { - return sloadWord(); -} - -contract C { - public function main() -> word { - return leak(1); - } -} diff --git a/crates/uitest/tests/fixtures/specialize/ergo_ct_public_param/diagnostics.snap b/crates/uitest/tests/fixtures/specialize/ergo_ct_public_param/diagnostics.snap index ccb5e4be..929e04ba 100644 --- a/crates/uitest/tests/fixtures/specialize/ergo_ct_public_param/diagnostics.snap +++ b/crates/uitest/tests/fixtures/specialize/ergo_ct_public_param/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/specialize/ergo_ct_public_param/main.solc +input_file: crates/uitest/tests/fixtures/specialize/ergo_ct_public_param/main.sol --- error[SC0413]: public function `double` cannot take comptime parameter `x` - --> /main/main.solc:9:26 + --> /main/main.sol:9:19 | 8 | contract CtPublicParam { - 9 | public function double(comptime x : word) -> word { - | ^^^^^^^^^^^^^^^^^ public entry parameter is runtime + 9 | function double(comptime x: word) public returns (word) { + | ^^^^^^^^^^^^^^^^ public entry parameter is runtime 10 | return x + x; | = note: public function parameters are supplied from calldata at runtime diff --git a/crates/uitest/tests/fixtures/specialize/ergo_ct_public_param/main.sol b/crates/uitest/tests/fixtures/specialize/ergo_ct_public_param/main.sol new file mode 100644 index 00000000..a9e931d2 --- /dev/null +++ b/crates/uitest/tests/fixtures/specialize/ergo_ct_public_param/main.sol @@ -0,0 +1,12 @@ +// comptime parameter on a *public* contract entry point. Public entry +// arguments come from calldata at runtime, so this can never be satisfied. +// Should be rejected with a clear "public functions cannot take comptime +// parameters" style error. +import * from std; +import * from std.dispatch; + +contract CtPublicParam { + function double(comptime x: word) public returns (word) { + return x + x; + } +} diff --git a/crates/uitest/tests/fixtures/specialize/ergo_ct_public_param/main.solc b/crates/uitest/tests/fixtures/specialize/ergo_ct_public_param/main.solc deleted file mode 100644 index 0bf6b6ae..00000000 --- a/crates/uitest/tests/fixtures/specialize/ergo_ct_public_param/main.solc +++ /dev/null @@ -1,12 +0,0 @@ -// comptime parameter on a *public* contract entry point. Public entry -// arguments come from calldata at runtime, so this can never be satisfied. -// Should be rejected with a clear "public functions cannot take comptime -// parameters" style error. -import std.{*}; -import std.dispatch.{*}; - -contract CtPublicParam { - public function double(comptime x : word) -> word { - return x + x; - } -} diff --git a/crates/uitest/tests/fixtures/specialize/ergo_free_tyvar_ctor/diagnostics.snap b/crates/uitest/tests/fixtures/specialize/ergo_free_tyvar_ctor/diagnostics.snap index 37ee6ce1..8a075634 100644 --- a/crates/uitest/tests/fixtures/specialize/ergo_free_tyvar_ctor/diagnostics.snap +++ b/crates/uitest/tests/fixtures/specialize/ergo_free_tyvar_ctor/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/specialize/ergo_free_tyvar_ctor/main.solc +input_file: crates/uitest/tests/fixtures/specialize/ergo_free_tyvar_ctor/main.sol --- error[SC0401]: cannot specialize expression: unresolved type parameter in Option(_) - --> /main/main.solc:10:13 + --> /main/main.sol:10:13 | - 9 | function main() -> word { + 9 | function main() returns (word) { 10 | let x = Option.None; | ^^^^^^^^^^^ type must be concrete here 11 | return 1; diff --git a/crates/uitest/tests/fixtures/specialize/ergo_free_tyvar_ctor/main.sol b/crates/uitest/tests/fixtures/specialize/ergo_free_tyvar_ctor/main.sol new file mode 100644 index 00000000..4fad5d0f --- /dev/null +++ b/crates/uitest/tests/fixtures/specialize/ergo_free_tyvar_ctor/main.sol @@ -0,0 +1,13 @@ +// Unconstrained constructor: the type argument of Option is never fixed, +// so specialization sees a free type variable. Judge whether the error +// points at `None` and names the type variable usefully. +import std; + +enum Option { None, Some(a) } + +contract FreeTyVarCtor { + function main() returns (word) { + let x = Option.None; + return 1; + } +} diff --git a/crates/uitest/tests/fixtures/specialize/ergo_free_tyvar_ctor/main.solc b/crates/uitest/tests/fixtures/specialize/ergo_free_tyvar_ctor/main.solc deleted file mode 100644 index cf1e3df1..00000000 --- a/crates/uitest/tests/fixtures/specialize/ergo_free_tyvar_ctor/main.solc +++ /dev/null @@ -1,13 +0,0 @@ -// Unconstrained constructor: the type argument of Option is never fixed, -// so specialization sees a free type variable. Judge whether the error -// points at `None` and names the type variable usefully. -import std; - -data Option(a) = None | Some(a); - -contract FreeTyVarCtor { - function main() -> word { - let x = Option.None; - return 1; - } -} diff --git a/crates/uitest/tests/fixtures/specialize/ergo_integer_erasure_branch/diagnostics.snap b/crates/uitest/tests/fixtures/specialize/ergo_integer_erasure_branch/diagnostics.snap index 64d7ea5e..dac8087b 100644 --- a/crates/uitest/tests/fixtures/specialize/ergo_integer_erasure_branch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/specialize/ergo_integer_erasure_branch/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/specialize/ergo_integer_erasure_branch/main.solc +input_file: crates/uitest/tests/fixtures/specialize/ergo_integer_erasure_branch/main.sol --- error[SC0401]: cannot specialize expression: type is not concrete - --> /main/main.solc:15:13 + --> /main/main.sol:15:13 | 14 | let b : Box = Box.MkBox(1); 15 | if (v > 0) { diff --git a/crates/uitest/tests/fixtures/specialize/ergo_integer_erasure_branch/main.sol b/crates/uitest/tests/fixtures/specialize/ergo_integer_erasure_branch/main.sol new file mode 100644 index 00000000..ef0eaf15 --- /dev/null +++ b/crates/uitest/tests/fixtures/specialize/ergo_integer_erasure_branch/main.sol @@ -0,0 +1,24 @@ +// integer value that survives to runtime because it is chosen by a runtime +// branch: the comptime evaluator cannot fold sload, so the integer inside +// Box cannot be erased. Judge cascade volume and span quality. +import std; + +enum Box { MkBox(integer) } + +contract IntegerEscapesBranch { + function main() returns (word) { + let v : word; + assembly { + v := sload(0) + } + let b : Box = Box.MkBox(1); + if (v > 0) { + b = Box.MkBox(2); + } + match (b) { +case Box.MkBox(i) { +return wordFromInteger(i); +} +} + } +} diff --git a/crates/uitest/tests/fixtures/specialize/ergo_integer_erasure_branch/main.solc b/crates/uitest/tests/fixtures/specialize/ergo_integer_erasure_branch/main.solc deleted file mode 100644 index f23c4beb..00000000 --- a/crates/uitest/tests/fixtures/specialize/ergo_integer_erasure_branch/main.solc +++ /dev/null @@ -1,22 +0,0 @@ -// integer value that survives to runtime because it is chosen by a runtime -// branch: the comptime evaluator cannot fold sload, so the integer inside -// Box cannot be erased. Judge cascade volume and span quality. -import std; - -data Box = MkBox(integer); - -contract IntegerEscapesBranch { - function main() -> word { - let v : word; - assembly { - v := sload(0) - } - let b : Box = Box.MkBox(1); - if (v > 0) { - b = Box.MkBox(2); - } - match b { - | Box.MkBox(i) => return wordFromInteger(i); - } - } -} diff --git a/crates/uitest/tests/fixtures/specialize/ergo_poly_entry/diagnostics.snap b/crates/uitest/tests/fixtures/specialize/ergo_poly_entry/diagnostics.snap index 2ea6e4b4..2b2ae0e2 100644 --- a/crates/uitest/tests/fixtures/specialize/ergo_poly_entry/diagnostics.snap +++ b/crates/uitest/tests/fixtures/specialize/ergo_poly_entry/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/specialize/ergo_poly_entry/main.solc +input_file: crates/uitest/tests/fixtures/specialize/ergo_poly_entry/main.sol --- error[SC0401]: entry point must have a concrete, non-polymorphic type before specialization - --> /main/main.solc:5:1 + --> /main/main.sol:5:1 | 4 | -5 | / forall a . function main(x : a) -> a { +5 | / function main(x: a) returns (a) { 6 | | return x; 7 | | } | |_^ type must be concrete here diff --git a/crates/uitest/tests/fixtures/specialize/ergo_poly_entry/main.sol b/crates/uitest/tests/fixtures/specialize/ergo_poly_entry/main.sol new file mode 100644 index 00000000..cc064337 --- /dev/null +++ b/crates/uitest/tests/fixtures/specialize/ergo_poly_entry/main.sol @@ -0,0 +1,7 @@ +// Entry point whose type never becomes ground: `main` is polymorphic and is +// the specialization root (no contract), so ensure_closed fails with +// context "entry specialization". Judge the phrasing of that message. + +function main(x: a) returns (a) { + return x; +} diff --git a/crates/uitest/tests/fixtures/specialize/ergo_poly_entry/main.solc b/crates/uitest/tests/fixtures/specialize/ergo_poly_entry/main.solc deleted file mode 100644 index 0035c63c..00000000 --- a/crates/uitest/tests/fixtures/specialize/ergo_poly_entry/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -// Entry point whose type never becomes ground: `main` is polymorphic and is -// the specialization root (no contract), so ensure_closed fails with -// context "entry specialization". Judge the phrasing of that message. - -forall a . function main(x : a) -> a { - return x; -} diff --git a/crates/uitest/tests/fixtures/specialize/free_type_variable/diagnostics.snap b/crates/uitest/tests/fixtures/specialize/free_type_variable/diagnostics.snap index 00990fe9..cb45eaad 100644 --- a/crates/uitest/tests/fixtures/specialize/free_type_variable/diagnostics.snap +++ b/crates/uitest/tests/fixtures/specialize/free_type_variable/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/specialize/free_type_variable/main.solc +input_file: crates/uitest/tests/fixtures/specialize/free_type_variable/main.sol --- error[SC0401]: cannot specialize expression: type is not concrete - --> /main/main.solc:8:13 + --> /main/main.sol:8:13 | -7 | public function main() -> () { +7 | function main() public { 8 | let x = leak(); | ^^^^^^ type must be concrete here 9 | return (); diff --git a/crates/uitest/tests/fixtures/specialize/free_type_variable/main.sol b/crates/uitest/tests/fixtures/specialize/free_type_variable/main.sol new file mode 100644 index 00000000..45f2b1a1 --- /dev/null +++ b/crates/uitest/tests/fixtures/specialize/free_type_variable/main.sol @@ -0,0 +1,11 @@ +function leak() returns (a) { + let y : a; + return y; +} + +contract C { + function main() public { + let x = leak(); + return (); + } +} diff --git a/crates/uitest/tests/fixtures/specialize/free_type_variable/main.solc b/crates/uitest/tests/fixtures/specialize/free_type_variable/main.solc deleted file mode 100644 index 7ee19421..00000000 --- a/crates/uitest/tests/fixtures/specialize/free_type_variable/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -forall a . function leak() -> a { - let y : a; - return y; -} - -contract C { - public function main() -> () { - let x = leak(); - return (); - } -} diff --git a/crates/uitest/tests/fixtures/specialize/integer_erasure/diagnostics.snap b/crates/uitest/tests/fixtures/specialize/integer_erasure/diagnostics.snap index 6870eb9c..15245fea 100644 --- a/crates/uitest/tests/fixtures/specialize/integer_erasure/diagnostics.snap +++ b/crates/uitest/tests/fixtures/specialize/integer_erasure/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/specialize/integer_erasure/main.solc +input_file: crates/uitest/tests/fixtures/specialize/integer_erasure/main.sol --- error[SC0411]: runtime lowering cannot represent `integer` in return type of `main` - --> /main/main.solc:2:3 + --> /main/main.sol:2:3 | 1 | contract C { -2 | / public function main() -> integer { +2 | / function main() public returns (integer) { 3 | | return 1; 4 | | } | |___^ not representable at runtime diff --git a/crates/uitest/tests/fixtures/specialize/integer_erasure/main.sol b/crates/uitest/tests/fixtures/specialize/integer_erasure/main.sol new file mode 100644 index 00000000..74f1f2d9 --- /dev/null +++ b/crates/uitest/tests/fixtures/specialize/integer_erasure/main.sol @@ -0,0 +1,5 @@ +contract C { + function main() public returns (integer) { + return 1; + } +} diff --git a/crates/uitest/tests/fixtures/specialize/integer_erasure/main.solc b/crates/uitest/tests/fixtures/specialize/integer_erasure/main.solc deleted file mode 100644 index 09f639c8..00000000 --- a/crates/uitest/tests/fixtures/specialize/integer_erasure/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -contract C { - public function main() -> integer { - return 1; - } -} diff --git a/crates/uitest/tests/fixtures/specialize/non_comptime_unconditional_recursion/diagnostics.snap b/crates/uitest/tests/fixtures/specialize/non_comptime_unconditional_recursion/diagnostics.snap index 21245e69..4479d9f7 100644 --- a/crates/uitest/tests/fixtures/specialize/non_comptime_unconditional_recursion/diagnostics.snap +++ b/crates/uitest/tests/fixtures/specialize/non_comptime_unconditional_recursion/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/specialize/non_comptime_unconditional_recursion/main.solc +input_file: crates/uitest/tests/fixtures/specialize/non_comptime_unconditional_recursion/main.sol --- error[SC0414]: `id` cannot be reduced at compile time: recursive calls form a cycle with no base case (infinite recursion) - --> /main/main.solc:4:48 + --> /main/main.sol:4:55 | -3 | contract Answer { function main() -> word { return id(0); } -4 | public function id(x: word) -> word { return id(x); } - | ^^^^^ recursive call cannot be reduced here +3 | contract Answer { function main() returns (word) { return id(0); } +4 | function id(x: word) public returns (word) { return id(x); } + | ^^^^^ recursive call cannot be reduced here 5 | } | = note: help: add a base case, or guard the recursive call behind a runtime condition so it compiles to a runtime call diff --git a/crates/uitest/tests/fixtures/specialize/non_comptime_unconditional_recursion/main.sol b/crates/uitest/tests/fixtures/specialize/non_comptime_unconditional_recursion/main.sol new file mode 100644 index 00000000..41d54c43 --- /dev/null +++ b/crates/uitest/tests/fixtures/specialize/non_comptime_unconditional_recursion/main.sol @@ -0,0 +1,5 @@ +function id(x: word) returns (word) { return x; } + +contract Answer { function main() returns (word) { return id(0); } + function id(x: word) public returns (word) { return id(x); } +} diff --git a/crates/uitest/tests/fixtures/specialize/non_comptime_unconditional_recursion/main.solc b/crates/uitest/tests/fixtures/specialize/non_comptime_unconditional_recursion/main.solc deleted file mode 100644 index e31178f5..00000000 --- a/crates/uitest/tests/fixtures/specialize/non_comptime_unconditional_recursion/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -function id(x: word) -> word { return x; } - -contract Answer { function main() -> word { return id(0); } - public function id(x: word) -> word { return id(x); } -} diff --git a/crates/uitest/tests/fixtures/specialize/polyrec_type_size_fuel/diagnostics.snap b/crates/uitest/tests/fixtures/specialize/polyrec_type_size_fuel/diagnostics.snap index 29b632d4..e42061e8 100644 --- a/crates/uitest/tests/fixtures/specialize/polyrec_type_size_fuel/diagnostics.snap +++ b/crates/uitest/tests/fixtures/specialize/polyrec_type_size_fuel/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/specialize/polyrec_type_size_fuel/main.solc +input_file: crates/uitest/tests/fixtures/specialize/polyrec_type_size_fuel/main.sol --- error[SC0412]: specialization type size exceeded at 4096 type nodes - --> /main/main.solc:2:10 + --> /main/main.sol:2:10 | -1 | forall a . function go(x: a) -> word { +1 | function go(x: a) returns (word) { 2 | return go((x, x)); | ^^^^^^^^^^ specialization type size limit reached here 3 | } diff --git a/crates/uitest/tests/fixtures/specialize/polyrec_type_size_fuel/main.sol b/crates/uitest/tests/fixtures/specialize/polyrec_type_size_fuel/main.sol new file mode 100644 index 00000000..eb6e49b3 --- /dev/null +++ b/crates/uitest/tests/fixtures/specialize/polyrec_type_size_fuel/main.sol @@ -0,0 +1,9 @@ +function go(x: a) returns (word) { + return go((x, x)); +} + +contract C { + function main(x: word) public returns (word) { + return go(x); + } +} diff --git a/crates/uitest/tests/fixtures/specialize/polyrec_type_size_fuel/main.solc b/crates/uitest/tests/fixtures/specialize/polyrec_type_size_fuel/main.solc deleted file mode 100644 index 0b0948e3..00000000 --- a/crates/uitest/tests/fixtures/specialize/polyrec_type_size_fuel/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -forall a . function go(x: a) -> word { - return go((x, x)); -} - -contract C { - public function main(x: word) -> word { - return go(x); - } -} diff --git a/crates/uitest/tests/fixtures/typeck/audit_class_as_type_lowering/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/audit_class_as_type_lowering/diagnostics.snap index 72c069dc..808d823e 100644 --- a/crates/uitest/tests/fixtures/typeck/audit_class_as_type_lowering/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/audit_class_as_type_lowering/diagnostics.snap @@ -1,23 +1,23 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/audit_class_as_type_lowering/main.solc +input_file: crates/uitest/tests/fixtures/typeck/audit_class_as_type_lowering/main.sol --- -error[SC0229]: class name used as type: `C` - --> /main/main.solc:4:10 +error[SC0229]: trait name used as type: `C` + --> /main/main.sol:4:10 | -3 | function class_annotation() -> word { +3 | function class_annotation() returns (word) { 4 | let x: C; - | ^ class is not a type + | ^ trait is not a type 5 | return 0; | --- -error[SC0229]: class name used as type: `Int` - --> /main/main.solc:9:10 +error[SC0229]: trait name used as type: `Int` + --> /main/main.sol:9:10 | - 8 | function builtin_class_annotation() -> word { + 8 | function builtin_class_annotation() returns (word) { 9 | let x: Int = 1; - | ^^^ class is not a type + | ^^^ trait is not a type 10 | return x; | diff --git a/crates/uitest/tests/fixtures/typeck/audit_class_as_type_lowering/main.sol b/crates/uitest/tests/fixtures/typeck/audit_class_as_type_lowering/main.sol new file mode 100644 index 00000000..bda4ce80 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/audit_class_as_type_lowering/main.sol @@ -0,0 +1,11 @@ +trait C {} + +function class_annotation() returns (word) { + let x: C; + return 0; +} + +function builtin_class_annotation() returns (word) { + let x: Int = 1; + return x; +} diff --git a/crates/uitest/tests/fixtures/typeck/audit_class_as_type_lowering/main.solc b/crates/uitest/tests/fixtures/typeck/audit_class_as_type_lowering/main.solc deleted file mode 100644 index b5373087..00000000 --- a/crates/uitest/tests/fixtures/typeck/audit_class_as_type_lowering/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -forall a . class a:C {} - -function class_annotation() -> word { - let x: C; - return 0; -} - -function builtin_class_annotation() -> word { - let x: Int = 1; - return x; -} diff --git a/crates/uitest/tests/fixtures/typeck/audit_ctor_arity_none/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/audit_ctor_arity_none/diagnostics.snap index d38f72ac..2927f527 100644 --- a/crates/uitest/tests/fixtures/typeck/audit_ctor_arity_none/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/audit_ctor_arity_none/diagnostics.snap @@ -1,19 +1,19 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/audit_ctor_arity_none/main.solc +input_file: crates/uitest/tests/fixtures/typeck/audit_ctor_arity_none/main.sol --- error[SC0203]: constructor expects 0 arguments, but 1 was provided - --> /main/main.solc:4:10 + --> /main/main.sol:4:10 | -1 | data Opt = Some(word) | None; - | ---- `None` defined here +1 | enum Opt { Some(word), None } + | ---- `None` defined here 2 | -3 | function f() -> Opt { +3 | function f() returns (Opt) { 4 | return Opt.None(1); | ^^^^^^^^^^^ wrong number of arguments 5 | } | = note: expected 0 arguments = note: found 1 argument - = note: `None` has signature `None() -> Opt` + = note: `None` has signature `None() returns (Opt)` diff --git a/crates/uitest/tests/fixtures/typeck/audit_ctor_arity_none/main.sol b/crates/uitest/tests/fixtures/typeck/audit_ctor_arity_none/main.sol new file mode 100644 index 00000000..43a3ad50 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/audit_ctor_arity_none/main.sol @@ -0,0 +1,5 @@ +enum Opt { Some(word), None } + +function f() returns (Opt) { + return Opt.None(1); +} diff --git a/crates/uitest/tests/fixtures/typeck/audit_ctor_arity_none/main.solc b/crates/uitest/tests/fixtures/typeck/audit_ctor_arity_none/main.solc deleted file mode 100644 index a2ed2b1e..00000000 --- a/crates/uitest/tests/fixtures/typeck/audit_ctor_arity_none/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Opt = Some(word) | None; - -function f() -> Opt { - return Opt.None(1); -} diff --git a/crates/uitest/tests/fixtures/typeck/audit_literal_concrete_matrix/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/audit_literal_concrete_matrix/diagnostics.snap index 38ebfe9c..fd37c451 100644 --- a/crates/uitest/tests/fixtures/typeck/audit_literal_concrete_matrix/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/audit_literal_concrete_matrix/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/audit_literal_concrete_matrix/main.solc +input_file: crates/uitest/tests/fixtures/typeck/audit_literal_concrete_matrix/main.sol --- error[SC0201]: type mismatch: expected numeric, found Opt - --> /main/main.solc:5:10 + --> /main/main.sol:5:10 | -4 | function opt_ret() -> Opt { +4 | function opt_ret() returns (Opt) { 5 | return 1; | ^ expression has mismatched type 6 | } @@ -16,9 +16,9 @@ error[SC0201]: type mismatch: expected numeric, found Opt --- error[SC0201]: type mismatch: expected numeric, found bool - --> /main/main.solc:9:10 + --> /main/main.sol:9:10 | - 8 | function bool_ret() -> bool { + 8 | function bool_ret() returns (bool) { 9 | return 1; | ^ expression has mismatched type 10 | } @@ -28,34 +28,34 @@ error[SC0201]: type mismatch: expected numeric, found bool --- error[SC0103]: undefined type constructor: string - --> /main/main.solc:12:26 + --> /main/main.sol:12:32 | 11 | -12 | function string_ret() -> string { - | ^^^^^^ undefined type constructor +12 | function string_ret() returns (string) { + | ^^^^^^ undefined type constructor 13 | return 1; | --- error[SC0299]: ambiguous inferred type - --> /main/main.solc:12:33 + --> /main/main.sol:12:40 | 11 | -12 | function string_ret() -> string { - | _________________________________^ +12 | function string_ret() returns (string) { + | ________________________________________^ 13 | | return 1; 14 | | } | |_^ ambiguous inferred type 15 | | - = note: forall _ . _ : Int => () -> - = help: add a type annotation or a matching instance to fix the ambiguous type variable + = note: <_> function() returns () where _: Int + = help: add a type annotation or a matching impl to fix the ambiguous type variable --- error[SC0201]: type mismatch: expected numeric, found () - --> /main/main.solc:17:10 + --> /main/main.sol:17:10 | -16 | function unit_ret() -> () { +16 | function unit_ret() { 17 | return 1; | ^ expression has mismatched type 18 | } @@ -65,9 +65,9 @@ error[SC0201]: type mismatch: expected numeric, found () --- error[SC0201]: type mismatch: expected numeric, found K - --> /main/main.solc:21:10 + --> /main/main.sol:21:10 | -20 | function contract_ret() -> K { +20 | function contract_ret() returns (K) { 21 | return 1; | ^ expression has mismatched type 22 | } @@ -76,34 +76,34 @@ error[SC0201]: type mismatch: expected numeric, found K = note: found type: K --- -error[SC0201]: type mismatch: expected numeric, found pair(word, word) - --> /main/main.solc:25:10 +error[SC0201]: type mismatch: expected numeric, found pair + --> /main/main.sol:25:10 | -24 | function pair_ret() -> pair(word, word) { +24 | function pair_ret() returns (pair) { 25 | return 1; | ^ expression has mismatched type 26 | } | = note: expected type: numeric - = note: found type: pair(word, word) + = note: found type: pair --- -error[SC0201]: type mismatch: expected numeric, found sum(word, word) - --> /main/main.solc:29:10 +error[SC0201]: type mismatch: expected numeric, found sum + --> /main/main.sol:29:10 | -28 | function sum_ret() -> sum(word, word) { +28 | function sum_ret() returns (sum) { 29 | return 1; | ^ expression has mismatched type 30 | } | = note: expected type: numeric - = note: found type: sum(word, word) + = note: found type: sum --- error[SC0201]: type mismatch: expected numeric, found (word, word) - --> /main/main.solc:33:10 + --> /main/main.sol:33:10 | -32 | function tuple_ret() -> (word, word) { +32 | function tuple_ret() returns (word, word) { 33 | return 1; | ^ expression has mismatched type 34 | } @@ -112,13 +112,13 @@ error[SC0201]: type mismatch: expected numeric, found (word, word) = note: found type: (word, word) --- -error[SC0201]: type mismatch: expected numeric, found () -> word - --> /main/main.solc:37:10 +error[SC0201]: type mismatch: expected numeric, found function() returns (word) + --> /main/main.sol:37:10 | -36 | function function_ret() -> () -> word { +36 | function function_ret() returns (function() returns (word)) { 37 | return 1; | ^ expression has mismatched type 38 | } | = note: expected type: numeric - = note: found type: () -> word + = note: found type: function() returns (word) diff --git a/crates/uitest/tests/fixtures/typeck/audit_literal_concrete_matrix/main.sol b/crates/uitest/tests/fixtures/typeck/audit_literal_concrete_matrix/main.sol new file mode 100644 index 00000000..0916de9e --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/audit_literal_concrete_matrix/main.sol @@ -0,0 +1,38 @@ +enum Opt { Some(word), None } +contract K { function main() returns (word) { return 0; } } + +function opt_ret() returns (Opt) { + return 1; +} + +function bool_ret() returns (bool) { + return 1; +} + +function string_ret() returns (string) { + return 1; +} + +function unit_ret() { + return 1; +} + +function contract_ret() returns (K) { + return 1; +} + +function pair_ret() returns (pair) { + return 1; +} + +function sum_ret() returns (sum) { + return 1; +} + +function tuple_ret() returns (word, word) { + return 1; +} + +function function_ret() returns (function() returns (word)) { + return 1; +} diff --git a/crates/uitest/tests/fixtures/typeck/audit_literal_concrete_matrix/main.solc b/crates/uitest/tests/fixtures/typeck/audit_literal_concrete_matrix/main.solc deleted file mode 100644 index 956ee781..00000000 --- a/crates/uitest/tests/fixtures/typeck/audit_literal_concrete_matrix/main.solc +++ /dev/null @@ -1,38 +0,0 @@ -data Opt = Some(word) | None; -contract K { function main() -> word { return 0; } } - -function opt_ret() -> Opt { - return 1; -} - -function bool_ret() -> bool { - return 1; -} - -function string_ret() -> string { - return 1; -} - -function unit_ret() -> () { - return 1; -} - -function contract_ret() -> K { - return 1; -} - -function pair_ret() -> pair(word, word) { - return 1; -} - -function sum_ret() -> sum(word, word) { - return 1; -} - -function tuple_ret() -> (word, word) { - return 1; -} - -function function_ret() -> () -> word { - return 1; -} diff --git a/crates/uitest/tests/fixtures/typeck/audit_literal_vs_opt/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/audit_literal_vs_opt/diagnostics.snap index 5264f289..07f3b85e 100644 --- a/crates/uitest/tests/fixtures/typeck/audit_literal_vs_opt/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/audit_literal_vs_opt/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/audit_literal_vs_opt/main.solc +input_file: crates/uitest/tests/fixtures/typeck/audit_literal_vs_opt/main.sol --- error[SC0201]: type mismatch: expected numeric, found Opt - --> /main/main.solc:4:10 + --> /main/main.sol:4:10 | -3 | function f() -> Opt { +3 | function f() returns (Opt) { 4 | return 1; | ^ expression has mismatched type 5 | } diff --git a/crates/uitest/tests/fixtures/typeck/audit_literal_vs_opt/main.sol b/crates/uitest/tests/fixtures/typeck/audit_literal_vs_opt/main.sol new file mode 100644 index 00000000..1f6f82e8 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/audit_literal_vs_opt/main.sol @@ -0,0 +1,5 @@ +enum Opt { Some(word), None } + +function f() returns (Opt) { + return 1; +} diff --git a/crates/uitest/tests/fixtures/typeck/audit_literal_vs_opt/main.solc b/crates/uitest/tests/fixtures/typeck/audit_literal_vs_opt/main.solc deleted file mode 100644 index 144221a0..00000000 --- a/crates/uitest/tests/fixtures/typeck/audit_literal_vs_opt/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Opt = Some(word) | None; - -function f() -> Opt { - return 1; -} diff --git a/crates/uitest/tests/fixtures/typeck/audit_obligation_classification/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/audit_obligation_classification/diagnostics.snap index cba4e522..88ff553c 100644 --- a/crates/uitest/tests/fixtures/typeck/audit_obligation_classification/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/audit_obligation_classification/diagnostics.snap @@ -1,22 +1,22 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/audit_obligation_classification/main.solc +input_file: crates/uitest/tests/fixtures/typeck/audit_obligation_classification/main.sol --- -error[SC0201]: type mismatch: expected numeric, found () -> word - --> /main/main.solc:2:10 +error[SC0201]: type mismatch: expected numeric, found function() returns (word) + --> /main/main.sol:2:10 | -1 | function literal_as_callee() -> word { +1 | function literal_as_callee() returns (word) { 2 | return 1(); | ^ expression has mismatched type 3 | } | = note: expected type: numeric - = note: found type: () -> word + = note: found type: function() returns (word) --- error[SC0206]: non-callable value of type word - --> /main/main.solc:7:10 + --> /main/main.sol:7:10 | 6 | let x: word; 7 | return x(); @@ -26,25 +26,25 @@ error[SC0206]: non-callable value of type word --- error[SC0201]: argument type mismatch in call to `fromInteger` - --> /main/main.solc:11:26 + --> /main/main.sol:11:26 | -10 | function from_integer_bad_arg() -> word { +10 | function from_integer_bad_arg() returns (word) { 11 | return Int.fromInteger(true); | ^^^^ argument has mismatched type 12 | } | = note: expected `integer` because parameter 1 of `fromInteger` has type `integer` = note: found type: bool - = note: `fromInteger` has signature `fromInteger(integer) -> _` + = note: `fromInteger` has signature `fromInteger(integer) returns (_)` --- -error[SC0207]: cannot satisfy class constraint: a : invokable((), word) - --> /main/main.solc:14:8 +error[SC0207]: cannot satisfy trait constraint: a: invokable<(), word> + --> /main/main.sol:14:25 | 13 | -14 | forall a . function open_invokable(x: a) -> word { - | ^ constraint originates here +14 | function open_invokable(x: a) returns (word) { + | ^ constraint originates here 15 | return invoke(x, ()); | - = note: no visible instance matches `a : invokable((), word)` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `a: invokable<(), word>` + = help: add a matching impl or strengthen the surrounding type context diff --git a/crates/uitest/tests/fixtures/typeck/audit_obligation_classification/main.sol b/crates/uitest/tests/fixtures/typeck/audit_obligation_classification/main.sol new file mode 100644 index 00000000..e1282080 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/audit_obligation_classification/main.sol @@ -0,0 +1,16 @@ +function literal_as_callee() returns (word) { + return 1(); +} + +function word_as_callee() returns (word) { + let x: word; + return x(); +} + +function from_integer_bad_arg() returns (word) { + return Int.fromInteger(true); +} + +function open_invokable(x: a) returns (word) { + return invoke(x, ()); +} diff --git a/crates/uitest/tests/fixtures/typeck/audit_obligation_classification/main.solc b/crates/uitest/tests/fixtures/typeck/audit_obligation_classification/main.solc deleted file mode 100644 index 0e19263c..00000000 --- a/crates/uitest/tests/fixtures/typeck/audit_obligation_classification/main.solc +++ /dev/null @@ -1,16 +0,0 @@ -function literal_as_callee() -> word { - return 1(); -} - -function word_as_callee() -> word { - let x: word; - return x(); -} - -function from_integer_bad_arg() -> word { - return Int.fromInteger(true); -} - -forall a . function open_invokable(x: a) -> word { - return invoke(x, ()); -} diff --git a/crates/uitest/tests/fixtures/typeck/audit_return_type_name/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/audit_return_type_name/diagnostics.snap index 331459d1..685df53f 100644 --- a/crates/uitest/tests/fixtures/typeck/audit_return_type_name/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/audit_return_type_name/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/audit_return_type_name/main.solc +input_file: crates/uitest/tests/fixtures/typeck/audit_return_type_name/main.sol --- error[SC0228]: type name used as value: `Opt` - --> /main/main.solc:4:10 + --> /main/main.sol:4:10 | -3 | function f() -> Opt { +3 | function f() returns (Opt) { 4 | return Opt; | ^^^ not a value 5 | } diff --git a/crates/uitest/tests/fixtures/typeck/audit_return_type_name/main.sol b/crates/uitest/tests/fixtures/typeck/audit_return_type_name/main.sol new file mode 100644 index 00000000..03576ac9 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/audit_return_type_name/main.sol @@ -0,0 +1,5 @@ +enum Opt { Some(word), None } + +function f() returns (Opt) { + return Opt; +} diff --git a/crates/uitest/tests/fixtures/typeck/audit_return_type_name/main.solc b/crates/uitest/tests/fixtures/typeck/audit_return_type_name/main.solc deleted file mode 100644 index 2a916f46..00000000 --- a/crates/uitest/tests/fixtures/typeck/audit_return_type_name/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Opt = Some(word) | None; - -function f() -> Opt { - return Opt; -} diff --git a/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/diagnostics.snap index 95a41815..26102ed0 100644 --- a/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/main.solc +input_file: crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/main.sol --- error[SC0228]: type name used as value: `Opt` - --> /main/main.solc:9:10 + --> /main/main.sol:9:10 | - 8 | function adt_value() -> word { + 8 | function adt_value() returns (word) { 9 | return Opt; | ^^^ not a value 10 | } @@ -15,9 +15,9 @@ error[SC0228]: type name used as value: `Opt` --- error[SC0228]: type name used as value: `Alias` - --> /main/main.solc:13:10 + --> /main/main.sol:13:10 | -12 | function alias_value() -> word { +12 | function alias_value() returns (word) { 13 | return Alias; | ^^^^^ not a value 14 | } @@ -26,9 +26,9 @@ error[SC0228]: type name used as value: `Alias` --- error[SC0228]: type name used as value: `K` - --> /main/main.solc:17:10 + --> /main/main.sol:17:10 | -16 | function contract_value() -> word { +16 | function contract_value() returns (word) { 17 | return K; | ^ not a value 18 | } @@ -36,10 +36,10 @@ error[SC0228]: type name used as value: `K` = help: use a constructor or value binding here, not a namespace name --- -error[SC0228]: class name used as value: `C` - --> /main/main.solc:21:10 +error[SC0228]: trait name used as value: `C` + --> /main/main.sol:21:10 | -20 | function class_value() -> word { +20 | function class_value() returns (word) { 21 | return C; | ^ not a value 22 | } @@ -48,9 +48,9 @@ error[SC0228]: class name used as value: `C` --- error[SC0228]: type name used as value: `word` - --> /main/main.solc:25:10 + --> /main/main.sol:25:10 | -24 | function builtin_type_value() -> word { +24 | function builtin_type_value() returns (word) { 25 | return word; | ^^^^ not a value 26 | } @@ -58,10 +58,10 @@ error[SC0228]: type name used as value: `word` = help: use a constructor or value binding here, not a namespace name --- -error[SC0228]: class name used as value: `Int` - --> /main/main.solc:29:10 +error[SC0228]: trait name used as value: `Int` + --> /main/main.sol:29:10 | -28 | function builtin_class_value() -> word { +28 | function builtin_class_value() returns (word) { 29 | return Int; | ^^^ not a value 30 | } @@ -70,9 +70,9 @@ error[SC0228]: class name used as value: `Int` --- error[SC0228]: type variable used as value: `a` - --> /main/main.solc:33:10 + --> /main/main.sol:33:10 | -32 | forall a . function type_var_value() -> word { +32 | function type_var_value() returns (word) { 33 | return a; | ^ not a value 34 | } @@ -81,9 +81,9 @@ error[SC0228]: type variable used as value: `a` --- error[SC0228]: module used as value: `U` - --> /main/main.solc:37:10 + --> /main/main.sol:37:10 | -36 | function module_value() -> word { +36 | function module_value() returns (word) { 37 | return U; | ^ not a value 38 | } @@ -92,9 +92,9 @@ error[SC0228]: module used as value: `U` --- error[SC0228]: type name used as callee: `Opt` - --> /main/main.solc:41:10 + --> /main/main.sol:41:10 | -40 | function type_as_callee() -> word { +40 | function type_as_callee() returns (word) { 41 | return Opt(); | ^^^ not a value 42 | } @@ -103,9 +103,9 @@ error[SC0228]: type name used as callee: `Opt` --- error[SC0228]: module used as callee: `U` - --> /main/main.solc:45:10 + --> /main/main.sol:45:10 | -44 | function module_as_callee() -> word { +44 | function module_as_callee() returns (word) { 45 | return U(); | ^ not a value 46 | } @@ -113,22 +113,22 @@ error[SC0228]: module used as callee: `U` = help: use a constructor or value binding here, not a namespace name --- -error[SC0207]: cannot satisfy class constraint: operator Add.add - --> /main/main.solc:49:10 +error[SC0207]: cannot satisfy trait constraint: operator Add.add + --> /main/main.sol:49:10 | -48 | function type_in_binop() -> word { +48 | function type_in_binop() returns (word) { 49 | return Opt + 1; | ^^^^^^^ constraint originates here 50 | } | - = note: no visible instance matches `operator Add.add` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `operator Add.add` + = help: add a matching impl or strengthen the surrounding type context --- error[SC0228]: type name used as value: `Opt` - --> /main/main.solc:49:10 + --> /main/main.sol:49:10 | -48 | function type_in_binop() -> word { +48 | function type_in_binop() returns (word) { 49 | return Opt + 1; | ^^^ not a value 50 | } diff --git a/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/main.sol b/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/main.sol new file mode 100644 index 00000000..a3ac8db1 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/main.sol @@ -0,0 +1,50 @@ +import * as U from util; + +enum Opt { Some(word), None } +type Alias = word; +contract K { function main() returns (word) { return 0; } } +trait C {} + +function adt_value() returns (word) { + return Opt; +} + +function alias_value() returns (word) { + return Alias; +} + +function contract_value() returns (word) { + return K; +} + +function class_value() returns (word) { + return C; +} + +function builtin_type_value() returns (word) { + return word; +} + +function builtin_class_value() returns (word) { + return Int; +} + +function type_var_value() returns (word) { + return a; +} + +function module_value() returns (word) { + return U; +} + +function type_as_callee() returns (word) { + return Opt(); +} + +function module_as_callee() returns (word) { + return U(); +} + +function type_in_binop() returns (word) { + return Opt + 1; +} diff --git a/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/main.solc b/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/main.solc deleted file mode 100644 index b9ed700d..00000000 --- a/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/main.solc +++ /dev/null @@ -1,50 +0,0 @@ -import util as U; - -data Opt = Some(word) | None; -type Alias = word; -contract K { function main() -> word { return 0; } } -forall a . class a:C {} - -function adt_value() -> word { - return Opt; -} - -function alias_value() -> word { - return Alias; -} - -function contract_value() -> word { - return K; -} - -function class_value() -> word { - return C; -} - -function builtin_type_value() -> word { - return word; -} - -function builtin_class_value() -> word { - return Int; -} - -forall a . function type_var_value() -> word { - return a; -} - -function module_value() -> word { - return U; -} - -function type_as_callee() -> word { - return Opt(); -} - -function module_as_callee() -> word { - return U(); -} - -function type_in_binop() -> word { - return Opt + 1; -} diff --git a/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/util.sol b/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/util.sol new file mode 100644 index 00000000..763b1212 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/util.sol @@ -0,0 +1,3 @@ +function g() returns (word) { + return 0; +} diff --git a/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/util.solc b/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/util.solc deleted file mode 100644 index dd7050c6..00000000 --- a/crates/uitest/tests/fixtures/typeck/audit_value_namespace_matrix/util.solc +++ /dev/null @@ -1,3 +0,0 @@ -function g() -> word { - return 0; -} diff --git a/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/diagnostics.snap index 8c9793a6..bcb9164b 100644 --- a/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/diagnostics.snap @@ -1,23 +1,23 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/call_arg_defined_here/main.solc +input_file: crates/uitest/tests/fixtures/typeck/call_arg_defined_here/main.sol --- error[SC0201]: argument type mismatch in call to `paint` - --> /main/main.solc:4:21 + --> /main/main.sol:4:21 | -3 | function go() -> L.Color { +3 | function go() returns (L.Color) { 4 | return L.paint(1, true); | ^^^^ argument has mismatched type 5 | } | - ::: /main/lib.solc:4 + ::: /main/lib.sol:4 | 4 | -5 | function paint(name: word, c: Color) -> Color { +5 | function paint(name: word, c: Color) returns (Color) { | - parameter `c` defined here 6 | return c; | = note: expected `Color` because parameter `c` of `paint` has type `Color` = note: found type: bool - = note: `paint` has signature `paint(name: word, c: Color) -> Color` + = note: `paint` has signature `paint(name: word, c: Color) returns (Color)` diff --git a/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/lib.sol b/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/lib.sol new file mode 100644 index 00000000..7e951062 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/lib.sol @@ -0,0 +1,7 @@ +export { Color(*), paint }; + +enum Color { Red, Green } + +function paint(name: word, c: Color) returns (Color) { + return c; +} diff --git a/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/lib.solc b/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/lib.solc deleted file mode 100644 index 77c0e4e6..00000000 --- a/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/lib.solc +++ /dev/null @@ -1,7 +0,0 @@ -export { Color(*), paint }; - -data Color = Red | Green; - -function paint(name: word, c: Color) -> Color { - return c; -} diff --git a/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/main.sol b/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/main.sol new file mode 100644 index 00000000..89ebf496 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/main.sol @@ -0,0 +1,5 @@ +import * as L from lib; + +function go() returns (L.Color) { + return L.paint(1, true); +} diff --git a/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/main.solc b/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/main.solc deleted file mode 100644 index c229ec8a..00000000 --- a/crates/uitest/tests/fixtures/typeck/call_arg_defined_here/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import lib as L; - -function go() -> L.Color { - return L.paint(1, true); -} diff --git a/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/diagnostics.snap index fd3c113e..f7300c2d 100644 --- a/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/diagnostics.snap @@ -1,23 +1,23 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/call_arity_defined_here/main.solc +input_file: crates/uitest/tests/fixtures/typeck/call_arity_defined_here/main.sol --- error[SC0203]: call expects 1 argument, but 0 were provided - --> /main/main.solc:4:10 + --> /main/main.sol:4:10 | -3 | function go() -> word { +3 | function go() returns (word) { 4 | return L.id(); | ^^^^^^ wrong number of arguments 5 | } | - ::: /main/lib.solc:2 + ::: /main/lib.sol:2 | 2 | -3 | function id(x: word) -> word { +3 | function id(x: word) returns (word) { | -- `id` defined here 4 | return x; | = note: expected 1 argument = note: found 0 arguments - = note: `id` has signature `id(x: word) -> word` + = note: `id` has signature `id(x: word) returns (word)` diff --git a/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/lib.sol b/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/lib.sol new file mode 100644 index 00000000..28a558b3 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/lib.sol @@ -0,0 +1,5 @@ +export { id }; + +function id(x: word) returns (word) { + return x; +} diff --git a/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/lib.solc b/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/lib.solc deleted file mode 100644 index 052e7b55..00000000 --- a/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/lib.solc +++ /dev/null @@ -1,5 +0,0 @@ -export { id }; - -function id(x: word) -> word { - return x; -} diff --git a/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/main.sol b/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/main.sol new file mode 100644 index 00000000..208079b6 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/main.sol @@ -0,0 +1,5 @@ +import * as L from lib; + +function go() returns (word) { + return L.id(); +} diff --git a/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/main.solc b/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/main.solc deleted file mode 100644 index 8db73d4b..00000000 --- a/crates/uitest/tests/fixtures/typeck/call_arity_defined_here/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -import lib as L; - -function go() -> word { - return L.id(); -} diff --git a/crates/uitest/tests/fixtures/typeck/call_wrong_arity/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/call_wrong_arity/diagnostics.snap index b5a40312..1a3aeb2c 100644 --- a/crates/uitest/tests/fixtures/typeck/call_wrong_arity/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/call_wrong_arity/diagnostics.snap @@ -1,21 +1,21 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/call_wrong_arity/main.solc +input_file: crates/uitest/tests/fixtures/typeck/call_wrong_arity/main.sol --- error[SC0203]: call expects 1 argument, but 0 were provided - --> /main/main.solc:6:10 + --> /main/main.sol:6:10 | -1 | function f(x: word) -> word { +1 | function f(x: word) returns (word) { | - `f` defined here 2 | return x; 3 | } 4 | -5 | function g() -> word { +5 | function g() returns (word) { 6 | return f(); | ^^^ wrong number of arguments 7 | } | = note: expected 1 argument = note: found 0 arguments - = note: `f` has signature `f(x: word) -> word` + = note: `f` has signature `f(x: word) returns (word)` diff --git a/crates/uitest/tests/fixtures/typeck/call_wrong_arity/main.sol b/crates/uitest/tests/fixtures/typeck/call_wrong_arity/main.sol new file mode 100644 index 00000000..a257e1c5 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/call_wrong_arity/main.sol @@ -0,0 +1,7 @@ +function f(x: word) returns (word) { + return x; +} + +function g() returns (word) { + return f(); +} diff --git a/crates/uitest/tests/fixtures/typeck/call_wrong_arity/main.solc b/crates/uitest/tests/fixtures/typeck/call_wrong_arity/main.solc deleted file mode 100644 index 2d86a90a..00000000 --- a/crates/uitest/tests/fixtures/typeck/call_wrong_arity/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -function f(x: word) -> word { - return x; -} - -function g() -> word { - return f(); -} diff --git a/crates/uitest/tests/fixtures/typeck/canonical_location_wrapper_user_adt_payload/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/canonical_location_wrapper_user_adt_payload/diagnostics.snap index 8ba01b50..80993b22 100644 --- a/crates/uitest/tests/fixtures/typeck/canonical_location_wrapper_user_adt_payload/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/canonical_location_wrapper_user_adt_payload/diagnostics.snap @@ -1,35 +1,35 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/canonical_location_wrapper_user_adt_payload/main.solc +input_file: crates/uitest/tests/fixtures/typeck/canonical_location_wrapper_user_adt_payload/main.sol --- -error[SC0207]: cannot satisfy class constraint: Contract(Method(DispatchNameTy_C_roundtrip, NonPayable, memory(Point), word, (memory(Point)) -> word), Fallback(NonPayable, (), (), () -> ())) : RunContract - --> /main/main.solc:6:10 +error[SC0207]: cannot satisfy trait constraint: Contract, word, function(memory) returns (word)>, Fallback>: RunContract + --> /main/main.sol:6:10 | 5 | 6 | contract C { | ^ constraint originates here -7 | public function roundtrip(value: memory(Point)) -> word { return 0; } +7 | function roundtrip(value: memory) public returns (word) { return 0; } | - = note: no visible instance matches `Contract(Method(DispatchNameTy_C_roundtrip, NonPayable, memory(Point), word, (memory(Point)) -> word), Fallback(NonPayable, (), (), () -> ())) : RunContract` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `Contract, word, function(memory) returns (word)>, Fallback>: RunContract` + = help: add a matching impl or strengthen the surrounding type context --- -error[SC0231]: ABI parameter cannot be represented in the ABI: adt:Point (only memory(string) and memory(bytes) have canonical ABI evidence) - --> /main/main.solc:7:3 +error[SC0231]: ABI parameter cannot be represented in the ABI: adt:Point (only memory and memory have canonical ABI evidence) + --> /main/main.sol:7:3 | 6 | contract C { -7 | public function roundtrip(value: memory(Point)) -> word { return 0; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type +7 | function roundtrip(value: memory) public returns (word) { return 0; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type 8 | } | --- -error[SC0231]: roundtrip cannot be represented in the ABI: adt:Point (only memory(string) and memory(bytes) have canonical ABI evidence) - --> /main/main.solc:7:3 +error[SC0231]: roundtrip cannot be represented in the ABI: adt:Point (only memory and memory have canonical ABI evidence) + --> /main/main.sol:7:3 | 6 | contract C { -7 | public function roundtrip(value: memory(Point)) -> word { return 0; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type +7 | function roundtrip(value: memory) public returns (word) { return 0; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type 8 | } | diff --git a/crates/uitest/tests/fixtures/typeck/canonical_location_wrapper_user_adt_payload/main.sol b/crates/uitest/tests/fixtures/typeck/canonical_location_wrapper_user_adt_payload/main.sol new file mode 100644 index 00000000..47278d9c --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/canonical_location_wrapper_user_adt_payload/main.sol @@ -0,0 +1,8 @@ +import * from std; +import * from std.dispatch; + +enum Point { Point(word, bool) } + +contract C { + function roundtrip(value: memory) public returns (word) { return 0; } +} diff --git a/crates/uitest/tests/fixtures/typeck/canonical_location_wrapper_user_adt_payload/main.solc b/crates/uitest/tests/fixtures/typeck/canonical_location_wrapper_user_adt_payload/main.solc deleted file mode 100644 index 2b24766d..00000000 --- a/crates/uitest/tests/fixtures/typeck/canonical_location_wrapper_user_adt_payload/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -data Point = Point(word, bool); - -contract C { - public function roundtrip(value: memory(Point)) -> word { return 0; } -} diff --git a/crates/uitest/tests/fixtures/typeck/comptime_class_head_method_signature/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/comptime_class_head_method_signature/diagnostics.snap index 2fda645f..e20822ff 100644 --- a/crates/uitest/tests/fixtures/typeck/comptime_class_head_method_signature/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/comptime_class_head_method_signature/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/comptime_class_head_method_signature/main.solc +input_file: crates/uitest/tests/fixtures/typeck/comptime_class_head_method_signature/main.sol --- -error[SC0221]: invalid instance member signature for `f`: expected (word) -> word, got (word) -> bool - --> /main/main.solc:6:3 +error[SC0221]: invalid impl member signature for `f`: expected function(word) returns (word), got function(word) returns (bool) + --> /main/main.sol:6:3 | -5 | instance word : C { -6 | function f(x : word) -> bool { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid instance method signature +5 | impl C { +6 | function f(x: word) returns (bool) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid impl method signature 7 | return true; | - = note: the instance method must match the class method after substituting the instance head + = note: the impl method must match the trait method after substituting the impl head diff --git a/crates/uitest/tests/fixtures/typeck/comptime_class_head_method_signature/main.sol b/crates/uitest/tests/fixtures/typeck/comptime_class_head_method_signature/main.sol new file mode 100644 index 00000000..6257f5e6 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/comptime_class_head_method_signature/main.sol @@ -0,0 +1,9 @@ +trait C { + function f(x: a) returns (a) ; +} + +impl C { + function f(x: word) returns (bool) { + return true; + } +} diff --git a/crates/uitest/tests/fixtures/typeck/comptime_class_head_method_signature/main.solc b/crates/uitest/tests/fixtures/typeck/comptime_class_head_method_signature/main.solc deleted file mode 100644 index 03d5bb0d..00000000 --- a/crates/uitest/tests/fixtures/typeck/comptime_class_head_method_signature/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -forall a. class comptime a : C { - function f(x : a) -> a; -} - -instance word : C { - function f(x : word) -> bool { - return true; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/comptime_class_method_runtime_arg/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/comptime_class_method_runtime_arg/diagnostics.snap index 7f3450cf..1fe61711 100644 --- a/crates/uitest/tests/fixtures/typeck/comptime_class_method_runtime_arg/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/comptime_class_method_runtime_arg/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/comptime_class_method_runtime_arg/main.solc +input_file: crates/uitest/tests/fixtures/typeck/comptime_class_method_runtime_arg/main.sol --- error[SC0240]: runtime value passed to comptime parameter 'x' of 'Wrap.unwrap' - --> /main/main.solc:25:20 + --> /main/main.sol:25:20 | -24 | public function main() -> word { +24 | function main() public returns (word) { 25 | return process(sloadWord()); | ^^^^^^^^^^^ runtime value passed here 26 | } diff --git a/crates/uitest/tests/fixtures/typeck/comptime_class_method_runtime_arg/main.sol b/crates/uitest/tests/fixtures/typeck/comptime_class_method_runtime_arg/main.sol new file mode 100644 index 00000000..935d46c4 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/comptime_class_method_runtime_arg/main.sol @@ -0,0 +1,27 @@ +trait Wrap { + function unwrap(comptime x: t) returns (comptime) ; +} + +impl Wrap { + function unwrap(comptime x: word) returns (comptime) { + return x; + } +} + +function process(z: t) returns (word) where t: Wrap { + return Wrap.unwrap(z); +} + +function sloadWord() returns (word) { + let v : word; + assembly { + v := sload(0) + } + return v; +} + +contract C { + function main() public returns (word) { + return process(sloadWord()); + } +} diff --git a/crates/uitest/tests/fixtures/typeck/comptime_class_method_runtime_arg/main.solc b/crates/uitest/tests/fixtures/typeck/comptime_class_method_runtime_arg/main.solc deleted file mode 100644 index 0bb3931b..00000000 --- a/crates/uitest/tests/fixtures/typeck/comptime_class_method_runtime_arg/main.solc +++ /dev/null @@ -1,27 +0,0 @@ -forall t. class t : Wrap { - function unwrap(comptime x : t) -> comptime word; -} - -instance word : Wrap { - function unwrap(comptime x : word) -> comptime word { - return x; - } -} - -forall t. t:Wrap => function process(z : t) -> word { - return Wrap.unwrap(z); -} - -function sloadWord() -> word { - let v : word; - assembly { - v := sload(0) - } - return v; -} - -contract C { - public function main() -> word { - return process(sloadWord()); - } -} diff --git a/crates/uitest/tests/fixtures/typeck/contract_field_initializer_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/contract_field_initializer_mismatch/diagnostics.snap index 8e29e97d..7e961b14 100644 --- a/crates/uitest/tests/fixtures/typeck/contract_field_initializer_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/contract_field_initializer_mismatch/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/contract_field_initializer_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/typeck/contract_field_initializer_mismatch/main.sol --- error[SC0201]: type mismatch: expected word, found bool - --> /main/main.solc:2:13 + --> /main/main.sol:2:13 | 1 | contract C { 2 | x: word = true; | ^^^^ expression has mismatched type -3 | function main() -> () { return (); } +3 | function main() { return (); } | = note: expected type: word = note: found type: bool diff --git a/crates/uitest/tests/fixtures/typeck/contract_field_initializer_mismatch/main.sol b/crates/uitest/tests/fixtures/typeck/contract_field_initializer_mismatch/main.sol new file mode 100644 index 00000000..790f3163 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/contract_field_initializer_mismatch/main.sol @@ -0,0 +1,4 @@ +contract C { + x: word = true; + function main() { return (); } +} diff --git a/crates/uitest/tests/fixtures/typeck/contract_field_initializer_mismatch/main.solc b/crates/uitest/tests/fixtures/typeck/contract_field_initializer_mismatch/main.solc deleted file mode 100644 index f8d6ebdc..00000000 --- a/crates/uitest/tests/fixtures/typeck/contract_field_initializer_mismatch/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -contract C { - x: word = true; - function main() -> () { return (); } -} diff --git a/crates/uitest/tests/fixtures/typeck/desugar_origin_spans/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/desugar_origin_spans/diagnostics.snap index 9cc5ae10..ba7c327c 100644 --- a/crates/uitest/tests/fixtures/typeck/desugar_origin_spans/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/desugar_origin_spans/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/desugar_origin_spans/main.solc +input_file: crates/uitest/tests/fixtures/typeck/desugar_origin_spans/main.sol --- error[SC0201]: type mismatch: expected word, found bool - --> /main/main.solc:2:29 + --> /main/main.sol:2:26 | -1 | contract C { function main() -> word { return 0; } -2 | seed: word = if true then false else 1; - | ^^^^^ expression has mismatched type +1 | contract C { function main() returns (word) { return 0; } +2 | seed: word = true ? false : 1; + | ^^^^^ expression has mismatched type 3 | } | = note: expected type: word @@ -16,36 +16,36 @@ error[SC0201]: type mismatch: expected word, found bool --- error[SC0203]: tuple pattern expects 2 arguments, but 3 were provided - --> /main/main.solc:7:5 + --> /main/main.sol:7:6 | -6 | match p { -7 | | (a, b, c) => return a; - | ^^^^^^^^^ wrong number of arguments -8 | } +6 | match (p) { +7 | case (a, b, c) { + | ^^^^^^^^^ wrong number of arguments +8 | return a; | = note: expected 2 arguments = note: found 3 arguments --- error[SC0201]: type mismatch: expected word, found bool - --> /main/main.solc:12:27 + --> /main/main.sol:14:23 | -11 | function if_source(b: bool) -> word { -12 | return if b then 1 else false; - | ^^^^^ expression has mismatched type -13 | } +13 | function if_source(b: bool) returns (word) { +14 | return b ? 1 : false; + | ^^^^^ expression has mismatched type +15 | } | = note: expected type: word = note: found type: bool --- error[SC0201]: type mismatch: expected word, found bool - --> /main/main.solc:16:10 + --> /main/main.sol:18:10 | -15 | function bool_source() -> word { -16 | return true; +17 | function bool_source() returns (word) { +18 | return true; | ^^^^ expression has mismatched type -17 | } +19 | } | = note: expected type: word = note: found type: bool diff --git a/crates/uitest/tests/fixtures/typeck/desugar_origin_spans/main.sol b/crates/uitest/tests/fixtures/typeck/desugar_origin_spans/main.sol new file mode 100644 index 00000000..32b742ca --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/desugar_origin_spans/main.sol @@ -0,0 +1,19 @@ +contract C { function main() returns (word) { return 0; } + seed: word = true ? false : 1; +} + +function pat_source(p: (word, word)) returns (word) { + match (p) { +case (a, b, c) { +return a; +} +} +} + +function if_source(b: bool) returns (word) { + return b ? 1 : false; +} + +function bool_source() returns (word) { + return true; +} diff --git a/crates/uitest/tests/fixtures/typeck/desugar_origin_spans/main.solc b/crates/uitest/tests/fixtures/typeck/desugar_origin_spans/main.solc deleted file mode 100644 index 55fbacc7..00000000 --- a/crates/uitest/tests/fixtures/typeck/desugar_origin_spans/main.solc +++ /dev/null @@ -1,17 +0,0 @@ -contract C { function main() -> word { return 0; } - seed: word = if true then false else 1; -} - -function pat_source(p: (word, word)) -> word { - match p { - | (a, b, c) => return a; - } -} - -function if_source(b: bool) -> word { - return if b then 1 else false; -} - -function bool_source() -> word { - return true; -} diff --git a/crates/uitest/tests/fixtures/typeck/dispatch_name_collision_full/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/dispatch_name_collision_full/diagnostics.snap index 14ab52e4..39b808d1 100644 --- a/crates/uitest/tests/fixtures/typeck/dispatch_name_collision_full/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/dispatch_name_collision_full/diagnostics.snap @@ -1,18 +1,18 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/dispatch_name_collision_full/main.solc +input_file: crates/uitest/tests/fixtures/typeck/dispatch_name_collision_full/main.sol --- error[SC0229]: duplicate type definition: DispatchNameTy_C_ping - --> /main/main.solc:4:6 + --> /main/main.sol:4:6 | 3 | -4 | data DispatchNameTy_C_ping = Collision; +4 | enum DispatchNameTy_C_ping { Collision } | ^^^^^^^^^^^^^^^^^^^^^ duplicate type 5 | 6 | contract C { -7 | public function ping() -> uint256 { - | ---- existing definition +7 | function ping() public returns (uint256) { + | ---- existing definition 8 | return uint256(0); | = note: rename or remove the duplicate type definition diff --git a/crates/uitest/tests/fixtures/typeck/dispatch_name_collision_full/main.sol b/crates/uitest/tests/fixtures/typeck/dispatch_name_collision_full/main.sol new file mode 100644 index 00000000..075c36f9 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/dispatch_name_collision_full/main.sol @@ -0,0 +1,10 @@ +import * from std; +import * from std.dispatch; + +enum DispatchNameTy_C_ping { Collision } + +contract C { + function ping() public returns (uint256) { + return uint256(0); + } +} diff --git a/crates/uitest/tests/fixtures/typeck/dispatch_name_collision_full/main.solc b/crates/uitest/tests/fixtures/typeck/dispatch_name_collision_full/main.solc deleted file mode 100644 index 97da477d..00000000 --- a/crates/uitest/tests/fixtures/typeck/dispatch_name_collision_full/main.solc +++ /dev/null @@ -1,10 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -data DispatchNameTy_C_ping = Collision; - -contract C { - public function ping() -> uint256 { - return uint256(0); - } -} diff --git a/crates/uitest/tests/fixtures/typeck/duplicate_literal_unreachable/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/duplicate_literal_unreachable/diagnostics.snap index 8c6f8b5c..7afc005b 100644 --- a/crates/uitest/tests/fixtures/typeck/duplicate_literal_unreachable/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/duplicate_literal_unreachable/diagnostics.snap @@ -1,14 +1,16 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/duplicate_literal_unreachable/main.solc +input_file: crates/uitest/tests/fixtures/typeck/duplicate_literal_unreachable/main.sol --- warning[SC0303]: unreachable match arm - --> /main/main.solc:4:3 + --> /main/main.sol:6:1 | -3 | | 0 => return 0; -4 | | 0 => return 1; - | ^^^^^^^^^^^^^^^^ this arm is unreachable -5 | | _ => return 2; +5 | } +6 | / case 0 { +7 | | return 1; +8 | | } + | |_^ this arm is unreachable +9 | default { | = note: this arm is covered by previous match arms diff --git a/crates/uitest/tests/fixtures/typeck/duplicate_literal_unreachable/main.sol b/crates/uitest/tests/fixtures/typeck/duplicate_literal_unreachable/main.sol new file mode 100644 index 00000000..52700733 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/duplicate_literal_unreachable/main.sol @@ -0,0 +1,13 @@ +function pick(x: word) returns (word) { + match (x) { +case 0 { +return 0; +} +case 0 { +return 1; +} +default { +return 2; +} +} +} diff --git a/crates/uitest/tests/fixtures/typeck/duplicate_literal_unreachable/main.solc b/crates/uitest/tests/fixtures/typeck/duplicate_literal_unreachable/main.solc deleted file mode 100644 index 25ac1933..00000000 --- a/crates/uitest/tests/fixtures/typeck/duplicate_literal_unreachable/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -function pick(x : word) -> word { - match x { - | 0 => return 0; - | 0 => return 1; - | _ => return 2; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/equivalent_numeric_literal_unreachable/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/equivalent_numeric_literal_unreachable/diagnostics.snap index 9cf7654b..219137ca 100644 --- a/crates/uitest/tests/fixtures/typeck/equivalent_numeric_literal_unreachable/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/equivalent_numeric_literal_unreachable/diagnostics.snap @@ -1,25 +1,29 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/equivalent_numeric_literal_unreachable/main.solc +input_file: crates/uitest/tests/fixtures/typeck/equivalent_numeric_literal_unreachable/main.sol --- warning[SC0303]: unreachable match arm - --> /main/main.solc:4:3 + --> /main/main.sol:6:1 | -3 | | 0x0A => return 0; -4 | | 10 => return 1; - | ^^^^^^^^^^^^^^^^^ this arm is unreachable -5 | | _ => return 2; +5 | } +6 | / case 10 { +7 | | return 1; +8 | | } + | |_^ this arm is unreachable +9 | default { | = note: this arm is covered by previous match arms --- warning[SC0303]: unreachable match arm - --> /main/main.solc:12:3 + --> /main/main.sol:20:1 | -11 | | 0 => return 0; -12 | | 115792089237316195423570985008687907853269984665640564039457584007913129639936 => return 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this arm is unreachable -13 | | _ => return 2; +19 | } +20 | / case 115792089237316195423570985008687907853269984665640564039457584007913129639936 { +21 | | return 1; +22 | | } + | |_^ this arm is unreachable +23 | default { | = note: this arm is covered by previous match arms diff --git a/crates/uitest/tests/fixtures/typeck/equivalent_numeric_literal_unreachable/main.sol b/crates/uitest/tests/fixtures/typeck/equivalent_numeric_literal_unreachable/main.sol new file mode 100644 index 00000000..2d481932 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/equivalent_numeric_literal_unreachable/main.sol @@ -0,0 +1,41 @@ +function pick(x: word) returns (word) { + match (x) { +case 0x0A { +return 0; +} +case 10 { +return 1; +} +default { +return 2; +} +} +} + +function wrapped(x: word) returns (word) { + match (x) { +case 0 { +return 0; +} +case 115792089237316195423570985008687907853269984665640564039457584007913129639936 { +return 1; +} +default { +return 2; +} +} +} + +function exact(x: integer) returns (word) { + match (x) { +case 0 { +return 0; +} +case 115792089237316195423570985008687907853269984665640564039457584007913129639936 { +return 1; +} +default { +return 2; +} +} +} diff --git a/crates/uitest/tests/fixtures/typeck/equivalent_numeric_literal_unreachable/main.solc b/crates/uitest/tests/fixtures/typeck/equivalent_numeric_literal_unreachable/main.solc deleted file mode 100644 index 008e89bb..00000000 --- a/crates/uitest/tests/fixtures/typeck/equivalent_numeric_literal_unreachable/main.solc +++ /dev/null @@ -1,23 +0,0 @@ -function pick(x : word) -> word { - match x { - | 0x0A => return 0; - | 10 => return 1; - | _ => return 2; - } -} - -function wrapped(x : word) -> word { - match x { - | 0 => return 0; - | 115792089237316195423570985008687907853269984665640564039457584007913129639936 => return 1; - | _ => return 2; - } -} - -function exact(x : integer) -> word { - match x { - | 0 => return 0; - | 115792089237316195423570985008687907853269984665640564039457584007913129639936 => return 1; - | _ => return 2; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_arg_type_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_arg_type_mismatch/diagnostics.snap index 06f241ff..b39584c3 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_arg_type_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_arg_type_mismatch/diagnostics.snap @@ -1,21 +1,21 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_arg_type_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_arg_type_mismatch/main.sol --- error[SC0201]: argument type mismatch in call to `paint` - --> /main/main.solc:8:19 + --> /main/main.sol:8:19 | 2 | -3 | function paint(name: word, c: Color) -> Color { +3 | function paint(name: word, c: Color) returns (Color) { | - parameter `c` defined here 4 | return c; ... -7 | function go() -> Color { +7 | function go() returns (Color) { 8 | return paint(1, true); | ^^^^ argument has mismatched type 9 | } | = note: expected `Color` because parameter `c` of `paint` has type `Color` = note: found type: bool - = note: `paint` has signature `paint(name: word, c: Color) -> Color` + = note: `paint` has signature `paint(name: word, c: Color) returns (Color)` diff --git a/crates/uitest/tests/fixtures/typeck/ergo_arg_type_mismatch/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_arg_type_mismatch/main.sol new file mode 100644 index 00000000..289fb05a --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_arg_type_mismatch/main.sol @@ -0,0 +1,9 @@ +enum Color { Red, Green } + +function paint(name: word, c: Color) returns (Color) { + return c; +} + +function go() returns (Color) { + return paint(1, true); +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_arg_type_mismatch/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_arg_type_mismatch/main.solc deleted file mode 100644 index 3b6f6aa1..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_arg_type_mismatch/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -data Color = Red | Green; - -function paint(name: word, c: Color) -> Color { - return c; -} - -function go() -> Color { - return paint(1, true); -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_assign_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_assign_mismatch/diagnostics.snap index 86ab1212..3a1b4a08 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_assign_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_assign_mismatch/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_assign_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_assign_mismatch/main.sol --- error[SC0201]: type mismatch: expected word, found bool - --> /main/main.solc:3:7 + --> /main/main.sol:3:7 | 2 | let x : word = 1; 3 | x = true; diff --git a/crates/uitest/tests/fixtures/typeck/ergo_assign_mismatch/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_assign_mismatch/main.sol new file mode 100644 index 00000000..2e3ed65c --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_assign_mismatch/main.sol @@ -0,0 +1,5 @@ +function f() returns (word) { + let x : word = 1; + x = true; + return x; +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_assign_mismatch/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_assign_mismatch/main.solc deleted file mode 100644 index 640b0e9e..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_assign_mismatch/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -function f() -> word { - let x : word = 1; - x = true; - return x; -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_call_too_few_args/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_call_too_few_args/diagnostics.snap index a88c92ef..feceab3e 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_call_too_few_args/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_call_too_few_args/diagnostics.snap @@ -1,21 +1,21 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_call_too_few_args/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_call_too_few_args/main.sol --- error[SC0203]: call expects 3 arguments, but 1 was provided - --> /main/main.solc:6:10 + --> /main/main.sol:6:10 | -1 | function clamp(lo: word, hi: word, v: word) -> word { +1 | function clamp(lo: word, hi: word, v: word) returns (word) { | ----- `clamp` defined here 2 | return v; 3 | } 4 | -5 | function g() -> word { +5 | function g() returns (word) { 6 | return clamp(1); | ^^^^^^^^ wrong number of arguments 7 | } | = note: expected 3 arguments = note: found 1 argument - = note: `clamp` has signature `clamp(lo: word, hi: word, v: word) -> word` + = note: `clamp` has signature `clamp(lo: word, hi: word, v: word) returns (word)` diff --git a/crates/uitest/tests/fixtures/typeck/ergo_call_too_few_args/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_call_too_few_args/main.sol new file mode 100644 index 00000000..d26bc65d --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_call_too_few_args/main.sol @@ -0,0 +1,7 @@ +function clamp(lo: word, hi: word, v: word) returns (word) { + return v; +} + +function g() returns (word) { + return clamp(1); +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_call_too_few_args/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_call_too_few_args/main.solc deleted file mode 100644 index 4ac82b3b..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_call_too_few_args/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -function clamp(lo: word, hi: word, v: word) -> word { - return v; -} - -function g() -> word { - return clamp(1); -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_call_too_many_args/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_call_too_many_args/diagnostics.snap index 1901e7a9..cb9d79e5 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_call_too_many_args/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_call_too_many_args/diagnostics.snap @@ -1,21 +1,21 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_call_too_many_args/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_call_too_many_args/main.sol --- error[SC0203]: call expects 1 argument, but 3 were provided - --> /main/main.solc:6:10 + --> /main/main.sol:6:10 | -1 | function double(x: word) -> word { +1 | function double(x: word) returns (word) { | ------ `double` defined here 2 | return x; 3 | } 4 | -5 | function g() -> word { +5 | function g() returns (word) { 6 | return double(1, 2, 3); | ^^^^^^^^^^^^^^^ wrong number of arguments 7 | } | = note: expected 1 argument = note: found 3 arguments - = note: `double` has signature `double(x: word) -> word` + = note: `double` has signature `double(x: word) returns (word)` diff --git a/crates/uitest/tests/fixtures/typeck/ergo_call_too_many_args/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_call_too_many_args/main.sol new file mode 100644 index 00000000..f4933dc5 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_call_too_many_args/main.sol @@ -0,0 +1,7 @@ +function double(x: word) returns (word) { + return x; +} + +function g() returns (word) { + return double(1, 2, 3); +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_call_too_many_args/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_call_too_many_args/main.solc deleted file mode 100644 index a11a51e8..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_call_too_many_args/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -function double(x: word) -> word { - return x; -} - -function g() -> word { - return double(1, 2, 3); -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_class_head_no_forall/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_class_head_no_forall/diagnostics.snap deleted file mode 100644 index 2c2ec171..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_class_head_no_forall/diagnostics.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: crates/test-utils/src/lib.rs -expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_class_head_no_forall/main.solc ---- -error[SC0102]: undefined type variables: a - --> /main/main.solc:1:7 - | -1 | class a : C {} - | ^ undefined type variable diff --git a/crates/uitest/tests/fixtures/typeck/ergo_class_head_no_forall/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_class_head_no_forall/main.solc deleted file mode 100644 index aa10816a..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_class_head_no_forall/main.solc +++ /dev/null @@ -1 +0,0 @@ -class a : C {} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_ct_indirect_escape/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_ct_indirect_escape/diagnostics.snap index 7d1c42e6..c382faba 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_ct_indirect_escape/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_ct_indirect_escape/diagnostics.snap @@ -1,24 +1,24 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_ct_indirect_escape/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_ct_indirect_escape/main.sol --- -error[SC0207]: cannot satisfy class constraint: operator Add.add - --> /main/main.solc:17:12 +error[SC0207]: cannot satisfy trait constraint: operator Add.add + --> /main/main.sol:17:12 | -16 | function double(comptime x : word) -> comptime word { +16 | function double(comptime x: word) returns (comptime) { 17 | return x + x; | ^^^^^ constraint originates here 18 | } | - = note: no visible instance matches `operator Add.add` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `operator Add.add` + = help: add a matching impl or strengthen the surrounding type context --- error[SC0240]: runtime value passed to comptime parameter 'x' of 'double' - --> /main/main.solc:20:44 + --> /main/main.sol:20:44 | -19 | function main() -> word { +19 | function main() returns (word) { 20 | let g = lam (y : word) { return double(y); }; | ^ runtime value passed here 21 | return g(sloadWord()); diff --git a/crates/uitest/tests/fixtures/typeck/ergo_ct_indirect_escape/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_ct_indirect_escape/main.sol new file mode 100644 index 00000000..79097b57 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_ct_indirect_escape/main.sol @@ -0,0 +1,23 @@ +// Smuggle a runtime value into a comptime parameter through a function +// value: bind the comptime function to a local, then call the local with +// a runtime argument. If the SAIL comptime check only looks at direct +// calls, this silently defeats the comptime contract (accept-bug). +import std; + +function sloadWord() returns (word) { + let v : word; + assembly { + v := sload(0) + } + return v; +} + +contract CtIndirectEscape { + function double(comptime x: word) returns (comptime) { + return x + x; + } + function main() returns (word) { + let g = lam (y : word) { return double(y); }; + return g(sloadWord()); + } +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_ct_indirect_escape/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_ct_indirect_escape/main.solc deleted file mode 100644 index 1dd4c171..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_ct_indirect_escape/main.solc +++ /dev/null @@ -1,23 +0,0 @@ -// Smuggle a runtime value into a comptime parameter through a function -// value: bind the comptime function to a local, then call the local with -// a runtime argument. If the SAIL comptime check only looks at direct -// calls, this silently defeats the comptime contract (accept-bug). -import std; - -function sloadWord() -> word { - let v : word; - assembly { - v := sload(0) - } - return v; -} - -contract CtIndirectEscape { - function double(comptime x : word) -> comptime word { - return x + x; - } - function main() -> word { - let g = lam (y : word) { return double(y); }; - return g(sloadWord()); - } -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_expr/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_expr/diagnostics.snap index 43b6d91d..cb87aacb 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_expr/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_expr/diagnostics.snap @@ -1,19 +1,19 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_expr/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_expr/main.sol --- error[SC0203]: constructor expects 2 arguments, but 1 was provided - --> /main/main.solc:4:10 + --> /main/main.sol:4:10 | -1 | data Pair(a, b) = Mk(a, b); +1 | enum Pair { Mk(a, b) } | -- `Mk` defined here 2 | -3 | function f() -> Pair(word, word) { +3 | function f() returns (Pair) { 4 | return Pair.Mk(1); | ^^^^^^^^^^ wrong number of arguments 5 | } | = note: expected 2 arguments = note: found 1 argument - = note: `Mk` has signature `Mk(a, b) -> Pair(a, b)` + = note: `Mk` has signature `Mk(a, b) returns (Pair)` diff --git a/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_expr/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_expr/main.sol new file mode 100644 index 00000000..1c00e4fe --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_expr/main.sol @@ -0,0 +1,5 @@ +enum Pair { Mk(a, b) } + +function f() returns (Pair) { + return Pair.Mk(1); +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_expr/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_expr/main.solc deleted file mode 100644 index ff1ed568..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_expr/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Pair(a, b) = Mk(a, b); - -function f() -> Pair(word, word) { - return Pair.Mk(1); -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_pattern/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_pattern/diagnostics.snap index 6b5ca58f..7c8baf09 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_pattern/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_pattern/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_pattern/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_pattern/main.sol --- error[SC0203]: constructor pattern expects 2 arguments, but 1 was provided - --> /main/main.solc:5:5 + --> /main/main.sol:5:6 | -4 | match p { -5 | | Pair.Mk(x) => return x; - | ^^^^^^^^^^ wrong number of arguments -6 | } +4 | match (p) { +5 | case Pair.Mk(x) { + | ^^^^^^^^^^ wrong number of arguments +6 | return x; | = note: expected 2 arguments = note: found 1 argument diff --git a/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_pattern/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_pattern/main.sol new file mode 100644 index 00000000..209f37ba --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_pattern/main.sol @@ -0,0 +1,9 @@ +enum Pair { Mk(a, b) } + +function f(p: Pair) returns (word) { + match (p) { +case Pair.Mk(x) { +return x; +} +} +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_pattern/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_pattern/main.solc deleted file mode 100644 index de09ff0e..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_ctor_arity_pattern/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -data Pair(a, b) = Mk(a, b); - -function f(p: Pair(word, word)) -> word { - match p { - | Pair.Mk(x) => return x; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_deep_nested_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_deep_nested_mismatch/diagnostics.snap index dd4588d9..2f88dab2 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_deep_nested_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_deep_nested_mismatch/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_deep_nested_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_deep_nested_mismatch/main.sol --- error[SC0201]: argument type mismatch in call to `add3` - --> /main/main.solc:7:34 + --> /main/main.sol:7:34 | -1 | function add3(a: word, b: word, c: word) -> word { +1 | function add3(a: word, b: word, c: word) returns (word) { | - parameter `b` defined here 2 | return a; 3 | } @@ -18,4 +18,4 @@ error[SC0201]: argument type mismatch in call to `add3` | = note: expected `word` because parameter `b` of `add3` has type `word` = note: found type: bool - = note: `add3` has signature `add3(a: word, b: word, c: word) -> word` + = note: `add3` has signature `add3(a: word, b: word, c: word) returns (word)` diff --git a/crates/uitest/tests/fixtures/typeck/ergo_deep_nested_mismatch/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_deep_nested_mismatch/main.sol new file mode 100644 index 00000000..0e503f28 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_deep_nested_mismatch/main.sol @@ -0,0 +1,9 @@ +function add3(a: word, b: word, c: word) returns (word) { + return a; +} + +function f(x: word) returns (word) { + return add3(add3(x, x, add3(x, add3(x, x, x), x)), + add3(x, x, add3(x, true, x)), + x); +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_deep_nested_mismatch/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_deep_nested_mismatch/main.solc deleted file mode 100644 index 217dee14..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_deep_nested_mismatch/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -function add3(a: word, b: word, c: word) -> word { - return a; -} - -function f(x: word) -> word { - return add3(add3(x, x, add3(x, add3(x, x, x), x)), - add3(x, x, add3(x, true, x)), - x); -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_field_access_non_struct/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_field_access_non_struct/diagnostics.snap index 0cff5b05..79d04538 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_field_access_non_struct/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_field_access_non_struct/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_field_access_non_struct/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_field_access_non_struct/main.sol --- error[SC0205]: cannot resolve field `red` - --> /main/main.solc:4:12 + --> /main/main.sol:4:12 | -3 | function f(c: Color) -> word { +3 | function f(c: Color) returns (word) { 4 | return c.red; | ^^^ unknown field 5 | } diff --git a/crates/uitest/tests/fixtures/typeck/ergo_field_access_non_struct/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_field_access_non_struct/main.sol new file mode 100644 index 00000000..3eabe9bf --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_field_access_non_struct/main.sol @@ -0,0 +1,5 @@ +enum Color { Red, Green } + +function f(c: Color) returns (word) { + return c.red; +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_field_access_non_struct/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_field_access_non_struct/main.solc deleted file mode 100644 index 04fe2b4c..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_field_access_non_struct/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Color = Red | Green; - -function f(c: Color) -> word { - return c.red; -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_forall_tyvar_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_forall_tyvar_mismatch/diagnostics.snap index f4e129dc..ed455949 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_forall_tyvar_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_forall_tyvar_mismatch/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_forall_tyvar_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_forall_tyvar_mismatch/main.sol --- -error[SC0207]: cannot satisfy class constraint: a : Int - --> /main/main.solc:1:8 +error[SC0207]: cannot satisfy trait constraint: a: Int + --> /main/main.sol:1:16 | -1 | forall a . function ident(x: a) -> a { - | ^ constraint originates here +1 | function ident(x: a) returns (a) { + | ^ constraint originates here 2 | return 1; 3 | } | - = note: no visible instance matches `a : Int` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `a: Int` + = help: add a matching impl or strengthen the surrounding type context diff --git a/crates/uitest/tests/fixtures/typeck/ergo_forall_tyvar_mismatch/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_forall_tyvar_mismatch/main.sol new file mode 100644 index 00000000..0e598ee8 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_forall_tyvar_mismatch/main.sol @@ -0,0 +1,3 @@ +function ident(x: a) returns (a) { + return 1; +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_forall_tyvar_mismatch/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_forall_tyvar_mismatch/main.solc deleted file mode 100644 index 56faa7a1..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_forall_tyvar_mismatch/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -forall a . function ident(x: a) -> a { - return 1; -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_call_arity/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_call_arity/diagnostics.snap index 7ab227ac..5a285596 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_call_arity/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_call_arity/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_hull_asm_call_arity/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_hull_asm_call_arity/main.sol --- error[SC0203]: Yul call `dbl` expects 1 argument, but 2 were provided - --> /main/main.solc:8:12 + --> /main/main.sol:8:12 | 7 | } 8 | x := dbl(1, 2) diff --git a/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_call_arity/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_call_arity/main.sol new file mode 100644 index 00000000..76d13330 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_call_arity/main.sol @@ -0,0 +1,12 @@ +contract C { + function main() public returns (word) { + let x : word; + assembly { + function dbl(a) -> r { + r := add(a, a) + } + x := dbl(1, 2) + } + return x; + } +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_call_arity/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_call_arity/main.solc deleted file mode 100644 index 0c657464..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_call_arity/main.solc +++ /dev/null @@ -1,12 +0,0 @@ -contract C { - public function main() -> word { - let x : word; - assembly { - function dbl(a) -> r { - r := add(a, a) - } - x := dbl(1, 2) - } - return x; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_undefined_var/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_undefined_var/diagnostics.snap index 0caaef49..28802d6a 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_undefined_var/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_undefined_var/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_hull_asm_undefined_var/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_hull_asm_undefined_var/main.sol --- error[SC0211]: unknown Yul identifier or function: someUndefinedThing - --> /main/main.solc:5:12 + --> /main/main.sol:5:12 | 4 | assembly { 5 | x := someUndefinedThing diff --git a/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_undefined_var/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_undefined_var/main.sol new file mode 100644 index 00000000..78a0c8e4 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_undefined_var/main.sol @@ -0,0 +1,9 @@ +contract C { + function main() public returns (word) { + let x : word; + assembly { + x := someUndefinedThing + } + return x; + } +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_undefined_var/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_undefined_var/main.solc deleted file mode 100644 index 91140972..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_hull_asm_undefined_var/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -contract C { - public function main() -> word { - let x : word; - assembly { - x := someUndefinedThing - } - return x; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_hull_match_arm_arity/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_hull_match_arm_arity/diagnostics.snap deleted file mode 100644 index 107d5b01..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_hull_match_arm_arity/diagnostics.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/test-utils/src/lib.rs -expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_hull_match_arm_arity/main.solc ---- -error[SC0203]: match arm expects 2 arguments, but 1 was provided - --> /main/main.solc:5:3 - | -4 | match x, y { -5 | | Nat.Zero => return 0; - | ^^^^^^^^^^^^^^^^^^^^^^^ wrong number of arguments -6 | | Nat.Succ(a), Nat.Zero => return 1; - | - = note: expected 2 arguments - = note: found 1 argument diff --git a/crates/uitest/tests/fixtures/typeck/ergo_hull_match_arm_arity/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_hull_match_arm_arity/main.solc deleted file mode 100644 index e9a677dd..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_hull_match_arm_arity/main.solc +++ /dev/null @@ -1,15 +0,0 @@ -data Nat = Zero | Succ(Nat); - -function pick(x : Nat, y : Nat) -> word { - match x, y { - | Nat.Zero => return 0; - | Nat.Succ(a), Nat.Zero => return 1; - | Nat.Succ(a), Nat.Succ(b) => return 2; - } -} - -contract T { - public function main() -> word { - return pick(Nat.Zero, Nat.Zero); - } -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_if_expr_branch_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_if_expr_branch_mismatch/diagnostics.snap index 782592ec..6be87762 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_if_expr_branch_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_if_expr_branch_mismatch/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_if_expr_branch_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_if_expr_branch_mismatch/main.sol --- error[SC0201]: type mismatch: expected numeric, found bool - --> /main/main.solc:2:28 + --> /main/main.sol:2:24 | -1 | function f(b: bool) -> word { -2 | let x = if b then 1 else false; - | ^^^^^ expression has mismatched type +1 | function f(b: bool) returns (word) { +2 | let x = b ? 1 : false; + | ^^^^^ expression has mismatched type 3 | return x; | = note: expected type: numeric diff --git a/crates/uitest/tests/fixtures/typeck/ergo_if_expr_branch_mismatch/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_if_expr_branch_mismatch/main.sol new file mode 100644 index 00000000..06bd57b3 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_if_expr_branch_mismatch/main.sol @@ -0,0 +1,4 @@ +function f(b: bool) returns (word) { + let x = b ? 1 : false; + return x; +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_if_expr_branch_mismatch/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_if_expr_branch_mismatch/main.solc deleted file mode 100644 index 5fd1191f..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_if_expr_branch_mismatch/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -function f(b: bool) -> word { - let x = if b then 1 else false; - return x; -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_incomplete_sig_accepted/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_incomplete_sig_accepted/diagnostics.snap deleted file mode 100644 index d52f7162..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_incomplete_sig_accepted/diagnostics.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/test-utils/src/lib.rs -expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_incomplete_sig_accepted/main.solc ---- -error[SC0220]: top-level function must have complete type annotations - --> /main/main.solc:2:19 - | -1 | contract C { -2 | public function id(x) { - | ^^ incomplete signature -3 | return x; - | - = note: signature: public function id(x) - = note: annotate every parameter (name : Type) and provide a return type (-> Type) diff --git a/crates/uitest/tests/fixtures/typeck/ergo_incomplete_sig_accepted/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_incomplete_sig_accepted/main.solc deleted file mode 100644 index f1ea7766..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_incomplete_sig_accepted/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -contract C { - public function id(x) { - return x; - } - - function main() -> word { - return 0; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_lambda_body_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_lambda_body_mismatch/diagnostics.snap index 4c709be6..5ddbaac9 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_lambda_body_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_lambda_body_mismatch/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_lambda_body_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_lambda_body_mismatch/main.sol --- error[SC0201]: type mismatch: expected word, found bool - --> /main/main.solc:6:39 + --> /main/main.sol:6:39 | -5 | function g() -> word { +5 | function g() returns (word) { 6 | return apply(lam (y: word) { return true; }, 1); | ^^^^ expression has mismatched type 7 | } diff --git a/crates/uitest/tests/fixtures/typeck/ergo_lambda_body_mismatch/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_lambda_body_mismatch/main.sol new file mode 100644 index 00000000..54943cdf --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_lambda_body_mismatch/main.sol @@ -0,0 +1,7 @@ +function apply(f: function(word) returns (word), x: word) returns (word) { + return f(x); +} + +function g() returns (word) { + return apply(lam (y: word) { return true; }, 1); +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_lambda_body_mismatch/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_lambda_body_mismatch/main.solc deleted file mode 100644 index 9f2c770f..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_lambda_body_mismatch/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -function apply(f: (word) -> word, x: word) -> word { - return f(x); -} - -function g() -> word { - return apply(lam (y: word) { return true; }, 1); -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_match_branch_divergence/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_match_branch_divergence/diagnostics.snap index a09c33ba..d9195fe7 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_match_branch_divergence/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_match_branch_divergence/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_match_branch_divergence/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_match_branch_divergence/main.sol --- error[SC0201]: type mismatch: expected word, found bool - --> /main/main.solc:6:31 - | -5 | | Shape.Circle(r) => return r; -6 | | Shape.Square(w) => return true; - | ^^^^ expression has mismatched type -7 | } - | - = note: expected type: word - = note: found type: bool + --> /main/main.sol:9:8 + | + 8 | case Shape.Square(w) { + 9 | return true; + | ^^^^ expression has mismatched type +10 | } + | + = note: expected type: word + = note: found type: bool diff --git a/crates/uitest/tests/fixtures/typeck/ergo_match_branch_divergence/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_match_branch_divergence/main.sol new file mode 100644 index 00000000..f123e94c --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_match_branch_divergence/main.sol @@ -0,0 +1,12 @@ +enum Shape { Circle(word), Square(word) } + +function area(s: Shape) returns (word) { + match (s) { +case Shape.Circle(r) { +return r; +} +case Shape.Square(w) { +return true; +} +} +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_match_branch_divergence/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_match_branch_divergence/main.solc deleted file mode 100644 index 51ec788b..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_match_branch_divergence/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -data Shape = Circle(word) | Square(word); - -function area(s: Shape) -> word { - match s { - | Shape.Circle(r) => return r; - | Shape.Square(w) => return true; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_multi_independent_errors/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_multi_independent_errors/diagnostics.snap index fd90829f..0d54998c 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_multi_independent_errors/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_multi_independent_errors/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_multi_independent_errors/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_multi_independent_errors/main.sol --- error[SC0201]: type mismatch: expected word, found bool - --> /main/main.solc:2:10 + --> /main/main.sol:2:10 | -1 | function a() -> word { +1 | function a() returns (word) { 2 | return true; | ^^^^ expression has mismatched type 3 | } @@ -16,9 +16,9 @@ error[SC0201]: type mismatch: expected word, found bool --- error[SC0201]: type mismatch: expected numeric, found bool - --> /main/main.solc:6:10 + --> /main/main.sol:6:10 | -5 | function b() -> bool { +5 | function b() returns (bool) { 6 | return 1; | ^ expression has mismatched type 7 | } @@ -28,9 +28,9 @@ error[SC0201]: type mismatch: expected numeric, found bool --- error[SC0206]: non-callable value of type word - --> /main/main.solc:10:10 + --> /main/main.sol:10:10 | - 9 | function c(x: word) -> word { + 9 | function c(x: word) returns (word) { 10 | return x(1); | ^ callee is not callable 11 | } diff --git a/crates/uitest/tests/fixtures/typeck/ergo_multi_independent_errors/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_multi_independent_errors/main.sol new file mode 100644 index 00000000..9d2e8642 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_multi_independent_errors/main.sol @@ -0,0 +1,11 @@ +function a() returns (word) { + return true; +} + +function b() returns (bool) { + return 1; +} + +function c(x: word) returns (word) { + return x(1); +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_multi_independent_errors/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_multi_independent_errors/main.solc deleted file mode 100644 index 9eb19488..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_multi_independent_errors/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -function a() -> word { - return true; -} - -function b() -> bool { - return 1; -} - -function c(x: word) -> word { - return x(1); -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_occurs_lambda_msg/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_occurs_lambda_msg/diagnostics.snap index 49325339..555e8f97 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_occurs_lambda_msg/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_occurs_lambda_msg/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_occurs_lambda_msg/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_occurs_lambda_msg/main.sol --- error[SC0202]: recursive type would be required - --> /main/main.solc:4:12 + --> /main/main.sol:4:12 | 3 | let g = x(y); 4 | return g(x); @@ -12,5 +12,5 @@ error[SC0202]: recursive type would be required 5 | }; | = note: an inferred type would need to contain itself - = note: recursive shape: ((_) -> _) -> _ + = note: recursive shape: function(function(_) returns (_)) returns (_) = help: add an explicit type annotation or split the recursive call diff --git a/crates/uitest/tests/fixtures/typeck/ergo_occurs_lambda_msg/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_occurs_lambda_msg/main.sol new file mode 100644 index 00000000..e81eea79 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_occurs_lambda_msg/main.sol @@ -0,0 +1,7 @@ +function f() { + let s = lam (x, y) { + let g = x(y); + return g(x); + }; + return (); +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_occurs_lambda_msg/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_occurs_lambda_msg/main.solc deleted file mode 100644 index 8d4d8640..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_occurs_lambda_msg/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -function f() -> () { - let s = lam (x, y) { - let g = x(y); - return g(x); - }; - return (); -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_pattern_wrong_type/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_pattern_wrong_type/diagnostics.snap index 75359cc7..f735cc71 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_pattern_wrong_type/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_pattern_wrong_type/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_pattern_wrong_type/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_pattern_wrong_type/main.sol --- error[SC0201]: type mismatch: expected Shape, found Color - --> /main/main.solc:6:5 + --> /main/main.sol:6:6 | -5 | match c { -6 | | Shape.Circle(r) => return r; - | ^^^^^^^^^^^^^^^ expression has mismatched type -7 | } +5 | match (c) { +6 | case Shape.Circle(r) { + | ^^^^^^^^^^^^^^^ expression has mismatched type +7 | return r; | = note: expected type: Shape = note: found type: Color diff --git a/crates/uitest/tests/fixtures/typeck/ergo_pattern_wrong_type/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_pattern_wrong_type/main.sol new file mode 100644 index 00000000..9f1ac5d8 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_pattern_wrong_type/main.sol @@ -0,0 +1,10 @@ +enum Color { Red, Green } +enum Shape { Circle(word) } + +function f(c: Color) returns (word) { + match (c) { +case Shape.Circle(r) { +return r; +} +} +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_pattern_wrong_type/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_pattern_wrong_type/main.solc deleted file mode 100644 index b8aa7553..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_pattern_wrong_type/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -data Color = Red | Green; -data Shape = Circle(word); - -function f(c: Color) -> word { - match c { - | Shape.Circle(r) => return r; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_recovery_no_cascade/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_recovery_no_cascade/diagnostics.snap index 8abbc598..5f35dc04 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_recovery_no_cascade/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_recovery_no_cascade/diagnostics.snap @@ -1,33 +1,33 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_recovery_no_cascade/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_recovery_no_cascade/main.sol --- error[SC0201]: argument type mismatch in call to `first` - --> /main/main.solc:8:17 - | -1 | function first(p: (word, word)) -> word { - | - parameter `p` defined here -2 | match p { -3 | | (a, b) => return a; + --> /main/main.sol:10:17 + | + 1 | function first(p: (word, word)) returns (word) { + | - parameter `p` defined here + 2 | match (p) { + 3 | case (a, b) { ... -7 | function f() -> word { -8 | let x = first(true); - | ^^^^ argument has mismatched type -9 | return x; - | - = note: expected `(word, word)` because parameter `p` of `first` has type `(word, word)` - = note: found type: bool - = note: `first` has signature `first(p: (word, word)) -> word` + 9 | function f() returns (word) { +10 | let x = first(true); + | ^^^^ argument has mismatched type +11 | return x; + | + = note: expected `(word, word)` because parameter `p` of `first` has type `(word, word)` + = note: found type: bool + = note: `first` has signature `first(p: (word, word)) returns (word)` --- error[SC0201]: type mismatch: expected numeric, found bool - --> /main/main.solc:13:10 + --> /main/main.sol:15:10 | -12 | function g() -> bool { -13 | return 42; +14 | function g() returns (bool) { +15 | return 42; | ^^ expression has mismatched type -14 | } +16 | } | = note: expected type: numeric = note: found type: bool diff --git a/crates/uitest/tests/fixtures/typeck/ergo_recovery_no_cascade/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_recovery_no_cascade/main.sol new file mode 100644 index 00000000..8d5ceb8d --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_recovery_no_cascade/main.sol @@ -0,0 +1,16 @@ +function first(p: (word, word)) returns (word) { + match (p) { +case (a, b) { +return a; +} +} +} + +function f() returns (word) { + let x = first(true); + return x; +} + +function g() returns (bool) { + return 42; +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_recovery_no_cascade/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_recovery_no_cascade/main.solc deleted file mode 100644 index d5063df6..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_recovery_no_cascade/main.solc +++ /dev/null @@ -1,14 +0,0 @@ -function first(p: (word, word)) -> word { - match p { - | (a, b) => return a; - } -} - -function f() -> word { - let x = first(true); - return x; -} - -function g() -> bool { - return 42; -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_return_type_mismatch_data/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_return_type_mismatch_data/diagnostics.snap index c58d3871..7bf7c453 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_return_type_mismatch_data/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_return_type_mismatch_data/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_return_type_mismatch_data/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_return_type_mismatch_data/main.sol --- error[SC0201]: type mismatch: expected word, found Color - --> /main/main.solc:4:10 + --> /main/main.sol:4:10 | -3 | function pick() -> word { +3 | function pick() returns (word) { 4 | return Color.Red; | ^^^^^^^^^ expression has mismatched type 5 | } diff --git a/crates/uitest/tests/fixtures/typeck/ergo_return_type_mismatch_data/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_return_type_mismatch_data/main.sol new file mode 100644 index 00000000..cb11e971 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_return_type_mismatch_data/main.sol @@ -0,0 +1,5 @@ +enum Color { Red, Green } + +function pick() returns (word) { + return Color.Red; +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_return_type_mismatch_data/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_return_type_mismatch_data/main.solc deleted file mode 100644 index ee8697b3..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_return_type_mismatch_data/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Color = Red | Green; - -function pick() -> word { - return Color.Red; -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_tuple_arity_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_tuple_arity_mismatch/diagnostics.snap index 4142d3d1..9daf8f0e 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_tuple_arity_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_tuple_arity_mismatch/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_tuple_arity_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_tuple_arity_mismatch/main.sol --- error[SC0203]: tuple expects 3 arguments, but 2 were provided - --> /main/main.solc:2:10 + --> /main/main.sol:2:10 | -1 | function f() -> (word, word, word) { +1 | function f() returns (word, word, word) { 2 | return (1, 2); | ^^^^^^ wrong number of arguments 3 | } diff --git a/crates/uitest/tests/fixtures/typeck/ergo_tuple_arity_mismatch/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_tuple_arity_mismatch/main.sol new file mode 100644 index 00000000..7a8d1d94 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_tuple_arity_mismatch/main.sol @@ -0,0 +1,3 @@ +function f() returns (word, word, word) { + return (1, 2); +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_tuple_arity_mismatch/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_tuple_arity_mismatch/main.solc deleted file mode 100644 index a7884503..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_tuple_arity_mismatch/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -function f() -> (word, word, word) { - return (1, 2); -} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_type_as_value/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ergo_type_as_value/diagnostics.snap index 59e5e8c7..1f06b330 100644 --- a/crates/uitest/tests/fixtures/typeck/ergo_type_as_value/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ergo_type_as_value/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ergo_type_as_value/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ergo_type_as_value/main.sol --- error[SC0228]: type name used as value: `Pair` - --> /main/main.solc:4:11 + --> /main/main.sol:4:11 | -3 | function main() -> word { +3 | function main() returns (word) { 4 | let p = Pair; | ^^^^ not a value 5 | return 0; diff --git a/crates/uitest/tests/fixtures/typeck/ergo_type_as_value/main.sol b/crates/uitest/tests/fixtures/typeck/ergo_type_as_value/main.sol new file mode 100644 index 00000000..f4804731 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ergo_type_as_value/main.sol @@ -0,0 +1,6 @@ +enum Pair { MkPair(word, word) } + +function main() returns (word) { + let p = Pair; + return 0; +} diff --git a/crates/uitest/tests/fixtures/typeck/ergo_type_as_value/main.solc b/crates/uitest/tests/fixtures/typeck/ergo_type_as_value/main.solc deleted file mode 100644 index d50d184d..00000000 --- a/crates/uitest/tests/fixtures/typeck/ergo_type_as_value/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -data Pair = MkPair(word, word); - -function main() -> word { - let p = Pair; - return 0; -} diff --git a/crates/uitest/tests/fixtures/typeck/final_if_branch_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/final_if_branch_mismatch/diagnostics.snap index c4c8f945..65115518 100644 --- a/crates/uitest/tests/fixtures/typeck/final_if_branch_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/final_if_branch_mismatch/diagnostics.snap @@ -1,14 +1,14 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/final_if_branch_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/typeck/final_if_branch_mismatch/main.sol --- error[SC0201]: type mismatch: expected word, found () - --> /main/main.solc:2:3 + --> /main/main.sol:2:3 | -1 | function f(x : bool) -> word { -2 | if x { 1; } else { true; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expression has mismatched type +1 | function f(x: bool) returns (word) { +2 | if ( x ) { 1; } else { true; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expression has mismatched type 3 | } | = note: expected type: word diff --git a/crates/uitest/tests/fixtures/typeck/final_if_branch_mismatch/main.sol b/crates/uitest/tests/fixtures/typeck/final_if_branch_mismatch/main.sol new file mode 100644 index 00000000..e1f4609a --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/final_if_branch_mismatch/main.sol @@ -0,0 +1,3 @@ +function f(x: bool) returns (word) { + if ( x ) { 1; } else { true; } +} diff --git a/crates/uitest/tests/fixtures/typeck/final_if_branch_mismatch/main.solc b/crates/uitest/tests/fixtures/typeck/final_if_branch_mismatch/main.solc deleted file mode 100644 index 4b1c6f21..00000000 --- a/crates/uitest/tests/fixtures/typeck/final_if_branch_mismatch/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -function f(x : bool) -> word { - if x { 1; } else { true; } -} diff --git a/crates/uitest/tests/fixtures/typeck/generated_dispatch_requires_explicit_imports/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/generated_dispatch_requires_explicit_imports/diagnostics.snap index 5d671bc4..9fcd7117 100644 --- a/crates/uitest/tests/fixtures/typeck/generated_dispatch_requires_explicit_imports/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/generated_dispatch_requires_explicit_imports/diagnostics.snap @@ -1,174 +1,174 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/generated_dispatch_requires_explicit_imports/main.solc +input_file: crates/uitest/tests/fixtures/typeck/generated_dispatch_requires_explicit_imports/main.sol --- error[SC0101]: undefined name: Contract - --> /main/main.solc:1:10 + --> /main/main.sol:1:10 | 1 | contract C { | ^ unknown name -2 | public function echo(value: uint256) -> uint256 { return value; } +2 | function echo(value: uint256) public returns (uint256) { return value; } 3 | } | --- error[SC0101]: undefined name: Fallback - --> /main/main.solc:1:10 + --> /main/main.sol:1:10 | 1 | contract C { | ^ unknown name -2 | public function echo(value: uint256) -> uint256 { return value; } +2 | function echo(value: uint256) public returns (uint256) { return value; } 3 | } | --- error[SC0101]: undefined name: Method - --> /main/main.solc:1:10 + --> /main/main.sol:1:10 | 1 | contract C { | ^ unknown name -2 | public function echo(value: uint256) -> uint256 { return value; } +2 | function echo(value: uint256) public returns (uint256) { return value; } 3 | } | = help: did you mean `echo`? --- error[SC0101]: undefined name: Proxy - --> /main/main.solc:1:10 + --> /main/main.sol:1:10 | 1 | contract C { | ^ unknown name -2 | public function echo(value: uint256) -> uint256 { return value; } +2 | function echo(value: uint256) public returns (uint256) { return value; } 3 | } | --- error[SC0101]: undefined name: RunContract - --> /main/main.solc:1:10 + --> /main/main.sol:1:10 | 1 | contract C { | ^ unknown name -2 | public function echo(value: uint256) -> uint256 { return value; } +2 | function echo(value: uint256) public returns (uint256) { return value; } 3 | } | --- error[SC0101]: undefined name: fallback_default_implementation - --> /main/main.solc:1:10 + --> /main/main.sol:1:10 | 1 | contract C { | ^ unknown name -2 | public function echo(value: uint256) -> uint256 { return value; } +2 | function echo(value: uint256) public returns (uint256) { return value; } 3 | } | --- error[SC0103]: undefined type constructor: NonPayable - --> /main/main.solc:1:10 + --> /main/main.sol:1:10 | 1 | contract C { | ^ undefined type constructor -2 | public function echo(value: uint256) -> uint256 { return value; } +2 | function echo(value: uint256) public returns (uint256) { return value; } 3 | } | --- error[SC0103]: undefined type constructor: Proxy - --> /main/main.solc:1:10 + --> /main/main.sol:1:10 | 1 | contract C { | ^ undefined type constructor -2 | public function echo(value: uint256) -> uint256 { return value; } +2 | function echo(value: uint256) public returns (uint256) { return value; } 3 | } | --- error[SC0103]: undefined type constructor: NonPayable - --> /main/main.solc:2:3 + --> /main/main.sol:2:3 | 1 | contract C { -2 | public function echo(value: uint256) -> uint256 { return value; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ undefined type constructor +2 | function echo(value: uint256) public returns (uint256) { return value; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ undefined type constructor 3 | } | --- error[SC0103]: undefined type constructor: Proxy - --> /main/main.solc:2:3 + --> /main/main.sol:2:3 | 1 | contract C { -2 | public function echo(value: uint256) -> uint256 { return value; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ undefined type constructor +2 | function echo(value: uint256) public returns (uint256) { return value; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ undefined type constructor 3 | } | --- error[SC0103]: undefined type constructor: string - --> /main/main.solc:2:3 + --> /main/main.sol:2:3 | 1 | contract C { -2 | public function echo(value: uint256) -> uint256 { return value; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ undefined type constructor +2 | function echo(value: uint256) public returns (uint256) { return value; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ undefined type constructor 3 | } | --- -error[SC0105]: undefined class: SigString - --> /main/main.solc:2:3 +error[SC0105]: undefined trait: SigString + --> /main/main.sol:2:3 | 1 | contract C { -2 | public function echo(value: uint256) -> uint256 { return value; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ undefined class +2 | function echo(value: uint256) public returns (uint256) { return value; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ undefined trait 3 | } | --- error[SC0231]: ABI output cannot be represented in the ABI: - --> /main/main.solc:2:3 + --> /main/main.sol:2:3 | 1 | contract C { -2 | public function echo(value: uint256) -> uint256 { return value; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type +2 | function echo(value: uint256) public returns (uint256) { return value; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type 3 | } | --- error[SC0231]: ABI parameter cannot be represented in the ABI: - --> /main/main.solc:2:3 + --> /main/main.sol:2:3 | 1 | contract C { -2 | public function echo(value: uint256) -> uint256 { return value; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type +2 | function echo(value: uint256) public returns (uint256) { return value; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type 3 | } | --- error[SC0231]: echo cannot be represented in the ABI: - --> /main/main.solc:2:3 + --> /main/main.sol:2:3 | 1 | contract C { -2 | public function echo(value: uint256) -> uint256 { return value; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type +2 | function echo(value: uint256) public returns (uint256) { return value; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type 3 | } | --- error[SC0103]: undefined type constructor: uint256 - --> /main/main.solc:2:31 + --> /main/main.sol:2:24 | 1 | contract C { -2 | public function echo(value: uint256) -> uint256 { return value; } - | ^^^^^^^ undefined type constructor +2 | function echo(value: uint256) public returns (uint256) { return value; } + | ^^^^^^^ undefined type constructor 3 | } | --- error[SC0103]: undefined type constructor: uint256 - --> /main/main.solc:2:43 + --> /main/main.sol:2:49 | 1 | contract C { -2 | public function echo(value: uint256) -> uint256 { return value; } - | ^^^^^^^ undefined type constructor +2 | function echo(value: uint256) public returns (uint256) { return value; } + | ^^^^^^^ undefined type constructor 3 | } | diff --git a/crates/uitest/tests/fixtures/typeck/generated_dispatch_requires_explicit_imports/main.sol b/crates/uitest/tests/fixtures/typeck/generated_dispatch_requires_explicit_imports/main.sol new file mode 100644 index 00000000..6b78753c --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/generated_dispatch_requires_explicit_imports/main.sol @@ -0,0 +1,3 @@ +contract C { + function echo(value: uint256) public returns (uint256) { return value; } +} diff --git a/crates/uitest/tests/fixtures/typeck/generated_dispatch_requires_explicit_imports/main.solc b/crates/uitest/tests/fixtures/typeck/generated_dispatch_requires_explicit_imports/main.solc deleted file mode 100644 index 237106ac..00000000 --- a/crates/uitest/tests/fixtures/typeck/generated_dispatch_requires_explicit_imports/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -contract C { - public function echo(value: uint256) -> uint256 { return value; } -} diff --git a/crates/uitest/tests/fixtures/typeck/inferred_poly_compose/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/inferred_poly_compose/diagnostics.snap deleted file mode 100644 index e883fe09..00000000 --- a/crates/uitest/tests/fixtures/typeck/inferred_poly_compose/diagnostics.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/test-utils/src/lib.rs -expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/inferred_poly_compose/main.solc ---- -error[SC0220]: top-level function must have complete type annotations - --> /main/main.solc:2:19 - | -1 | contract C { -2 | public function compose(f, g) { - | ^^^^^^^ incomplete signature -3 | return lam (x) { - | - = note: signature: public function compose(f, g) - = note: annotate every parameter (name : Type) and provide a return type (-> Type) diff --git a/crates/uitest/tests/fixtures/typeck/inferred_poly_compose/main.solc b/crates/uitest/tests/fixtures/typeck/inferred_poly_compose/main.solc deleted file mode 100644 index 27fbb023..00000000 --- a/crates/uitest/tests/fixtures/typeck/inferred_poly_compose/main.solc +++ /dev/null @@ -1,16 +0,0 @@ -contract C { - public function compose(f, g) { - return lam (x) { - return f(g(x)); - }; - } - - public function id(x : word) -> word { - return x; - } - - public function main() -> word { - let f = compose(id, id); - return f(42); - } -} diff --git a/crates/uitest/tests/fixtures/typeck/let_unannotated_literal/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/let_unannotated_literal/diagnostics.snap index 2a9cb335..83fa3dd1 100644 --- a/crates/uitest/tests/fixtures/typeck/let_unannotated_literal/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/let_unannotated_literal/diagnostics.snap @@ -1,17 +1,17 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/let_unannotated_literal/main.solc +input_file: crates/uitest/tests/fixtures/typeck/let_unannotated_literal/main.sol --- error[SC0299]: ambiguous inferred type - --> /main/main.solc:1:22 + --> /main/main.sol:1:29 | -1 | function f() -> word { - | ______________________^ +1 | function f() returns (word) { + | _____________________________^ 2 | | let y = 7; 3 | | return 0; 4 | | } | |_^ ambiguous inferred type | - = note: forall _ . _ : Int => () -> word - = help: add a type annotation or a matching instance to fix the ambiguous type variable + = note: <_> function() returns (word) where _: Int + = help: add a type annotation or a matching impl to fix the ambiguous type variable diff --git a/crates/uitest/tests/fixtures/typeck/let_unannotated_literal/main.sol b/crates/uitest/tests/fixtures/typeck/let_unannotated_literal/main.sol new file mode 100644 index 00000000..6b91a3a2 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/let_unannotated_literal/main.sol @@ -0,0 +1,4 @@ +function f() returns (word) { + let y = 7; + return 0; +} diff --git a/crates/uitest/tests/fixtures/typeck/let_unannotated_literal/main.solc b/crates/uitest/tests/fixtures/typeck/let_unannotated_literal/main.solc deleted file mode 100644 index 15d56e87..00000000 --- a/crates/uitest/tests/fixtures/typeck/let_unannotated_literal/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -function f() -> word { - let y = 7; - return 0; -} diff --git a/crates/uitest/tests/fixtures/typeck/manual_generic_adt_external_abi/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/manual_generic_adt_external_abi/diagnostics.snap index 1f9a9116..d91b64bf 100644 --- a/crates/uitest/tests/fixtures/typeck/manual_generic_adt_external_abi/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/manual_generic_adt_external_abi/diagnostics.snap @@ -1,45 +1,45 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/manual_generic_adt_external_abi/main.solc +input_file: crates/uitest/tests/fixtures/typeck/manual_generic_adt_external_abi/main.sol --- -error[SC0207]: cannot satisfy class constraint: Contract(Method(DispatchNameTy_Shapes_roundtrip, NonPayable, Point, Point, (Point) -> Point), Fallback(NonPayable, (), (), () -> ())) : RunContract - --> /main/main.solc:7:10 +error[SC0207]: cannot satisfy trait constraint: Contract, Fallback>: RunContract + --> /main/main.sol:7:10 | 6 | 7 | contract Shapes { | ^^^^^^ constraint originates here -8 | public function roundtrip(p: Point) -> Point { return p; } +8 | function roundtrip(p: Point) public returns (Point) { return p; } | - = note: no visible instance matches `Contract(Method(DispatchNameTy_Shapes_roundtrip, NonPayable, Point, Point, (Point) -> Point), Fallback(NonPayable, (), (), () -> ())) : RunContract` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `Contract, Fallback>: RunContract` + = help: add a matching impl or strengthen the surrounding type context --- error[SC0231]: ABI output cannot be represented in the ABI: Point (manual or excluded Generic representations are not canonical ABI layouts) - --> /main/main.solc:8:3 + --> /main/main.sol:8:3 | 7 | contract Shapes { -8 | public function roundtrip(p: Point) -> Point { return p; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type +8 | function roundtrip(p: Point) public returns (Point) { return p; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type 9 | } | --- error[SC0231]: ABI parameter cannot be represented in the ABI: Point (manual or excluded Generic representations are not canonical ABI layouts) - --> /main/main.solc:8:3 + --> /main/main.sol:8:3 | 7 | contract Shapes { -8 | public function roundtrip(p: Point) -> Point { return p; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type +8 | function roundtrip(p: Point) public returns (Point) { return p; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type 9 | } | --- error[SC0231]: roundtrip cannot be represented in the ABI: Point (manual or excluded Generic representations are not canonical ABI layouts) - --> /main/main.solc:8:3 + --> /main/main.sol:8:3 | 7 | contract Shapes { -8 | public function roundtrip(p: Point) -> Point { return p; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type +8 | function roundtrip(p: Point) public returns (Point) { return p; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type 9 | } | diff --git a/crates/uitest/tests/fixtures/typeck/manual_generic_adt_external_abi/main.sol b/crates/uitest/tests/fixtures/typeck/manual_generic_adt_external_abi/main.sol new file mode 100644 index 00000000..06ba30f8 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/manual_generic_adt_external_abi/main.sol @@ -0,0 +1,9 @@ +import * from std; +import * from std.dispatch; + +pragma no-generic-instance-for Point; +enum Point { Point(word, word) } + +contract Shapes { + function roundtrip(p: Point) public returns (Point) { return p; } +} diff --git a/crates/uitest/tests/fixtures/typeck/manual_generic_adt_external_abi/main.solc b/crates/uitest/tests/fixtures/typeck/manual_generic_adt_external_abi/main.solc deleted file mode 100644 index 3faa48e9..00000000 --- a/crates/uitest/tests/fixtures/typeck/manual_generic_adt_external_abi/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -pragma no-generic-instance-for Point; -data Point = Point(word, word); - -contract Shapes { - public function roundtrip(p: Point) -> Point { return p; } -} diff --git a/crates/uitest/tests/fixtures/typeck/match_branch_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/match_branch_mismatch/diagnostics.snap index ff18876c..8cf71fca 100644 --- a/crates/uitest/tests/fixtures/typeck/match_branch_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/match_branch_mismatch/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/match_branch_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/typeck/match_branch_mismatch/main.sol --- error[SC0201]: type mismatch: expected word, found bool - --> /main/main.solc:4:21 + --> /main/main.sol:7:8 | -3 | | true => return 1; -4 | | false => return true; - | ^^^^ expression has mismatched type -5 | } +6 | case false { +7 | return true; + | ^^^^ expression has mismatched type +8 | } | = note: expected type: word = note: found type: bool diff --git a/crates/uitest/tests/fixtures/typeck/match_branch_mismatch/main.sol b/crates/uitest/tests/fixtures/typeck/match_branch_mismatch/main.sol new file mode 100644 index 00000000..7ce17c13 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/match_branch_mismatch/main.sol @@ -0,0 +1,10 @@ +function h(x: bool) returns (word) { + match (x) { +case true { +return 1; +} +case false { +return true; +} +} +} diff --git a/crates/uitest/tests/fixtures/typeck/match_branch_mismatch/main.solc b/crates/uitest/tests/fixtures/typeck/match_branch_mismatch/main.solc deleted file mode 100644 index 787982dd..00000000 --- a/crates/uitest/tests/fixtures/typeck/match_branch_mismatch/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -function h(x : bool) -> word { - match x { - | true => return 1; - | false => return true; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/missing_word_abi_evidence/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/missing_word_abi_evidence/diagnostics.snap index cba6e5b9..36807a0a 100644 --- a/crates/uitest/tests/fixtures/typeck/missing_word_abi_evidence/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/missing_word_abi_evidence/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/missing_word_abi_evidence/main.solc +input_file: crates/uitest/tests/fixtures/typeck/missing_word_abi_evidence/main.sol --- -error[SC0207]: cannot satisfy class constraint: Contract(Method(DispatchNameTy_WordAbiProbe_echo, NonPayable, word, word, (word) -> word), Fallback(NonPayable, (), (), () -> ())) : RunContract - --> /main/main.solc:7:10 +error[SC0207]: cannot satisfy trait constraint: Contract, Fallback>: RunContract + --> /main/main.sol:7:10 | 6 | // with a bounded solver diagnostic while that evidence is missing. 7 | contract WordAbiProbe { | ^^^^^^^^^^^^ constraint originates here -8 | public function echo(value: word) -> word { +8 | function echo(value: word) public returns (word) { | - = note: no visible instance matches `Contract(Method(DispatchNameTy_WordAbiProbe_echo, NonPayable, word, word, (word) -> word), Fallback(NonPayable, (), (), () -> ())) : RunContract` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `Contract, Fallback>: RunContract` + = help: add a matching impl or strengthen the surrounding type context diff --git a/crates/uitest/tests/fixtures/typeck/missing_word_abi_evidence/main.sol b/crates/uitest/tests/fixtures/typeck/missing_word_abi_evidence/main.sol new file mode 100644 index 00000000..3d1252d7 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/missing_word_abi_evidence/main.sol @@ -0,0 +1,11 @@ +import * from std; +import * from std.dispatch; + +// `word` has ABI metadata (`uint256`) but the pinned shared std does not yet +// provide its selector/decode/encode evidence. The frontend must terminate +// with a bounded solver diagnostic while that evidence is missing. +contract WordAbiProbe { + function echo(value: word) public returns (word) { + return value; + } +} diff --git a/crates/uitest/tests/fixtures/typeck/missing_word_abi_evidence/main.solc b/crates/uitest/tests/fixtures/typeck/missing_word_abi_evidence/main.solc deleted file mode 100644 index 3d060bdc..00000000 --- a/crates/uitest/tests/fixtures/typeck/missing_word_abi_evidence/main.solc +++ /dev/null @@ -1,11 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -// `word` has ABI metadata (`uint256`) but the pinned shared std does not yet -// provide its selector/decode/encode evidence. The frontend must terminate -// with a bounded solver diagnostic while that evidence is missing. -contract WordAbiProbe { - public function echo(value: word) -> word { - return value; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/mutual_recursive_data/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/mutual_recursive_data/diagnostics.snap index 9b0cd6e8..768bf69f 100644 --- a/crates/uitest/tests/fixtures/typeck/mutual_recursive_data/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/mutual_recursive_data/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/mutual_recursive_data/main.solc +input_file: crates/uitest/tests/fixtures/typeck/mutual_recursive_data/main.sol --- error[SC0203]: undefined type: A - --> /main/main.solc:2:12 + --> /main/main.sol:2:12 | -1 | data A = A(B); -2 | data B = B(A); +1 | enum A { A(B) } +2 | enum B { B(A) } | ^ undefined type 3 | | diff --git a/crates/uitest/tests/fixtures/typeck/mutual_recursive_data/main.sol b/crates/uitest/tests/fixtures/typeck/mutual_recursive_data/main.sol new file mode 100644 index 00000000..c7cf965d --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/mutual_recursive_data/main.sol @@ -0,0 +1,6 @@ +enum A { A(B) } +enum B { B(A) } + +function f(x: A) returns (word) { + return 0; +} diff --git a/crates/uitest/tests/fixtures/typeck/mutual_recursive_data/main.solc b/crates/uitest/tests/fixtures/typeck/mutual_recursive_data/main.solc deleted file mode 100644 index f3d03ddb..00000000 --- a/crates/uitest/tests/fixtures/typeck/mutual_recursive_data/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -data A = A(B); -data B = B(A); - -function f(x: A) -> word { - return 0; -} diff --git a/crates/uitest/tests/fixtures/typeck/nested_constructor_nonexhaustive/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/nested_constructor_nonexhaustive/diagnostics.snap index adc65a9d..a9c84a43 100644 --- a/crates/uitest/tests/fixtures/typeck/nested_constructor_nonexhaustive/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/nested_constructor_nonexhaustive/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/nested_constructor_nonexhaustive/main.solc +input_file: crates/uitest/tests/fixtures/typeck/nested_constructor_nonexhaustive/main.sol --- error[SC0302]: non-exhaustive pattern match - --> /main/main.solc:5:9 + --> /main/main.sol:5:10 | -4 | function pick(x : Outer) -> word { -5 | match x { - | ^ non-exhaustive match -6 | | Outer.Other => return 0; +4 | function pick(x: Outer) returns (word) { +5 | match (x) { + | ^ non-exhaustive match +6 | case Outer.Other { | = note: missing case: Outer.Wrap(Inner.B) = note: help: add a clause that covers the missing case diff --git a/crates/uitest/tests/fixtures/typeck/nested_constructor_nonexhaustive/main.sol b/crates/uitest/tests/fixtures/typeck/nested_constructor_nonexhaustive/main.sol new file mode 100644 index 00000000..7bb7e9db --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/nested_constructor_nonexhaustive/main.sol @@ -0,0 +1,13 @@ +enum Inner { A, B } +enum Outer { Other, Wrap(Inner) } + +function pick(x: Outer) returns (word) { + match (x) { +case Outer.Other { +return 0; +} +case Outer.Wrap(Inner.A) { +return 1; +} +} +} diff --git a/crates/uitest/tests/fixtures/typeck/nested_constructor_nonexhaustive/main.solc b/crates/uitest/tests/fixtures/typeck/nested_constructor_nonexhaustive/main.solc deleted file mode 100644 index e7585c81..00000000 --- a/crates/uitest/tests/fixtures/typeck/nested_constructor_nonexhaustive/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -data Inner = A | B; -data Outer = Other | Wrap(Inner); - -function pick(x : Outer) -> word { - match x { - | Outer.Other => return 0; - | Outer.Wrap(Inner.A) => return 1; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/nested_constructor_unreachable/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/nested_constructor_unreachable/diagnostics.snap index e2bbd80e..e589f977 100644 --- a/crates/uitest/tests/fixtures/typeck/nested_constructor_unreachable/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/nested_constructor_unreachable/diagnostics.snap @@ -1,14 +1,16 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/nested_constructor_unreachable/main.solc +input_file: crates/uitest/tests/fixtures/typeck/nested_constructor_unreachable/main.sol --- warning[SC0303]: unreachable match arm - --> /main/main.solc:7:3 - | -6 | | Outer.Wrap(_) => return 0; -7 | | Outer.Wrap(Inner.A) => return 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this arm is unreachable -8 | | Outer.Other => return 2; - | - = note: this arm is covered by previous match arms + --> /main/main.sol:9:1 + | + 8 | } + 9 | / case Outer.Wrap(Inner.A) { +10 | | return 1; +11 | | } + | |_^ this arm is unreachable +12 | case Outer.Other { + | + = note: this arm is covered by previous match arms diff --git a/crates/uitest/tests/fixtures/typeck/nested_constructor_unreachable/main.sol b/crates/uitest/tests/fixtures/typeck/nested_constructor_unreachable/main.sol new file mode 100644 index 00000000..8c803b5f --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/nested_constructor_unreachable/main.sol @@ -0,0 +1,16 @@ +enum Inner { A, B } +enum Outer { Other, Wrap(Inner) } + +function pick(x: Outer) returns (word) { + match (x) { +case Outer.Wrap(_) { +return 0; +} +case Outer.Wrap(Inner.A) { +return 1; +} +case Outer.Other { +return 2; +} +} +} diff --git a/crates/uitest/tests/fixtures/typeck/nested_constructor_unreachable/main.solc b/crates/uitest/tests/fixtures/typeck/nested_constructor_unreachable/main.solc deleted file mode 100644 index 2ca82cce..00000000 --- a/crates/uitest/tests/fixtures/typeck/nested_constructor_unreachable/main.solc +++ /dev/null @@ -1,10 +0,0 @@ -data Inner = A | B; -data Outer = Other | Wrap(Inner); - -function pick(x : Outer) -> word { - match x { - | Outer.Wrap(_) => return 0; - | Outer.Wrap(Inner.A) => return 1; - | Outer.Other => return 2; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/nonexhaustive_contract/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/nonexhaustive_contract/diagnostics.snap index 03207c60..de513b53 100644 --- a/crates/uitest/tests/fixtures/typeck/nonexhaustive_contract/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/nonexhaustive_contract/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/nonexhaustive_contract/main.solc +input_file: crates/uitest/tests/fixtures/typeck/nonexhaustive_contract/main.sol --- error[SC0302]: non-exhaustive pattern match - --> /main/main.solc:5:11 + --> /main/main.sol:5:12 | -4 | public function pick(x : Flag) -> word { -5 | match x { - | ^ non-exhaustive match -6 | | Flag.Off => return 0; +4 | function pick(x: Flag) public returns (word) { +5 | match (x) { + | ^ non-exhaustive match +6 | case Flag.Off { | = note: missing case: Flag.On = note: help: add a clause that covers the missing case diff --git a/crates/uitest/tests/fixtures/typeck/nonexhaustive_contract/main.sol b/crates/uitest/tests/fixtures/typeck/nonexhaustive_contract/main.sol new file mode 100644 index 00000000..41f3d391 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/nonexhaustive_contract/main.sol @@ -0,0 +1,15 @@ +contract C { + enum Flag { Off, On } + + function pick(x: Flag) public returns (word) { + match (x) { +case Flag.Off { +return 0; +} +} + } + + function main() returns (word) { + return 0; + } +} diff --git a/crates/uitest/tests/fixtures/typeck/nonexhaustive_contract/main.solc b/crates/uitest/tests/fixtures/typeck/nonexhaustive_contract/main.solc deleted file mode 100644 index a721e785..00000000 --- a/crates/uitest/tests/fixtures/typeck/nonexhaustive_contract/main.solc +++ /dev/null @@ -1,13 +0,0 @@ -contract C { - data Flag = Off | On; - - public function pick(x : Flag) -> word { - match x { - | Flag.Off => return 0; - } - } - - function main() -> word { - return 0; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/nonexhaustive_free_fn/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/nonexhaustive_free_fn/diagnostics.snap index 75ca3d01..ff302011 100644 --- a/crates/uitest/tests/fixtures/typeck/nonexhaustive_free_fn/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/nonexhaustive_free_fn/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/nonexhaustive_free_fn/main.solc +input_file: crates/uitest/tests/fixtures/typeck/nonexhaustive_free_fn/main.sol --- error[SC0302]: non-exhaustive pattern match - --> /main/main.solc:4:9 + --> /main/main.sol:4:10 | -3 | function pick(x : Flag) -> word { -4 | match x { - | ^ non-exhaustive match -5 | | Flag.Off => return 0; +3 | function pick(x: Flag) returns (word) { +4 | match (x) { + | ^ non-exhaustive match +5 | case Flag.Off { | = note: missing case: Flag.On = note: help: add a clause that covers the missing case diff --git a/crates/uitest/tests/fixtures/typeck/nonexhaustive_free_fn/main.sol b/crates/uitest/tests/fixtures/typeck/nonexhaustive_free_fn/main.sol new file mode 100644 index 00000000..67635787 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/nonexhaustive_free_fn/main.sol @@ -0,0 +1,9 @@ +enum Flag { Off, On } + +function pick(x: Flag) returns (word) { + match (x) { +case Flag.Off { +return 0; +} +} +} diff --git a/crates/uitest/tests/fixtures/typeck/nonexhaustive_free_fn/main.solc b/crates/uitest/tests/fixtures/typeck/nonexhaustive_free_fn/main.solc deleted file mode 100644 index 5498afc9..00000000 --- a/crates/uitest/tests/fixtures/typeck/nonexhaustive_free_fn/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -data Flag = Off | On; - -function pick(x : Flag) -> word { - match x { - | Flag.Off => return 0; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/nonfinal_return/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/nonfinal_return/diagnostics.snap index 03865a0d..e437a42b 100644 --- a/crates/uitest/tests/fixtures/typeck/nonfinal_return/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/nonfinal_return/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/nonfinal_return/main.solc +input_file: crates/uitest/tests/fixtures/typeck/nonfinal_return/main.sol --- error[SC0222]: illegal return statement - --> /main/main.solc:2:3 + --> /main/main.sol:2:3 | -1 | function g() -> word { +1 | function g() returns (word) { 2 | return 1; | ^^^^^^^^^ return before end of block 3 | return 2; diff --git a/crates/uitest/tests/fixtures/typeck/nonfinal_return/main.sol b/crates/uitest/tests/fixtures/typeck/nonfinal_return/main.sol new file mode 100644 index 00000000..3ef4fe10 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/nonfinal_return/main.sol @@ -0,0 +1,4 @@ +function g() returns (word) { + return 1; + return 2; +} diff --git a/crates/uitest/tests/fixtures/typeck/nonfinal_return/main.solc b/crates/uitest/tests/fixtures/typeck/nonfinal_return/main.solc deleted file mode 100644 index ba6c25bb..00000000 --- a/crates/uitest/tests/fixtures/typeck/nonfinal_return/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -function g() -> word { - return 1; - return 2; -} diff --git a/crates/uitest/tests/fixtures/typeck/nullary_type_applied_let/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/nullary_type_applied_let/diagnostics.snap index c863da97..b3e34509 100644 --- a/crates/uitest/tests/fixtures/typeck/nullary_type_applied_let/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/nullary_type_applied_let/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/nullary_type_applied_let/main.solc +input_file: crates/uitest/tests/fixtures/typeck/nullary_type_applied_let/main.sol --- error[SC0299]: Invalid number of type arguments! - --> /main/main.solc:4:10 + --> /main/main.sol:4:10 | -3 | function f() -> word { -4 | let x: M(word) = M.Mk; +3 | function f() returns (word) { +4 | let x: M = M.Mk; | ^^^^^^^ diagnostic reported here 5 | return 0; | = note: Type M is expected to have 0 type arguments - = note: but, type M(word) has 1 arguments + = note: but, type M has 1 arguments diff --git a/crates/uitest/tests/fixtures/typeck/nullary_type_applied_let/main.sol b/crates/uitest/tests/fixtures/typeck/nullary_type_applied_let/main.sol new file mode 100644 index 00000000..fdd1397d --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/nullary_type_applied_let/main.sol @@ -0,0 +1,6 @@ +enum M { Mk } + +function f() returns (word) { + let x: M = M.Mk; + return 0; +} diff --git a/crates/uitest/tests/fixtures/typeck/nullary_type_applied_let/main.solc b/crates/uitest/tests/fixtures/typeck/nullary_type_applied_let/main.solc deleted file mode 100644 index ef437697..00000000 --- a/crates/uitest/tests/fixtures/typeck/nullary_type_applied_let/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -data M = Mk; - -function f() -> word { - let x: M(word) = M.Mk; - return 0; -} diff --git a/crates/uitest/tests/fixtures/typeck/nullary_type_applied_signature/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/nullary_type_applied_signature/diagnostics.snap index add658ba..5fefce41 100644 --- a/crates/uitest/tests/fixtures/typeck/nullary_type_applied_signature/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/nullary_type_applied_signature/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/nullary_type_applied_signature/main.solc +input_file: crates/uitest/tests/fixtures/typeck/nullary_type_applied_signature/main.sol --- error[SC0299]: Invalid number of type arguments! - --> /main/main.solc:3:15 + --> /main/main.sol:3:15 | 2 | -3 | function f(x: M(word)) -> word { +3 | function f(x: M) returns (word) { | ^^^^^^^ diagnostic reported here 4 | return 0; | = note: Type M is expected to have 0 type arguments - = note: but, type M(word) has 1 arguments + = note: but, type M has 1 arguments diff --git a/crates/uitest/tests/fixtures/typeck/nullary_type_applied_signature/main.sol b/crates/uitest/tests/fixtures/typeck/nullary_type_applied_signature/main.sol new file mode 100644 index 00000000..f0cc7d05 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/nullary_type_applied_signature/main.sol @@ -0,0 +1,5 @@ +enum M { Mk } + +function f(x: M) returns (word) { + return 0; +} diff --git a/crates/uitest/tests/fixtures/typeck/nullary_type_applied_signature/main.solc b/crates/uitest/tests/fixtures/typeck/nullary_type_applied_signature/main.solc deleted file mode 100644 index f7272704..00000000 --- a/crates/uitest/tests/fixtures/typeck/nullary_type_applied_signature/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data M = Mk; - -function f(x: M(word)) -> word { - return 0; -} diff --git a/crates/uitest/tests/fixtures/typeck/occurs_check/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/occurs_check/diagnostics.snap index 26777c44..7914f07a 100644 --- a/crates/uitest/tests/fixtures/typeck/occurs_check/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/occurs_check/diagnostics.snap @@ -1,16 +1,16 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/occurs_check/main.solc +input_file: crates/uitest/tests/fixtures/typeck/occurs_check/main.sol --- error[SC0202]: recursive type would be required - --> /main/main.solc:2:30 + --> /main/main.sol:2:30 | -1 | function f() -> () { +1 | function f() { 2 | let self = lam(x) { return x(x); }; | ^^^^ recursive type required here 3 | return (); | = note: an inferred type would need to contain itself - = note: recursive shape: (_) -> _ + = note: recursive shape: function(_) returns (_) = help: add an explicit type annotation or split the recursive call diff --git a/crates/uitest/tests/fixtures/typeck/occurs_check/main.sol b/crates/uitest/tests/fixtures/typeck/occurs_check/main.sol new file mode 100644 index 00000000..9f1546e6 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/occurs_check/main.sol @@ -0,0 +1,4 @@ +function f() { + let self = lam(x) { return x(x); }; + return (); +} diff --git a/crates/uitest/tests/fixtures/typeck/occurs_check/main.solc b/crates/uitest/tests/fixtures/typeck/occurs_check/main.solc deleted file mode 100644 index da2e54f8..00000000 --- a/crates/uitest/tests/fixtures/typeck/occurs_check/main.solc +++ /dev/null @@ -1,4 +0,0 @@ -function f() -> () { - let self = lam(x) { return x(x); }; - return (); -} diff --git a/crates/uitest/tests/fixtures/typeck/ok_enum_without_semicolon/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ok_enum_without_semicolon/diagnostics.snap new file mode 100644 index 00000000..dfea30b3 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ok_enum_without_semicolon/diagnostics.snap @@ -0,0 +1,6 @@ +--- +source: crates/test-utils/src/lib.rs +expression: rendered +input_file: crates/uitest/tests/fixtures/typeck/ok_enum_without_semicolon/main.sol +--- +no diagnostics diff --git a/crates/uitest/tests/fixtures/typeck/ok_enum_without_semicolon/main.sol b/crates/uitest/tests/fixtures/typeck/ok_enum_without_semicolon/main.sol new file mode 100644 index 00000000..8f391975 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ok_enum_without_semicolon/main.sol @@ -0,0 +1 @@ +enum D { C } diff --git a/crates/uitest/tests/fixtures/typeck/ok_trailing_import_comma/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ok_trailing_import_comma/diagnostics.snap new file mode 100644 index 00000000..f48d1c95 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ok_trailing_import_comma/diagnostics.snap @@ -0,0 +1,6 @@ +--- +source: crates/test-utils/src/lib.rs +expression: rendered +input_file: crates/uitest/tests/fixtures/typeck/ok_trailing_import_comma/main.sol +--- +no diagnostics diff --git a/crates/uitest/tests/fixtures/typeck/ok_trailing_import_comma/m.sol b/crates/uitest/tests/fixtures/typeck/ok_trailing_import_comma/m.sol new file mode 100644 index 00000000..c800724e --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ok_trailing_import_comma/m.sol @@ -0,0 +1,4 @@ +function a() {} +function b() {} + +export { a, b }; diff --git a/crates/uitest/tests/fixtures/typeck/ok_trailing_import_comma/main.sol b/crates/uitest/tests/fixtures/typeck/ok_trailing_import_comma/main.sol new file mode 100644 index 00000000..58d5056b --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ok_trailing_import_comma/main.sol @@ -0,0 +1 @@ +import {a, b,} from m; diff --git a/crates/uitest/tests/fixtures/typeck/ok_trait_head_generic/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ok_trait_head_generic/diagnostics.snap new file mode 100644 index 00000000..303da73d --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ok_trait_head_generic/diagnostics.snap @@ -0,0 +1,6 @@ +--- +source: crates/test-utils/src/lib.rs +expression: rendered +input_file: crates/uitest/tests/fixtures/typeck/ok_trait_head_generic/main.sol +--- +no diagnostics diff --git a/crates/uitest/tests/fixtures/typeck/ok_trait_head_generic/main.sol b/crates/uitest/tests/fixtures/typeck/ok_trait_head_generic/main.sol new file mode 100644 index 00000000..2c4726e0 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ok_trait_head_generic/main.sol @@ -0,0 +1 @@ +trait C {} diff --git a/crates/uitest/tests/fixtures/typeck/ok_uint256_binops_class_methods/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/ok_uint256_binops_class_methods/diagnostics.snap index b567edf5..dd91192e 100644 --- a/crates/uitest/tests/fixtures/typeck/ok_uint256_binops_class_methods/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/ok_uint256_binops_class_methods/diagnostics.snap @@ -1,6 +1,6 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/ok_uint256_binops_class_methods/main.solc +input_file: crates/uitest/tests/fixtures/typeck/ok_uint256_binops_class_methods/main.sol --- no diagnostics diff --git a/crates/uitest/tests/fixtures/typeck/ok_uint256_binops_class_methods/main.sol b/crates/uitest/tests/fixtures/typeck/ok_uint256_binops_class_methods/main.sol new file mode 100644 index 00000000..15f218b0 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/ok_uint256_binops_class_methods/main.sol @@ -0,0 +1,28 @@ +import * from std; +import * from std.dispatch; + +contract Uint256Binops { + function mul_u256(x: uint256, y: uint256) public returns (uint256) { + return x * y; + } + + function div_u256(x: uint256, y: uint256) public returns (uint256) { + return x / y; + } + + function mod_u256(x: uint256, y: uint256) public returns (uint256) { + return x % y; + } + + function band_u256(x: uint256, y: uint256) public returns (uint256) { + return x & y; + } + + function bxor_u256(x: uint256, y: uint256) public returns (uint256) { + return x ^ y; + } + + function bor_u256(x: uint256, y: uint256) public returns (uint256) { + return x | y; + } +} diff --git a/crates/uitest/tests/fixtures/typeck/ok_uint256_binops_class_methods/main.solc b/crates/uitest/tests/fixtures/typeck/ok_uint256_binops_class_methods/main.solc deleted file mode 100644 index 3baa837e..00000000 --- a/crates/uitest/tests/fixtures/typeck/ok_uint256_binops_class_methods/main.solc +++ /dev/null @@ -1,28 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -contract Uint256Binops { - public function mul_u256(x : uint256, y : uint256) -> uint256 { - return x * y; - } - - public function div_u256(x : uint256, y : uint256) -> uint256 { - return x / y; - } - - public function mod_u256(x : uint256, y : uint256) -> uint256 { - return x % y; - } - - public function band_u256(x : uint256, y : uint256) -> uint256 { - return x & y; - } - - public function bxor_u256(x : uint256, y : uint256) -> uint256 { - return x ^ y; - } - - public function bor_u256(x : uint256, y : uint256) -> uint256 { - return x | y; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/omitted_forall_poly/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/omitted_forall_poly/diagnostics.snap deleted file mode 100644 index 48e100f1..00000000 --- a/crates/uitest/tests/fixtures/typeck/omitted_forall_poly/diagnostics.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/test-utils/src/lib.rs -expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/omitted_forall_poly/main.solc ---- -error[SC0220]: top-level function must have complete type annotations - --> /main/main.solc:1:10 - | -1 | function id(x) { - | ^^ incomplete signature -2 | return x; -3 | } - | - = note: signature: function id(x) - = note: annotate every parameter (name : Type) and provide a return type (-> Type) diff --git a/crates/uitest/tests/fixtures/typeck/omitted_forall_poly/main.solc b/crates/uitest/tests/fixtures/typeck/omitted_forall_poly/main.solc deleted file mode 100644 index 1cd02c73..00000000 --- a/crates/uitest/tests/fixtures/typeck/omitted_forall_poly/main.solc +++ /dev/null @@ -1,9 +0,0 @@ -function id(x) { - return x; -} - -contract C { - public function main() -> word { - return id(42); - } -} diff --git a/crates/uitest/tests/fixtures/typeck/return_bool_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/return_bool_mismatch/diagnostics.snap index 47986aad..1c4464ce 100644 --- a/crates/uitest/tests/fixtures/typeck/return_bool_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/return_bool_mismatch/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/return_bool_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/typeck/return_bool_mismatch/main.sol --- error[SC0201]: type mismatch: expected word, found bool - --> /main/main.solc:2:10 + --> /main/main.sol:2:10 | -1 | function f() -> word { +1 | function f() returns (word) { 2 | return true; | ^^^^ expression has mismatched type 3 | } diff --git a/crates/uitest/tests/fixtures/typeck/return_bool_mismatch/main.sol b/crates/uitest/tests/fixtures/typeck/return_bool_mismatch/main.sol new file mode 100644 index 00000000..1925917c --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/return_bool_mismatch/main.sol @@ -0,0 +1,3 @@ +function f() returns (word) { + return true; +} diff --git a/crates/uitest/tests/fixtures/typeck/return_bool_mismatch/main.solc b/crates/uitest/tests/fixtures/typeck/return_bool_mismatch/main.solc deleted file mode 100644 index 35053f21..00000000 --- a/crates/uitest/tests/fixtures/typeck/return_bool_mismatch/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -function f() -> word { - return true; -} diff --git a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_ambiguous/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_ambiguous/diagnostics.snap index 5713f249..8fdb6afd 100644 --- a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_ambiguous/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_ambiguous/diagnostics.snap @@ -1,24 +1,24 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/shorthand_constructor_ambiguous/main.solc +input_file: crates/uitest/tests/fixtures/typeck/shorthand_constructor_ambiguous/main.sol --- error[SC0108]: duplicate declaration `Choice.Same` in term namespace - --> /main/main.solc:1:28 + --> /main/main.sol:1:27 | -1 | data Choice = Same(word) | Same(bool); - | ---- ^^^^ duplicate declaration +1 | enum Choice { Same(word), Same(bool) } + | ---- ^^^^ duplicate declaration | | | previous declaration 2 | -3 | function ambiguous() -> Choice { +3 | function ambiguous() returns (Choice) { | --- error[SC0224]: cannot resolve shorthand constructor `.Same`: ambiguous candidates: Same, Same - --> /main/main.solc:4:10 + --> /main/main.sol:4:10 | -3 | function ambiguous() -> Choice { +3 | function ambiguous() returns (Choice) { 4 | return .Same(1); | ^^^^^^^^ shorthand constructor 5 | } diff --git a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_ambiguous/main.sol b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_ambiguous/main.sol new file mode 100644 index 00000000..b32d27ce --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_ambiguous/main.sol @@ -0,0 +1,5 @@ +enum Choice { Same(word), Same(bool) } + +function ambiguous() returns (Choice) { + return .Same(1); +} diff --git a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_ambiguous/main.solc b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_ambiguous/main.solc deleted file mode 100644 index f43cac3f..00000000 --- a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_ambiguous/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Choice = Same(word) | Same(bool); - -function ambiguous() -> Choice { - return .Same(1); -} diff --git a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_assignment_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_assignment_mismatch/diagnostics.snap index 265fc387..3892964b 100644 --- a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_assignment_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_assignment_mismatch/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/shorthand_constructor_assignment_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/typeck/shorthand_constructor_assignment_mismatch/main.sol --- error[SC0201]: argument type mismatch in call to `Some` - --> /main/main.solc:5:13 + --> /main/main.sol:5:13 | -1 | data Option = None | Some(word); - | ---- parameter 1 defined here +1 | enum Option { None, Some(word) } + | ---- parameter 1 defined here 2 | -3 | function bad() -> word { +3 | function bad() returns (word) { 4 | let x : Option; 5 | x = .Some(true); | ^^^^ argument has mismatched type @@ -17,4 +17,4 @@ error[SC0201]: argument type mismatch in call to `Some` | = note: expected `word` because parameter 1 of `Some` has type `word` = note: found type: bool - = note: `Some` has signature `Some(word) -> Option` + = note: `Some` has signature `Some(word) returns (Option)` diff --git a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_assignment_mismatch/main.sol b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_assignment_mismatch/main.sol new file mode 100644 index 00000000..f896e9df --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_assignment_mismatch/main.sol @@ -0,0 +1,7 @@ +enum Option { None, Some(word) } + +function bad() returns (word) { + let x : Option; + x = .Some(true); + return 0; +} diff --git a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_assignment_mismatch/main.solc b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_assignment_mismatch/main.solc deleted file mode 100644 index 32337e7a..00000000 --- a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_assignment_mismatch/main.solc +++ /dev/null @@ -1,7 +0,0 @@ -data Option = None | Some(word); - -function bad() -> word { - let x : Option; - x = .Some(true); - return 0; -} diff --git a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_context/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_context/diagnostics.snap index 074e970c..da066943 100644 --- a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_context/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_context/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_context/main.solc +input_file: crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_context/main.sol --- error[SC0224]: cannot resolve shorthand constructor `.Some`: cannot resolve without expected constructor type - --> /main/main.solc:4:11 + --> /main/main.sol:4:11 | -3 | function noContext() -> word { +3 | function noContext() returns (word) { 4 | let x = .Some(1); | ^^^^^^^^ shorthand constructor 5 | return 0; diff --git a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_context/main.sol b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_context/main.sol new file mode 100644 index 00000000..c27b4b85 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_context/main.sol @@ -0,0 +1,6 @@ +enum Option { None, Some(word) } + +function noContext() returns (word) { + let x = .Some(1); + return 0; +} diff --git a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_context/main.solc b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_context/main.solc deleted file mode 100644 index 2939a370..00000000 --- a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_context/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -data Option = None | Some(word); - -function noContext() -> word { - let x = .Some(1); - return 0; -} diff --git a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_match/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_match/diagnostics.snap index 24c901db..17d85201 100644 --- a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_match/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_match/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_match/main.solc +input_file: crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_match/main.sol --- error[SC0101]: undefined name: Some - --> /main/main.solc:4:11 + --> /main/main.sol:4:11 | -3 | function noMatch() -> Other { +3 | function noMatch() returns (Other) { 4 | return .Some(1); | ^^^^ unknown name 5 | } diff --git a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_match/main.sol b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_match/main.sol new file mode 100644 index 00000000..77800cd2 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_match/main.sol @@ -0,0 +1,5 @@ +enum Other { Other } + +function noMatch() returns (Other) { + return .Some(1); +} diff --git a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_match/main.solc b/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_match/main.solc deleted file mode 100644 index 12e7362c..00000000 --- a/crates/uitest/tests/fixtures/typeck/shorthand_constructor_no_match/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data Other = Other; - -function noMatch() -> Other { - return .Some(1); -} diff --git a/crates/uitest/tests/fixtures/typeck/source_runtime_main_constructor_abi_error/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/source_runtime_main_constructor_abi_error/diagnostics.snap index 99d46b6f..44a9353f 100644 --- a/crates/uitest/tests/fixtures/typeck/source_runtime_main_constructor_abi_error/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/source_runtime_main_constructor_abi_error/diagnostics.snap @@ -1,25 +1,25 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/source_runtime_main_constructor_abi_error/main.solc +input_file: crates/uitest/tests/fixtures/typeck/source_runtime_main_constructor_abi_error/main.sol --- -error[SC0207]: cannot satisfy class constraint: ABIDecoder(Choice, MemoryWordReader) : ABIDecode(Choice) - --> /main/main.solc:6:3 +error[SC0207]: cannot satisfy trait constraint: ABIDecoder: ABIDecode + --> /main/main.sol:6:3 | 5 | contract C { 6 | constructor(value: Choice) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constraint originates here -7 | function main() -> () { return (); } +7 | function main() { return (); } | - = note: no visible instance matches `ABIDecoder(Choice, MemoryWordReader) : ABIDecode(Choice)` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `ABIDecoder: ABIDecode` + = help: add a matching impl or strengthen the surrounding type context --- error[SC0231]: ABI parameter cannot be represented in the ABI: Choice (user-defined ADTs are not supported by the canonical external ABI) - --> /main/main.solc:6:3 + --> /main/main.sol:6:3 | 5 | contract C { 6 | constructor(value: Choice) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsupported ABI type -7 | function main() -> () { return (); } +7 | function main() { return (); } | diff --git a/crates/uitest/tests/fixtures/typeck/source_runtime_main_constructor_abi_error/main.sol b/crates/uitest/tests/fixtures/typeck/source_runtime_main_constructor_abi_error/main.sol new file mode 100644 index 00000000..da789021 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/source_runtime_main_constructor_abi_error/main.sol @@ -0,0 +1,8 @@ +import * from std; + +enum Choice { Left(word), Right(word) } + +contract C { + constructor(value: Choice) {} + function main() { return (); } +} diff --git a/crates/uitest/tests/fixtures/typeck/source_runtime_main_constructor_abi_error/main.solc b/crates/uitest/tests/fixtures/typeck/source_runtime_main_constructor_abi_error/main.solc deleted file mode 100644 index 93cb5f64..00000000 --- a/crates/uitest/tests/fixtures/typeck/source_runtime_main_constructor_abi_error/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -import std.{*}; - -data Choice = Left(word) | Right(word); - -contract C { - constructor(value: Choice) {} - function main() -> () { return (); } -} diff --git a/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_add_bool/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_add_bool/diagnostics.snap index c43ef022..c4d63125 100644 --- a/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_add_bool/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_add_bool/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/storage_mapping_compound_add_bool/main.solc +input_file: crates/uitest/tests/fixtures/typeck/storage_mapping_compound_add_bool/main.sol --- -error[SC0207]: cannot satisfy class constraint: bool : Add - --> /main/main.solc:22:31 +error[SC0207]: cannot satisfy trait constraint: bool: Add + --> /main/main.sol:22:25 | -21 | m: mapping(word, bool); -22 | function f(k: word) -> () { m[k] += true; } - | ^^^^^^^^^^^^ constraint originates here -23 | function main() -> () { return (); } +21 | m: mapping(word => bool); +22 | function f(k: word) { m[k] += true; } + | ^^^^^^^^^^^^ constraint originates here +23 | function main() { return (); } | - = note: no visible instance matches `bool : Add` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `bool: Add` + = help: add a matching impl or strengthen the surrounding type context diff --git a/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_add_bool/main.sol b/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_add_bool/main.sol new file mode 100644 index 00000000..10cb00f8 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_add_bool/main.sol @@ -0,0 +1,24 @@ +enum mapping { mapping(word) } +enum uint256 { uint256(word) } + +trait Add { + function add(l: t, r: t) returns (t) ; +} +trait Sub { + function sub(l: t, r: t) returns (t) ; +} +impl Add { + function add(l: word, r: word) returns (word) { return l; } +} +impl Sub { + function sub(l: word, r: word) returns (word) { return l; } +} +impl Add { + function add(l: uint256, r: uint256) returns (uint256) { return l; } +} + +contract C { + m: mapping(word => bool); + function f(k: word) { m[k] += true; } + function main() { return (); } +} diff --git a/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_add_bool/main.solc b/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_add_bool/main.solc deleted file mode 100644 index d191a415..00000000 --- a/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_add_bool/main.solc +++ /dev/null @@ -1,24 +0,0 @@ -data mapping(key, value) = mapping(word); -data uint256 = uint256(word); - -forall t . class t:Add { - function add(l: t, r: t) -> t; -} -forall t . class t:Sub { - function sub(l: t, r: t) -> t; -} -instance word:Add { - function add(l: word, r: word) -> word { return l; } -} -instance word:Sub { - function sub(l: word, r: word) -> word { return l; } -} -instance uint256:Add { - function add(l: uint256, r: uint256) -> uint256 { return l; } -} - -contract C { - m: mapping(word, bool); - function f(k: word) -> () { m[k] += true; } - function main() -> () { return (); } -} diff --git a/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_sub_bool/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_sub_bool/diagnostics.snap index cce7a139..ecc2c7a5 100644 --- a/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_sub_bool/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_sub_bool/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/storage_mapping_compound_sub_bool/main.solc +input_file: crates/uitest/tests/fixtures/typeck/storage_mapping_compound_sub_bool/main.sol --- -error[SC0207]: cannot satisfy class constraint: bool : Sub - --> /main/main.solc:22:31 +error[SC0207]: cannot satisfy trait constraint: bool: Sub + --> /main/main.sol:22:25 | -21 | m: mapping(word, bool); -22 | function f(k: word) -> () { m[k] -= true; } - | ^^^^^^^^^^^^ constraint originates here -23 | function main() -> () { return (); } +21 | m: mapping(word => bool); +22 | function f(k: word) { m[k] -= true; } + | ^^^^^^^^^^^^ constraint originates here +23 | function main() { return (); } | - = note: no visible instance matches `bool : Sub` - = help: add a matching instance or strengthen the surrounding type context + = note: no visible impl matches `bool: Sub` + = help: add a matching impl or strengthen the surrounding type context diff --git a/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_sub_bool/main.sol b/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_sub_bool/main.sol new file mode 100644 index 00000000..8e249b72 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_sub_bool/main.sol @@ -0,0 +1,24 @@ +enum mapping { mapping(word) } +enum uint256 { uint256(word) } + +trait Add { + function add(l: t, r: t) returns (t) ; +} +trait Sub { + function sub(l: t, r: t) returns (t) ; +} +impl Add { + function add(l: word, r: word) returns (word) { return l; } +} +impl Sub { + function sub(l: word, r: word) returns (word) { return l; } +} +impl Add { + function add(l: uint256, r: uint256) returns (uint256) { return l; } +} + +contract C { + m: mapping(word => bool); + function f(k: word) { m[k] -= true; } + function main() { return (); } +} diff --git a/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_sub_bool/main.solc b/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_sub_bool/main.solc deleted file mode 100644 index d7eb0176..00000000 --- a/crates/uitest/tests/fixtures/typeck/storage_mapping_compound_sub_bool/main.solc +++ /dev/null @@ -1,24 +0,0 @@ -data mapping(key, value) = mapping(word); -data uint256 = uint256(word); - -forall t . class t:Add { - function add(l: t, r: t) -> t; -} -forall t . class t:Sub { - function sub(l: t, r: t) -> t; -} -instance word:Add { - function add(l: word, r: word) -> word { return l; } -} -instance word:Sub { - function sub(l: word, r: word) -> word { return l; } -} -instance uint256:Add { - function add(l: uint256, r: uint256) -> uint256 { return l; } -} - -contract C { - m: mapping(word, bool); - function f(k: word) -> () { m[k] -= true; } - function main() -> () { return (); } -} diff --git a/crates/uitest/tests/fixtures/typeck/type_alias_expansion_limit/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/type_alias_expansion_limit/diagnostics.snap index df6c2e8f..98c6bec3 100644 --- a/crates/uitest/tests/fixtures/typeck/type_alias_expansion_limit/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/type_alias_expansion_limit/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/type_alias_expansion_limit/main.solc +input_file: crates/uitest/tests/fixtures/typeck/type_alias_expansion_limit/main.sol --- error[SC0243]: type synonym expansion exceeded 16384 type nodes - --> /main/main.solc:14:6 + --> /main/main.sol:14:6 | 13 | type T12 = (T11, T11); 14 | type T13 = (T12, T12); diff --git a/crates/uitest/tests/fixtures/typeck/type_alias_expansion_limit/main.sol b/crates/uitest/tests/fixtures/typeck/type_alias_expansion_limit/main.sol new file mode 100644 index 00000000..de1099ae --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/type_alias_expansion_limit/main.sol @@ -0,0 +1,18 @@ +type T0 = word; +type T1 = (T0, T0); +type T2 = (T1, T1); +type T3 = (T2, T2); +type T4 = (T3, T3); +type T5 = (T4, T4); +type T6 = (T5, T5); +type T7 = (T6, T6); +type T8 = (T7, T7); +type T9 = (T8, T8); +type T10 = (T9, T9); +type T11 = (T10, T10); +type T12 = (T11, T11); +type T13 = (T12, T12); + +function use_bomb(x: T13) returns (T13) { + return x; +} diff --git a/crates/uitest/tests/fixtures/typeck/type_alias_expansion_limit/main.solc b/crates/uitest/tests/fixtures/typeck/type_alias_expansion_limit/main.solc deleted file mode 100644 index 7c329a74..00000000 --- a/crates/uitest/tests/fixtures/typeck/type_alias_expansion_limit/main.solc +++ /dev/null @@ -1,18 +0,0 @@ -type T0 = word; -type T1 = (T0, T0); -type T2 = (T1, T1); -type T3 = (T2, T2); -type T4 = (T3, T3); -type T5 = (T4, T4); -type T6 = (T5, T5); -type T7 = (T6, T6); -type T8 = (T7, T7); -type T9 = (T8, T8); -type T10 = (T9, T9); -type T11 = (T10, T10); -type T12 = (T11, T11); -type T13 = (T12, T12); - -function use_bomb(x: T13) -> T13 { - return x; -} diff --git a/crates/uitest/tests/fixtures/typeck/type_annotation_kind_mismatch/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/type_annotation_kind_mismatch/diagnostics.snap index a12716f7..4e0e2018 100644 --- a/crates/uitest/tests/fixtures/typeck/type_annotation_kind_mismatch/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/type_annotation_kind_mismatch/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/type_annotation_kind_mismatch/main.solc +input_file: crates/uitest/tests/fixtures/typeck/type_annotation_kind_mismatch/main.sol --- error[SC0299]: Invalid number of type arguments! - --> /main/main.solc:3:17 + --> /main/main.sol:3:17 | 2 | -3 | function f(x: P(word(word))) -> word { +3 | function f(x: P>) returns (word) { | ^^^^^^^^^^ diagnostic reported here 4 | return 0; | = note: Type word is expected to have 0 type arguments - = note: but, type word(word) has 1 arguments + = note: but, type word has 1 arguments diff --git a/crates/uitest/tests/fixtures/typeck/type_annotation_kind_mismatch/main.sol b/crates/uitest/tests/fixtures/typeck/type_annotation_kind_mismatch/main.sol new file mode 100644 index 00000000..4a23952e --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/type_annotation_kind_mismatch/main.sol @@ -0,0 +1,5 @@ +enum P { Mk(a) } + +function f(x: P>) returns (word) { + return 0; +} diff --git a/crates/uitest/tests/fixtures/typeck/type_annotation_kind_mismatch/main.solc b/crates/uitest/tests/fixtures/typeck/type_annotation_kind_mismatch/main.solc deleted file mode 100644 index 212fe581..00000000 --- a/crates/uitest/tests/fixtures/typeck/type_annotation_kind_mismatch/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data P(a) = Mk(a); - -function f(x: P(word(word))) -> word { - return 0; -} diff --git a/crates/uitest/tests/fixtures/typeck/unary_type_unapplied_signature/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/unary_type_unapplied_signature/diagnostics.snap index 23725549..c88c4626 100644 --- a/crates/uitest/tests/fixtures/typeck/unary_type_unapplied_signature/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/unary_type_unapplied_signature/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/unary_type_unapplied_signature/main.solc +input_file: crates/uitest/tests/fixtures/typeck/unary_type_unapplied_signature/main.sol --- error[SC0299]: Invalid number of type arguments! - --> /main/main.solc:3:15 + --> /main/main.sol:3:15 | 2 | -3 | function f(x: P) -> word { +3 | function f(x: P) returns (word) { | ^ diagnostic reported here 4 | return 0; | diff --git a/crates/uitest/tests/fixtures/typeck/unary_type_unapplied_signature/main.sol b/crates/uitest/tests/fixtures/typeck/unary_type_unapplied_signature/main.sol new file mode 100644 index 00000000..f0e74d08 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/unary_type_unapplied_signature/main.sol @@ -0,0 +1,5 @@ +enum P { Mk(a) } + +function f(x: P) returns (word) { + return 0; +} diff --git a/crates/uitest/tests/fixtures/typeck/unary_type_unapplied_signature/main.solc b/crates/uitest/tests/fixtures/typeck/unary_type_unapplied_signature/main.solc deleted file mode 100644 index 264483d2..00000000 --- a/crates/uitest/tests/fixtures/typeck/unary_type_unapplied_signature/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -data P(a) = Mk(a); - -function f(x: P) -> word { - return 0; -} diff --git a/crates/uitest/tests/fixtures/typeck/unknown_field/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/unknown_field/diagnostics.snap index 5aeee6ea..7b878c8c 100644 --- a/crates/uitest/tests/fixtures/typeck/unknown_field/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/unknown_field/diagnostics.snap @@ -1,12 +1,12 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/unknown_field/main.solc +input_file: crates/uitest/tests/fixtures/typeck/unknown_field/main.sol --- error[SC0205]: cannot resolve field `foo` - --> /main/main.solc:2:12 + --> /main/main.sol:2:12 | -1 | function f(x: word) -> word { +1 | function f(x: word) returns (word) { 2 | return x.foo; | ^^^ unknown field 3 | } diff --git a/crates/uitest/tests/fixtures/typeck/unknown_field/main.sol b/crates/uitest/tests/fixtures/typeck/unknown_field/main.sol new file mode 100644 index 00000000..6e85e9ef --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/unknown_field/main.sol @@ -0,0 +1,3 @@ +function f(x: word) returns (word) { + return x.foo; +} diff --git a/crates/uitest/tests/fixtures/typeck/unknown_field/main.solc b/crates/uitest/tests/fixtures/typeck/unknown_field/main.solc deleted file mode 100644 index 6ef4228f..00000000 --- a/crates/uitest/tests/fixtures/typeck/unknown_field/main.solc +++ /dev/null @@ -1,3 +0,0 @@ -function f(x: word) -> word { - return x.foo; -} diff --git a/crates/uitest/tests/fixtures/typeck/unreachable_match_arm/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/unreachable_match_arm/diagnostics.snap index aa4cc818..189ddab6 100644 --- a/crates/uitest/tests/fixtures/typeck/unreachable_match_arm/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/unreachable_match_arm/diagnostics.snap @@ -1,14 +1,16 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/unreachable_match_arm/main.solc +input_file: crates/uitest/tests/fixtures/typeck/unreachable_match_arm/main.sol --- warning[SC0303]: unreachable match arm - --> /main/main.solc:6:3 - | -5 | | _ => return 0; -6 | | Flag.Off => return 1; - | ^^^^^^^^^^^^^^^^^^^^^^^ this arm is unreachable -7 | } - | - = note: this arm is covered by previous match arms + --> /main/main.sol:8:1 + | + 7 | } + 8 | / case Flag.Off { + 9 | | return 1; +10 | | } + | |_^ this arm is unreachable +11 | } + | + = note: this arm is covered by previous match arms diff --git a/crates/uitest/tests/fixtures/typeck/unreachable_match_arm/main.sol b/crates/uitest/tests/fixtures/typeck/unreachable_match_arm/main.sol new file mode 100644 index 00000000..86a0edc9 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/unreachable_match_arm/main.sol @@ -0,0 +1,12 @@ +enum Flag { Off, On } + +function pick(x: Flag) returns (word) { + match (x) { +case _ { +return 0; +} +case Flag.Off { +return 1; +} +} +} diff --git a/crates/uitest/tests/fixtures/typeck/unreachable_match_arm/main.solc b/crates/uitest/tests/fixtures/typeck/unreachable_match_arm/main.solc deleted file mode 100644 index 2fa579e4..00000000 --- a/crates/uitest/tests/fixtures/typeck/unreachable_match_arm/main.solc +++ /dev/null @@ -1,8 +0,0 @@ -data Flag = Off | On; - -function pick(x : Flag) -> word { - match x { - | _ => return 0; - | Flag.Off => return 1; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/visible_manual_std_abi_instances/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/visible_manual_std_abi_instances/diagnostics.snap index ea141ea1..07f3505c 100644 --- a/crates/uitest/tests/fixtures/typeck/visible_manual_std_abi_instances/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/visible_manual_std_abi_instances/diagnostics.snap @@ -1,51 +1,51 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/visible_manual_std_abi_instances/main.solc +input_file: crates/uitest/tests/fixtures/typeck/visible_manual_std_abi_instances/main.sol --- error[SC0231]: public function `echo` ABI for contract `C` cannot use visible manual `ABIAttribs` evidence - --> /main/main.solc:22:3 + --> /main/main.sol:22:3 | 21 | contract C { -22 | public function echo(value: word) -> word { return value; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ external ABI evidence must be compiler-owned and canonical +22 | function echo(value: word) public returns (word) { return value; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ external ABI evidence must be compiler-owned and canonical 23 | } | - = note: instance `ABIAttribs` can override canonical `ABIAttribs` behavior - = help: remove the visible manual ABI instance or keep this declaration out of the external ABI + = note: impl `ABIAttribs` can override canonical `ABIAttribs` behavior + = help: remove the visible manual ABI impl or keep this declaration out of the external ABI --- error[SC0231]: public function `echo` ABI for contract `C` cannot use visible manual `ABIDecode` evidence - --> /main/main.solc:22:3 + --> /main/main.sol:22:3 | 21 | contract C { -22 | public function echo(value: word) -> word { return value; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ external ABI evidence must be compiler-owned and canonical +22 | function echo(value: word) public returns (word) { return value; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ external ABI evidence must be compiler-owned and canonical 23 | } | - = note: instance `ABIDecode` can override canonical `ABIDecode` behavior - = help: remove the visible manual ABI instance or keep this declaration out of the external ABI + = note: impl `ABIDecode` can override canonical `ABIDecode` behavior + = help: remove the visible manual ABI impl or keep this declaration out of the external ABI --- error[SC0231]: public function `echo` ABI for contract `C` cannot use visible manual `ABIEncode` evidence - --> /main/main.solc:22:3 + --> /main/main.sol:22:3 | 21 | contract C { -22 | public function echo(value: word) -> word { return value; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ external ABI evidence must be compiler-owned and canonical +22 | function echo(value: word) public returns (word) { return value; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ external ABI evidence must be compiler-owned and canonical 23 | } | - = note: instance `ABIEncode` can override canonical `ABIEncode` behavior - = help: remove the visible manual ABI instance or keep this declaration out of the external ABI + = note: impl `ABIEncode` can override canonical `ABIEncode` behavior + = help: remove the visible manual ABI impl or keep this declaration out of the external ABI --- error[SC0231]: public function `echo` ABI for contract `C` cannot use visible manual `SigString` evidence - --> /main/main.solc:22:3 + --> /main/main.sol:22:3 | 21 | contract C { -22 | public function echo(value: word) -> word { return value; } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ external ABI evidence must be compiler-owned and canonical +22 | function echo(value: word) public returns (word) { return value; } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ external ABI evidence must be compiler-owned and canonical 23 | } | - = note: instance `SigString` can override canonical `SigString` behavior - = help: remove the visible manual ABI instance or keep this declaration out of the external ABI + = note: impl `SigString` can override canonical `SigString` behavior + = help: remove the visible manual ABI impl or keep this declaration out of the external ABI diff --git a/crates/uitest/tests/fixtures/typeck/visible_manual_std_abi_instances/main.sol b/crates/uitest/tests/fixtures/typeck/visible_manual_std_abi_instances/main.sol new file mode 100644 index 00000000..0c9d6149 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/visible_manual_std_abi_instances/main.sol @@ -0,0 +1,23 @@ +import * from std; +import * from std.dispatch; + +impl ABIAttribs { + function headSize(p: Proxy) returns (word) { return 32; } + function isStatic(p: Proxy) returns (bool) { return true; } +} + +impl ABIEncode { + function encodeInto(x: word, base: word, offset: word, tail: word) returns (word) { return tail; } +} + +impl ABIDecode, word> { + function decode(d: ABIDecoder, offset: word) returns (word) { return 0; } +} + +impl SigString { + function sigStr(p: Proxy) returns (string) { return "uint256"; } +} + +contract C { + function echo(value: word) public returns (word) { return value; } +} diff --git a/crates/uitest/tests/fixtures/typeck/visible_manual_std_abi_instances/main.solc b/crates/uitest/tests/fixtures/typeck/visible_manual_std_abi_instances/main.solc deleted file mode 100644 index 042d32d8..00000000 --- a/crates/uitest/tests/fixtures/typeck/visible_manual_std_abi_instances/main.solc +++ /dev/null @@ -1,23 +0,0 @@ -import std.{*}; -import std.dispatch.{*}; - -instance word:ABIAttribs { - function headSize(p: Proxy(word)) -> word { return 32; } - function isStatic(p: Proxy(word)) -> bool { return true; } -} - -instance word:ABIEncode { - function encodeInto(x: word, base: word, offset: word, tail: word) -> word { return tail; } -} - -instance ABIDecoder(word, CalldataWordReader):ABIDecode(word) { - function decode(d: ABIDecoder(word, CalldataWordReader), offset: word) -> word { return 0; } -} - -instance word:SigString { - function sigStr(p: Proxy(word)) -> string { return "uint256"; } -} - -contract C { - public function echo(value: word) -> word { return value; } -} diff --git a/crates/uitest/tests/fixtures/typeck/whole_mapping_private_full/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/whole_mapping_private_full/diagnostics.snap index cbc8a5f9..fc80f787 100644 --- a/crates/uitest/tests/fixtures/typeck/whole_mapping_private_full/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/whole_mapping_private_full/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/whole_mapping_private_full/main.solc +input_file: crates/uitest/tests/fixtures/typeck/whole_mapping_private_full/main.sol --- -error[SC0201]: type mismatch: expected mapping(address, uint256), found storage(mapping(address, uint256)) - --> /main/main.solc:10:12 +error[SC0201]: type mismatch: expected mapping(address => uint256), found storage uint256)> + --> /main/main.sol:10:12 | - 9 | function leak() -> mapping(address, uint256) { + 9 | function leak() returns (mapping(address => uint256)) { 10 | return balances; | ^^^^^^^^ expression has mismatched type 11 | } | - = note: expected type: mapping(address, uint256) - = note: found type: storage(mapping(address, uint256)) + = note: expected type: mapping(address => uint256) + = note: found type: storage uint256)> diff --git a/crates/uitest/tests/fixtures/typeck/whole_mapping_private_full/main.sol b/crates/uitest/tests/fixtures/typeck/whole_mapping_private_full/main.sol new file mode 100644 index 00000000..888e173c --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/whole_mapping_private_full/main.sol @@ -0,0 +1,16 @@ +enum address { address(word) } +enum uint256 { uint256(word) } +enum mapping { mapping(word) } +enum storage { storage(word) } + +contract C { + balances : mapping(address => uint256); + + function leak() returns (mapping(address => uint256)) { + return balances; + } + + function main() returns (word) { + return 0; + } +} diff --git a/crates/uitest/tests/fixtures/typeck/whole_mapping_private_full/main.solc b/crates/uitest/tests/fixtures/typeck/whole_mapping_private_full/main.solc deleted file mode 100644 index d5fb42a8..00000000 --- a/crates/uitest/tests/fixtures/typeck/whole_mapping_private_full/main.solc +++ /dev/null @@ -1,16 +0,0 @@ -data address = address(word); -data uint256 = uint256(word); -data mapping(index, member) = mapping(word); -data storage(t) = storage(word); - -contract C { - balances : mapping(address, uint256); - - function leak() -> mapping(address, uint256) { - return balances; - } - - function main() -> word { - return 0; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/word_literals_nonexhaustive/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/word_literals_nonexhaustive/diagnostics.snap index c4a7e7d7..531a5f67 100644 --- a/crates/uitest/tests/fixtures/typeck/word_literals_nonexhaustive/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/word_literals_nonexhaustive/diagnostics.snap @@ -1,15 +1,15 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/word_literals_nonexhaustive/main.solc +input_file: crates/uitest/tests/fixtures/typeck/word_literals_nonexhaustive/main.sol --- error[SC0302]: non-exhaustive pattern match - --> /main/main.solc:2:9 + --> /main/main.sol:2:10 | -1 | function pick(x : word) -> word { -2 | match x { - | ^ non-exhaustive match -3 | | 0 => return 0; +1 | function pick(x: word) returns (word) { +2 | match (x) { + | ^ non-exhaustive match +3 | case 0 { | = note: missing case: _ = note: help: add a clause that covers the missing case diff --git a/crates/uitest/tests/fixtures/typeck/word_literals_nonexhaustive/main.sol b/crates/uitest/tests/fixtures/typeck/word_literals_nonexhaustive/main.sol new file mode 100644 index 00000000..8aaa2479 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/word_literals_nonexhaustive/main.sol @@ -0,0 +1,10 @@ +function pick(x: word) returns (word) { + match (x) { +case 0 { +return 0; +} +case 1 { +return 1; +} +} +} diff --git a/crates/uitest/tests/fixtures/typeck/word_literals_nonexhaustive/main.solc b/crates/uitest/tests/fixtures/typeck/word_literals_nonexhaustive/main.solc deleted file mode 100644 index 970e27f0..00000000 --- a/crates/uitest/tests/fixtures/typeck/word_literals_nonexhaustive/main.solc +++ /dev/null @@ -1,6 +0,0 @@ -function pick(x : word) -> word { - match x { - | 0 => return 0; - | 1 => return 1; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/yul_multi_return_arity/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/yul_multi_return_arity/diagnostics.snap index 42a48bd7..1ef5bdff 100644 --- a/crates/uitest/tests/fixtures/typeck/yul_multi_return_arity/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/yul_multi_return_arity/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/yul_multi_return_arity/main.solc +input_file: crates/uitest/tests/fixtures/typeck/yul_multi_return_arity/main.sol --- error[SC0203]: Yul assignment expects 3 arguments, but 2 were provided - --> /main/main.solc:11:7 + --> /main/main.sol:11:7 | 10 | } 11 | x, y, z := pair() diff --git a/crates/uitest/tests/fixtures/typeck/yul_multi_return_arity/main.sol b/crates/uitest/tests/fixtures/typeck/yul_multi_return_arity/main.sol new file mode 100644 index 00000000..c83ab153 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/yul_multi_return_arity/main.sol @@ -0,0 +1,15 @@ +contract YulMultiRetBad { + function main() public returns (word) { + let x : word; + let y : word; + let z : word; + assembly { + function pair() -> a, b { + a := 1 + b := 2 + } + x, y, z := pair() + } + return x; + } +} diff --git a/crates/uitest/tests/fixtures/typeck/yul_multi_return_arity/main.solc b/crates/uitest/tests/fixtures/typeck/yul_multi_return_arity/main.solc deleted file mode 100644 index 02263c08..00000000 --- a/crates/uitest/tests/fixtures/typeck/yul_multi_return_arity/main.solc +++ /dev/null @@ -1,15 +0,0 @@ -contract YulMultiRetBad { - public function main() -> word { - let x : word; - let y : word; - let z : word; - assembly { - function pair() -> a, b { - a := 1 - b := 2 - } - x, y, z := pair() - } - return x; - } -} diff --git a/crates/uitest/tests/fixtures/typeck/yul_non_word_sail_variable/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/yul_non_word_sail_variable/diagnostics.snap index 0e1e81d0..a6ac73e7 100644 --- a/crates/uitest/tests/fixtures/typeck/yul_non_word_sail_variable/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/yul_non_word_sail_variable/diagnostics.snap @@ -1,13 +1,13 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/yul_non_word_sail_variable/main.solc +input_file: crates/uitest/tests/fixtures/typeck/yul_non_word_sail_variable/main.sol --- error[SC0204]: Yul reference `b` requires word type, got bool - --> /main/main.solc:3:14 + --> /main/main.sol:3:14 | 2 | let b : bool = false; 3 | assembly { b := add(1, 1) } | ^ Yul reference has non-word type -4 | if b { return 1; } else { return 0; } +4 | if ( b ) { return 1; } else { return 0; } | diff --git a/crates/uitest/tests/fixtures/typeck/yul_non_word_sail_variable/main.sol b/crates/uitest/tests/fixtures/typeck/yul_non_word_sail_variable/main.sol new file mode 100644 index 00000000..69c8cc39 --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/yul_non_word_sail_variable/main.sol @@ -0,0 +1,5 @@ +function main() returns (word) { + let b : bool = false; + assembly { b := add(1, 1) } + if ( b ) { return 1; } else { return 0; } +} diff --git a/crates/uitest/tests/fixtures/typeck/yul_non_word_sail_variable/main.solc b/crates/uitest/tests/fixtures/typeck/yul_non_word_sail_variable/main.solc deleted file mode 100644 index 0c5dfe65..00000000 --- a/crates/uitest/tests/fixtures/typeck/yul_non_word_sail_variable/main.solc +++ /dev/null @@ -1,5 +0,0 @@ -function main() -> word { - let b : bool = false; - assembly { b := add(1, 1) } - if b { return 1; } else { return 0; } -} diff --git a/crates/uitest/tests/fixtures/typeck/yul_opcode_errors/diagnostics.snap b/crates/uitest/tests/fixtures/typeck/yul_opcode_errors/diagnostics.snap index 2f3efaf3..aa668d2a 100644 --- a/crates/uitest/tests/fixtures/typeck/yul_opcode_errors/diagnostics.snap +++ b/crates/uitest/tests/fixtures/typeck/yul_opcode_errors/diagnostics.snap @@ -1,10 +1,10 @@ --- source: crates/test-utils/src/lib.rs expression: rendered -input_file: crates/uitest/tests/fixtures/typeck/yul_opcode_errors/main.solc +input_file: crates/uitest/tests/fixtures/typeck/yul_opcode_errors/main.sol --- error[SC0203]: Yul call `add` expects 2 arguments, but 1 was provided - --> /main/main.solc:4:16 + --> /main/main.sol:4:16 | 3 | assembly { 4 | let one := add(1) @@ -16,7 +16,7 @@ error[SC0203]: Yul call `add` expects 2 arguments, but 1 was provided --- error[SC0201]: type mismatch: expected word, found string - --> /main/main.solc:5:20 + --> /main/main.sol:5:20 | 4 | let one := add(1) 5 | let two := add("bad", 1) @@ -28,7 +28,7 @@ error[SC0201]: type mismatch: expected word, found string --- error[SC0203]: Yul assignment expects 1 argument, but 0 were provided - --> /main/main.solc:6:5 + --> /main/main.sol:6:5 | 5 | let two := add("bad", 1) 6 | x := mstore(1, 1) @@ -40,7 +40,7 @@ error[SC0203]: Yul assignment expects 1 argument, but 0 were provided --- error[SC0211]: unknown Yul identifier or function: missing - --> /main/main.solc:7:14 + --> /main/main.sol:7:14 | 6 | x := mstore(1, 1) 7 | x := add(missing, 1) diff --git a/crates/uitest/tests/fixtures/typeck/yul_opcode_errors/main.sol b/crates/uitest/tests/fixtures/typeck/yul_opcode_errors/main.sol new file mode 100644 index 00000000..23a7840a --- /dev/null +++ b/crates/uitest/tests/fixtures/typeck/yul_opcode_errors/main.sol @@ -0,0 +1,10 @@ +function badYul() returns (word) { + let x : word; + assembly { + let one := add(1) + let two := add("bad", 1) + x := mstore(1, 1) + x := add(missing, 1) + } + return x; +} diff --git a/crates/uitest/tests/fixtures/typeck/yul_opcode_errors/main.solc b/crates/uitest/tests/fixtures/typeck/yul_opcode_errors/main.solc deleted file mode 100644 index 3a081449..00000000 --- a/crates/uitest/tests/fixtures/typeck/yul_opcode_errors/main.solc +++ /dev/null @@ -1,10 +0,0 @@ -function badYul() -> word { - let x : word; - assembly { - let one := add(1) - let two := add("bad", 1) - x := mstore(1, 1) - x := add(missing, 1) - } - return x; -} diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index f865111a..59d325e9 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs @@ -33,56 +33,53 @@ pub const EXT_ROOT: &str = "/ext"; /// Embedded standard-library files, mounted under [`STD_ROOT`]. pub const STD_FILES: &[(&str, &str)] = &[ ( - "std.solc", - include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../std/std.solc")), + "std.sol", + include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../std/std.sol")), ), ( - "dispatch.solc", + "dispatch.sol", include_str!(concat!( env!("CARGO_MANIFEST_DIR"), - "/../../std/dispatch.solc" + "/../../std/dispatch.sol" )), ), ( - "opcodes.solc", + "opcodes.sol", include_str!(concat!( env!("CARGO_MANIFEST_DIR"), - "/../../std/opcodes.solc" + "/../../std/opcodes.sol" )), ), ( - "Generic.solc", + "Generic.sol", include_str!(concat!( env!("CARGO_MANIFEST_DIR"), - "/../../std/Generic.solc" + "/../../std/Generic.sol" )), ), ( - "ABIGeneric.solc", + "ABIGeneric.sol", include_str!(concat!( env!("CARGO_MANIFEST_DIR"), - "/../../std/ABIGeneric.solc" + "/../../std/ABIGeneric.sol" )), ), ( - "StorageGeneric.solc", + "StorageGeneric.sol", include_str!(concat!( env!("CARGO_MANIFEST_DIR"), - "/../../std/StorageGeneric.solc" + "/../../std/StorageGeneric.sol" )), ), ( - "eip712.solc", - include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../std/eip712.solc" - )), + "eip712.sol", + include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../std/eip712.sol")), ), ( - "eip7951.solc", + "eip7951.sol", include_str!(concat!( env!("CARGO_MANIFEST_DIR"), - "/../../std/eip7951.solc" + "/../../std/eip7951.sol" )), ), ]; @@ -401,7 +398,7 @@ impl Workspace { /// Adds or replaces a user file under `/main`. /// - /// Both `main.solc` and `/main/main.solc` refer to `/main/main.solc`. + /// Both `main.sol` and `/main/main.sol` refer to `/main/main.sol`. pub fn set_file(&mut self, path: &str, contents: String) { self.apply_file_changes([WorkspaceFileChange::Set { path: path.to_owned(), @@ -556,10 +553,16 @@ impl Workspace { fn entry_key(&self) -> Option { let path = self.entry_path.as_ref()?; + if !is_solcore_module_path(path) { + return None; + } self.main_key_for_path(path) } fn main_key_for_path(&self, path: &Path) -> Option { + if !is_solcore_module_path(path) { + return None; + } let tree = self .host .module_tree @@ -836,7 +839,7 @@ fn module_fs_snapshot_from_paths<'a>( let mut existing_files = BTreeSet::new(); let mut sibling_stems = BTreeMap::>::new(); for path in paths { - if path.extension().and_then(|extension| extension.to_str()) != Some("solc") { + if path.extension().and_then(|extension| extension.to_str()) != Some("sol") { continue; } existing_files.insert(path.clone()); @@ -858,7 +861,7 @@ fn module_fs_snapshot_from_paths<'a>( } fn is_solcore_module_path(path: &Path) -> bool { - path.extension().and_then(|extension| extension.to_str()) == Some("solc") + path.extension().and_then(|extension| extension.to_str()) == Some("sol") } fn normalize_absolute_path(path: PathBuf) -> PathBuf { @@ -927,8 +930,8 @@ mod tests { fn workspace_with_main(source: &str) -> Workspace { let mut workspace = Workspace::new(); - workspace.set_file("main.solc", source.to_owned()); - workspace.set_entry("main.solc"); + workspace.set_file("main.sol", source.to_owned()); + workspace.set_entry("main.sol"); workspace } @@ -950,7 +953,7 @@ mod tests { fn driver_style_messages(source: &str) -> Vec { let mut host = AnalysisHost::new(); - let path = main_path("main.solc"); + let path = main_path("main.sol"); host.set_virtual_file(path.clone(), source.to_owned()); let tree = host .module_tree @@ -990,7 +993,7 @@ mod tests { #[test] fn main_only_clean_program_has_driver_ordered_diagnostics() { - let source = "function main() -> word {\n return 1;\n}\n"; + let source = "function main() returns (word) {\n return 1;\n}\n"; let workspace = workspace_with_main(source); assert_eq!(messages(&workspace), driver_style_messages(source)); @@ -999,8 +1002,7 @@ mod tests { #[test] fn owned_diagnostics_preserve_heuristic_suggestion_applicability() { - let source = - "function value() -> word { return 1; }\nfunction main() -> word { return vaue(); }\n"; + let source = "function value() returns (word) { return 1; }\nfunction main() returns (word) { return vaue(); }\n"; let workspace = workspace_with_main(source); let diagnostic = workspace .diagnostics() @@ -1025,7 +1027,7 @@ mod tests { suggestion.edits, vec![DiagnosticTextEdit { range: DiagRange { - file_url: "file:///main/main.solc".to_owned(), + file_url: "file:///main/main.sol".to_owned(), start: typo, end: typo + "vaue".len() as u32, }, @@ -1036,7 +1038,7 @@ mod tests { #[test] fn owned_diagnostics_preserve_exact_suggestion_applicability() { - let source = "data Option = None | Some(word);\nfunction main(x: word) -> Option { return Some(x); }\n"; + let source = "enum Option {None , Some(word)}\nfunction main(x: word) returns (Option) { return Some(x); }\n"; let workspace = workspace_with_main(source); let diagnostic = workspace .diagnostics() @@ -1061,7 +1063,7 @@ mod tests { suggestion.edits, vec![DiagnosticTextEdit { range: DiagRange { - file_url: "file:///main/main.solc".to_owned(), + file_url: "file:///main/main.sol".to_owned(), start: constructor, end: constructor + "Some".len() as u32, }, @@ -1072,7 +1074,7 @@ mod tests { #[test] fn main_only_type_error_matches_lowered_driver_messages() { - let source = "function f() -> word {\n return true;\n}\n"; + let source = "function f() returns (word) {\n return true;\n}\n"; let workspace = workspace_with_main(source); let diagnostics = workspace.diagnostics(); @@ -1087,7 +1089,7 @@ mod tests { #[test] fn main_only_name_resolution_error_matches_lowered_driver_messages() { - let source = "function addOne(x: word) -> word {\n return x + missingVar;\n}\n"; + let source = "function addOne(x: word) returns (word) {\n return x + missingVar;\n}\n"; let workspace = workspace_with_main(source); let diagnostics = workspace.diagnostics(); @@ -1113,7 +1115,7 @@ mod tests { // for whole-frontend analysis of the embedded standard library. solcore_test_utils::run_in_large_stack(|| { let workspace = workspace_with_main( - "import std.{addWord};\n\nfunction main() -> word {\n return addWord(1, 2);\n}\n", + "import {addWord} from std;\n\nfunction main() returns (word) {\n return addWord(1, 2);\n}\n", ); assert!(workspace.diagnostics().is_empty()); @@ -1126,14 +1128,15 @@ mod tests { fn non_solcore_twin_never_replaces_or_unregisters_a_module() { let mut workspace = Workspace::new(); workspace.set_file( - "foo.solc", - "function value() -> word { return 1; }\nexport { value };\n".to_owned(), + "foo.sol", + "function value() returns (word) { return 1; }\nexport { value };\n".to_owned(), ); workspace.set_file( - "main.solc", - "import foo.{value};\nfunction main() -> word { return value(); }\n".to_owned(), + "main.sol", + "import {value} from foo;\nfunction main() returns (word) { return value(); }\n" + .to_owned(), ); - workspace.set_entry("main.solc"); + workspace.set_entry("main.sol"); assert!(workspace.diagnostics().is_empty()); workspace.set_file("foo.txt", "not solcore source".to_owned()); @@ -1143,23 +1146,39 @@ mod tests { assert!(workspace.diagnostics().is_empty()); } + #[test] + fn non_sol_entry_is_not_a_module_even_when_the_file_exists() { + let mut workspace = Workspace::new(); + workspace.set_file("main.solc", "function main() {}\n".to_owned()); + workspace.set_entry("main.solc"); + + assert!(workspace.entry_module().is_none()); + assert!(workspace.raw_diagnostics().is_empty()); + + workspace.set_file("main.sol", "function main() {}\n".to_owned()); + workspace.set_entry("main.sol"); + assert!(workspace.entry_module().is_some()); + assert!(workspace.raw_diagnostics().is_empty()); + } + #[test] fn loading_reachable_module_invalidates_cached_not_loaded_import() { let mut workspace = Workspace::new(); workspace.set_file( - "main.solc", - "import math.{double};\n\nfunction main() -> word {\n return double(21);\n}\n" + "main.sol", + "import {double} from math;\n\nfunction main() returns (word) {\n return double(21);\n}\n" .to_owned(), ); workspace.set_file( - "math.solc", - "function double(x: word) -> word { return x; }\n\nexport { double };\n".to_owned(), + "math.sol", + "function double(x: word) returns (word) { return x; }\n\nexport { double };\n" + .to_owned(), ); - workspace.set_entry("main.solc"); + workspace.set_entry("main.sol"); let math_key = workspace .host - .module_key_for_virtual_path(&main_path("math.solc")) + .module_key_for_virtual_path(&main_path("math.sol")) .expect("math module key"); assert!(workspace.host.module_files.remove(&math_key).is_some()); workspace.host.sync_module_file_snapshot(); @@ -1186,29 +1205,29 @@ mod tests { #[test] fn incremental_file_updates_reanalyze_existing_source_file() { - let clean = "function main() -> word {\n return 1;\n}\n"; + let clean = "function main() returns (word) {\n return 1;\n}\n"; let mut workspace = workspace_with_main(clean); assert!(workspace.diagnostics().is_empty()); let before_file = workspace .db() - .source_file(main_path("main.solc")) + .source_file(main_path("main.sol")) .expect("main source file"); workspace.set_file( - "main.solc", - "function addOne(x: word) -> word {\n return x + missingVar;\n}\n".to_owned(), + "main.sol", + "function addOne(x: word) returns (word) {\n return x + missingVar;\n}\n".to_owned(), ); let after_file = workspace .db() - .source_file(main_path("main.solc")) + .source_file(main_path("main.sol")) .expect("main source file"); assert_eq!(before_file, after_file); assert_eq!(workspace.diagnostics().len(), 1); - workspace.set_file("main.solc", clean.to_owned()); + workspace.set_file("main.sol", clean.to_owned()); let restored_file = workspace .db() - .source_file(main_path("main.solc")) + .source_file(main_path("main.sol")) .expect("main source file"); assert_eq!(before_file, restored_file); assert!(workspace.diagnostics().is_empty()); @@ -1216,9 +1235,9 @@ mod tests { #[test] fn removed_virtual_file_is_revived_with_the_same_salsa_identity() { - let source = "function main() -> word { return 1; }\n"; + let source = "function main() returns (word) { return 1; }\n"; let mut host = AnalysisHost::new(); - let path = main_path("main.solc"); + let path = main_path("main.sol"); let original = host.set_virtual_file(path.clone(), source.to_owned()); let _ = parser::parse_file_to_hir(&host, original); @@ -1234,13 +1253,13 @@ mod tests { #[test] fn identical_virtual_and_workspace_updates_do_not_reexecute_queries() { - let source = "function main() -> word { return 1; }\n"; + let source = "function main() returns (word) { return 1; }\n"; let (mut host, executed) = host_with_execution_log(); - let file = host.set_virtual_file(main_path("main.solc"), source.to_owned()); + let file = host.set_virtual_file(main_path("main.sol"), source.to_owned()); let _ = parser::parse_file_to_hir(&host, file); let _ = take_executed(&executed); - let same_file = host.set_virtual_file(main_path("main.solc"), source.to_owned()); + let same_file = host.set_virtual_file(main_path("main.sol"), source.to_owned()); assert_eq!(same_file, file); let _ = parser::parse_file_to_hir(&host, same_file); let events = take_executed(&executed); @@ -1255,11 +1274,11 @@ mod tests { host, entry_path: None, }; - workspace.set_entry("main.solc"); + workspace.set_entry("main.sol"); assert!(workspace.diagnostics().is_empty()); let _ = take_executed(&executed); - workspace.set_file("main.solc", source.to_owned()); + workspace.set_file("main.sol", source.to_owned()); assert!(workspace.diagnostics().is_empty()); let events = take_executed(&executed); assert_eq!( @@ -1271,52 +1290,53 @@ mod tests { #[test] fn incremental_diagnostics_match_a_fresh_workspace_across_batch_changes() { - let initial_main = "import util.{value};\nfunction main() -> word { return value(); }\n"; - let initial_util = "function value() -> word { return 1; }\nexport { value };\n"; + let initial_main = + "import {value} from util;\nfunction main() returns (word) { return value(); }\n"; + let initial_util = "function value() returns (word) { return 1; }\nexport { value };\n"; let mut incremental = workspace_from_files( - &[("main.solc", initial_main), ("util.solc", initial_util)], - "main.solc", + &[("main.sol", initial_main), ("util.sol", initial_util)], + "main.sol", ); assert!(incremental.diagnostics().is_empty()); - let broken_main = - "import helper.{answer};\nfunction main() -> word { return answer(missing); }\n"; - let broken_helper = "function answer(x: bool) -> word { return x; }\nexport { answer };\n"; + let broken_main = "import {answer} from helper;\nfunction main() returns (word) { return answer(missing); }\n"; + let broken_helper = + "function answer(x: bool) returns (word) { return x; }\nexport { answer };\n"; incremental.apply_file_changes([ WorkspaceFileChange::Set { - path: "main.solc".to_owned(), + path: "main.sol".to_owned(), contents: broken_main.to_owned(), }, WorkspaceFileChange::Remove { - path: "util.solc".to_owned(), + path: "util.sol".to_owned(), }, WorkspaceFileChange::Set { - path: "helper.solc".to_owned(), + path: "helper.sol".to_owned(), contents: broken_helper.to_owned(), }, ]); let fresh = workspace_from_files( - &[("main.solc", broken_main), ("helper.solc", broken_helper)], - "main.solc", + &[("main.sol", broken_main), ("helper.sol", broken_helper)], + "main.sol", ); assert_eq!(incremental.diagnostics(), fresh.diagnostics()); - let fixed_main = - "import helper.{answer};\nfunction main() -> word { return answer(true); }\n"; - let fixed_helper = "function answer(x: bool) -> word { return 1; }\nexport { answer };\n"; + let fixed_main = "import {answer} from helper;\nfunction main() returns (word) { return answer(true); }\n"; + let fixed_helper = + "function answer(x: bool) returns (word) { return 1; }\nexport { answer };\n"; incremental.apply_file_changes([ WorkspaceFileChange::Set { - path: "main.solc".to_owned(), + path: "main.sol".to_owned(), contents: fixed_main.to_owned(), }, WorkspaceFileChange::Set { - path: "helper.solc".to_owned(), + path: "helper.sol".to_owned(), contents: fixed_helper.to_owned(), }, ]); let fresh = workspace_from_files( - &[("main.solc", fixed_main), ("helper.solc", fixed_helper)], - "main.solc", + &[("main.sol", fixed_main), ("helper.sol", fixed_helper)], + "main.sol", ); assert_eq!(incremental.diagnostics(), fresh.diagnostics()); assert!(incremental.diagnostics().is_empty()); @@ -1327,19 +1347,19 @@ mod tests { let workspace = workspace_from_files( &[ ( - "main.solc", - "import a.{fromA};\nfunction main() -> word { return fromA(); }\n", + "main.sol", + "import {fromA} from a;\nfunction main() returns (word) { return fromA(); }\n", ), ( - "a.solc", - "import b.{value};\nfunction fromA() -> word { return value(); }\nexport { fromA };\n", + "a.sol", + "import {value} from b;\nfunction fromA() returns (word) { return value(); }\nexport { fromA };\n", ), ( - "b.solc", - "function value() -> word { return 42; }\nexport { value };\n", + "b.sol", + "function value() returns (word) { return 42; }\nexport { value };\n", ), ], - "main.solc", + "main.sol", ); assert!(workspace.diagnostics().is_empty()); } @@ -1353,14 +1373,14 @@ mod tests { assert_eq!( names, BTreeSet::from([ - "ABIGeneric.solc", - "Generic.solc", - "StorageGeneric.solc", - "dispatch.solc", - "eip712.solc", - "eip7951.solc", - "opcodes.solc", - "std.solc", + "ABIGeneric.sol", + "Generic.sol", + "StorageGeneric.sol", + "dispatch.sol", + "eip712.sol", + "eip7951.sol", + "opcodes.sol", + "std.sol", ]) ); assert!(STD_FILES.iter().all(|(_, contents)| !contents.is_empty())); @@ -1370,18 +1390,18 @@ mod tests { fn virtual_file_urls_encode_special_path_characters() { let mut workspace = Workspace::new(); workspace.set_file( - "nested/数 学#1.solc", - "function value() -> word { return 1; }\n".to_owned(), + "nested/数 学#1.sol", + "function value() returns (word) { return 1; }\n".to_owned(), ); let file = workspace .db() - .source_file("/main/nested/数 学#1.solc") + .source_file("/main/nested/数 学#1.sol") .expect("virtual source file"); assert_eq!( file.url(workspace.db()).as_str(), - "file:///main/nested/%E6%95%B0%20%E5%AD%A6%231.solc" + "file:///main/nested/%E6%95%B0%20%E5%AD%A6%231.sol" ); } @@ -1392,11 +1412,11 @@ mod tests { let mut source = String::new(); for index in 0..256 { source.push_str(&format!( - "function value{index}(x: word) -> word {{ return x; }}\n" + "function value{index}(x: word) returns (word) {{ return x; }}\n" )); } source.push_str(&format!( - "function main() -> word {{ return value255({revision}); }}\n" + "function main() returns (word) {{ return value255({revision}); }}\n" )); source } @@ -1404,7 +1424,7 @@ mod tests { let mut workspace = workspace_with_main(&source(0)); assert!(workspace.diagnostics().is_empty()); for revision in 1..=64 { - workspace.set_file("main.solc", source(revision)); + workspace.set_file("main.sol", source(revision)); assert!(workspace.diagnostics().is_empty(), "revision {revision}"); } } diff --git a/crates/wasm/src/lib.rs b/crates/wasm/src/lib.rs index 062bd9fd..2c4ab9ba 100644 --- a/crates/wasm/src/lib.rs +++ b/crates/wasm/src/lib.rs @@ -116,8 +116,8 @@ pub(crate) struct Label { #[derive(Clone, Serialize)] #[serde(rename_all = "camelCase")] pub(crate) struct Pos { - /// UI-facing source path. `/main/foo.solc` is `foo.solc`, `/std/std.solc` - /// is `std:std.solc`, and `/ext/lib/foo.solc` is `ext:lib/foo.solc`. + /// UI-facing source path. `/main/foo.sol` is `foo.sol`, `/std/std.sol` + /// is `std:std.sol`, and `/ext/lib/foo.sol` is `ext:lib/foo.sol`. pub(crate) file: String, pub(crate) start_byte: u32, pub(crate) end_byte: u32, @@ -136,6 +136,27 @@ struct FileOutput { /// Compiles already-deserialized input. Tests use this native helper directly. pub(crate) fn compile_impl(input: CompileInput) -> CompileResult { + if Path::new(&input.entry) + .extension() + .and_then(|extension| extension.to_str()) + != Some("sol") + { + return CompileResult { + success: false, + diagnostics: vec![message_diag( + DiagnosticSeverity::Error, + format!( + "entry file `{}` must use the `.sol` source extension", + input.entry + ), + )], + hull: None, + yul: None, + sonatina: None, + abi: None, + }; + } + let mut workspace = Workspace::new(); workspace.apply_file_changes( input @@ -508,22 +529,45 @@ mod tests { fn input(source: &str, options: Options) -> CompileInput { CompileInput { files: vec![FileInput { - path: "main.solc".to_owned(), + path: "main.sol".to_owned(), content: source.to_owned(), }], - entry: "main.solc".to_owned(), + entry: "main.sol".to_owned(), options, } } + #[test] + fn compile_accepts_only_sol_entry_files() { + let valid = compile_impl(input("function main() {}\n", Options::default())); + assert!(valid.success); + assert!(valid.diagnostics.is_empty()); + + let invalid = compile_impl(CompileInput { + files: vec![FileInput { + path: "main.solc".to_owned(), + content: "function main() {}\n".to_owned(), + }], + entry: "main.solc".to_owned(), + options: Options::default(), + }); + assert!(!invalid.success); + assert!(invalid.diagnostics.iter().any(|diagnostic| { + diagnostic.is_error() + && diagnostic + .message + .contains("entry file `main.solc` must use the `.sol` source extension") + })); + } + #[test] fn clean_program_emits_all_playground_outputs() { let result = compile_impl(input( concat!( - "import std.{*};\n", - "import std.dispatch.{*};\n", + "import * from std;\n", + "import * from std.dispatch;\n", "contract Main {\n", - " public function answer() -> uint256 {\n", + " function answer() public returns (uint256) {\n", " return uint256(42);\n", " }\n", "}\n", @@ -557,7 +601,7 @@ mod tests { #[test] fn sonatina_only_runs_the_shared_hull_pipeline() { let result = compile_impl(input( - "contract Main {\n public function main() -> word {\n return 1;\n }\n}\n", + "contract Main {\n function main() public returns (word) {\n return 1;\n }\n}\n", Options { emit_hull: false, emit_yul: false, @@ -581,8 +625,8 @@ mod tests { fn combined_artifacts_follow_cli_fail_fast_order() { let result = compile_impl(input( concat!( - "contract A { public function main() -> word { return 1; } }\n", - "contract B { public function main() -> word { return 2; } }\n", + "contract A { function main() public returns (word) { return 1; } }\n", + "contract B { function main() public returns (word) { return 2; } }\n", ), Options { emit_hull: false, @@ -613,10 +657,10 @@ mod tests { fn abi_only_emits_contract_json() { let result = compile_impl(input( concat!( - "import std.{*};\n", - "import std.dispatch.{*};\n", + "import * from std;\n", + "import * from std.dispatch;\n", "contract Main {\n", - " public function answer() -> uint256 {\n", + " function answer() public returns (uint256) {\n", " return uint256(42);\n", " }\n", "}\n", @@ -644,22 +688,24 @@ mod tests { let result = compile_impl(CompileInput { files: vec![ FileInput { - path: "main.solc".to_owned(), - content: "import a; import b; function main() -> word { return 0; }\n" + path: "main.sol".to_owned(), + content: "import a; import b; function main() returns (word) { return 0; }\n" .to_owned(), }, FileInput { - path: "a.solc".to_owned(), - content: "contract Token { public function main() -> word { return 1; } }\n" - .to_owned(), + path: "a.sol".to_owned(), + content: + "contract Token { function main() public returns (word) { return 1; } }\n" + .to_owned(), }, FileInput { - path: "b.solc".to_owned(), - content: "contract Token { public function main() -> word { return 2; } }\n" - .to_owned(), + path: "b.sol".to_owned(), + content: + "contract Token { function main() public returns (word) { return 2; } }\n" + .to_owned(), }, ], - entry: "main.solc".to_owned(), + entry: "main.sol".to_owned(), options: Options { emit_hull: false, emit_yul: false, @@ -686,15 +732,16 @@ mod tests { let mut workspace = Workspace::new(); workspace.set_external_file( "pkg", - "token.solc", - "contract ExternalToken { public function main() -> word { return 7; } }\n".to_owned(), + "token.sol", + "contract ExternalToken { function main() public returns (word) { return 7; } }\n" + .to_owned(), ); workspace.set_file( - "main.solc", - "import @pkg.token; contract Local { public function main() -> word { return 1; } }\n" + "main.sol", + "import @pkg.token; contract Local { function main() public returns (word) { return 1; } }\n" .to_owned(), ); - workspace.set_entry("main.solc"); + workspace.set_entry("main.sol"); assert!(workspace.diagnostics().is_empty()); let entry = workspace.entry_module().expect("entry module"); @@ -722,9 +769,9 @@ mod tests { fn backend_diagnostic_uses_shared_vfs_conversion() { let result = compile_impl(input( concat!( - "import std.{string};\n", + "import {string} from std;\n", "contract Main {\n", - " public function main() -> string { return \"nope\"; }\n", + " function main() public returns (string) { return \"nope\"; }\n", "}\n", ), Options { @@ -752,7 +799,7 @@ mod tests { #[test] fn bad_program_reports_position_and_skips_backend() { let result = compile_impl(input( - "function f() -> word {\n return true;\n}\n", + "function f() returns (word) {\n return true;\n}\n", Options { emit_hull: true, emit_yul: true, diff --git a/crates/yul/tests/e2e.rs b/crates/yul/tests/e2e.rs index 6ad3bf42..47bb217e 100644 --- a/crates/yul/tests/e2e.rs +++ b/crates/yul/tests/e2e.rs @@ -35,7 +35,7 @@ static SOLC_FOR_E2E: OnceLock, E2eFailure>> = OnceLock::n #[dir_test( dir: "$CARGO_MANIFEST_DIR/../../tests/e2e", - glob: "**/main.solc" + glob: "**/main.sol" )] fn yul_evm_e2e_fixture(fixture: Fixture<&str>) { if !e2e_enabled() { @@ -165,7 +165,7 @@ fn resolve_fixture_directives( } Item::InstanceDef(instance) => { for function in instance.methods(db) { - reject_non_dispatch_directives(db, *function, "instance method")?; + reject_non_dispatch_directives(db, *function, "impl method")?; } } Item::ContractDef(contract) => { diff --git a/crates/yul/tests/fixtures/data_type_storage_full/main.sol b/crates/yul/tests/fixtures/data_type_storage_full/main.sol new file mode 100644 index 00000000..f08e33e1 --- /dev/null +++ b/crates/yul/tests/fixtures/data_type_storage_full/main.sol @@ -0,0 +1,39 @@ +import * from std; + +enum Box { Box(word) } + +impl StorageType { + function load(ptr: word) returns (Box) { + return Box(StorageType.load(ptr)); + } + + function store(ptr: word, value: Box) { + match (value) { +case Box(inner) { +StorageType.store(ptr, inner); +} +} + } +} + +impl CanStore, Box> { + function load(ptr: storage) returns (Box) { + return StorageType.load(Typedef.rep(ptr)); + } + + function store(ptr: storage, value: Box) { + StorageType.store(Typedef.rep(ptr), value); + } +} + +contract DataTypeStorageFull { + box : Box; + + function main() public returns (word) { + match (box) { +case Box(inner) { +return inner; +} +} + } +} diff --git a/crates/yul/tests/fixtures/data_type_storage_full/main.solc b/crates/yul/tests/fixtures/data_type_storage_full/main.solc deleted file mode 100644 index ec9e8492..00000000 --- a/crates/yul/tests/fixtures/data_type_storage_full/main.solc +++ /dev/null @@ -1,35 +0,0 @@ -import std.{*}; - -data Box = Box(word); - -instance Box : StorageType { - function load(ptr : word) -> Box { - return Box(StorageType.load(ptr):word); - } - - function store(ptr : word, value : Box) -> () { - match value { - | Box(inner) => StorageType.store(ptr, inner); - } - } -} - -instance storage(Box) : CanStore(Box) { - function load(ptr : storage(Box)) -> Box { - return StorageType.load(Typedef.rep(ptr)):Box; - } - - function store(ptr : storage(Box), value : Box) -> () { - StorageType.store(Typedef.rep(ptr), value); - } -} - -contract DataTypeStorageFull { - box : Box; - - public function main() -> word { - match box { - | Box(inner) => return inner; - } - } -} diff --git a/crates/yul/tests/snapshots.rs b/crates/yul/tests/snapshots.rs index 44254c68..e720bf85 100644 --- a/crates/yul/tests/snapshots.rs +++ b/crates/yul/tests/snapshots.rs @@ -109,11 +109,11 @@ fn doc_id_yul_snapshot() { render_source( "doc_id", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract IdDoc { - public function id(x : uint256) -> uint256 { + function id(x : uint256) public returns (uint256) { return x; } } @@ -130,16 +130,15 @@ fn doc_option_maybe_yul_snapshot() { "doc_option_maybe", r#" contract OptionDoc { - data Option(a) = None | Some(a); + enum Option {None , Some(a)} - function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.None => return n; - | Option.Some(x) => return x; - } + function maybe(n : word, o : Option) returns (word) { + match (o) { + case Option.None { return n; } +case Option.Some(x) { return x; }} } - public function main() -> word { + function main() public returns (word) { return maybe(0, Option.Some(42)); } } @@ -151,14 +150,14 @@ contract OptionDoc { #[test] fn doc_color_yul_snapshot() { let fixture = - repo_root().join("crates/parser/tests/fixtures/corpus/ok/test/examples/spec/047rgb.solc"); + repo_root().join("crates/parser/tests/fixtures/corpus/ok/test/examples/spec/047rgb.sol"); insta::assert_snapshot!("doc_color_yul_snapshot", render_fixture(&fixture)); } #[test] fn doc_add1_yul_snapshot() { let fixture = - repo_root().join("crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Add1.solc"); + repo_root().join("crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Add1.sol"); insta::assert_snapshot!("doc_add1_yul_snapshot", render_fixture(&fixture)); } @@ -169,15 +168,15 @@ fn dispatch_basic_shape_yul_snapshot() { render_source( "dispatch_basic_shape", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract DispatchBasicShape { - public function id(x : uint256) -> uint256 { + function id(x : uint256) public returns (uint256) { return x; } - public function answer() -> uint256 { + function answer() public returns (uint256) { return uint256(42); } } @@ -188,7 +187,7 @@ contract DispatchBasicShape { #[test] fn data_type_storage_full_yul_snapshot() { - let fixture = repo_root().join("crates/yul/tests/fixtures/data_type_storage_full/main.solc"); + let fixture = repo_root().join("crates/yul/tests/fixtures/data_type_storage_full/main.sol"); insta::assert_snapshot!("data_type_storage_full", render_fixture(&fixture)); } @@ -549,7 +548,7 @@ fn assembly_let_shadowing_does_not_substitute_shadowed_name() { "assembly_let_shadowing", r#" contract AssemblyLetShadowing { - public function main() -> word { + function main() public returns (word) { let x : bool = false; let r : word = 0; assembly { @@ -585,7 +584,7 @@ fn assembly_nested_block_shadowing_is_block_local() { "assembly_nested_block_shadowing", r#" contract AssemblyNestedBlockShadowing { - public function main() -> word { + function main() public returns (word) { let x : bool = false; let r : word = 0; assembly { @@ -619,7 +618,7 @@ fn assembly_function_params_and_returns_shadow_hull_locals() { "assembly_function_shadowing", r#" contract AssemblyFunctionShadowing { - public function main() -> word { + function main() public returns (word) { let x : bool = false; let y : bool = true; let r : word = 0; @@ -666,7 +665,7 @@ fn assembly_function_names_are_hoisted_for_forward_and_mutual_calls() { "assembly_function_mutual_recursion", r#" contract AssemblyFunctionMutualRecursion { - public function main() -> word { + function main() public returns (word) { let result : word; assembly { result := even(6) @@ -710,7 +709,7 @@ contract AssemblyFunctionMutualRecursion { #[test] fn polymorphic_inline_yul_terminators_render_in_value_functions() { let fixture = - repo_root().join("crates/hir-ty/tests/fixtures/ok/yul_polymorphic_terminators/main.solc"); + repo_root().join("crates/hir-ty/tests/fixtures/ok/yul_polymorphic_terminators/main.sol"); let yul = render_fixture(&fixture); for terminator in ["stop()", "invalid()", "selfdestruct(", "revert("] { @@ -754,7 +753,7 @@ fn object_less_source_calls_its_mangled_main_before_returning() { let yul = render_source( "object_less_main", r#" -function main() -> word { return 42; } +function main() returns (word) { return 42; } "#, ); @@ -772,15 +771,14 @@ fn value_equal_literal_spellings_emit_one_yul_case() { "equal_literal_spellings", r#" contract C { - function pick(x : word) -> word { - match x { - | 0x2a => return 111; - | 0042 => return 222; - | _ => return 333; - } + function pick(x : word) returns (word) { + match (x) { + case 0x2a { return 111; } +case 0042 { return 222; } +default { return 333; }} } - function main() -> word { + function main() returns (word) { let x : word = 0; assembly { x := calldataload(0) } return pick(x); @@ -811,7 +809,7 @@ fn hygienic_names_canonical_literals_and_break_validation() { "reserved_add_name", r#" contract ReservedAddName { - public function main() -> word { + function main() public returns (word) { let add : word = 1; return add; } @@ -825,7 +823,7 @@ contract ReservedAddName { "asm_shadow", r#" contract AsmShadow { - public function main() -> word { + function main() public returns (word) { let x : bool = false; let r : word = 0; assembly { @@ -844,7 +842,7 @@ contract AsmShadow { "leading_zero_decimal", r#" contract LeadingZeroDecimal { - public function main() -> word { + function main() public returns (word) { return 01; } } @@ -871,7 +869,7 @@ contract LeadingZeroDecimal { "asm_break_outside_loop", r#" contract BadBreak { - public function main() -> word { + function main() public returns (word) { assembly { break } return 0; } @@ -887,7 +885,7 @@ contract BadBreak { "asm_continue_post", r#" contract BadContinuePost { - public function main() -> word { + function main() public returns (word) { assembly { for {} 1 { continue } {} } return 0; } @@ -904,11 +902,11 @@ contract BadContinuePost { fn strict_assembly_artifact_requires_one_top_level_object_or_selection() { let multi_contract = r#" contract A { - public function main() -> word { return 1; } + function main() public returns (word) { return 1; } } contract B { - public function main() -> word { return 2; } + function main() public returns (word) { return 2; } } "#; let error = render_source_error("multi_contract_yul", multi_contract); @@ -936,11 +934,11 @@ fn solc_strict_assembly_compiles_snapshots_and_repros_when_present() { let fixtures = repo_root().join("crates/parser/tests/fixtures/corpus/ok/test/examples/cases"); cases.push(( "repro_for_body_shadow".to_owned(), - render_fixture(&fixtures.join("for-body-shadow.solc")), + render_fixture(&fixtures.join("for-body-shadow.sol")), )); cases.push(( "repro_for_init_shadow".to_owned(), - render_fixture(&fixtures.join("for-init-shadow.solc")), + render_fixture(&fixtures.join("for-init-shadow.sol")), )); cases.push(( "repro_reserved_add_name".to_owned(), @@ -948,7 +946,7 @@ fn solc_strict_assembly_compiles_snapshots_and_repros_when_present() { "repro_reserved_add_name", r#" contract C { - public function main() -> word { + function main() public returns (word) { let add : word = 1; return add; } @@ -962,7 +960,7 @@ contract C { "repro_decimal_leading_zero", r#" contract C { - public function main() -> word { + function main() public returns (word) { return 01; } } @@ -975,7 +973,7 @@ contract C { "repro_assembly_shadow_lvalue", r#" contract C { - public function main() -> word { + function main() public returns (word) { let x : bool = false; let r : word = 0; assembly { let x := 1 r := x } @@ -996,21 +994,19 @@ fn nested_pair_tail_binding_preserves_the_tail_product() { let yul = render_source( "nested_pair_tail_binding", r#" -forall a b . function nestedSnd(p: (a, b)) -> b { +function nestedSnd(p: (a, b)) returns (b) { assembly { mstore(0, 0) } - match p { - | (_, tail) => return tail; - } + match (p) { + case (_, tail) { return tail; }} } contract C { - public function main() -> word { + function main() public returns (word) { let x: word; assembly { x := sload(0) } let tail = nestedSnd((x, (x, x))); - match tail { - | (head, _) => return head; - } + match (tail) { + case (head, _) { return head; }} } } "#, @@ -1071,7 +1067,7 @@ fn specialize_src(name: &str, src: &str) -> (&'static TestDb, SpecializeOutput<' db.module_tree = Some(tree); db.module_fs_snapshot = Some(fs_snapshot); - let path = main_root.join(format!("{name}.solc")); + let path = main_root.join(format!("{name}.sol")); let key = module_key_for_path(LibraryId::Main, &main_root, &path) .expect("inline source under virtual main root"); let file = SourceFile::new( @@ -1103,7 +1099,7 @@ fn yul_function<'a>(yul: &'a str, name: &str) -> &'a str { fn test_span<'db>(db: &'db TestDb) -> Span<'db> { let file = SourceFile::new( db, - "memory:///yul_snapshots_hull.solc" + "memory:///yul_snapshots_hull.sol" .parse() .expect("valid URL"), Some(String::new()), @@ -1168,7 +1164,7 @@ fn collect_module_fs_snapshot( }; for entry in entries.flatten() { let path = entry.path(); - if path.extension().and_then(|extension| extension.to_str()) == Some("solc") { + if path.extension().and_then(|extension| extension.to_str()) == Some("sol") { if path.is_file() { existing_files.insert(path.clone()); } @@ -1298,11 +1294,11 @@ fn snapshot_yul_cases() -> Vec<(String, String)> { render_source( "doc_id", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract IdDoc { - public function id(x : uint256) -> uint256 { + function id(x : uint256) public returns (uint256) { return x; } } @@ -1315,16 +1311,15 @@ contract IdDoc { "doc_option_maybe", r#" contract OptionDoc { - data Option(a) = None | Some(a); + enum Option {None , Some(a)} - function maybe(n : word, o : Option(word)) -> word { - match o { - | Option.None => return n; - | Option.Some(x) => return x; - } + function maybe(n : word, o : Option) returns (word) { + match (o) { + case Option.None { return n; } +case Option.Some(x) { return x; }} } - public function main() -> word { + function main() public returns (word) { return maybe(0, Option.Some(42)); } } @@ -1334,13 +1329,13 @@ contract OptionDoc { ( "snapshot_doc_color".to_owned(), render_fixture( - &repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/spec/047rgb.solc"), + &repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/spec/047rgb.sol"), ), ), ( "snapshot_doc_add1".to_owned(), render_fixture( - &repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Add1.solc"), + &repo.join("crates/parser/tests/fixtures/corpus/ok/test/examples/cases/Add1.sol"), ), ), ( @@ -1348,15 +1343,15 @@ contract OptionDoc { render_source( "dispatch_basic_shape", r#" -import std.{*}; -import std.dispatch.{*}; +import * from std; +import * from std.dispatch; contract DispatchBasicShape { - public function id(x : uint256) -> uint256 { + function id(x : uint256) public returns (uint256) { return x; } - public function answer() -> uint256 { + function answer() public returns (uint256) { return uint256(42); } } diff --git a/crates/yul/tests/snapshots/snapshots__dispatch_basic_shape.snap b/crates/yul/tests/snapshots/snapshots__dispatch_basic_shape.snap index b8c63bc5..e0b22d43 100644 --- a/crates/yul/tests/snapshots/snapshots__dispatch_basic_shape.snap +++ b/crates/yul/tests/snapshots/snapshots__dispatch_basic_shape.snap @@ -485,7 +485,6 @@ object "DispatchBasicShapeDeploy" { } function usr$std_set_free_memory_d65c817cd(src$loc_103) { usr$opcodes_mstore_d7415bc7e(0x40, src$loc_103) - leave } usr$dispatch_basic_shape_DispatchBasicShape_main_d302fef00() } diff --git a/crates/yul/tests/snapshots/snapshots__doc_id.snap b/crates/yul/tests/snapshots/snapshots__doc_id.snap index d7d910f1..c0f2085a 100644 --- a/crates/yul/tests/snapshots/snapshots__doc_id.snap +++ b/crates/yul/tests/snapshots/snapshots__doc_id.snap @@ -422,7 +422,6 @@ object "IdDocDeploy" { } function usr$std_set_free_memory_d65c817cd(src$loc_98) { usr$opcodes_mstore_d7415bc7e(0x40, src$loc_98) - leave } usr$doc_id_IdDoc_main_d45c46589() } diff --git a/editors/README.md b/editors/README.md index 32cf5920..cd2c303a 100644 --- a/editors/README.md +++ b/editors/README.md @@ -25,7 +25,7 @@ Every editor integration can start the same stdio server. By default they use `solcore-lsp` on `PATH`. VS Code and Neovim also expose editor-specific command overrides for local development. -On initialization, the native server indexes `.solc` files below every +On initialization, the native server indexes `.sol` files below every workspace folder. Each root has an isolated compiler namespace, and dynamic workspace-folder and watched-file changes keep unopened import targets and workspace symbols up to date. @@ -44,5 +44,5 @@ smart selection ranges, semantic tokens, and inlay hints. ## Packages - `vscode-solcore`: VS Code extension with TextMate highlighting and LSP client. -- `vim-solcore`: Vim/Neovim package with `.solc` highlighting and LSP setup. +- `vim-solcore`: Vim/Neovim package with `.sol` highlighting and LSP setup. - `emacs-solcore`: Emacs major mode plus eglot/lsp-mode setup. diff --git a/editors/emacs-solcore/README.md b/editors/emacs-solcore/README.md index f95fac70..a03c69b1 100644 --- a/editors/emacs-solcore/README.md +++ b/editors/emacs-solcore/README.md @@ -1,9 +1,9 @@ # Solcore Emacs mode -This directory contains Emacs support for Solcore `.solc` files: +This directory contains Emacs support for Solcore `.sol` files: - `solcore-mode.el` provides a `prog-mode`-derived major mode. -- `.solc` files are added to `auto-mode-alist`. +- `.sol` files are added to `auto-mode-alist`. - Syntax highlighting uses Emacs font-lock for Solcore keywords, declarations, primitive types, constants, numbers, operators, and function calls. - Optional LSP registration is provided for both `lsp-mode` and Eglot. @@ -22,7 +22,7 @@ With `use-package`: ```elisp (use-package solcore-mode :load-path "/path/to/solcore-rs/editors/emacs-solcore" - :mode ("\\.solc\\'" . solcore-mode)) + :mode ("\\.sol\\'" . solcore-mode)) ``` ## LSP server command @@ -63,7 +63,7 @@ Enable it with a hook: (use-package solcore-mode :load-path "/path/to/solcore-rs/editors/emacs-solcore" - :mode ("\\.solc\\'" . solcore-mode) + :mode ("\\.sol\\'" . solcore-mode) :hook (solcore-mode . lsp-deferred)) ``` @@ -78,7 +78,7 @@ with a hook: (use-package solcore-mode :load-path "/path/to/solcore-rs/editors/emacs-solcore" - :mode ("\\.solc\\'" . solcore-mode)) + :mode ("\\.sol\\'" . solcore-mode)) ``` For non-`use-package` setups: @@ -91,7 +91,7 @@ For non-`use-package` setups: ## Manual checks -Open any `.solc` file and run: +Open any `.sol` file and run: ```elisp M-x solcore-mode diff --git a/editors/emacs-solcore/solcore-mode.el b/editors/emacs-solcore/solcore-mode.el index 338369ba..e9ae1467 100644 --- a/editors/emacs-solcore/solcore-mode.el +++ b/editors/emacs-solcore/solcore-mode.el @@ -11,7 +11,7 @@ ;;; Commentary: ;; Major mode, font-lock highlighting, and optional LSP client registration for -;; Solcore `.solc' files. +;; Solcore `.sol' files. ;; ;; The LSP server command is resolved from SOLCORE_LSP_SERVER when that ;; environment variable is non-empty. Otherwise `solcore-lsp-server-command' @@ -55,19 +55,21 @@ program name followed by arguments." "Characters that keep a Solcore identifier or keyword going.") (defconst solcore--control-keywords - '("if" "else" "for" "switch" "case" "default" "match" "return" + '("if" "else" "for" "while" "switch" "case" "default" "match" "return" "leave" "continue" "break")) (defconst solcore--declaration-keywords - '("contract" "import" "export" "as" "let" "data" "class" "forall" - "instance" "type" "function" "constructor" "fallback" "assembly" - "pragma" "lam")) + '("contract" "import" "from" "hiding" "export" "as" "let" "enum" "trait" + "impl" "where" "type" "function" "returns" "constructor" "fallback" + "assembly" "pragma" "lam" "comptime" "derive")) (defconst solcore--modifier-keywords '("public" "payable")) (defconst solcore--primitive-types - '("word" "bool" "unit")) + '("word" "bool" "string" "integer" "pair" "sum" "uint256" "address" + "byte" "bytes" "bytes4" "bytes32" "memory" "storage" "calldata" + "returndata" "mapping" "array")) (defconst solcore--constants '("true" "false" "_")) @@ -95,7 +97,7 @@ left to the caller so declaration patterns can consume whitespace once." "\\s-+\\(" solcore--identifier-re "\\)") (1 font-lock-keyword-face) (2 font-lock-function-name-face nil t)) - (,(concat (solcore--keyword-prefix-regexp '("data" "class" "type")) + (,(concat (solcore--keyword-prefix-regexp '("enum" "trait" "type")) "\\s-+\\(" solcore--identifier-re "\\)") (1 font-lock-keyword-face) (2 font-lock-type-face nil t)) @@ -128,9 +130,9 @@ left to the caller so declaration patterns can consume whitespace once." "\\(?:\\'\\|[^[:alpha:][:digit:]_]\\)") 1 font-lock-constant-face) (,(concat "\\(" - (regexp-opt '(":=" "+=" "-=" "^=" "&=" "|=" "%=" "->" "=>" + (regexp-opt '(":=" "+=" "-=" "*=" "/=" "^=" "&=" "|=" "%=" "~=" "->" "=>" "==" "!=" ">=" "<=" "&&" "||")) - "\\|[+*/%!?=<>|&^@-]\\)") + "\\|[+*/%!?=<>|&^@~-]\\)") 1 font-lock-builtin-face)) "Font-lock rules for `solcore-mode'.") @@ -148,7 +150,7 @@ left to the caller so declaration patterns can consume whitespace once." (defvar solcore-imenu-generic-expression `((nil ,(concat "^\\s-*function\\s-+\\(" solcore--identifier-re "\\)") 1) ("Contracts" ,(concat "^\\s-*contract\\s-+\\(" solcore--identifier-re "\\)") 1) - ("Types" ,(concat "^\\s-*\\(?:data\\|class\\|type\\)\\s-+\\(" + ("Types" ,(concat "^\\s-*\\(?:enum\\|trait\\|type\\)\\s-+\\(" solcore--identifier-re "\\)") 1)) "Imenu expressions for `solcore-mode'.") @@ -193,7 +195,7 @@ left to the caller so declaration patterns can consume whitespace once." ;;;###autoload (define-derived-mode solcore-mode prog-mode "Solcore" - "Major mode for editing Solcore `.solc' files." + "Major mode for editing Solcore `.sol' files." :syntax-table solcore-mode-syntax-table (setq-local font-lock-defaults '(solcore-font-lock-keywords)) (setq-local comment-start "// ") @@ -207,7 +209,7 @@ left to the caller so declaration patterns can consume whitespace once." (append "{}();," electric-indent-chars))) ;;;###autoload -(add-to-list 'auto-mode-alist '("\\.solc\\'" . solcore-mode)) +(add-to-list 'auto-mode-alist '("\\.sol\\'" . solcore-mode)) (defvar lsp-language-id-configuration) (declare-function lsp-activate-on "lsp-mode") diff --git a/editors/vim-solcore/README.md b/editors/vim-solcore/README.md index d6ddbe72..ac1fcc80 100644 --- a/editors/vim-solcore/README.md +++ b/editors/vim-solcore/README.md @@ -1,8 +1,8 @@ # Solcore Vim/Neovim support -This directory provides Vim runtime files for Solcore `.solc` files: +This directory provides Vim runtime files for Solcore `.sol` files: -- `ftdetect/solcore.vim` detects `*.solc` as the `solcore` filetype. +- `ftdetect/solcore.vim` detects `*.sol` as the `solcore` filetype. - `ftplugin/solcore.vim` configures comments, formatting, suffix lookup, and word movement for Solcore buffers. - `syntax/solcore.vim` provides Vim script syntax highlighting. @@ -21,7 +21,7 @@ Plugin managers can point at this directory as a local plugin. ## Syntax Highlighting -Open any `.solc` file after the runtime path is configured. Vim/Neovim will set +Open any `.sol` file after the runtime path is configured. Vim/Neovim will set `filetype=solcore` and load `syntax/solcore.vim` when syntax highlighting is enabled: diff --git a/editors/vim-solcore/ftdetect/solcore.vim b/editors/vim-solcore/ftdetect/solcore.vim index b09995ff..9f96626e 100644 --- a/editors/vim-solcore/ftdetect/solcore.vim +++ b/editors/vim-solcore/ftdetect/solcore.vim @@ -1,4 +1,4 @@ augroup solcore_filetype autocmd! - autocmd BufNewFile,BufRead *.solc setfiletype solcore + autocmd BufNewFile,BufRead *.sol setfiletype solcore augroup END diff --git a/editors/vim-solcore/ftplugin/solcore.vim b/editors/vim-solcore/ftplugin/solcore.vim index a2fbae01..ac48182d 100644 --- a/editors/vim-solcore/ftplugin/solcore.vim +++ b/editors/vim-solcore/ftplugin/solcore.vim @@ -8,7 +8,7 @@ let b:undo_ftplugin = 'setlocal commentstring< comments< formatoptions< include< setlocal commentstring=//\ %s setlocal comments=s1:/*,mb:*,ex:*/,:// let &l:include = '^\s*\%(import\|export\)\s\+' -setlocal suffixesadd=.solc +setlocal suffixesadd=.sol setlocal formatoptions-=t setlocal formatoptions+=croql diff --git a/editors/vim-solcore/syntax/solcore.vim b/editors/vim-solcore/syntax/solcore.vim index 3fc012f9..f5d63695 100644 --- a/editors/vim-solcore/syntax/solcore.vim +++ b/editors/vim-solcore/syntax/solcore.vim @@ -15,18 +15,18 @@ syntax region solcoreString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=solcoreE syntax match solcoreContractDeclaration #\v(^|[^[:alnum:]_-])contract\s+\zs[[:alpha:]][[:alnum:]_]*(-[[:alpha:]][[:alnum:]_]*)*# syntax match solcoreFunctionDeclaration #\v(^|[^[:alnum:]_-])function\s+\zs[[:alpha:]][[:alnum:]_]*(-[[:alpha:]][[:alnum:]_]*)*# -syntax match solcoreTypeDeclaration #\v(^|[^[:alnum:]_-])(data|class|type)\s+\zs[[:alpha:]][[:alnum:]_]*(-[[:alpha:]][[:alnum:]_]*)*# +syntax match solcoreTypeDeclaration #\v(^|[^[:alnum:]_-])(enum|trait|type)\s+\zs[[:alpha:]][[:alnum:]_]*(-[[:alpha:]][[:alnum:]_]*)*# syntax match solcoreVariableDeclaration #\v(^|[^[:alnum:]_-])let\s+\zs[[:alpha:]][[:alnum:]_]*(-[[:alpha:]][[:alnum:]_]*)*# syntax match solcorePragmaDeclaration #\v(^|[^[:alnum:]_-])pragma\s+\zs[[:alpha:]][[:alnum:]_]*(-[[:alpha:]][[:alnum:]_]*)*# -syntax match solcoreControlKeyword #\v(^|[^[:alnum:]_-])\zs(if|else|for|switch|case|default|match|return|leave|continue|break)\ze([^[:alnum:]_-]|$)# -syntax match solcoreDeclarationKeyword #\v(^|[^[:alnum:]_-])\zs(contract|import|export|as|let|data|class|forall|instance|type|function|constructor|fallback|assembly|pragma|lam)\ze([^[:alnum:]_-]|$)# +syntax match solcoreControlKeyword #\v(^|[^[:alnum:]_-])\zs(if|else|for|while|switch|case|default|match|return|leave|continue|break)\ze([^[:alnum:]_-]|$)# +syntax match solcoreDeclarationKeyword #\v(^|[^[:alnum:]_-])\zs(contract|import|from|hiding|export|as|let|enum|trait|impl|where|type|function|returns|constructor|fallback|assembly|pragma|lam|comptime|derive)\ze([^[:alnum:]_-]|$)# syntax match solcoreStorageModifier #\v(^|[^[:alnum:]_-])\zs(public|payable)\ze([^[:alnum:]_-]|$)# syntax match solcoreBoolean #\v(^|[^[:alnum:]_-])\zs(true|false)\ze([^[:alnum:]_-]|$)# syntax match solcoreWildcard #\v(^|[^[:alnum:]_-])\zs_\ze([^[:alnum:]_-]|$)# -syntax match solcorePrimitiveType #\v(^|[^[:alnum:]_-])\zs(word|bool|unit)\ze([^[:alnum:]_-]|$)# +syntax match solcorePrimitiveType #\v(^|[^[:alnum:]_-])\zs(word|bool|string|integer|pair|sum|uint256|address|byte|bytes|bytes4|bytes32|memory|storage|calldata|returndata|mapping|array)\ze([^[:alnum:]_-]|$)# syntax match solcoreTypeIdentifier #\v(^|[^[:alnum:]_-])\zs[A-Z][[:alnum:]_]*(-[[:alpha:]][[:alnum:]_]*)*# syntax match solcoreHexNumber #\v(^|[^[:alnum:]_])\zs0x[0-9a-fA-F]+\ze([^[:alnum:]_]|$)# @@ -37,10 +37,13 @@ syntax match solcoreFunctionCall #\v[[:alpha:]][[:alnum:]_]*(-[[:alpha:]][[:alnu syntax match solcoreOperator #:=# syntax match solcoreOperator #+=# syntax match solcoreOperator #-=# +syntax match solcoreOperator #\*=# +syntax match solcoreOperator #/=# syntax match solcoreOperator #\^=# syntax match solcoreOperator #&=# syntax match solcoreOperator #|=# syntax match solcoreOperator #%=# +syntax match solcoreOperator #\~=# syntax match solcoreOperator #-># syntax match solcoreOperator #=># syntax match solcoreOperator #==# @@ -60,6 +63,7 @@ syntax match solcoreOperator #%# syntax match solcoreOperator #|# syntax match solcoreOperator #&# syntax match solcoreOperator #\^# +syntax match solcoreOperator #\~# syntax match solcoreOperator #@# syntax match solcoreOperator #?# syntax match solcoreOperator #=# diff --git a/editors/vscode-solcore/README.md b/editors/vscode-solcore/README.md index f67170c4..a7c2c781 100644 --- a/editors/vscode-solcore/README.md +++ b/editors/vscode-solcore/README.md @@ -1,6 +1,6 @@ # Solcore editor grammar -This directory contains a VS Code extension for Solcore `.solc` files. It ships +This directory contains a VS Code extension for Solcore `.sol` files. It ships the reusable TextMate grammar used by the playground and starts the native `solcore-lsp` stdio server when a Solcore file opens. @@ -10,7 +10,7 @@ The package is shaped like a small VS Code extension: - `language-configuration.json` provides comments, brackets, auto-close pairs, indentation, folding markers, and the Solcore word pattern. - `extension.js` starts `solcore-lsp` through `vscode-languageclient`. -- `package.json` wires the `.solc` extension to the grammar, configuration, and +- `package.json` wires the `.sol` extension to the grammar, configuration, and language client. ## Language server diff --git a/editors/vscode-solcore/extension.js b/editors/vscode-solcore/extension.js index ada2707d..0f379739 100644 --- a/editors/vscode-solcore/extension.js +++ b/editors/vscode-solcore/extension.js @@ -104,7 +104,7 @@ function scheduleClientReplacement(outputChannel, fileWatcher) { function activate(context) { const outputChannel = vscode.window.createOutputChannel("Solcore Language Server"); - const fileWatcher = vscode.workspace.createFileSystemWatcher("**/*.solc"); + const fileWatcher = vscode.workspace.createFileSystemWatcher("**/*.sol"); context.subscriptions.push(outputChannel, fileWatcher); void scheduleClientReplacement(outputChannel, fileWatcher); diff --git a/editors/vscode-solcore/package.json b/editors/vscode-solcore/package.json index c62798ae..3f567ad2 100644 --- a/editors/vscode-solcore/package.json +++ b/editors/vscode-solcore/package.json @@ -1,7 +1,7 @@ { "name": "solcore-language", "displayName": "Solcore Language", - "description": "Syntax highlighting and editor configuration for Solcore .solc files.", + "description": "Syntax highlighting and editor configuration for Solcore .sol files.", "version": "0.0.0", "publisher": "solcore", "license": "Apache-2.0", @@ -19,8 +19,8 @@ "languages": [ { "id": "solcore", - "aliases": ["Solcore", "solc"], - "extensions": [".solc"], + "aliases": ["Solcore", "Core Solidity"], + "extensions": [".sol"], "configuration": "./language-configuration.json" } ], diff --git a/editors/vscode-solcore/syntaxes/solcore.tmLanguage.json b/editors/vscode-solcore/syntaxes/solcore.tmLanguage.json index 8ff6730c..28ea171e 100644 --- a/editors/vscode-solcore/syntaxes/solcore.tmLanguage.json +++ b/editors/vscode-solcore/syntaxes/solcore.tmLanguage.json @@ -2,7 +2,7 @@ "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", "name": "Solcore", "scopeName": "source.solcore", - "fileTypes": ["solc"], + "fileTypes": ["sol"], "patterns": [ { "include": "#comments" }, { "include": "#strings" }, @@ -83,7 +83,7 @@ }, { "name": "meta.declaration.type.solcore", - "match": "(? word { return x; } diff --git a/fuzz/corpus/frontend/basic.sol b/fuzz/corpus/frontend/basic.sol new file mode 100644 index 00000000..337b66d0 --- /dev/null +++ b/fuzz/corpus/frontend/basic.sol @@ -0,0 +1 @@ +function main() returns (word) { return 0; } diff --git a/fuzz/corpus/frontend/basic.solc b/fuzz/corpus/frontend/basic.solc deleted file mode 100644 index d92b95a4..00000000 --- a/fuzz/corpus/frontend/basic.solc +++ /dev/null @@ -1 +0,0 @@ -function main() -> word { return 0; } diff --git a/fuzz/corpus/parser/basic.sol b/fuzz/corpus/parser/basic.sol new file mode 100644 index 00000000..12e4cfae --- /dev/null +++ b/fuzz/corpus/parser/basic.sol @@ -0,0 +1 @@ +function id(x: word) returns (word) { return x; } diff --git a/fuzz/corpus/parser/basic.solc b/fuzz/corpus/parser/basic.solc deleted file mode 100644 index 50fb803b..00000000 --- a/fuzz/corpus/parser/basic.solc +++ /dev/null @@ -1 +0,0 @@ -function id(x: word) -> word { return x; } diff --git a/fuzz/src/lib.rs b/fuzz/src/lib.rs index 6a221092..2227ba47 100644 --- a/fuzz/src/lib.rs +++ b/fuzz/src/lib.rs @@ -68,7 +68,7 @@ fn backend(source: &str) { let entry_file = workspace .db() - .source_file(Path::new(vfs::MAIN_ROOT).join("main.solc")) + .source_file(Path::new(vfs::MAIN_ROOT).join("main.sol")) .expect("fuzz entry file was inserted into the VFS"); let _ = compiler::build_checked_hull( workspace.db(), @@ -79,8 +79,8 @@ fn backend(source: &str) { fn workspace_with_entry(source: &str) -> Workspace { let mut workspace = Workspace::new(); - workspace.set_file("main.solc", source.to_owned()); - workspace.set_entry("main.solc"); + workspace.set_file("main.sol", source.to_owned()); + workspace.set_entry("main.sol"); workspace } @@ -107,7 +107,7 @@ impl hir::Db for ParserDb { impl parser::Db for ParserDb {} fn source_file(db: &ParserDb, source: &str) -> SourceFile { - let url = url::Url::parse("memory:///fuzz/main.solc").expect("constant URL is valid"); + let url = url::Url::parse("memory:///fuzz/main.sol").expect("constant URL is valid"); SourceFile::new(db, url, Some(source.to_owned())) } @@ -115,8 +115,8 @@ fn source_file(db: &ParserDb, source: &str) -> SourceFile { mod tests { use super::*; - const ACCEPTED: &[u8] = b"function id(x: word) -> word { return x; }\n"; - const REJECTED: &[u8] = b"function main() -> word { return true; }\n"; + const ACCEPTED: &[u8] = b"function id(x: word) returns (word) { return x; }\n"; + const REJECTED: &[u8] = b"function main() returns (word) { return true; }\n"; #[test] fn every_target_accepts_compiler_diagnostics_normally() { diff --git a/playground/README.md b/playground/README.md index 6e0f95af..da94beb6 100644 --- a/playground/README.md +++ b/playground/README.md @@ -109,7 +109,7 @@ compilation stopped before that backend ran, or (for ABI) the workspace contains ## File key contract -The canonical file key is always a workspace-relative path string, for example `main.solc` or `sub/Foo.solc`. +The canonical file key is always a workspace-relative path string, for example `main.sol` or `sub/Foo.sol`. Use that exact key everywhere: diff --git a/playground/src/components/FileExplorer.tsx b/playground/src/components/FileExplorer.tsx index edbfde97..47203b73 100644 --- a/playground/src/components/FileExplorer.tsx +++ b/playground/src/components/FileExplorer.tsx @@ -16,7 +16,7 @@ export function FileExplorer(): JSX.Element { const problemsByFile = useMemo(() => fileProblemSummaries(result), [result]); const handleAdd = (): void => { - const path = window.prompt("New file path", "untitled.solc"); + const path = window.prompt("New file path", "untitled.sol"); if (path) { createFile(path); } diff --git a/playground/src/components/TopBar.tsx b/playground/src/components/TopBar.tsx index e6aba22f..bf7eb93a 100644 --- a/playground/src/components/TopBar.tsx +++ b/playground/src/components/TopBar.tsx @@ -36,7 +36,7 @@ export function TopBar({ sidebarOpen, onToggleSidebar }: TopBarProps): JSX.Eleme const loadExample = useWorkspaceStore((state) => state.loadExample); const [selectedExample, setSelectedExample] = useState(examples[0]?.id ?? "hello"); const [compilerVersion, setCompilerVersion] = useState(null); - const solcFiles = order.filter((path) => path.endsWith(".solc")); + const solFiles = order.filter((path) => path.endsWith(".sol")); const compileElapsedMs = useCompileElapsed(); const compileIsOutdated = lastCompiledVersion !== null && lastCompiledVersion !== workspaceVersion; @@ -114,7 +114,7 @@ export function TopBar({ sidebarOpen, onToggleSidebar }: TopBarProps): JSX.Eleme Entry