Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e191b0c88 | |||
| f70fc56df8 | |||
| 4ae24722f4 | |||
| 55a97852ed | |||
| 1380142082 | |||
| 1754763e23 | |||
| 83abfae32b | |||
| 4ca90feea0 | |||
| 09638daa90 | |||
| d59023a9f4 | |||
| 34b52739fd | |||
| 1c83287d45 | |||
| da92ddbd5d | |||
| c1c169273c | |||
| ad4c341a4a | |||
| f964fa89a1 | |||
| 8d8d9c6b83 | |||
| c98ea25e8b |
+40
-2
@@ -4,10 +4,47 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [CERES 8.0.0]
|
||||||
### Added
|
### Added
|
||||||
|
- Bitwise operations for integers: `band`, `bor`, `bxor`, `bnot`, `<<` and `>>`.
|
||||||
|
- `Int.mulmod` - combined builtin operation for multiplication and modulus.
|
||||||
|
- `Crypto.poseidon` - a ZK/SNARK-friendly hash function (over the BLS12-381 scalar field).
|
||||||
|
- `Address.to_bytes` - convert an address to its binary representation (for hashing, etc.).
|
||||||
|
- Raw data pointers added to AENS. In short we have introduced a new namespace
|
||||||
|
`AENSv2`; they contain types similar to the old `AENS`; `AENS.name` and
|
||||||
|
`AENS.pointee`, where the latter now has a constructor `DataPt(string)`. All
|
||||||
|
AENS actions have been moved to `AENSv2`, and `AENSv2.lookup` and
|
||||||
|
`AENSv2.update` consume and produce the new types. The old `AENS` namespace
|
||||||
|
only contains the old datatypes, that can be used to interface existing
|
||||||
|
contracts. Standard library `AENSCompat` is added to convert between old and
|
||||||
|
new pointers.
|
||||||
### Changed
|
### Changed
|
||||||
### Removed
|
### Removed
|
||||||
|
- `Bitwise.aes` standard library is removed - the builtin operations are superior.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- Options to enable/disable certain optimizations.
|
||||||
|
- The ability to call a different instance of the current contract
|
||||||
|
```
|
||||||
|
contract Main =
|
||||||
|
entrypoint spend(x : int) : int = x
|
||||||
|
entrypoint f(c : Main) : int = c.spend(10)
|
||||||
|
```
|
||||||
|
- Return a mapping from variables to FATE registers in the compilation output.
|
||||||
|
### Changed
|
||||||
|
- Type definitions serialised to ACI as `typedefs` field instead of `type_defs` to increase compatibility.
|
||||||
|
### Removed
|
||||||
|
### Fixed
|
||||||
|
- Typechecker crashes if Chain.create or Chain.clone are used without arguments.
|
||||||
|
|
||||||
|
## [7.0.1]
|
||||||
|
### Added
|
||||||
|
- Add CONTRIBUTING.md file.
|
||||||
|
### Changed
|
||||||
|
- Update Sophia syntax docs to include missing information about existing syntax.
|
||||||
|
### Fixed
|
||||||
|
- [404](https://github.com/aeternity/aesophia/issues/404) Contract polymorphism crashes on non-obvious child contract typing.
|
||||||
|
|
||||||
## [7.0.0]
|
## [7.0.0]
|
||||||
### Added
|
### Added
|
||||||
@@ -352,7 +389,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Simplify calldata creation - instead of passing a compiled contract, simply
|
- Simplify calldata creation - instead of passing a compiled contract, simply
|
||||||
pass a (stubbed) contract string.
|
pass a (stubbed) contract string.
|
||||||
|
|
||||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v7.0.0...HEAD
|
[Unreleased]: https://github.com/aeternity/aesophia/compare/v7.0.1...HEAD
|
||||||
|
[7.0.1]: https://github.com/aeternity/aesophia/compare/v7.0.0...v7.0.1
|
||||||
[7.0.0]: https://github.com/aeternity/aesophia/compare/v6.1.0...v7.0.0
|
[7.0.0]: https://github.com/aeternity/aesophia/compare/v6.1.0...v7.0.0
|
||||||
[6.1.0]: https://github.com/aeternity/aesophia/compare/v6.0.2...v6.1.0
|
[6.1.0]: https://github.com/aeternity/aesophia/compare/v6.0.2...v6.1.0
|
||||||
[6.0.2]: https://github.com/aeternity/aesophia/compare/v6.0.1...v6.0.2
|
[6.0.2]: https://github.com/aeternity/aesophia/compare/v6.0.1...v6.0.2
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Contributing to Sophia
|
||||||
|
|
||||||
|
## Checklist For Creating New Pull Requests
|
||||||
|
|
||||||
|
The following points should be considered before creating a new PR to the Sophia compiler.
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- The [Changelog](CHANGELOG.md) file should be updated for all PRs.
|
||||||
|
- If a PR introduces a new feature that is relevant to the users of the language, the [Sophia Features Documentation](docs/sophia_features.md) should be updated to describe the new feature.
|
||||||
|
- If a PR introduces new syntax (e.g. changes in [aeso_syntax.erl](src/aeso_syntax.erl), [aeso_scan.erl](src/aeso_scan.erl), or [aeso_parser.erl](src/aeso_parser.erl)), the [Sophia Syntax Documentation](docs/sophia_syntax.md) should be updated to include the new syntax.
|
||||||
|
- If a PR introduces a new library, the public interface of the new library should be fully documented in the [Sophia Standard Library Documentation](docs/sophia_stdlib.md).
|
||||||
|
- If a PR introduces a new compiler option, the new option should be documented in the file
|
||||||
|
[aeso_compiler.md](docs/aeso_compiler.md).
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
- If a PR introduces new syntax (e.g. changes in [aeso_syntax.erl](src/aeso_syntax.erl), [aeso_scan.erl](src/aeso_scan.erl), or [aeso_parser.erl](src/aeso_parser.erl)), the contract [all_syntax.aes](test/contracts/all_syntax.aes) should be updated to include the new syntax.
|
||||||
|
- If a PR fixes a bug, the code that replicates the bug should be added as a new passing test contract.
|
||||||
|
- If a PR introduces a new feature, add tests for both successful and failing usage of that feature. In order to run the entire compilation pipeline and to avoid erroring during intermediate steps, failing tests should not be mixed with the successful ones.
|
||||||
|
|
||||||
|
### Source Code
|
||||||
|
|
||||||
|
- If a PR introduces new syntax (e.g. changes in [aeso_syntax.erl](src/aeso_syntax.erl), [aeso_scan.erl](src/aeso_scan.erl), or [aeso_parser.erl](src/aeso_parser.erl)), the following code should be updated to handle the new syntax:
|
||||||
|
- The function `aeso_syntax_utils:fold/4` in the file [aeso_syntax_utils.erl](src/aeso_syntax_utils.erl).
|
||||||
|
- Any related pretty printing function in the file [aeso_pretty.erl](src/aeso_pretty.erl), depending on the type of the newly added syntax.
|
||||||
|
|
||||||
|
## Checklist For Creating a Release
|
||||||
|
|
||||||
|
- Update the version in the file [aesophia.app.src](src/aesophia.app.src).
|
||||||
|
- Update the version in the file [rebar.config](rebar.config).
|
||||||
|
- In the [Changelog](CHANGELOG.md):
|
||||||
|
- Update the `Unreleased` changes to be under the new version.
|
||||||
|
- Update the version at the bottom of the file.
|
||||||
|
- Commit and the changes and create a new PR (check the commit of [v6.1.0](https://github.com/aeternity/aesophia/commit/5ad5270e381f6e810d7b8b5cdc168d283e7a90bb) for reference).
|
||||||
|
- Create a release after merging the new PR to `master` branch.
|
||||||
|
- After releasing `aesophia`, refer to each of the following repositories and create new releases as well, using the new `aesophia` release:
|
||||||
|
- [aesophia_cli](https://github.com/aeternity/aesophia_cli)
|
||||||
|
- [aesophia_http](https://github.com/aeternity/aesophia_http)
|
||||||
|
- [aerepl](https://github.com/aeternity/aerepl)
|
||||||
@@ -14,6 +14,7 @@ The compiler is currently being used three places
|
|||||||
* [Features](docs/sophia_features.md)
|
* [Features](docs/sophia_features.md)
|
||||||
* [Standard library](docs/sophia_stdlib.md)
|
* [Standard library](docs/sophia_stdlib.md)
|
||||||
* [Contract examples](docs/sophia_examples.md)
|
* [Contract examples](docs/sophia_examples.md)
|
||||||
|
* [Contributing](CONTRIBUTING.md)
|
||||||
|
|
||||||
Additionally you can check out the [contracts section](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) of the æternity blockchain specification.
|
Additionally you can check out the [contracts section](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) of the æternity blockchain specification.
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -67,7 +67,7 @@ generates the following JSON structure representing the contract interface:
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"type_defs": [
|
"typedefs": [
|
||||||
{
|
{
|
||||||
"name": "answers",
|
"name": "answers",
|
||||||
"typedef": {
|
"typedef": {
|
||||||
@@ -138,7 +138,7 @@ be included inside another contract.
|
|||||||
state =>
|
state =>
|
||||||
#{record =>
|
#{record =>
|
||||||
[#{name => <<"a">>,type => <<"Answers.answers">>}]},
|
[#{name => <<"a">>,type => <<"Answers.answers">>}]},
|
||||||
type_defs =>
|
typedefs =>
|
||||||
[#{name => <<"answers">>,
|
[#{name => <<"answers">>,
|
||||||
typedef => #{<<"map">> => [<<"string">>,<<"int">>]},
|
typedef => #{<<"map">> => [<<"string">>,<<"int">>]},
|
||||||
vars => []}]}}]}
|
vars => []}]}}]}
|
||||||
|
|||||||
@@ -51,6 +51,36 @@ The **pp_** options all print to standard output the following:
|
|||||||
|
|
||||||
`pp_assembler` - print the generated assembler code
|
`pp_assembler` - print the generated assembler code
|
||||||
|
|
||||||
|
The option `include_child_contract_symbols` includes the symbols of child contracts functions in the generated fate code. It is turned off by default to avoid making contracts bigger on chain.
|
||||||
|
|
||||||
|
The option `debug_info` includes information related to debugging in the compiler output. Currently this option only includes the mapping from variables to registers.
|
||||||
|
|
||||||
|
#### Options to control which compiler optimizations should run:
|
||||||
|
|
||||||
|
By default all optimizations are turned on, to disable an optimization, it should be
|
||||||
|
explicitly set to false and passed as a compiler option.
|
||||||
|
|
||||||
|
List of optimizations:
|
||||||
|
|
||||||
|
- optimize_inliner
|
||||||
|
- optimize_inline_local_functions
|
||||||
|
- optimize_bind_subexpressions
|
||||||
|
- optimize_let_floating
|
||||||
|
- optimize_simplifier
|
||||||
|
- optimize_drop_unused_lets
|
||||||
|
- optimize_push_consume
|
||||||
|
- optimize_one_shot_var
|
||||||
|
- optimize_write_to_dead_var
|
||||||
|
- optimize_inline_switch_target
|
||||||
|
- optimize_swap_push
|
||||||
|
- optimize_swap_pop
|
||||||
|
- optimize_swap_write
|
||||||
|
- optimize_constant_propagation
|
||||||
|
- optimize_prune_impossible_branches
|
||||||
|
- optimize_single_successful_branch
|
||||||
|
- optimize_inline_store
|
||||||
|
- optimize_float_switch_bod
|
||||||
|
|
||||||
#### check_call(ContractString, Options) -> CheckRet
|
#### check_call(ContractString, Options) -> CheckRet
|
||||||
|
|
||||||
Types
|
Types
|
||||||
|
|||||||
@@ -564,6 +564,14 @@ All operations are *safe* with respect to overflow and underflow.
|
|||||||
The division and modulo operations throw an arithmetic error if the
|
The division and modulo operations throw an arithmetic error if the
|
||||||
right-hand operand is zero.
|
right-hand operand is zero.
|
||||||
|
|
||||||
|
Sophia arbitrary-sized integers (FATE) also supports the following bitwise operations:
|
||||||
|
- bitwise and (`x band y`)
|
||||||
|
- bitwise or (`x bor y`)
|
||||||
|
- bitwise xor (`x bxor y`)
|
||||||
|
- bitwise not (`bnot x`)
|
||||||
|
- arithmetic bitshift left (`x << n`)
|
||||||
|
- arithmetic bitshift right (`x >> n`)
|
||||||
|
|
||||||
## Bit fields
|
## Bit fields
|
||||||
|
|
||||||
Sophia integers do not support bit arithmetic. Instead there is a separate
|
Sophia integers do not support bit arithmetic. Instead there is a separate
|
||||||
|
|||||||
+104
-95
@@ -14,6 +14,7 @@ The out-of-the-box namespaces are:
|
|||||||
|
|
||||||
- [Address](#address)
|
- [Address](#address)
|
||||||
- [AENS](#aens)
|
- [AENS](#aens)
|
||||||
|
- [AENSv2](#aensv2)
|
||||||
- [Auth](#auth)
|
- [Auth](#auth)
|
||||||
- [Bits](#bits)
|
- [Bits](#bits)
|
||||||
- [Bytes](#bytes)
|
- [Bytes](#bytes)
|
||||||
@@ -31,6 +32,7 @@ The following ones need to be included as regular files with `.aes` suffix, for
|
|||||||
include "List.aes"
|
include "List.aes"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- [AENSCompat](#aenscompat)
|
||||||
- [Bitwise](#bitwise)
|
- [Bitwise](#bitwise)
|
||||||
- [BLS12_381](#bls12_381)
|
- [BLS12_381](#bls12_381)
|
||||||
- [Func](#func)
|
- [Func](#func)
|
||||||
@@ -90,13 +92,10 @@ Cast address to contract type C (where `C` is a contract)
|
|||||||
|
|
||||||
### AENS
|
### AENS
|
||||||
|
|
||||||
The following functionality is available for interacting with the æternity
|
The old AENS namespace, kept in the compiler to be able to interact with
|
||||||
naming system (AENS).
|
contracts from before Ceres, compiled using aesophia compiler version 7.x and
|
||||||
If `owner` is equal to `Contract.address` the signature `signature` is
|
earlier. Used in [AENSCompat](#aenscompat) when converting between old and new
|
||||||
ignored, and can be left out since it is a named argument. Otherwise we need
|
pointers.
|
||||||
a signature to prove that we are allowed to do AENS operations on behalf of
|
|
||||||
`owner`. The [signature is tied to a network id](https://github.com/aeternity/protocol/blob/iris/consensus/consensus.md#transaction-signature),
|
|
||||||
i.e. the signature material should be prefixed by the network id.
|
|
||||||
|
|
||||||
#### Types
|
#### Types
|
||||||
|
|
||||||
@@ -113,12 +112,41 @@ datatype pointee = AccountPt(address) | OraclePt(address)
|
|||||||
| ContractPt(address) | ChannelPt(address)
|
| ContractPt(address) | ChannelPt(address)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### AENSv2
|
||||||
|
|
||||||
|
Note: introduced in v8.0
|
||||||
|
|
||||||
|
The following functionality is available for interacting with the æternity
|
||||||
|
naming system (AENS). If `owner` is equal to `Contract.address` the signature
|
||||||
|
`signature` is ignored, and can be left out since it is a named argument.
|
||||||
|
Otherwise we need a signature to prove that we are allowed to do AENS
|
||||||
|
operations on behalf of `owner`. The [signature is tied to a network
|
||||||
|
id](https://github.com/aeternity/protocol/blob/iris/consensus/consensus.md#transaction-signature),
|
||||||
|
i.e. the signature material should be prefixed by the network id.
|
||||||
|
|
||||||
|
#### Types
|
||||||
|
|
||||||
|
##### name
|
||||||
|
```
|
||||||
|
datatype name = Name(address, Chain.ttl, map(string, AENSv2.pointee))
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
##### pointee
|
||||||
|
|
||||||
|
```
|
||||||
|
datatype pointee = AccountPt(address) | OraclePt(address)
|
||||||
|
| ContractPt(address) | ChannelPt(address) | DataPt(string)
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: on-chain there is a maximum length enforced for `DataPt`, it is 1024 bytes.
|
||||||
|
Sophia itself does _not_ enforce this.
|
||||||
|
|
||||||
#### Functions
|
#### Functions
|
||||||
|
|
||||||
##### resolve
|
##### resolve
|
||||||
```
|
```
|
||||||
AENS.resolve(name : string, key : string) : option('a)
|
AENSv2.resolve(name : string, key : string) : option('a)
|
||||||
```
|
```
|
||||||
|
|
||||||
Name resolution. Here `name` should be a registered name and `key` one of the attributes
|
Name resolution. Here `name` should be a registered name and `key` one of the attributes
|
||||||
@@ -129,41 +157,53 @@ type checked against this type at run time.
|
|||||||
|
|
||||||
##### lookup
|
##### lookup
|
||||||
```
|
```
|
||||||
AENS.lookup(name : string) : option(AENS.name)
|
AENSv2.lookup(name : string) : option(AENSv2.name)
|
||||||
```
|
```
|
||||||
|
|
||||||
If `name` is an active name `AENS.lookup` returns a name object.
|
If `name` is an active name `AENSv2.lookup` returns a name object.
|
||||||
The three arguments to `Name` are `owner`, `expiry` and a map of the
|
The three arguments to `Name` are `owner`, `expiry` and a map of the
|
||||||
`pointees` for the name. Note: the expiry of the name is always a fixed TTL.
|
`pointees` for the name. Note: the expiry of the name is always a fixed TTL.
|
||||||
For example:
|
For example:
|
||||||
```
|
```
|
||||||
let Some(Name(owner, FixedTTL(expiry), ptrs)) = AENS.lookup("example.chain")
|
let Some(AENSv2.Name(owner, FixedTTL(expiry), ptrs)) = AENSv2.lookup("example.chain")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note: Changed to produce `AENSv2.name` in v8.0 (Ceres protocol upgrade).
|
||||||
|
|
||||||
##### preclaim
|
##### preclaim
|
||||||
```
|
```
|
||||||
AENS.preclaim(owner : address, commitment_hash : hash, <signature : signature>) : unit
|
AENSv2.preclaim(owner : address, commitment_hash : hash, <signature : signature>) : unit
|
||||||
```
|
```
|
||||||
|
|
||||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
The [signature](./sophia_features.md#delegation-signature) should be over
|
||||||
`network id` + `owner address` + `Contract.address` (concatenated as byte arrays).
|
`network id` + `owner address` + `Contract.address` (concatenated as byte arrays).
|
||||||
|
|
||||||
|
From Ceres (i.e. FATE VM version 3) the
|
||||||
|
[signature](./sophia_features.md#delegation-signature) can also be generic
|
||||||
|
(allowing _all_, existing and future, names to be delegated with one
|
||||||
|
signature), i.e. over `network id` + `owner address` + `string "AENS"` +
|
||||||
|
`Contract.address`.
|
||||||
|
|
||||||
|
|
||||||
##### claim
|
##### claim
|
||||||
```
|
```
|
||||||
AENS.claim(owner : address, name : string, salt : int, name_fee : int, <signature : signature>) : unit
|
AENSv2.claim(owner : address, name : string, salt : int, name_fee : int, <signature : signature>) : unit
|
||||||
```
|
```
|
||||||
|
|
||||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
The [signature](./sophia_features.md#delegation-signature) should be over
|
||||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
`network id` + `owner address` + `name_hash` + `Contract.address` (concatenated
|
||||||
(concatenated as byte arrays)
|
as byte arrays) using the private key of the `owner` account for signing.
|
||||||
using the private key of the `owner` account for signing.
|
|
||||||
|
From Ceres (i.e. FATE VM version 3) the
|
||||||
|
[signature](./sophia_features.md#delegation-signature) can also be generic
|
||||||
|
(allowing _all_, existing and future, names to be delegated with one
|
||||||
|
signature), i.e. over `network id` + `owner address` + `string "AENS"` +
|
||||||
|
`Contract.address`.
|
||||||
|
|
||||||
|
|
||||||
##### transfer
|
##### transfer
|
||||||
```
|
```
|
||||||
AENS.transfer(owner : address, new_owner : address, name : string, <signature : signature>) : unit
|
AENSv2.transfer(owner : address, new_owner : address, name : string, <signature : signature>) : unit
|
||||||
```
|
```
|
||||||
|
|
||||||
Transfers name to the new owner.
|
Transfers name to the new owner.
|
||||||
@@ -173,10 +213,16 @@ The [signature](./sophia_features.md#delegation-signature) should be over
|
|||||||
(concatenated as byte arrays)
|
(concatenated as byte arrays)
|
||||||
using the private key of the `owner` account for signing.
|
using the private key of the `owner` account for signing.
|
||||||
|
|
||||||
|
From Ceres (i.e. FATE VM version 3) the
|
||||||
|
[signature](./sophia_features.md#delegation-signature) can also be generic
|
||||||
|
(allowing _all_, existing and future, names to be delegated with one
|
||||||
|
signature), i.e. over `network id` + `owner address` + `string "AENS"` +
|
||||||
|
`Contract.address`.
|
||||||
|
|
||||||
|
|
||||||
##### revoke
|
##### revoke
|
||||||
```
|
```
|
||||||
AENS.revoke(owner : address, name : string, <signature : signature>) : unit
|
AENSv2.revoke(owner : address, name : string, <signature : signature>) : unit
|
||||||
```
|
```
|
||||||
|
|
||||||
Revokes the name to extend the ownership time.
|
Revokes the name to extend the ownership time.
|
||||||
@@ -186,17 +232,24 @@ The [signature](./sophia_features.md#delegation-signature) should be over
|
|||||||
(concatenated as byte arrays)
|
(concatenated as byte arrays)
|
||||||
using the private key of the `owner` account for signing.
|
using the private key of the `owner` account for signing.
|
||||||
|
|
||||||
|
From Ceres (i.e. FATE VM version 3) the
|
||||||
|
[signature](./sophia_features.md#delegation-signature) can also be generic
|
||||||
|
(allowing _all_, existing and future, names to be delegated with one
|
||||||
|
signature), i.e. over `network id` + `owner address` + `string "AENS"` +
|
||||||
|
`Contract.address`.
|
||||||
|
|
||||||
|
|
||||||
##### update
|
##### update
|
||||||
```
|
```
|
||||||
AENS.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
|
AENSv2.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
|
||||||
new_ptrs : map(string, AENS.pointee), <signature : signature>) : unit
|
new_ptrs : map(string, AENSv2.pointee), <signature : signature>) : unit
|
||||||
```
|
```
|
||||||
|
|
||||||
Updates the name. If the optional parameters are set to `None` that parameter
|
Updates the name. If the optional parameters are set to `None` that parameter
|
||||||
will not be updated, for example if `None` is passed as `expiry` the expiry
|
will not be updated, for example if `None` is passed as `expiry` the expiry
|
||||||
block of the name is not changed.
|
block of the name is not changed.
|
||||||
|
|
||||||
|
Note: Changed to consume `AENSv2.pointee` in v8.0 (Ceres protocol upgrade).
|
||||||
|
|
||||||
### Auth
|
### Auth
|
||||||
|
|
||||||
@@ -236,7 +289,10 @@ namespace Chain =
|
|||||||
Auth.tx_hash : option(hash)
|
Auth.tx_hash : option(hash)
|
||||||
```
|
```
|
||||||
|
|
||||||
Gets the transaction hash during authentication.
|
Gets the transaction hash during authentication. Note: `Auth.tx_hash`
|
||||||
|
computation differs between protocol versions (changed in Ceres!), see
|
||||||
|
[aeserialisation](https://github.com/aeternity/protocol/blob/master/serializations.md)
|
||||||
|
specification for details.
|
||||||
|
|
||||||
|
|
||||||
### Bits
|
### Bits
|
||||||
@@ -381,6 +437,12 @@ Call.gas_price : int
|
|||||||
|
|
||||||
The gas price of the current call.
|
The gas price of the current call.
|
||||||
|
|
||||||
|
#### mulmod
|
||||||
|
```
|
||||||
|
Int.mulmod : (a : int, b : int, q : int) : int
|
||||||
|
```
|
||||||
|
|
||||||
|
Combined multiplication and modulus, returns `(a * b) mod q`.
|
||||||
|
|
||||||
#### fee
|
#### fee
|
||||||
```
|
```
|
||||||
@@ -469,6 +531,13 @@ Chain.block_height : int"
|
|||||||
|
|
||||||
The height of the current block (i.e. the block in which the current call will be included).
|
The height of the current block (i.e. the block in which the current call will be included).
|
||||||
|
|
||||||
|
#### to_bytes
|
||||||
|
```
|
||||||
|
Address.to_bytes(a : address) : bytes(32)
|
||||||
|
```
|
||||||
|
|
||||||
|
The binary representation of the address.
|
||||||
|
|
||||||
|
|
||||||
##### coinbase
|
##### coinbase
|
||||||
```
|
```
|
||||||
@@ -538,6 +607,13 @@ charging the calling contract. Note that this won't be visible in `Call.value`
|
|||||||
in the `init` call of the new contract. It will be included in
|
in the `init` call of the new contract. It will be included in
|
||||||
`Contract.balance`, however.
|
`Contract.balance`, however.
|
||||||
|
|
||||||
|
#### poseidon
|
||||||
|
```
|
||||||
|
Crypto.poseidon(x1 : int, x2 : int) : int
|
||||||
|
```
|
||||||
|
|
||||||
|
Hash two integers (in the scalar field of BLS12-381) to another integer (in the scalar
|
||||||
|
field of BLS12-281). This is a ZK/SNARK-friendly hash function.
|
||||||
|
|
||||||
The type `'c` must be instantiated with a contract.
|
The type `'c` must be instantiated with a contract.
|
||||||
|
|
||||||
@@ -914,88 +990,21 @@ It returns `true` iff the oracle query exist and has the expected type.
|
|||||||
These need to be explicitly included (with `.aes` suffix)
|
These need to be explicitly included (with `.aes` suffix)
|
||||||
|
|
||||||
|
|
||||||
### Bitwise
|
### AENSCompat
|
||||||
|
|
||||||
Bitwise operations on arbitrary precision integers.
|
#### pointee\_to\_V2
|
||||||
|
|
||||||
#### bsr
|
|
||||||
```
|
```
|
||||||
Bitwise.bsr(n : int, x : int) : int
|
AENSCompat.pointee_to_V2(p : AENS.pointee) : AENSv2.pointee
|
||||||
```
|
```
|
||||||
|
|
||||||
Logical bit shift `x` right `n` positions.
|
Translate old pointee format to new, this is always possible.
|
||||||
|
|
||||||
|
#### pointee\_from\_V2
|
||||||
#### bsl
|
|
||||||
```
|
```
|
||||||
Bitwise.bsl(n : int, x : int) : int
|
AENSCompat.pointee_from_V2(p2 : AENSv2.pointee) : option(AENS.pointee)
|
||||||
```
|
```
|
||||||
|
|
||||||
Logical bit shift `x` left `n` positions.
|
Translate new pointee format to old, `DataPt` can't be translated, so `None` is returned in this case.
|
||||||
|
|
||||||
|
|
||||||
#### bsli
|
|
||||||
```
|
|
||||||
Bitwise.bsli(n : int, x : int, lim : int) : int
|
|
||||||
```
|
|
||||||
|
|
||||||
Logical bit shift `x` left `n` positions, limit to `lim` bits.
|
|
||||||
|
|
||||||
|
|
||||||
#### band
|
|
||||||
```
|
|
||||||
Bitwise.band(x : int, y : int) : int
|
|
||||||
```
|
|
||||||
|
|
||||||
Bitwise `and` of `x` and `y`.
|
|
||||||
|
|
||||||
|
|
||||||
#### bor
|
|
||||||
```
|
|
||||||
Bitwise.bor(x : int, y : int) : int
|
|
||||||
```
|
|
||||||
|
|
||||||
Bitwise `or` of `x` and `y`.
|
|
||||||
|
|
||||||
|
|
||||||
#### bxor
|
|
||||||
```
|
|
||||||
Bitwise.bxor(x : int, y : int) : int
|
|
||||||
```
|
|
||||||
|
|
||||||
Bitwise `xor` of `x` and `y`.
|
|
||||||
|
|
||||||
|
|
||||||
#### bnot
|
|
||||||
```
|
|
||||||
Bitwise.bnot(x : int) : int
|
|
||||||
```
|
|
||||||
|
|
||||||
Bitwise `not` of `x`. Defined and implemented as `bnot(x) = bxor(x, -1)`.
|
|
||||||
|
|
||||||
|
|
||||||
#### uband
|
|
||||||
```
|
|
||||||
Bitwise.uband(x : int, y : int) : int
|
|
||||||
```
|
|
||||||
|
|
||||||
Bitwise `and` of _non-negative_ numbers `x` and `y`.
|
|
||||||
|
|
||||||
|
|
||||||
#### ubor
|
|
||||||
```
|
|
||||||
Bitwise.ubor(x : int, y : int) : int
|
|
||||||
```
|
|
||||||
|
|
||||||
Bitwise `or` of _non-negative_ `x` and `y`.
|
|
||||||
|
|
||||||
|
|
||||||
#### ubxor
|
|
||||||
```
|
|
||||||
Bitwise.ubxor(x : int, y : int) : int
|
|
||||||
```
|
|
||||||
|
|
||||||
Bitwise `xor` of _non-negative_ `x` and `y`.
|
|
||||||
|
|
||||||
|
|
||||||
### BLS12\_381
|
### BLS12\_381
|
||||||
|
|||||||
+38
-14
@@ -10,8 +10,9 @@ and `*/` and can be nested.
|
|||||||
### Keywords
|
### Keywords
|
||||||
|
|
||||||
```
|
```
|
||||||
contract elif else entrypoint false function if import include let mod namespace
|
contract include let switch type record datatype if elif else function
|
||||||
private payable stateful switch true type record datatype main interface
|
stateful payable true false mod public entrypoint private indexed namespace
|
||||||
|
interface main using as for hiding
|
||||||
```
|
```
|
||||||
|
|
||||||
### Tokens
|
### Tokens
|
||||||
@@ -91,18 +92,30 @@ A Sophia file consists of a sequence of *declarations* in a layout block.
|
|||||||
```c
|
```c
|
||||||
File ::= Block(TopDecl)
|
File ::= Block(TopDecl)
|
||||||
|
|
||||||
TopDecl ::= ['payable'] 'contract' Con '=' Block(Decl)
|
TopDecl ::= ['payable'] ['main'] 'contract' Con [Implement] '=' Block(Decl)
|
||||||
| 'namespace' Con '=' Block(Decl)
|
| 'contract' 'interface' Con [Implement] '=' Block(Decl)
|
||||||
| '@compiler' PragmaOp Version
|
| 'namespace' Con '=' Block(Decl)
|
||||||
| 'include' String
|
| '@compiler' PragmaOp Version
|
||||||
|
| 'include' String
|
||||||
|
| Using
|
||||||
|
|
||||||
|
Implement ::= ':' Sep1(Con, ',')
|
||||||
|
|
||||||
Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
|
Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
|
||||||
| 'record' Id ['(' TVar* ')'] '=' RecordType
|
| 'record' Id ['(' TVar* ')'] '=' RecordType
|
||||||
| 'datatype' Id ['(' TVar* ')'] '=' DataType
|
| 'datatype' Id ['(' TVar* ')'] '=' DataType
|
||||||
| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
|
| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
|
||||||
|
| Using
|
||||||
|
|
||||||
FunDecl ::= Id ':' Type // Type signature
|
FunDecl ::= Id ':' Type // Type signature
|
||||||
| Id Args [':' Type] '=' Block(Stmt) // Definition
|
| Id Args [':' Type] '=' Block(Stmt) // Definition
|
||||||
|
| Id Args [':' Type] Block(GuardedDef) // Guarded definitions
|
||||||
|
|
||||||
|
GuardedDef ::= '|' Sep1(Expr, ',') '=' Block(Stmt)
|
||||||
|
|
||||||
|
Using ::= 'using' Con ['as' Con] [UsingParts]
|
||||||
|
UsingParts ::= 'for' '[' Sep1(Id, ',') ']'
|
||||||
|
| 'hiding' '[' Sep1(Id, ',') ']'
|
||||||
|
|
||||||
PragmaOp ::= '<' | '=<' | '==' | '>=' | '>'
|
PragmaOp ::= '<' | '=<' | '==' | '>=' | '>'
|
||||||
Version ::= Sep1(Int, '.')
|
Version ::= Sep1(Int, '.')
|
||||||
@@ -172,12 +185,17 @@ Stmt ::= 'switch' '(' Expr ')' Block(Case)
|
|||||||
| 'elif' '(' Expr ')' Block(Stmt)
|
| 'elif' '(' Expr ')' Block(Stmt)
|
||||||
| 'else' Block(Stmt)
|
| 'else' Block(Stmt)
|
||||||
| 'let' LetDef
|
| 'let' LetDef
|
||||||
|
| Using
|
||||||
| Expr
|
| Expr
|
||||||
|
|
||||||
LetDef ::= Id Args [':' Type] '=' Block(Stmt) // Function definition
|
LetDef ::= Id Args [':' Type] '=' Block(Stmt) // Function definition
|
||||||
| Pattern '=' Block(Stmt) // Value definition
|
| Pattern '=' Block(Stmt) // Value definition
|
||||||
|
|
||||||
Case ::= Pattern '=>' Block(Stmt)
|
Case ::= Pattern '=>' Block(Stmt)
|
||||||
|
| Pattern Block(GuardedCase)
|
||||||
|
|
||||||
|
GuardedCase ::= '|' Sep1(Expr, ',') '=>' Block(Stmt)
|
||||||
|
|
||||||
Pattern ::= Expr
|
Pattern ::= Expr
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -215,6 +233,7 @@ Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x +
|
|||||||
| '[' Expr '..' Expr ']' // List range [1..n]
|
| '[' Expr '..' Expr ']' // List range [1..n]
|
||||||
| '{' Sep(FieldUpdate, ',') '}' // Record or map value {x = 0, y = 1}, {[key] = val}
|
| '{' Sep(FieldUpdate, ',') '}' // Record or map value {x = 0, y = 1}, {[key] = val}
|
||||||
| '(' Expr ')' // Parens (1 + 2) * 3
|
| '(' Expr ')' // Parens (1 + 2) * 3
|
||||||
|
| '(' Expr '=' Expr ')' // Assign pattern (y = x::_)
|
||||||
| Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token
|
| Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token
|
||||||
| Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%'
|
| Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%'
|
||||||
| AccountAddress | ContractAddress // Chain identifiers
|
| AccountAddress | ContractAddress // Chain identifiers
|
||||||
@@ -235,8 +254,8 @@ Path ::= Id // Record field
|
|||||||
|
|
||||||
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||||
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
|
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
|
||||||
| '|>'
|
| 'band' | 'bor' | 'bxor' | '<<' | '>>' | '|>'
|
||||||
UnOp ::= '-' | '!'
|
UnOp ::= '-' | '!' | 'bnot'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Operators types
|
## Operators types
|
||||||
@@ -244,24 +263,29 @@ UnOp ::= '-' | '!'
|
|||||||
| Operators | Type
|
| Operators | Type
|
||||||
| --- | ---
|
| --- | ---
|
||||||
| `-` `+` `*` `/` `mod` `^` | arithmetic operators
|
| `-` `+` `*` `/` `mod` `^` | arithmetic operators
|
||||||
| `!` `&&` `\|\|` | logical operators
|
| `!` `&&` `||` | logical operators
|
||||||
|
| `band` `bor` `bxor` `bnot` `<<` `>>` | bitwise operators
|
||||||
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
|
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
|
||||||
| `::` `++` | list operators
|
| `::` `++` | list operators
|
||||||
| `\|>` | functional operators
|
| `|>` | functional operators
|
||||||
|
|
||||||
## Operator precendences
|
## Operator precedence
|
||||||
|
|
||||||
In order of highest to lowest precedence.
|
In order of highest to lowest precedence.
|
||||||
|
|
||||||
| Operators | Associativity
|
| Operators | Associativity
|
||||||
| --- | ---
|
| --- | ---
|
||||||
| `!` | right
|
| `!` `bnot`| right
|
||||||
| `^` | left
|
| `^` | left
|
||||||
| `*` `/` `mod` | left
|
| `*` `/` `mod` | left
|
||||||
| `-` (unary) | right
|
| `-` (unary) | right
|
||||||
| `+` `-` | left
|
| `+` `-` | left
|
||||||
|
| `<<` `>>` | left
|
||||||
| `::` `++` | right
|
| `::` `++` | right
|
||||||
| `<` `>` `=<` `>=` `==` `!=` | none
|
| `<` `>` `=<` `>=` `==` `!=` | none
|
||||||
|
| `band` | left
|
||||||
|
| `bxor` | left
|
||||||
|
| `bor` | left
|
||||||
| `&&` | right
|
| `&&` | right
|
||||||
| `\|\|` | right
|
| `||` | right
|
||||||
| `\|>` | left
|
| `|>` | left
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace AENSCompat =
|
||||||
|
// Translate old format to new format - always possible
|
||||||
|
function pointee_to_V2(p : AENS.pointee) : AENSv2.pointee =
|
||||||
|
switch(p)
|
||||||
|
AENS.AccountPt(a) => AENSv2.AccountPt(a)
|
||||||
|
AENS.OraclePt(a) => AENSv2.OraclePt(a)
|
||||||
|
AENS.ContractPt(a) => AENSv2.ContractPt(a)
|
||||||
|
AENS.ChannelPt(a) => AENSv2.ChannelPt(a)
|
||||||
|
|
||||||
|
// Translate new format to old format - option type!
|
||||||
|
function pointee_from_V2(p2 : AENSv2.pointee) : option(AENS.pointee) =
|
||||||
|
switch(p2)
|
||||||
|
AENSv2.AccountPt(a) => Some(AENS.AccountPt(a))
|
||||||
|
AENSv2.OraclePt(a) => Some(AENS.OraclePt(a))
|
||||||
|
AENSv2.ContractPt(a) => Some(AENS.ContractPt(a))
|
||||||
|
AENSv2.ChannelPt(a) => Some(AENS.ChannelPt(a))
|
||||||
|
AENSv2.DataPt(_) => None
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
@compiler >= 4.3
|
|
||||||
|
|
||||||
namespace Bitwise =
|
|
||||||
|
|
||||||
// bit shift 'x' right 'n' postions
|
|
||||||
function bsr(n : int, x : int) : int =
|
|
||||||
let step = 2^n
|
|
||||||
let res = x / step
|
|
||||||
if (x >= 0 || x mod step == 0)
|
|
||||||
res
|
|
||||||
else
|
|
||||||
res - 1
|
|
||||||
|
|
||||||
// bit shift 'x' left 'n' positions
|
|
||||||
function bsl(n : int, x : int) : int =
|
|
||||||
x * 2^n
|
|
||||||
|
|
||||||
// bit shift 'x' left 'n' positions, limit at 'lim' bits
|
|
||||||
function bsli(n : int, x : int, lim : int) : int =
|
|
||||||
(x * 2^n) mod (2^lim)
|
|
||||||
|
|
||||||
// bitwise 'and' for arbitrary precision integers
|
|
||||||
function band(a : int, b : int) : int =
|
|
||||||
if (a >= 0 && b >= 0)
|
|
||||||
uband_(a, b)
|
|
||||||
elif (b >= 0)
|
|
||||||
ubnand_(b, -1 - a)
|
|
||||||
elif (a >= 0)
|
|
||||||
ubnand_(a, -1 - b)
|
|
||||||
else
|
|
||||||
-1 - ubor_(-1 - a, -1 - b)
|
|
||||||
|
|
||||||
// bitwise 'or' for arbitrary precision integers
|
|
||||||
function
|
|
||||||
bor : (int, int) => int
|
|
||||||
bor(0, b) = b
|
|
||||||
bor(a, 0) = a
|
|
||||||
bor(a : int, b : int) : int =
|
|
||||||
if (a >= 0 && b >= 0)
|
|
||||||
ubor_(a, b)
|
|
||||||
elif (b >= 0)
|
|
||||||
-1 - ubnand_(-1 - a, b)
|
|
||||||
elif (a >= 0)
|
|
||||||
-1 - ubnand_(-1 - b, a)
|
|
||||||
else
|
|
||||||
-1 - uband_(-1 - a, -1 - b)
|
|
||||||
|
|
||||||
// bitwise 'xor' for arbitrary precision integers
|
|
||||||
function
|
|
||||||
bxor : (int, int) => int
|
|
||||||
bxor(0, b) = b
|
|
||||||
bxor(a, 0) = a
|
|
||||||
bxor(a, b) =
|
|
||||||
if (a >= 0 && b >= 0)
|
|
||||||
ubxor_(a, b)
|
|
||||||
elif (b >= 0)
|
|
||||||
-1 - ubxor_(-1 - a, b)
|
|
||||||
elif (a >= 0)
|
|
||||||
-1 - ubxor_(a, -1 - b)
|
|
||||||
else
|
|
||||||
ubxor_(-1 - a, -1 - b)
|
|
||||||
|
|
||||||
// bitwise 'not' for arbitrary precision integers
|
|
||||||
function bnot(a : int) = bxor(a, -1)
|
|
||||||
|
|
||||||
// Bitwise 'and' for non-negative integers
|
|
||||||
function uband(a : int, b : int) : int =
|
|
||||||
require(a >= 0 && b >= 0, "uband is only defined for non-negative integers")
|
|
||||||
switch((a, b))
|
|
||||||
(0, _) => 0
|
|
||||||
(_, 0) => 0
|
|
||||||
_ => uband__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function uband_(a, b) = uband__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function
|
|
||||||
uband__(0, b, val, acc) = acc
|
|
||||||
uband__(a, 0, val, acc) = acc
|
|
||||||
uband__(a, b, val, acc) =
|
|
||||||
switch (a mod 2 + b mod 2)
|
|
||||||
2 => uband__(a / 2, b / 2, val * 2, acc + val)
|
|
||||||
_ => uband__(a / 2, b / 2, val * 2, acc)
|
|
||||||
|
|
||||||
// Bitwise 'or' for non-negative integers
|
|
||||||
function ubor(a, b) =
|
|
||||||
require(a >= 0 && b >= 0, "ubor is only defined for non-negative integers")
|
|
||||||
switch((a, b))
|
|
||||||
(0, _) => b
|
|
||||||
(_, 0) => a
|
|
||||||
_ => ubor__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function ubor_(a, b) = ubor__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function
|
|
||||||
ubor__(0, 0, val, acc) = acc
|
|
||||||
ubor__(a, b, val, acc) =
|
|
||||||
switch (a mod 2 + b mod 2)
|
|
||||||
0 => ubor__(a / 2, b / 2, val * 2, acc)
|
|
||||||
_ => ubor__(a / 2, b / 2, val * 2, acc + val)
|
|
||||||
|
|
||||||
//Bitwise 'xor' for non-negative integers
|
|
||||||
function
|
|
||||||
ubxor : (int, int) => int
|
|
||||||
ubxor(0, b) = b
|
|
||||||
ubxor(a, 0) = a
|
|
||||||
ubxor(a, b) =
|
|
||||||
require(a >= 0 && b >= 0, "ubxor is only defined for non-negative integers")
|
|
||||||
ubxor__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function ubxor_(a, b) = ubxor__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function
|
|
||||||
ubxor__(0, 0, val, acc) = acc
|
|
||||||
ubxor__(a, b, val, acc) =
|
|
||||||
switch(a mod 2 + b mod 2)
|
|
||||||
1 => ubxor__(a / 2, b / 2, val * 2, acc + val)
|
|
||||||
_ => ubxor__(a / 2, b / 2, val * 2, acc)
|
|
||||||
|
|
||||||
private function ubnand_(a, b) = ubnand__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function
|
|
||||||
ubnand__(0, b, val, acc) = acc
|
|
||||||
ubnand__(a, b, val, acc) =
|
|
||||||
switch((a mod 2, b mod 2))
|
|
||||||
(1, 0) => ubnand__(a / 2, b / 2, val * 2, acc + val)
|
|
||||||
_ => ubnand__(a / 2, b / 2, val * 2, acc)
|
|
||||||
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{erl_opts, [debug_info]}.
|
{erl_opts, [debug_info]}.
|
||||||
|
|
||||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.1.1"}}}
|
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.2.0"}}}
|
||||||
, {getopt, "1.0.1"}
|
, {getopt, "1.0.1"}
|
||||||
, {eblake2, "1.0.0"}
|
, {eblake2, "1.0.0"}
|
||||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}
|
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{relx, [{release, {aesophia, "7.0.0"},
|
{relx, [{release, {aesophia, "8.0.0"},
|
||||||
[aesophia, aebytecode, getopt]},
|
[aesophia, aebytecode, getopt]},
|
||||||
|
|
||||||
{dev_mode, true},
|
{dev_mode, true},
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{"1.2.0",
|
{"1.2.0",
|
||||||
[{<<"aebytecode">>,
|
[{<<"aebytecode">>,
|
||||||
{git,"https://github.com/aeternity/aebytecode.git",
|
{git,"https://github.com/aeternity/aebytecode.git",
|
||||||
{ref,"8269dbd71e9011921c60141636f1baa270a0e784"}},
|
{ref,"2a0a397afad6b45da52572170f718194018bf33c"}},
|
||||||
0},
|
0},
|
||||||
{<<"aeserialization">>,
|
{<<"aeserialization">>,
|
||||||
{git,"https://github.com/aeternity/aeserialization.git",
|
{git,"https://github.com/aeternity/aeserialization.git",
|
||||||
|
|||||||
+4
-4
@@ -91,7 +91,7 @@ encode_contract(Contract = {Head, _, {con, _, Name}, _, _}) when ?IS_CONTRACT_HE
|
|||||||
{Es, Tdefs1} = lists:partition(FilterT(<<"event">>), Tdefs0),
|
{Es, Tdefs1} = lists:partition(FilterT(<<"event">>), Tdefs0),
|
||||||
{Ss, Tdefs} = lists:partition(FilterT(<<"state">>), Tdefs1),
|
{Ss, Tdefs} = lists:partition(FilterT(<<"state">>), Tdefs1),
|
||||||
|
|
||||||
C1 = C0#{type_defs => Tdefs},
|
C1 = C0#{typedefs => Tdefs},
|
||||||
|
|
||||||
C2 = case Es of
|
C2 = case Es of
|
||||||
[] -> C1;
|
[] -> C1;
|
||||||
@@ -111,7 +111,7 @@ encode_contract(Contract = {Head, _, {con, _, Name}, _, _}) when ?IS_CONTRACT_HE
|
|||||||
encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) ->
|
encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) ->
|
||||||
Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ],
|
Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ],
|
||||||
#{namespace => #{name => encode_name(Name),
|
#{namespace => #{name => encode_name(Name),
|
||||||
type_defs => Tdefs}}.
|
typedefs => Tdefs}}.
|
||||||
|
|
||||||
%% Encode a function definition. Currently we are only interested in
|
%% Encode a function definition. Currently we are only interested in
|
||||||
%% the interface and type.
|
%% the interface and type.
|
||||||
@@ -234,7 +234,7 @@ do_render_aci_json(Json) ->
|
|||||||
decode_contract(#{contract := #{name := Name,
|
decode_contract(#{contract := #{name := Name,
|
||||||
kind := Kind,
|
kind := Kind,
|
||||||
payable := Payable,
|
payable := Payable,
|
||||||
type_defs := Ts0,
|
typedefs := Ts0,
|
||||||
functions := Fs} = C}) ->
|
functions := Fs} = C}) ->
|
||||||
MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end,
|
MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end,
|
||||||
Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++
|
Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++
|
||||||
@@ -246,7 +246,7 @@ decode_contract(#{contract := #{name := Name,
|
|||||||
end,
|
end,
|
||||||
io_lib:format("~s", [Name])," =\n",
|
io_lib:format("~s", [Name])," =\n",
|
||||||
decode_tdefs(Ts), decode_funcs(Fs)];
|
decode_tdefs(Ts), decode_funcs(Fs)];
|
||||||
decode_contract(#{namespace := #{name := Name, type_defs := Ts}}) when Ts /= [] ->
|
decode_contract(#{namespace := #{name := Name, typedefs := Ts}}) when Ts /= [] ->
|
||||||
["namespace ", io_lib:format("~s", [Name])," =\n",
|
["namespace ", io_lib:format("~s", [Name])," =\n",
|
||||||
decode_tdefs(Ts)];
|
decode_tdefs(Ts)];
|
||||||
decode_contract(_) -> [].
|
decode_contract(_) -> [].
|
||||||
|
|||||||
+176
-85
@@ -270,15 +270,24 @@ bind_state(Env) ->
|
|||||||
false -> Env1
|
false -> Env1
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec bind_field(name(), field_info(), env()) -> env().
|
-spec bind_field_append(name(), field_info(), env()) -> env().
|
||||||
bind_field(X, Info, Env = #env{ fields = Fields }) ->
|
bind_field_append(X, Info, Env = #env{ fields = Fields }) ->
|
||||||
Fields1 = maps:update_with(X, fun(Infos) -> [Info | Infos] end, [Info], Fields),
|
Fields1 = maps:update_with(X, fun(Infos) -> [Info | Infos] end, [Info], Fields),
|
||||||
Env#env{ fields = Fields1 }.
|
Env#env{ fields = Fields1 }.
|
||||||
|
|
||||||
-spec bind_fields([{name(), field_info()}], env()) -> env().
|
-spec bind_field_update(name(), field_info(), env()) -> env().
|
||||||
bind_fields([], Env) -> Env;
|
bind_field_update(X, Info, Env = #env{ fields = Fields }) ->
|
||||||
bind_fields([{Id, Info} | Rest], Env) ->
|
Fields1 = maps:update_with(X, fun([_ | Infos]) -> [Info | Infos]; ([]) -> [Info] end, [Info], Fields),
|
||||||
bind_fields(Rest, bind_field(Id, Info, Env)).
|
Env#env{ fields = Fields1 }.
|
||||||
|
|
||||||
|
-spec bind_fields([{name(), field_info()}], typed | untyped, env()) -> env().
|
||||||
|
bind_fields([], _Typing, Env) -> Env;
|
||||||
|
bind_fields([{Id, Info} | Rest], Typing, Env) ->
|
||||||
|
NewEnv = case Typing of
|
||||||
|
untyped -> bind_field_append(Id, Info, Env);
|
||||||
|
typed -> bind_field_update(Id, Info, Env)
|
||||||
|
end,
|
||||||
|
bind_fields(Rest, Typing, NewEnv).
|
||||||
|
|
||||||
%% Contract entrypoints take three named arguments
|
%% Contract entrypoints take three named arguments
|
||||||
%% gas : int = Call.gas_left()
|
%% gas : int = Call.gas_left()
|
||||||
@@ -296,26 +305,27 @@ contract_call_type({fun_t, Ann, [], Args, Ret}) ->
|
|||||||
Named("protected", Typed({bool, Ann, false}, Id("bool")))],
|
Named("protected", Typed({bool, Ann, false}, Id("bool")))],
|
||||||
Args, {if_t, Ann, Id("protected"), {app_t, Ann, {id, Ann, "option"}, [Ret]}, Ret}}.
|
Args, {if_t, Ann, Id("protected"), {app_t, Ann, {id, Ann, "option"}, [Ret]}, Ret}}.
|
||||||
|
|
||||||
-spec bind_contract(aeso_syntax:decl(), env()) -> env().
|
-spec bind_contract(typed | untyped, aeso_syntax:decl(), env()) -> env().
|
||||||
bind_contract({Contract, Ann, Id, _Impls, Contents}, Env)
|
bind_contract(Typing, {Contract, Ann, Id, _Impls, Contents}, Env)
|
||||||
when ?IS_CONTRACT_HEAD(Contract) ->
|
when ?IS_CONTRACT_HEAD(Contract) ->
|
||||||
Key = name(Id),
|
Key = name(Id),
|
||||||
Sys = [{origin, system}],
|
Sys = [{origin, system}],
|
||||||
Fields =
|
TypeOrFresh = fun({typed, _, _, Type}) -> Type; (_) -> fresh_uvar(Sys) end,
|
||||||
|
Fields =
|
||||||
[ {field_t, AnnF, Entrypoint, contract_call_type(Type)}
|
[ {field_t, AnnF, Entrypoint, contract_call_type(Type)}
|
||||||
|| {fun_decl, AnnF, Entrypoint, Type} <- Contents ] ++
|
|| {fun_decl, AnnF, Entrypoint, Type = {fun_t, _, _, _, _}} <- Contents ] ++
|
||||||
[ {field_t, AnnF, Entrypoint,
|
[ {field_t, AnnF, Entrypoint,
|
||||||
contract_call_type(
|
contract_call_type(
|
||||||
{fun_t, AnnF, [], [ArgT || {typed, _, _, ArgT} <- Args], RetT})
|
{fun_t, AnnF, [], [TypeOrFresh(Arg) || Arg <- Args], TypeOrFresh(Ret)})
|
||||||
}
|
}
|
||||||
|| {letfun, AnnF, Entrypoint = {id, _, Name}, Args, _Type, [{guarded, _, [], {typed, _, _, RetT}}]} <- Contents,
|
|| {letfun, AnnF, Entrypoint = {id, _, Name}, Args, _Type, [{guarded, _, [], Ret}]} <- Contents,
|
||||||
Name =/= "init"
|
Name =/= "init"
|
||||||
] ++
|
] ++
|
||||||
%% Predefined fields
|
%% Predefined fields
|
||||||
[ {field_t, Sys, {id, Sys, "address"}, {id, Sys, "address"}} ] ++
|
[ {field_t, Sys, {id, Sys, "address"}, {id, Sys, "address"}} ] ++
|
||||||
[ {field_t, Sys, {id, Sys, ?CONSTRUCTOR_MOCK_NAME},
|
[ {field_t, Sys, {id, Sys, ?CONSTRUCTOR_MOCK_NAME},
|
||||||
contract_call_type(
|
contract_call_type(
|
||||||
case [ [ArgT || {typed, _, _, ArgT} <- Args]
|
case [ [TypeOrFresh(Arg) || Arg <- Args]
|
||||||
|| {letfun, AnnF, {id, _, "init"}, Args, _, _} <- Contents,
|
|| {letfun, AnnF, {id, _, "init"}, Args, _, _} <- Contents,
|
||||||
aeso_syntax:get_ann(entrypoint, AnnF, false)]
|
aeso_syntax:get_ann(entrypoint, AnnF, false)]
|
||||||
++ [ Args
|
++ [ Args
|
||||||
@@ -337,7 +347,7 @@ bind_contract({Contract, Ann, Id, _Impls, Contents}, Env)
|
|||||||
record_t = Id }}
|
record_t = Id }}
|
||||||
|| {field_t, _, {id, FieldAnn, Entrypoint}, Type} <- Fields ],
|
|| {field_t, _, {id, FieldAnn, Entrypoint}, Type} <- Fields ],
|
||||||
bind_type(Key, Ann, {[], {contract_t, Fields}},
|
bind_type(Key, Ann, {[], {contract_t, Fields}},
|
||||||
bind_fields(FieldInfo, Env)).
|
bind_fields(FieldInfo, Typing, Env)).
|
||||||
|
|
||||||
%% What scopes could a given name come from?
|
%% What scopes could a given name come from?
|
||||||
-spec possible_scopes(env(), qname()) -> [qname()].
|
-spec possible_scopes(env(), qname()) -> [qname()].
|
||||||
@@ -447,6 +457,9 @@ lookup_env1(#env{ namespace = Current, used_namespaces = UsedNamespaces, scopes
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
fun_arity({fun_t, _, _, Args, _}) -> length(Args);
|
||||||
|
fun_arity(_) -> none.
|
||||||
|
|
||||||
-spec lookup_record_field(env(), name()) -> [field_info()].
|
-spec lookup_record_field(env(), name()) -> [field_info()].
|
||||||
lookup_record_field(Env, FieldName) ->
|
lookup_record_field(Env, FieldName) ->
|
||||||
maps:get(FieldName, Env#env.fields, []).
|
maps:get(FieldName, Env#env.fields, []).
|
||||||
@@ -457,6 +470,11 @@ lookup_record_field(Env, FieldName, Kind) ->
|
|||||||
[ Fld || Fld = #field_info{ kind = K } <- lookup_record_field(Env, FieldName),
|
[ Fld || Fld = #field_info{ kind = K } <- lookup_record_field(Env, FieldName),
|
||||||
Kind == project orelse K /= contract ].
|
Kind == project orelse K /= contract ].
|
||||||
|
|
||||||
|
lookup_record_field_arity(Env, FieldName, Arity, Kind) ->
|
||||||
|
Fields = lookup_record_field(Env, FieldName, Kind),
|
||||||
|
[ Fld || Fld = #field_info{ field_t = FldType } <- Fields,
|
||||||
|
fun_arity(dereference_deep(FldType)) == Arity ].
|
||||||
|
|
||||||
%% -- Name manipulation ------------------------------------------------------
|
%% -- Name manipulation ------------------------------------------------------
|
||||||
|
|
||||||
-spec qname(type_id()) -> qname().
|
-spec qname(type_id()) -> qname().
|
||||||
@@ -522,6 +540,8 @@ global_env() ->
|
|||||||
TTL = {qid, Ann, ["Chain", "ttl"]},
|
TTL = {qid, Ann, ["Chain", "ttl"]},
|
||||||
Pointee = {qid, Ann, ["AENS", "pointee"]},
|
Pointee = {qid, Ann, ["AENS", "pointee"]},
|
||||||
AENSName = {qid, Ann, ["AENS", "name"]},
|
AENSName = {qid, Ann, ["AENS", "name"]},
|
||||||
|
PointeeV2 = {qid, Ann, ["AENSv2", "pointee"]},
|
||||||
|
AENSNameV2 = {qid, Ann, ["AENSv2", "name"]},
|
||||||
Fr = {qid, Ann, ["MCL_BLS12_381", "fr"]},
|
Fr = {qid, Ann, ["MCL_BLS12_381", "fr"]},
|
||||||
Fp = {qid, Ann, ["MCL_BLS12_381", "fp"]},
|
Fp = {qid, Ann, ["MCL_BLS12_381", "fp"]},
|
||||||
Fp2 = {tuple_t, Ann, [Fp, Fp]},
|
Fp2 = {tuple_t, Ann, [Fp, Fp]},
|
||||||
@@ -657,14 +677,7 @@ global_env() ->
|
|||||||
|
|
||||||
AENSScope = #scope
|
AENSScope = #scope
|
||||||
{ funs = MkDefs(
|
{ funs = MkDefs(
|
||||||
[{"resolve", Fun([String, String], option_t(Ann, A))},
|
[%% AENS pointee constructors
|
||||||
{"preclaim", SignFun([Address, Hash], Unit)},
|
|
||||||
{"claim", SignFun([Address, String, Int, Int], Unit)},
|
|
||||||
{"transfer", SignFun([Address, Address, String], Unit)},
|
|
||||||
{"revoke", SignFun([Address, String], Unit)},
|
|
||||||
{"update", SignFun([Address, String, Option(TTL), Option(Int), Option(Map(String, Pointee))], Unit)},
|
|
||||||
{"lookup", Fun([String], option_t(Ann, AENSName))},
|
|
||||||
%% AENS pointee constructors
|
|
||||||
{"AccountPt", Fun1(Address, Pointee)},
|
{"AccountPt", Fun1(Address, Pointee)},
|
||||||
{"OraclePt", Fun1(Address, Pointee)},
|
{"OraclePt", Fun1(Address, Pointee)},
|
||||||
{"ContractPt", Fun1(Address, Pointee)},
|
{"ContractPt", Fun1(Address, Pointee)},
|
||||||
@@ -674,6 +687,26 @@ global_env() ->
|
|||||||
])
|
])
|
||||||
, types = MkDefs([{"pointee", 0}, {"name", 0}]) },
|
, types = MkDefs([{"pointee", 0}, {"name", 0}]) },
|
||||||
|
|
||||||
|
AENSv2Scope = #scope
|
||||||
|
{ funs = MkDefs(
|
||||||
|
[{"resolve", Fun([String, String], option_t(Ann, A))},
|
||||||
|
{"preclaim", SignFun([Address, Hash], Unit)},
|
||||||
|
{"claim", SignFun([Address, String, Int, Int], Unit)},
|
||||||
|
{"transfer", SignFun([Address, Address, String], Unit)},
|
||||||
|
{"revoke", SignFun([Address, String], Unit)},
|
||||||
|
{"update", SignFun([Address, String, Option(TTL), Option(Int), Option(Map(String, PointeeV2))], Unit)},
|
||||||
|
{"lookup", Fun([String], option_t(Ann, AENSNameV2))},
|
||||||
|
%% AENS pointee constructors v2
|
||||||
|
{"AccountPt", Fun1(Address, PointeeV2)},
|
||||||
|
{"OraclePt", Fun1(Address, PointeeV2)},
|
||||||
|
{"ContractPt", Fun1(Address, PointeeV2)},
|
||||||
|
{"ChannelPt", Fun1(Address, PointeeV2)},
|
||||||
|
{"DataPt", Fun1(String, PointeeV2)},
|
||||||
|
%% Name object constructor v2
|
||||||
|
{"Name", Fun([Address, TTL, Map(String, PointeeV2)], AENSNameV2)}
|
||||||
|
])
|
||||||
|
, types = MkDefs([{"pointee", 0}, {"name", 0}]) },
|
||||||
|
|
||||||
MapScope = #scope
|
MapScope = #scope
|
||||||
{ funs = MkDefs(
|
{ funs = MkDefs(
|
||||||
[{"from_list", Fun1(List(Pair(K, V)), Map(K, V))},
|
[{"from_list", Fun1(List(Pair(K, V)), Map(K, V))},
|
||||||
@@ -693,7 +726,8 @@ global_env() ->
|
|||||||
{"ecrecover_secp256k1", Fun([Hash, Bytes(65)], Option(Bytes(20)))},
|
{"ecrecover_secp256k1", Fun([Hash, Bytes(65)], Option(Bytes(20)))},
|
||||||
{"sha3", Fun1(A, Hash)},
|
{"sha3", Fun1(A, Hash)},
|
||||||
{"sha256", Fun1(A, Hash)},
|
{"sha256", Fun1(A, Hash)},
|
||||||
{"blake2b", Fun1(A, Hash)}]) },
|
{"blake2b", Fun1(A, Hash)},
|
||||||
|
{"poseidon", Fun([Int, Int], Int)}]) },
|
||||||
|
|
||||||
%% Fancy BLS12-381 crypto operations
|
%% Fancy BLS12-381 crypto operations
|
||||||
MCL_BLS12_381_Scope = #scope
|
MCL_BLS12_381_Scope = #scope
|
||||||
@@ -778,14 +812,16 @@ global_env() ->
|
|||||||
]) },
|
]) },
|
||||||
|
|
||||||
%% Conversion
|
%% Conversion
|
||||||
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) },
|
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)},
|
||||||
|
{"mulmod", Fun([Int, Int, Int], Int)}]) },
|
||||||
|
|
||||||
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)},
|
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)},
|
||||||
|
{"to_bytes", Fun1(Address, Bytes(32))},
|
||||||
{"to_contract", FunC(address_to_contract, [Address], A)},
|
{"to_contract", FunC(address_to_contract, [Address], A)},
|
||||||
{"is_oracle", Fun1(Address, Bool)},
|
{"is_oracle", Fun1(Address, Bool)},
|
||||||
{"is_contract", Fun1(Address, Bool)},
|
{"is_contract", Fun1(Address, Bool)},
|
||||||
{"is_payable", Fun1(Address, Bool)}]) },
|
{"is_payable", Fun1(Address, Bool)}]) },
|
||||||
|
|
||||||
|
|
||||||
#env{ scopes =
|
#env{ scopes =
|
||||||
#{ [] => TopScope
|
#{ [] => TopScope
|
||||||
, ["Chain"] => ChainScope
|
, ["Chain"] => ChainScope
|
||||||
@@ -793,6 +829,7 @@ global_env() ->
|
|||||||
, ["Call"] => CallScope
|
, ["Call"] => CallScope
|
||||||
, ["Oracle"] => OracleScope
|
, ["Oracle"] => OracleScope
|
||||||
, ["AENS"] => AENSScope
|
, ["AENS"] => AENSScope
|
||||||
|
, ["AENSv2"] => AENSv2Scope
|
||||||
, ["Map"] => MapScope
|
, ["Map"] => MapScope
|
||||||
, ["Auth"] => AuthScope
|
, ["Auth"] => AuthScope
|
||||||
, ["Crypto"] => CryptoScope
|
, ["Crypto"] => CryptoScope
|
||||||
@@ -837,6 +874,7 @@ infer(Contracts, Options) ->
|
|||||||
ets_new(type_vars, [set]),
|
ets_new(type_vars, [set]),
|
||||||
ets_new(warnings, [bag]),
|
ets_new(warnings, [bag]),
|
||||||
ets_new(type_vars_variance, [set]),
|
ets_new(type_vars_variance, [set]),
|
||||||
|
ets_new(functions_to_implement, [set]),
|
||||||
%% Set the variance for builtin types
|
%% Set the variance for builtin types
|
||||||
ets_insert(type_vars_variance, {"list", [covariant]}),
|
ets_insert(type_vars_variance, {"list", [covariant]}),
|
||||||
ets_insert(type_vars_variance, {"option", [covariant]}),
|
ets_insert(type_vars_variance, {"option", [covariant]}),
|
||||||
@@ -871,7 +909,7 @@ infer(Contracts, Options) ->
|
|||||||
-spec infer1(env(), [aeso_syntax:decl()], [aeso_syntax:decl()], list(option())) ->
|
-spec infer1(env(), [aeso_syntax:decl()], [aeso_syntax:decl()], list(option())) ->
|
||||||
{env(), [aeso_syntax:decl()]}.
|
{env(), [aeso_syntax:decl()]}.
|
||||||
infer1(Env, [], Acc, _Options) -> {Env, lists:reverse(Acc)};
|
infer1(Env, [], Acc, _Options) -> {Env, lists:reverse(Acc)};
|
||||||
infer1(Env0, [{Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options)
|
infer1(Env0, [Contract0 = {Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options)
|
||||||
when ?IS_CONTRACT_HEAD(Contract) ->
|
when ?IS_CONTRACT_HEAD(Contract) ->
|
||||||
%% do type inference on each contract independently.
|
%% do type inference on each contract independently.
|
||||||
Env = Env0#env{ contract_parents = maps:put(name(ConName),
|
Env = Env0#env{ contract_parents = maps:put(name(ConName),
|
||||||
@@ -887,12 +925,15 @@ infer1(Env0, [{Contract, Ann, ConName, Impls, Code} | Rest], Acc, Options)
|
|||||||
contract -> ets_insert(defined_contracts, {qname(ConName)});
|
contract -> ets_insert(defined_contracts, {qname(ConName)});
|
||||||
contract_interface -> ok
|
contract_interface -> ok
|
||||||
end,
|
end,
|
||||||
{Env1, Code1} = infer_contract_top(push_scope(contract, ConName, Env), What, Code, Options),
|
populate_functions_to_implement(Env, ConName, Impls, Acc),
|
||||||
|
Env1 = bind_contract(untyped, Contract0, Env),
|
||||||
|
{Env2, Code1} = infer_contract_top(push_scope(contract, ConName, Env1), What, Code, Options),
|
||||||
|
report_unimplemented_functions(Env1, ConName),
|
||||||
Contract1 = {Contract, Ann, ConName, Impls, Code1},
|
Contract1 = {Contract, Ann, ConName, Impls, Code1},
|
||||||
check_implemented_interfaces(Env1, Contract1, Acc),
|
Env3 = pop_scope(Env2),
|
||||||
Env2 = pop_scope(Env1),
|
%% Rebinding because the qualifications of types are added during type inference. Could we do better?
|
||||||
Env3 = bind_contract(Contract1, Env2),
|
Env4 = bind_contract(typed, Contract1, Env3),
|
||||||
infer1(Env3, Rest, [Contract1 | Acc], Options);
|
infer1(Env4, Rest, [Contract1 | Acc], Options);
|
||||||
infer1(Env, [{namespace, Ann, Name, Code} | Rest], Acc, Options) ->
|
infer1(Env, [{namespace, Ann, Name, Code} | Rest], Acc, Options) ->
|
||||||
when_warning(warn_unused_includes,
|
when_warning(warn_unused_includes,
|
||||||
fun() ->
|
fun() ->
|
||||||
@@ -909,54 +950,52 @@ infer1(Env, [{pragma, _, _} | Rest], Acc, Options) ->
|
|||||||
%% Pragmas are checked in check_modifiers
|
%% Pragmas are checked in check_modifiers
|
||||||
infer1(Env, Rest, Acc, Options).
|
infer1(Env, Rest, Acc, Options).
|
||||||
|
|
||||||
check_implemented_interfaces(Env, {_Contract, _Ann, ConName, Impls, Code}, DefinedContracts) ->
|
%% Report all functions that were not implemented by the contract ContractName.
|
||||||
|
-spec report_unimplemented_functions(env(), ContractName) -> ok | no_return() when
|
||||||
|
ContractName :: aeso_syntax:con().
|
||||||
|
report_unimplemented_functions(Env, ContractName) ->
|
||||||
create_type_errors(),
|
create_type_errors(),
|
||||||
AllInterfaces = [{name(IName), I} || I = {contract_interface, _, IName, _, _} <- DefinedContracts],
|
[ type_error({unimplemented_interface_function, ContractName, name(I), FunName})
|
||||||
ImplsNames = lists:map(fun name/1, Impls),
|
|| {FunName, I, _} <- ets_tab2list(functions_to_implement) ],
|
||||||
|
|
||||||
%% All implemented intrefaces should already be defined
|
|
||||||
lists:foreach(fun(Impl) -> case proplists:get_value(name(Impl), AllInterfaces) of
|
|
||||||
undefined -> type_error({referencing_undefined_interface, Impl});
|
|
||||||
_ -> ok
|
|
||||||
end
|
|
||||||
end, Impls),
|
|
||||||
|
|
||||||
ImplementedInterfaces = [I || I <- [proplists:get_value(Name, AllInterfaces) || Name <- ImplsNames],
|
|
||||||
I /= undefined],
|
|
||||||
Funs = [ Fun || Fun <- Code,
|
|
||||||
element(1, Fun) == letfun orelse element(1, Fun) == fun_decl ],
|
|
||||||
check_implemented_interfaces1(Env, ImplementedInterfaces, ConName, Funs, AllInterfaces),
|
|
||||||
destroy_and_report_type_errors(Env).
|
destroy_and_report_type_errors(Env).
|
||||||
|
|
||||||
%% Recursively check that all directly and indirectly referenced interfaces are implemented
|
%% Return a list of all function declarations to be implemented, given the list
|
||||||
check_implemented_interfaces1(_, [], _, _, _) ->
|
%% of interfaces to be implemented Impls and all the previously defined
|
||||||
ok;
|
%% contracts DefinedContracts>
|
||||||
check_implemented_interfaces1(Env, [{contract_interface, _, IName, _, Decls} | Interfaces],
|
-spec functions_to_implement(Impls, DefinedContracts) -> [{InterfaceCon, FunDecl}] when
|
||||||
ConId, Impls, AllInterfaces) ->
|
Impls :: [aeso_syntax:con()],
|
||||||
Unmatched = match_impls(Env, Decls, ConId, name(IName), Impls),
|
DefinedContracts :: [aeso_syntax:decl()],
|
||||||
check_implemented_interfaces1(Env, Interfaces, ConId, Unmatched, AllInterfaces).
|
InterfaceCon :: aeso_syntax:con(),
|
||||||
|
FunDecl :: aeso_syntax:fundecl().
|
||||||
|
functions_to_implement(Impls, DefinedContracts) ->
|
||||||
|
ImplsNames = [ name(I) || I <- Impls ],
|
||||||
|
Interfaces = [ I || I = {contract_interface, _, Con, _, _} <- DefinedContracts,
|
||||||
|
lists:member(name(Con), ImplsNames) ],
|
||||||
|
|
||||||
%% Match the functions of the contract with the interfaces functions, and return unmatched functions
|
%% All implemented intrefaces should already be defined
|
||||||
match_impls(_, [], _, _, Impls) ->
|
InterfacesNames = [name(element(3, I)) || I <- Interfaces],
|
||||||
Impls;
|
[ begin
|
||||||
match_impls(Env, [{fun_decl, _, {id, _, FunName}, FunType = {fun_t, _, _, ArgsTypes, RetDecl}} | Decls], ConId, IName, Impls) ->
|
Found = lists:member(name(Impl), InterfacesNames),
|
||||||
Match = fun({letfun, _, {id, _, FName}, Args, RetFun, _}) when FName == FunName ->
|
Found orelse type_error({referencing_undefined_interface, Impl})
|
||||||
length(ArgsTypes) == length(Args) andalso
|
end || Impl <- Impls
|
||||||
unify(Env#env{unify_throws = false}, RetDecl, RetFun, unknown) andalso
|
],
|
||||||
lists:all(fun({T1, {typed, _, _, T2}}) -> unify(Env#env{unify_throws = false}, T1, T2, unknown) end,
|
|
||||||
lists:zip(ArgsTypes, Args));
|
lists:flatten([ [ {Con, Decl} || Decl <- Decls] || {contract_interface, _, Con, _, Decls} <- Interfaces ]).
|
||||||
({fun_decl, _, {id, _, FName}, FunT}) when FName == FunName ->
|
|
||||||
unify(Env#env{unify_throws = false}, FunT, FunType, unknown);
|
%% Fill the ets table functions_to_implement with functions from the implemented
|
||||||
(_) -> false
|
%% interfaces Impls.
|
||||||
end,
|
-spec populate_functions_to_implement(env(), ContractName, Impls, DefinedContracts) -> ok | no_return() when
|
||||||
UnmatchedImpls = case lists:search(Match, Impls) of
|
ContractName :: aeso_syntax:con(),
|
||||||
{value, V} ->
|
Impls :: [aeso_syntax:con()],
|
||||||
lists:delete(V, Impls);
|
DefinedContracts :: [aeso_syntax:decl()].
|
||||||
false ->
|
populate_functions_to_implement(Env, ContractName, Impls, DefinedContracts) ->
|
||||||
type_error({unimplemented_interface_function, ConId, IName, FunName}),
|
create_type_errors(),
|
||||||
Impls
|
[ begin
|
||||||
end,
|
Inserted = ets_insert_new(functions_to_implement, {name(Id), I, Decl}),
|
||||||
match_impls(Env, Decls, ConId, IName, UnmatchedImpls).
|
[{_, I2, _}] = ets_lookup(functions_to_implement, name(Id)),
|
||||||
|
Inserted orelse type_error({interface_implementation_conflict, ContractName, I, I2, Id})
|
||||||
|
end || {I, Decl = {fun_decl, _, Id, _}} <- functions_to_implement(Impls, DefinedContracts) ],
|
||||||
|
destroy_and_report_type_errors(Env).
|
||||||
|
|
||||||
%% Asserts that the main contract is somehow defined.
|
%% Asserts that the main contract is somehow defined.
|
||||||
identify_main_contract(Contracts, Options) ->
|
identify_main_contract(Contracts, Options) ->
|
||||||
@@ -1367,7 +1406,7 @@ check_named_arg(Env, {named_arg_t, Ann, Id, Type, Default}) ->
|
|||||||
-spec check_fields(env(), #{ name() => aeso_syntax:decl() }, type(), [aeso_syntax:field_t()]) -> env().
|
-spec check_fields(env(), #{ name() => aeso_syntax:decl() }, type(), [aeso_syntax:field_t()]) -> env().
|
||||||
check_fields(Env, _TypeMap, _, []) -> Env;
|
check_fields(Env, _TypeMap, _, []) -> Env;
|
||||||
check_fields(Env, TypeMap, RecTy, [{field_t, Ann, Id, Type} | Fields]) ->
|
check_fields(Env, TypeMap, RecTy, [{field_t, Ann, Id, Type} | Fields]) ->
|
||||||
Env1 = bind_field(name(Id), #field_info{ ann = Ann, kind = record, field_t = Type, record_t = RecTy }, Env),
|
Env1 = bind_field_append(name(Id), #field_info{ ann = Ann, kind = record, field_t = Type, record_t = RecTy }, Env),
|
||||||
check_fields(Env1, TypeMap, RecTy, Fields).
|
check_fields(Env1, TypeMap, RecTy, Fields).
|
||||||
|
|
||||||
check_parameterizable({id, Ann, "event"}, [_ | _]) ->
|
check_parameterizable({id, Ann, "event"}, [_ | _]) ->
|
||||||
@@ -1466,15 +1505,32 @@ check_reserved_entrypoints(Funs) ->
|
|||||||
-spec check_fundecl(env(), aeso_syntax:decl()) -> {{name(), typesig()}, aeso_syntax:decl()}.
|
-spec check_fundecl(env(), aeso_syntax:decl()) -> {{name(), typesig()}, aeso_syntax:decl()}.
|
||||||
check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type = {fun_t, _, _, _, _}}) ->
|
check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type = {fun_t, _, _, _, _}}) ->
|
||||||
Type1 = {fun_t, _, Named, Args, Ret} = check_type(Env, Type),
|
Type1 = {fun_t, _, Named, Args, Ret} = check_type(Env, Type),
|
||||||
{{Name, {type_sig, Ann, none, Named, Args, Ret}}, {fun_decl, Ann, Id, Type1}};
|
TypeSig = {type_sig, Ann, none, Named, Args, Ret},
|
||||||
|
register_implementation(Name),
|
||||||
|
{{Name, TypeSig}, {fun_decl, Ann, Id, Type1}};
|
||||||
check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type}) ->
|
check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type}) ->
|
||||||
type_error({fundecl_must_have_funtype, Ann, Id, Type}),
|
type_error({fundecl_must_have_funtype, Ann, Id, Type}),
|
||||||
{{Name, {type_sig, Ann, none, [], [], Type}}, check_type(Env, Type)}.
|
{{Name, {type_sig, Ann, none, [], [], Type}}, check_type(Env, Type)}.
|
||||||
|
|
||||||
|
%% Register the function FunName as implemented by deleting it from the functions
|
||||||
|
%% to be implemented table if it is included there, or return true otherwise.
|
||||||
|
-spec register_implementation(FunName) -> true | no_return() when
|
||||||
|
FunName :: string().
|
||||||
|
register_implementation(Name) ->
|
||||||
|
case ets_lookup(functions_to_implement, Name) of
|
||||||
|
[{Name, _, {fun_decl, _, _, _}}] ->
|
||||||
|
ets_delete(functions_to_implement, Name);
|
||||||
|
[] ->
|
||||||
|
true;
|
||||||
|
_ ->
|
||||||
|
error("Ets set has multiple keys")
|
||||||
|
end.
|
||||||
|
|
||||||
infer_nonrec(Env, LetFun) ->
|
infer_nonrec(Env, LetFun) ->
|
||||||
create_constraints(),
|
create_constraints(),
|
||||||
NewLetFun = infer_letfun(Env, LetFun),
|
NewLetFun = {{FunName, _}, _} = infer_letfun(Env, LetFun),
|
||||||
check_special_funs(Env, NewLetFun),
|
check_special_funs(Env, NewLetFun),
|
||||||
|
register_implementation(FunName),
|
||||||
solve_then_destroy_and_report_unsolved_constraints(Env),
|
solve_then_destroy_and_report_unsolved_constraints(Env),
|
||||||
Result = {TypeSig, _} = instantiate(NewLetFun),
|
Result = {TypeSig, _} = instantiate(NewLetFun),
|
||||||
print_typesig(TypeSig),
|
print_typesig(TypeSig),
|
||||||
@@ -1504,6 +1560,7 @@ infer_letrec(Env, Defs) ->
|
|||||||
Inferred =
|
Inferred =
|
||||||
[ begin
|
[ begin
|
||||||
Res = {{Name, TypeSig}, _} = infer_letfun(ExtendEnv, LF),
|
Res = {{Name, TypeSig}, _} = infer_letfun(ExtendEnv, LF),
|
||||||
|
register_implementation(Name),
|
||||||
Got = proplists:get_value(Name, Funs),
|
Got = proplists:get_value(Name, Funs),
|
||||||
Expect = typesig_to_fun_t(TypeSig),
|
Expect = typesig_to_fun_t(TypeSig),
|
||||||
unify(Env, Got, Expect, {check_typesig, Name, Got, Expect}),
|
unify(Env, Got, Expect, {check_typesig, Name, Got, Expect}),
|
||||||
@@ -1842,7 +1899,7 @@ infer_expr(Env, {app, Ann, Fun, Args0} = App) ->
|
|||||||
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When),
|
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When),
|
||||||
when_warning(warn_negative_spend, fun() -> warn_potential_negative_spend(Ann, NewFun1, NewArgs) end),
|
when_warning(warn_negative_spend, fun() -> warn_potential_negative_spend(Ann, NewFun1, NewArgs) end),
|
||||||
[ add_constraint({aens_resolve_type, GeneralResultType})
|
[ add_constraint({aens_resolve_type, GeneralResultType})
|
||||||
|| element(3, FunName) =:= ["AENS", "resolve"] ],
|
|| element(3, FunName) =:= ["AENSv2", "resolve"] ],
|
||||||
[ add_constraint({oracle_type, Ann, OType})
|
[ add_constraint({oracle_type, Ann, OType})
|
||||||
|| OType <- [get_oracle_type(FunName, ArgTypes, GeneralResultType)],
|
|| OType <- [get_oracle_type(FunName, ArgTypes, GeneralResultType)],
|
||||||
OType =/= false ],
|
OType =/= false ],
|
||||||
@@ -2129,6 +2186,11 @@ infer_infix({IntOp, As})
|
|||||||
IntOp == '^'; IntOp == 'mod' ->
|
IntOp == '^'; IntOp == 'mod' ->
|
||||||
Int = {id, As, "int"},
|
Int = {id, As, "int"},
|
||||||
{fun_t, As, [], [Int, Int], Int};
|
{fun_t, As, [], [Int, Int], Int};
|
||||||
|
infer_infix({BitOp, As})
|
||||||
|
when BitOp == 'band'; BitOp == 'bor'; BitOp == 'bxor';
|
||||||
|
BitOp == '<<'; BitOp == '>>' ->
|
||||||
|
Int = {id, As, "int"},
|
||||||
|
{fun_t, As, [], [Int, Int], Int};
|
||||||
infer_infix({RelOp, As})
|
infer_infix({RelOp, As})
|
||||||
when RelOp == '=='; RelOp == '!=';
|
when RelOp == '=='; RelOp == '!=';
|
||||||
RelOp == '<'; RelOp == '>';
|
RelOp == '<'; RelOp == '>';
|
||||||
@@ -2156,6 +2218,9 @@ infer_infix({'|>', As}) ->
|
|||||||
infer_prefix({'!',As}) ->
|
infer_prefix({'!',As}) ->
|
||||||
Bool = {id, As, "bool"},
|
Bool = {id, As, "bool"},
|
||||||
{fun_t, As, [], [Bool], Bool};
|
{fun_t, As, [], [Bool], Bool};
|
||||||
|
infer_prefix({BitOp,As}) when BitOp =:= 'bnot' ->
|
||||||
|
Int = {id, As, "int"},
|
||||||
|
{fun_t, As, [], [Int], Int};
|
||||||
infer_prefix({IntOp,As}) when IntOp =:= '-' ->
|
infer_prefix({IntOp,As}) when IntOp =:= '-' ->
|
||||||
Int = {id, As, "int"},
|
Int = {id, As, "int"},
|
||||||
{fun_t, As, [], [Int], Int}.
|
{fun_t, As, [], [Int], Int}.
|
||||||
@@ -2204,7 +2269,7 @@ next_count() ->
|
|||||||
ets_tables() ->
|
ets_tables() ->
|
||||||
[options, type_vars, constraints, freshen_tvars, type_errors,
|
[options, type_vars, constraints, freshen_tvars, type_errors,
|
||||||
defined_contracts, warnings, function_calls, all_functions,
|
defined_contracts, warnings, function_calls, all_functions,
|
||||||
type_vars_variance].
|
type_vars_variance, functions_to_implement].
|
||||||
|
|
||||||
clean_up_ets() ->
|
clean_up_ets() ->
|
||||||
[ catch ets_delete(Tab) || Tab <- ets_tables() ],
|
[ catch ets_delete(Tab) || Tab <- ets_tables() ],
|
||||||
@@ -2240,10 +2305,18 @@ ets_delete(Name) ->
|
|||||||
put(aeso_ast_infer_types, maps:remove(Name, Tabs)),
|
put(aeso_ast_infer_types, maps:remove(Name, Tabs)),
|
||||||
ets:delete(TabId).
|
ets:delete(TabId).
|
||||||
|
|
||||||
|
ets_delete(Name, Key) ->
|
||||||
|
TabId = ets_tabid(Name),
|
||||||
|
ets:delete(TabId, Key).
|
||||||
|
|
||||||
ets_insert(Name, Object) ->
|
ets_insert(Name, Object) ->
|
||||||
TabId = ets_tabid(Name),
|
TabId = ets_tabid(Name),
|
||||||
ets:insert(TabId, Object).
|
ets:insert(TabId, Object).
|
||||||
|
|
||||||
|
ets_insert_new(Name, Object) ->
|
||||||
|
TabId = ets_tabid(Name),
|
||||||
|
ets:insert_new(TabId, Object).
|
||||||
|
|
||||||
ets_lookup(Name, Key) ->
|
ets_lookup(Name, Key) ->
|
||||||
TabId = ets_tabid(Name),
|
TabId = ets_tabid(Name),
|
||||||
ets:lookup(TabId, Key).
|
ets:lookup(TabId, Key).
|
||||||
@@ -2310,7 +2383,12 @@ solve_constraints(Env) ->
|
|||||||
field_t = FieldType,
|
field_t = FieldType,
|
||||||
kind = Kind,
|
kind = Kind,
|
||||||
context = When }) ->
|
context = When }) ->
|
||||||
case lookup_record_field(Env, FieldName, Kind) of
|
Arity = fun_arity(dereference_deep(FieldType)),
|
||||||
|
FieldInfos = case Arity of
|
||||||
|
none -> lookup_record_field(Env, FieldName, Kind);
|
||||||
|
_ -> lookup_record_field_arity(Env, FieldName, Arity, Kind)
|
||||||
|
end,
|
||||||
|
case FieldInfos of
|
||||||
[] ->
|
[] ->
|
||||||
type_error({undefined_field, Field}),
|
type_error({undefined_field, Field}),
|
||||||
false;
|
false;
|
||||||
@@ -2805,6 +2883,8 @@ unify0(Env, A, B, Variance, When) ->
|
|||||||
|
|
||||||
unify1(_Env, {uvar, _, R}, {uvar, _, R}, _Variance, _When) ->
|
unify1(_Env, {uvar, _, R}, {uvar, _, R}, _Variance, _When) ->
|
||||||
true;
|
true;
|
||||||
|
unify1(_Env, {uvar, _, _}, {fun_t, _, _, var_args, _}, _Variance, When) ->
|
||||||
|
type_error({unify_varargs, When});
|
||||||
unify1(Env, {uvar, A, R}, T, _Variance, When) ->
|
unify1(Env, {uvar, A, R}, T, _Variance, When) ->
|
||||||
case occurs_check(R, T) of
|
case occurs_check(R, T) of
|
||||||
true ->
|
true ->
|
||||||
@@ -2828,6 +2908,12 @@ unify1(Env, [A|B], [C|D], Variance, When) ->
|
|||||||
unify0(Env, A, C, Variance, When) andalso unify0(Env, B, D, Variance, When);
|
unify0(Env, A, C, Variance, When) andalso unify0(Env, B, D, Variance, When);
|
||||||
unify1(_Env, X, X, _Variance, _When) ->
|
unify1(_Env, X, X, _Variance, _When) ->
|
||||||
true;
|
true;
|
||||||
|
unify1(_Env, _A, {id, _, "void"}, Variance, _When)
|
||||||
|
when Variance == covariant orelse Variance == bivariant ->
|
||||||
|
true;
|
||||||
|
unify1(_Env, {id, _, "void"}, _B, Variance, _When)
|
||||||
|
when Variance == contravariant orelse Variance == bivariant ->
|
||||||
|
true;
|
||||||
unify1(_Env, {id, _, Name}, {id, _, Name}, _Variance, _When) ->
|
unify1(_Env, {id, _, Name}, {id, _, Name}, _Variance, _When) ->
|
||||||
true;
|
true;
|
||||||
unify1(Env, A = {con, _, NameA}, B = {con, _, NameB}, Variance, When) ->
|
unify1(Env, A = {con, _, NameA}, B = {con, _, NameB}, Variance, When) ->
|
||||||
@@ -3568,7 +3654,7 @@ mk_error({multiple_main_contracts, Ann}) ->
|
|||||||
Msg = "Only one main contract can be defined.",
|
Msg = "Only one main contract can be defined.",
|
||||||
mk_t_err(pos(Ann), Msg);
|
mk_t_err(pos(Ann), Msg);
|
||||||
mk_error({unify_varargs, When}) ->
|
mk_error({unify_varargs, When}) ->
|
||||||
Msg = "Cannot unify variable argument list.",
|
Msg = "Cannot infer types for variable argument list.",
|
||||||
{Pos, Ctxt} = pp_when(When),
|
{Pos, Ctxt} = pp_when(When),
|
||||||
mk_t_err(Pos, Msg, Ctxt);
|
mk_t_err(Pos, Msg, Ctxt);
|
||||||
mk_error({clone_no_contract, Ann}) ->
|
mk_error({clone_no_contract, Ann}) ->
|
||||||
@@ -3632,7 +3718,7 @@ mk_error({higher_order_entrypoint, Ann, {id, _, Name}, Thing}) ->
|
|||||||
[ThingS, Name, Bad]),
|
[ThingS, Name, Bad]),
|
||||||
mk_t_err(pos(Ann), Msg);
|
mk_t_err(pos(Ann), Msg);
|
||||||
mk_error({invalid_aens_resolve_type, Ann, T}) ->
|
mk_error({invalid_aens_resolve_type, Ann, T}) ->
|
||||||
Msg = io_lib:format("Invalid return type of `AENS.resolve`:\n"
|
Msg = io_lib:format("Invalid return type of `AENSv2.resolve`:\n"
|
||||||
"~s`\n"
|
"~s`\n"
|
||||||
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)",
|
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)",
|
||||||
[pp_type(" `", T)]),
|
[pp_type(" `", T)]),
|
||||||
@@ -3643,6 +3729,11 @@ mk_error({invalid_oracle_type, Why, What, Ann, Type}) ->
|
|||||||
Msg = io_lib:format("Invalid oracle type\n~s`", [pp_type(" `", Type)]),
|
Msg = io_lib:format("Invalid oracle type\n~s`", [pp_type(" `", Type)]),
|
||||||
Cxt = io_lib:format("The ~s type must not be ~s", [What, WhyS]),
|
Cxt = io_lib:format("The ~s type must not be ~s", [What, WhyS]),
|
||||||
mk_t_err(pos(Ann), Msg, Cxt);
|
mk_t_err(pos(Ann), Msg, Cxt);
|
||||||
|
mk_error({interface_implementation_conflict, Contract, I1, I2, Fun}) ->
|
||||||
|
Msg = io_lib:format("Both interfaces `~s` and `~s` implemented by "
|
||||||
|
"the contract `~s` have a function called `~s`",
|
||||||
|
[name(I1), name(I2), name(Contract), name(Fun)]),
|
||||||
|
mk_t_err(pos(Contract), Msg);
|
||||||
mk_error(Err) ->
|
mk_error(Err) ->
|
||||||
Msg = io_lib:format("Unknown error: ~p", [Err]),
|
Msg = io_lib:format("Unknown error: ~p", [Err]),
|
||||||
mk_t_err(pos(0, 0), Msg).
|
mk_t_err(pos(0, 0), Msg).
|
||||||
|
|||||||
+89
-44
@@ -32,12 +32,13 @@
|
|||||||
|
|
||||||
-type op() :: '+' | '-' | '*' | '/' | mod | '^' | '++' | '::' |
|
-type op() :: '+' | '-' | '*' | '/' | mod | '^' | '++' | '::' |
|
||||||
'<' | '>' | '=<' | '>=' | '==' | '!=' | '!' |
|
'<' | '>' | '=<' | '>=' | '==' | '!=' | '!' |
|
||||||
|
'band' | 'bor' | 'bxor' | 'bnot' | '<<' | '>>' |
|
||||||
map_get | map_get_d | map_set | map_from_list | map_to_list |
|
map_get | map_get_d | map_set | map_from_list | map_to_list |
|
||||||
map_delete | map_member | map_size | string_length |
|
map_delete | map_member | map_size | string_length |
|
||||||
string_concat | bits_set | bits_clear | bits_test | bits_sum |
|
string_concat | bits_set | bits_clear | bits_test | bits_sum |
|
||||||
bits_intersection | bits_union | bits_difference |
|
bits_intersection | bits_union | bits_difference |
|
||||||
contract_to_address | address_to_contract | crypto_verify_sig | crypto_verify_sig_secp256k1 |
|
contract_to_address | address_to_contract | crypto_verify_sig | crypto_verify_sig_secp256k1 |
|
||||||
crypto_sha3 | crypto_sha256 | crypto_blake2b |
|
crypto_sha3 | crypto_sha256 | crypto_blake2b | crypto_poseidon |
|
||||||
crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1 |
|
crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1 |
|
||||||
mcl_bls12_381_g1_neg | mcl_bls12_381_g1_norm | mcl_bls12_381_g1_valid |
|
mcl_bls12_381_g1_neg | mcl_bls12_381_g1_norm | mcl_bls12_381_g1_valid |
|
||||||
mcl_bls12_381_g1_is_zero | mcl_bls12_381_g1_add | mcl_bls12_381_g1_mul |
|
mcl_bls12_381_g1_is_zero | mcl_bls12_381_g1_add | mcl_bls12_381_g1_mul |
|
||||||
@@ -149,17 +150,18 @@
|
|||||||
|
|
||||||
-type state_layout() :: {tuple, [state_layout()]} | {reg, state_reg()}.
|
-type state_layout() :: {tuple, [state_layout()]} | {reg, state_reg()}.
|
||||||
|
|
||||||
-type env() :: #{ type_env := type_env(),
|
-type env() :: #{ type_env := type_env(),
|
||||||
fun_env := fun_env(),
|
fun_env := fun_env(),
|
||||||
con_env := con_env(),
|
con_env := con_env(),
|
||||||
child_con_env := child_con_env(),
|
child_con_env := child_con_env(),
|
||||||
event_type => aeso_syntax:typedef(),
|
event_type => aeso_syntax:typedef(),
|
||||||
builtins := builtins(),
|
builtins := builtins(),
|
||||||
options := [option()],
|
options := [option()],
|
||||||
state_layout => state_layout(),
|
state_layout => state_layout(),
|
||||||
context => context(),
|
context => context(),
|
||||||
vars => [var_name()],
|
vars => [var_name()],
|
||||||
functions := #{ fun_name() => fun_def() }
|
functions := #{ fun_name() => fun_def() },
|
||||||
|
saved_fresh_names => #{ var_name() => var_name() }
|
||||||
}.
|
}.
|
||||||
|
|
||||||
-define(HASH_BYTES, 32).
|
-define(HASH_BYTES, 32).
|
||||||
@@ -170,7 +172,7 @@
|
|||||||
%% and produces Fate intermediate code.
|
%% and produces Fate intermediate code.
|
||||||
-spec ast_to_fcode(aeso_syntax:ast(), [option()]) -> {env(), fcode()}.
|
-spec ast_to_fcode(aeso_syntax:ast(), [option()]) -> {env(), fcode()}.
|
||||||
ast_to_fcode(Code, Options) ->
|
ast_to_fcode(Code, Options) ->
|
||||||
init_fresh_names(),
|
init_fresh_names(Options),
|
||||||
{Env1, FCode1} = to_fcode(init_env(Options), Code),
|
{Env1, FCode1} = to_fcode(init_env(Options), Code),
|
||||||
FCode2 = optimize(FCode1, Options),
|
FCode2 = optimize(FCode1, Options),
|
||||||
Env2 = Env1#{ child_con_env :=
|
Env2 = Env1#{ child_con_env :=
|
||||||
@@ -178,13 +180,18 @@ ast_to_fcode(Code, Options) ->
|
|||||||
fun (_, FC) -> optimize(FC, Options) end,
|
fun (_, FC) -> optimize(FC, Options) end,
|
||||||
maps:get(child_con_env, Env1)
|
maps:get(child_con_env, Env1)
|
||||||
)},
|
)},
|
||||||
clear_fresh_names(),
|
Env3 =
|
||||||
{Env2, FCode2}.
|
case proplists:get_value(debug_info, Options, false) of
|
||||||
|
true -> Env2#{ saved_fresh_names => get(saved_fresh_names) };
|
||||||
|
false -> Env2
|
||||||
|
end,
|
||||||
|
clear_fresh_names(Options),
|
||||||
|
{Env3, FCode2}.
|
||||||
|
|
||||||
optimize(FCode1, Options) ->
|
optimize(FCode1, Options) ->
|
||||||
Verbose = lists:member(pp_fcode, Options),
|
Verbose = lists:member(pp_fcode, Options),
|
||||||
[io:format("-- Before lambda lifting --\n~s\n\n", [format_fcode(FCode1)]) || Verbose],
|
[io:format("-- Before lambda lifting --\n~s\n\n", [format_fcode(FCode1)]) || Verbose],
|
||||||
FCode2 = optimize_fcode(FCode1),
|
FCode2 = optimize_fcode(FCode1, Options),
|
||||||
[ io:format("-- After optimization --\n~s\n\n", [format_fcode(FCode2)]) || Verbose, FCode2 /= FCode1 ],
|
[ io:format("-- After optimization --\n~s\n\n", [format_fcode(FCode2)]) || Verbose, FCode2 /= FCode1 ],
|
||||||
FCode3 = lambda_lift(FCode2),
|
FCode3 = lambda_lift(FCode2),
|
||||||
[ io:format("-- After lambda lifting --\n~s\n\n", [format_fcode(FCode3)]) || Verbose, FCode3 /= FCode2 ],
|
[ io:format("-- After lambda lifting --\n~s\n\n", [format_fcode(FCode3)]) || Verbose, FCode3 /= FCode2 ],
|
||||||
@@ -208,6 +215,12 @@ init_env(Options) ->
|
|||||||
["AENS", "ContractPt"] => #con_tag{ tag = 2, arities = [1, 1, 1, 1] },
|
["AENS", "ContractPt"] => #con_tag{ tag = 2, arities = [1, 1, 1, 1] },
|
||||||
["AENS", "ChannelPt"] => #con_tag{ tag = 3, arities = [1, 1, 1, 1] },
|
["AENS", "ChannelPt"] => #con_tag{ tag = 3, arities = [1, 1, 1, 1] },
|
||||||
["AENS", "Name"] => #con_tag{ tag = 0, arities = [3] },
|
["AENS", "Name"] => #con_tag{ tag = 0, arities = [3] },
|
||||||
|
["AENSv2", "AccountPt"] => #con_tag{ tag = 0, arities = [1, 1, 1, 1, 1] },
|
||||||
|
["AENSv2", "OraclePt"] => #con_tag{ tag = 1, arities = [1, 1, 1, 1, 1] },
|
||||||
|
["AENSv2", "ContractPt"] => #con_tag{ tag = 2, arities = [1, 1, 1, 1, 1] },
|
||||||
|
["AENSv2", "ChannelPt"] => #con_tag{ tag = 3, arities = [1, 1, 1, 1, 1] },
|
||||||
|
["AENSv2", "DataPt"] => #con_tag{ tag = 4, arities = [1, 1, 1, 1, 1] },
|
||||||
|
["AENSv2", "Name"] => #con_tag{ tag = 0, arities = [3] },
|
||||||
["Chain", "GAMetaTx"] => #con_tag{ tag = 0, arities = [2] },
|
["Chain", "GAMetaTx"] => #con_tag{ tag = 0, arities = [2] },
|
||||||
["Chain", "PayingForTx"] => #con_tag{ tag = 0, arities = [2] },
|
["Chain", "PayingForTx"] => #con_tag{ tag = 0, arities = [2] },
|
||||||
["Chain", "SpendTx"] => #con_tag{ tag = 0, arities = ChainTxArities },
|
["Chain", "SpendTx"] => #con_tag{ tag = 0, arities = ChainTxArities },
|
||||||
@@ -239,8 +252,11 @@ init_env(Options) ->
|
|||||||
|
|
||||||
-spec builtins() -> builtins().
|
-spec builtins() -> builtins().
|
||||||
builtins() ->
|
builtins() ->
|
||||||
MkName = fun(NS, Fun) ->
|
MkName = fun
|
||||||
list_to_atom(string:to_lower(string:join(NS ++ [Fun], "_")))
|
(["AENSv2"], Fun) ->
|
||||||
|
list_to_atom(string:to_lower("AENS_" ++ Fun));
|
||||||
|
(NS, Fun) ->
|
||||||
|
list_to_atom(string:to_lower(string:join(NS ++ [Fun], "_")))
|
||||||
end,
|
end,
|
||||||
Scopes = [{[], [{"abort", 1}, {"require", 2}, {"exit", 1}]},
|
Scopes = [{[], [{"abort", 1}, {"require", 2}, {"exit", 1}]},
|
||||||
{["Chain"], [{"spend", 2}, {"balance", 1}, {"block_hash", 1}, {"coinbase", none},
|
{["Chain"], [{"spend", 2}, {"balance", 1}, {"block_hash", 1}, {"coinbase", none},
|
||||||
@@ -252,13 +268,13 @@ builtins() ->
|
|||||||
{["Oracle"], [{"register", 4}, {"expiry", 1}, {"query_fee", 1}, {"query", 5}, {"get_question", 2},
|
{["Oracle"], [{"register", 4}, {"expiry", 1}, {"query_fee", 1}, {"query", 5}, {"get_question", 2},
|
||||||
{"respond", 4}, {"extend", 3}, {"get_answer", 2},
|
{"respond", 4}, {"extend", 3}, {"get_answer", 2},
|
||||||
{"check", 1}, {"check_query", 2}]},
|
{"check", 1}, {"check_query", 2}]},
|
||||||
{["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4},
|
{["AENSv2"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4},
|
||||||
{"revoke", 3}, {"update", 6}, {"lookup", 1}]},
|
{"revoke", 3}, {"update", 6}, {"lookup", 1}]},
|
||||||
{["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2},
|
{["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2},
|
||||||
{"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]},
|
{"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]},
|
||||||
{["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3},
|
{["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3},
|
||||||
{"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2},
|
{"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2},
|
||||||
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
|
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}, {"poseidon", 2}]},
|
||||||
{["MCL_BLS12_381"], [{"g1_neg", 1}, {"g1_norm", 1}, {"g1_valid", 1}, {"g1_is_zero", 1}, {"g1_add", 2}, {"g1_mul", 2},
|
{["MCL_BLS12_381"], [{"g1_neg", 1}, {"g1_norm", 1}, {"g1_valid", 1}, {"g1_is_zero", 1}, {"g1_add", 2}, {"g1_mul", 2},
|
||||||
{"g2_neg", 1}, {"g2_norm", 1}, {"g2_valid", 1}, {"g2_is_zero", 1}, {"g2_add", 2}, {"g2_mul", 2},
|
{"g2_neg", 1}, {"g2_norm", 1}, {"g2_valid", 1}, {"g2_is_zero", 1}, {"g2_add", 2}, {"g2_mul", 2},
|
||||||
{"gt_inv", 1}, {"gt_add", 2}, {"gt_mul", 2}, {"gt_pow", 2}, {"gt_is_one", 1},
|
{"gt_inv", 1}, {"gt_add", 2}, {"gt_mul", 2}, {"gt_pow", 2}, {"gt_is_one", 1},
|
||||||
@@ -271,8 +287,9 @@ builtins() ->
|
|||||||
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
|
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
|
||||||
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
|
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
|
||||||
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]},
|
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]},
|
||||||
{["Int"], [{"to_str", 1}]},
|
{["Int"], [{"to_str", 1}, {"mulmod", 2}]},
|
||||||
{["Address"], [{"to_str", 1}, {"to_contract", 1}, {"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]}
|
{["Address"], [{"to_str", 1}, {"to_bytes", 1}, {"to_contract", 1},
|
||||||
|
{"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]}
|
||||||
],
|
],
|
||||||
maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}}
|
maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}}
|
||||||
|| {NS, Funs} <- Scopes,
|
|| {NS, Funs} <- Scopes,
|
||||||
@@ -307,11 +324,13 @@ init_type_env() ->
|
|||||||
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}),
|
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}),
|
||||||
["AENS", "pointee"] => ?type({variant, [[address], [address], [address], [address]]}),
|
["AENS", "pointee"] => ?type({variant, [[address], [address], [address], [address]]}),
|
||||||
["AENS", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address]]}}]]}),
|
["AENS", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address]]}}]]}),
|
||||||
|
["AENSv2", "pointee"] => ?type({variant, [[address], [address], [address], [address], [string]]}),
|
||||||
|
["AENSv2", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address], [string]]}}]]}),
|
||||||
["Chain", "ga_meta_tx"] => ?type({variant, [[address, integer]]}),
|
["Chain", "ga_meta_tx"] => ?type({variant, [[address, integer]]}),
|
||||||
["Chain", "paying_for_tx"] => ?type({variant, [[address, integer]]}),
|
["Chain", "paying_for_tx"] => ?type({variant, [[address, integer]]}),
|
||||||
["Chain", "base_tx"] => ?type(BaseTx),
|
["Chain", "base_tx"] => ?type(BaseTx),
|
||||||
["MCL_BLS12_381", "fr"] => ?type({bytes, 32}),
|
["MCL_BLS12_381", "fr"] => ?type({bytes, 32}),
|
||||||
["MCL_BLS12_381", "fp"] => ?type({bytes, 48})
|
["MCL_BLS12_381", "fp"] => ?type({bytes, 48})
|
||||||
}.
|
}.
|
||||||
|
|
||||||
is_no_code(Env) ->
|
is_no_code(Env) ->
|
||||||
@@ -688,12 +707,13 @@ expr_to_fcode(Env, Type, {app, Ann, {Op, _}, [A, B]}) when is_atom(Op) ->
|
|||||||
end;
|
end;
|
||||||
expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) ->
|
expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) ->
|
||||||
case Op of
|
case Op of
|
||||||
'-' -> {op, '-', [{lit, {int, 0}}, expr_to_fcode(Env, A)]};
|
'-' -> {op, '-', [{lit, {int, 0}}, expr_to_fcode(Env, A)]};
|
||||||
'!' -> {op, '!', [expr_to_fcode(Env, A)]}
|
'bnot' -> {op, 'bnot', [expr_to_fcode(Env, A)]};
|
||||||
|
'!' -> {op, '!', [expr_to_fcode(Env, A)]}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
%% Function calls
|
%% Function calls
|
||||||
expr_to_fcode(Env, Type, {app, _, Fun = {typed, _, FunE, {fun_t, _, NamedArgsT, ArgsT, _}}, Args}) ->
|
expr_to_fcode(Env, _, {app, _, Fun = {typed, _, FunE, {fun_t, _, NamedArgsT, ArgsT, Type}}, Args}) ->
|
||||||
Args1 = get_named_args(NamedArgsT, Args),
|
Args1 = get_named_args(NamedArgsT, Args),
|
||||||
FArgs = [expr_to_fcode(Env, Arg) || Arg <- Args1],
|
FArgs = [expr_to_fcode(Env, Arg) || Arg <- Args1],
|
||||||
case expr_to_fcode(Env, Fun) of
|
case expr_to_fcode(Env, Fun) of
|
||||||
@@ -1080,9 +1100,9 @@ op_builtins() ->
|
|||||||
stringinternal_sha3, stringinternal_sha256, stringinternal_blake2b,
|
stringinternal_sha3, stringinternal_sha256, stringinternal_blake2b,
|
||||||
char_to_int, char_from_int, stringinternal_to_lower, stringinternal_to_upper,
|
char_to_int, char_from_int, stringinternal_to_lower, stringinternal_to_upper,
|
||||||
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
|
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
|
||||||
bits_difference, int_to_str, address_to_str, crypto_verify_sig,
|
bits_difference, int_to_str, int_mulmod, address_to_str, address_to_bytes, crypto_verify_sig,
|
||||||
address_to_contract,
|
address_to_contract,
|
||||||
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b,
|
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b, crypto_poseidon,
|
||||||
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1,
|
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1,
|
||||||
mcl_bls12_381_g1_neg, mcl_bls12_381_g1_norm, mcl_bls12_381_g1_valid,
|
mcl_bls12_381_g1_neg, mcl_bls12_381_g1_norm, mcl_bls12_381_g1_valid,
|
||||||
mcl_bls12_381_g1_is_zero, mcl_bls12_381_g1_add, mcl_bls12_381_g1_mul,
|
mcl_bls12_381_g1_is_zero, mcl_bls12_381_g1_add, mcl_bls12_381_g1_mul,
|
||||||
@@ -1287,20 +1307,28 @@ lambda_lift_exprs(Layout, As) -> [lambda_lift_expr(Layout, A) || A <- As].
|
|||||||
%% - Constant propagation
|
%% - Constant propagation
|
||||||
%% - Inlining
|
%% - Inlining
|
||||||
|
|
||||||
-spec optimize_fcode(fcode()) -> fcode().
|
-spec optimize_fcode(fcode(), [option()]) -> fcode().
|
||||||
optimize_fcode(Code = #{ functions := Funs }) ->
|
optimize_fcode(Code = #{ functions := Funs }, Options) ->
|
||||||
Code1 = Code#{ functions := maps:map(fun(Name, Def) -> optimize_fun(Code, Name, Def) end, Funs) },
|
Code1 = Code#{ functions := maps:map(fun(Name, Def) -> optimize_fun(Code, Name, Def, Options) end, Funs) },
|
||||||
eliminate_dead_code(Code1).
|
eliminate_dead_code(Code1).
|
||||||
|
|
||||||
-spec optimize_fun(fcode(), fun_name(), fun_def()) -> fun_def().
|
-spec optimize_fun(fcode(), fun_name(), fun_def(), [option()]) -> fun_def().
|
||||||
optimize_fun(Fcode, Fun, Def = #{ body := Body }) ->
|
optimize_fun(Fcode, Fun, Def = #{ body := Body0 }, Options) ->
|
||||||
%% io:format("Optimizing ~p =\n~s\n", [_Fun, prettypr:format(pp_fexpr(_Body))]),
|
Inliner = proplists:get_value(optimize_inliner, Options, true),
|
||||||
Def#{ body := drop_unused_lets(
|
InlineLocalFunctions = proplists:get_value(optimize_inline_local_functions, Options, true),
|
||||||
simplifier(
|
BindSubexpressions = proplists:get_value(optimize_bind_subexpressions, Options, true),
|
||||||
let_floating(
|
LetFloating = proplists:get_value(optimize_let_floating, Options, true),
|
||||||
bind_subexpressions(
|
Simplifier = proplists:get_value(optimize_simplifier, Options, true),
|
||||||
inline_local_functions(
|
DropUnusedLets = proplists:get_value(optimize_drop_unused_lets, Options, true),
|
||||||
inliner(Fcode, Fun, Body)))))) }.
|
|
||||||
|
Body1 = if Inliner -> inliner (Fcode, Fun, Body0); true -> Body0 end,
|
||||||
|
Body2 = if InlineLocalFunctions -> inline_local_functions(Body1); true -> Body1 end,
|
||||||
|
Body3 = if BindSubexpressions -> bind_subexpressions (Body2); true -> Body2 end,
|
||||||
|
Body4 = if LetFloating -> let_floating (Body3); true -> Body3 end,
|
||||||
|
Body5 = if Simplifier -> simplifier (Body4); true -> Body4 end,
|
||||||
|
Body6 = if DropUnusedLets -> drop_unused_lets (Body5); true -> Body5 end,
|
||||||
|
|
||||||
|
Def#{ body := Body6 }.
|
||||||
|
|
||||||
%% --- Inlining ---
|
%% --- Inlining ---
|
||||||
|
|
||||||
@@ -1720,12 +1748,29 @@ resolve_fun(#{ fun_env := Funs, builtins := Builtin } = Env, Q) ->
|
|||||||
{{Fun, Ar}, _} -> {def_u, Fun, Ar}
|
{{Fun, Ar}, _} -> {def_u, Fun, Ar}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
init_fresh_names() ->
|
init_fresh_names(Options) ->
|
||||||
|
proplists:get_value(debug_info, Options, false) andalso init_saved_fresh_names(),
|
||||||
put('%fresh', 0).
|
put('%fresh', 0).
|
||||||
|
|
||||||
clear_fresh_names() ->
|
clear_fresh_names(Options) ->
|
||||||
|
proplists:get_value(debug_info, Options, false) andalso clear_saved_fresh_names(),
|
||||||
erase('%fresh').
|
erase('%fresh').
|
||||||
|
|
||||||
|
init_saved_fresh_names() ->
|
||||||
|
put(saved_fresh_names, #{}).
|
||||||
|
|
||||||
|
clear_saved_fresh_names() ->
|
||||||
|
erase(saved_fresh_names).
|
||||||
|
|
||||||
|
-spec fresh_name_save(string()) -> var_name().
|
||||||
|
fresh_name_save(Name) ->
|
||||||
|
Fresh = fresh_name(),
|
||||||
|
case get(saved_fresh_names) of
|
||||||
|
undefined -> ok;
|
||||||
|
Old -> put(saved_fresh_names, Old#{Fresh => Name})
|
||||||
|
end,
|
||||||
|
Fresh.
|
||||||
|
|
||||||
-spec fresh_name() -> var_name().
|
-spec fresh_name() -> var_name().
|
||||||
fresh_name() -> fresh_name("%").
|
fresh_name() -> fresh_name("%").
|
||||||
|
|
||||||
@@ -1854,7 +1899,7 @@ bottom_up(F, Env, Expr) ->
|
|||||||
(_) -> true end,
|
(_) -> true end,
|
||||||
case ShouldFreshen(X) of
|
case ShouldFreshen(X) of
|
||||||
true ->
|
true ->
|
||||||
Z = fresh_name(),
|
Z = fresh_name_save(X),
|
||||||
Env1 = Env#{ Z => E1 },
|
Env1 = Env#{ Z => E1 },
|
||||||
{'let', Z, E1, bottom_up(F, Env1, rename([{X, Z}], Body))};
|
{'let', Z, E1, bottom_up(F, Env1, rename([{X, Z}], Body))};
|
||||||
false ->
|
false ->
|
||||||
|
|||||||
+12
-4
@@ -112,10 +112,12 @@ from_string(ContractString, Options) ->
|
|||||||
|
|
||||||
from_string1(ContractString, Options) ->
|
from_string1(ContractString, Options) ->
|
||||||
#{ fcode := FCode
|
#{ fcode := FCode
|
||||||
, fcode_env := #{child_con_env := ChildContracts}
|
, fcode_env := FCodeEnv
|
||||||
, folded_typed_ast := FoldedTypedAst
|
, folded_typed_ast := FoldedTypedAst
|
||||||
, warnings := Warnings } = string_to_code(ContractString, Options),
|
, warnings := Warnings } = string_to_code(ContractString, Options),
|
||||||
FateCode = aeso_fcode_to_fate:compile(ChildContracts, FCode, Options),
|
#{ child_con_env := ChildContracts } = FCodeEnv,
|
||||||
|
SavedFreshNames = maps:get(saved_fresh_names, FCodeEnv, #{}),
|
||||||
|
{FateCode, VarsRegs} = aeso_fcode_to_fate:compile(ChildContracts, FCode, SavedFreshNames, Options),
|
||||||
pp_assembler(FateCode, Options),
|
pp_assembler(FateCode, Options),
|
||||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||||
{ok, Version} = version(),
|
{ok, Version} = version(),
|
||||||
@@ -128,7 +130,13 @@ from_string1(ContractString, Options) ->
|
|||||||
payable => maps:get(payable, FCode),
|
payable => maps:get(payable, FCode),
|
||||||
warnings => Warnings
|
warnings => Warnings
|
||||||
},
|
},
|
||||||
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)}.
|
ResDbg = Res#{variables_registers => VarsRegs},
|
||||||
|
FinalRes =
|
||||||
|
case proplists:get_value(debug_info, Options, false) of
|
||||||
|
true -> ResDbg;
|
||||||
|
false -> Res
|
||||||
|
end,
|
||||||
|
{ok, maybe_generate_aci(FinalRes, FoldedTypedAst, Options)}.
|
||||||
|
|
||||||
maybe_generate_aci(Result, FoldedTypedAst, Options) ->
|
maybe_generate_aci(Result, FoldedTypedAst, Options) ->
|
||||||
case proplists:get_value(aci, Options) of
|
case proplists:get_value(aci, Options) of
|
||||||
@@ -185,7 +193,7 @@ check_call1(ContractString0, FunName, Args, Options) ->
|
|||||||
#{fcode := OrgFcode
|
#{fcode := OrgFcode
|
||||||
, fcode_env := #{child_con_env := ChildContracts}
|
, fcode_env := #{child_con_env := ChildContracts}
|
||||||
, ast := Ast} = string_to_code(ContractString0, Options),
|
, ast := Ast} = string_to_code(ContractString0, Options),
|
||||||
FateCode = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, []),
|
{FateCode, _} = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []),
|
||||||
%% collect all hashes and compute the first name without hash collision to
|
%% collect all hashes and compute the first name without hash collision to
|
||||||
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
||||||
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
||||||
|
|||||||
+141
-75
@@ -9,7 +9,7 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_fcode_to_fate).
|
-module(aeso_fcode_to_fate).
|
||||||
|
|
||||||
-export([compile/2, compile/3, term_to_fate/1, term_to_fate/2]).
|
-export([compile/3, compile/4, term_to_fate/1, term_to_fate/2]).
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
-export([optimize_fun/4, to_basic_blocks/1]).
|
-export([optimize_fun/4, to_basic_blocks/1]).
|
||||||
@@ -45,7 +45,14 @@
|
|||||||
-define(s(N), {store, N}).
|
-define(s(N), {store, N}).
|
||||||
-define(void, {var, 9999}).
|
-define(void, {var, 9999}).
|
||||||
|
|
||||||
-record(env, { contract, vars = [], locals = [], current_function, tailpos = true, child_contracts = #{}, options = []}).
|
-record(env, { contract,
|
||||||
|
vars = [],
|
||||||
|
locals = [],
|
||||||
|
current_function,
|
||||||
|
tailpos = true,
|
||||||
|
child_contracts = #{},
|
||||||
|
saved_fresh_names = #{},
|
||||||
|
options = [] }).
|
||||||
|
|
||||||
%% -- Debugging --------------------------------------------------------------
|
%% -- Debugging --------------------------------------------------------------
|
||||||
|
|
||||||
@@ -71,16 +78,27 @@ code_error(Err) ->
|
|||||||
%% -- Main -------------------------------------------------------------------
|
%% -- Main -------------------------------------------------------------------
|
||||||
|
|
||||||
%% @doc Main entry point.
|
%% @doc Main entry point.
|
||||||
compile(FCode, Options) ->
|
compile(FCode, SavedFreshNames, Options) ->
|
||||||
compile(#{}, FCode, Options).
|
compile(#{}, FCode, SavedFreshNames, Options).
|
||||||
compile(ChildContracts, FCode, Options) ->
|
compile(ChildContracts, FCode, SavedFreshNames, Options) ->
|
||||||
|
try
|
||||||
|
compile1(ChildContracts, FCode, SavedFreshNames, Options)
|
||||||
|
after
|
||||||
|
put(variables_registers, undefined)
|
||||||
|
end.
|
||||||
|
|
||||||
|
compile1(ChildContracts, FCode, SavedFreshNames, Options) ->
|
||||||
#{ contract_name := ContractName,
|
#{ contract_name := ContractName,
|
||||||
functions := Functions } = FCode,
|
functions := Functions } = FCode,
|
||||||
SFuns = functions_to_scode(ChildContracts, ContractName, Functions, Options),
|
SFuns = functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options),
|
||||||
SFuns1 = optimize_scode(SFuns, Options),
|
SFuns1 = optimize_scode(SFuns, Options),
|
||||||
FateCode = to_basic_blocks(SFuns1),
|
FateCode = to_basic_blocks(SFuns1),
|
||||||
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
|
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
|
||||||
FateCode.
|
FateCode1 = case proplists:get_value(include_child_contract_symbols, Options, false) of
|
||||||
|
false -> FateCode;
|
||||||
|
true -> add_child_symbols(ChildContracts, FateCode)
|
||||||
|
end,
|
||||||
|
{FateCode1, get_variables_registers()}.
|
||||||
|
|
||||||
make_function_id(X) ->
|
make_function_id(X) ->
|
||||||
aeb_fate_code:symbol_identifier(make_function_name(X)).
|
aeb_fate_code:symbol_identifier(make_function_name(X)).
|
||||||
@@ -89,22 +107,48 @@ make_function_name(event) -> <<"Chain.event">>;
|
|||||||
make_function_name({entrypoint, Name}) -> Name;
|
make_function_name({entrypoint, Name}) -> Name;
|
||||||
make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")).
|
make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")).
|
||||||
|
|
||||||
functions_to_scode(ChildContracts, ContractName, Functions, Options) ->
|
add_child_symbols(ChildContracts, FateCode) ->
|
||||||
|
Funs = lists:flatten([ maps:keys(ChildFuns) || {_, #{functions := ChildFuns}} <- maps:to_list(ChildContracts) ]),
|
||||||
|
Symbols = maps:from_list([ {make_function_id(FName), make_function_name(FName)} || FName <- Funs ]),
|
||||||
|
aeb_fate_code:update_symbols(FateCode, Symbols).
|
||||||
|
|
||||||
|
functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options) ->
|
||||||
FunNames = maps:keys(Functions),
|
FunNames = maps:keys(Functions),
|
||||||
maps:from_list(
|
maps:from_list(
|
||||||
[ {make_function_name(Name), function_to_scode(ChildContracts, ContractName, FunNames, Name, Attrs, Args, Body, Type, Options)}
|
[ {make_function_name(Name), function_to_scode(ChildContracts, ContractName, FunNames, Name, Attrs, Args, Body, Type, SavedFreshNames, Options)}
|
||||||
|| {Name, #{args := Args,
|
|| {Name, #{args := Args,
|
||||||
body := Body,
|
body := Body,
|
||||||
attrs := Attrs,
|
attrs := Attrs,
|
||||||
return := Type}} <- maps:to_list(Functions)]).
|
return := Type}} <- maps:to_list(Functions)]).
|
||||||
|
|
||||||
function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, Options) ->
|
function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, SavedFreshNames, Options) ->
|
||||||
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
|
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
|
||||||
Attrs = Attrs0 -- [stateful], %% Only track private and payable from here.
|
Attrs = Attrs0 -- [stateful], %% Only track private and payable from here.
|
||||||
Env = init_env(ChildContracts, ContractName, Functions, Name, Args, Options),
|
Env = init_env(ChildContracts, ContractName, Functions, Name, Args, SavedFreshNames, Options),
|
||||||
|
[ add_variables_register(Env, Arg, Register) ||
|
||||||
|
proplists:get_value(debug_info, Options, false),
|
||||||
|
{Arg, Register} <- Env#env.vars ],
|
||||||
SCode = to_scode(Env, Body),
|
SCode = to_scode(Env, Body),
|
||||||
{Attrs, {ArgTypes, ResType1}, SCode}.
|
{Attrs, {ArgTypes, ResType1}, SCode}.
|
||||||
|
|
||||||
|
get_variables_registers() ->
|
||||||
|
case get(variables_registers) of
|
||||||
|
undefined -> #{};
|
||||||
|
Vs -> Vs
|
||||||
|
end.
|
||||||
|
|
||||||
|
add_variables_register(Env = #env{saved_fresh_names = SavedFreshNames}, Name, Register) ->
|
||||||
|
Olds = get_variables_registers(),
|
||||||
|
RealName = maps:get(Name, SavedFreshNames, Name),
|
||||||
|
FunName =
|
||||||
|
case Env#env.current_function of
|
||||||
|
event -> "Chain.event";
|
||||||
|
{entrypoint, BinName} -> binary_to_list(BinName);
|
||||||
|
{local_fun, QualName} -> lists:last(QualName)
|
||||||
|
end,
|
||||||
|
New = {Env#env.contract, FunName, RealName},
|
||||||
|
put(variables_registers, Olds#{New => Register}).
|
||||||
|
|
||||||
-define(tvars, '$tvars').
|
-define(tvars, '$tvars').
|
||||||
|
|
||||||
typesig_to_scode(Args, Res) ->
|
typesig_to_scode(Args, Res) ->
|
||||||
@@ -149,19 +193,21 @@ types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts).
|
|||||||
|
|
||||||
%% -- Environment functions --
|
%% -- Environment functions --
|
||||||
|
|
||||||
init_env(ChildContracts, ContractName, FunNames, Name, Args, Options) ->
|
init_env(ChildContracts, ContractName, FunNames, Name, Args, SavedFreshNames, Options) ->
|
||||||
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
|
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
|
||||||
contract = ContractName,
|
contract = ContractName,
|
||||||
child_contracts = ChildContracts,
|
child_contracts = ChildContracts,
|
||||||
locals = FunNames,
|
locals = FunNames,
|
||||||
current_function = Name,
|
current_function = Name,
|
||||||
options = Options,
|
options = Options,
|
||||||
tailpos = true }.
|
tailpos = true,
|
||||||
|
saved_fresh_names = SavedFreshNames }.
|
||||||
|
|
||||||
next_var(#env{ vars = Vars }) ->
|
next_var(#env{ vars = Vars }) ->
|
||||||
1 + lists:max([-1 | [J || {_, {var, J}} <- Vars]]).
|
1 + lists:max([-1 | [J || {_, {var, J}} <- Vars]]).
|
||||||
|
|
||||||
bind_var(Name, Var, Env = #env{ vars = Vars }) ->
|
bind_var(Name, Var, Env = #env{ vars = Vars }) ->
|
||||||
|
proplists:get_value(debug_info, Env#env.options, false) andalso add_variables_register(Env, Name, Var),
|
||||||
Env#env{ vars = [{Name, Var} | Vars] }.
|
Env#env{ vars = [{Name, Var} | Vars] }.
|
||||||
|
|
||||||
bind_local(Name, Env) ->
|
bind_local(Name, Env) ->
|
||||||
@@ -185,9 +231,10 @@ serialize_contract_code(Env, C) ->
|
|||||||
end,
|
end,
|
||||||
case maps:get(C, Cache, none) of
|
case maps:get(C, Cache, none) of
|
||||||
none ->
|
none ->
|
||||||
Options = Env#env.options,
|
Options = Env#env.options,
|
||||||
FCode = maps:get(C, Env#env.child_contracts),
|
SavedFreshNames = Env#env.saved_fresh_names,
|
||||||
FateCode = compile(Env#env.child_contracts, FCode, Options),
|
FCode = maps:get(C, Env#env.child_contracts),
|
||||||
|
{FateCode, _} = compile1(Env#env.child_contracts, FCode, SavedFreshNames, Options),
|
||||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||||
{ok, Version} = aeso_compiler:version(),
|
{ok, Version} = aeso_compiler:version(),
|
||||||
OriginalSourceCode = proplists:get_value(original_src, Options, ""),
|
OriginalSourceCode = proplists:get_value(original_src, Options, ""),
|
||||||
@@ -635,6 +682,12 @@ op_to_scode('>=') -> aeb_fate_ops:egt(?a, ?a, ?a);
|
|||||||
op_to_scode('==') -> aeb_fate_ops:eq(?a, ?a, ?a);
|
op_to_scode('==') -> aeb_fate_ops:eq(?a, ?a, ?a);
|
||||||
op_to_scode('!=') -> aeb_fate_ops:neq(?a, ?a, ?a);
|
op_to_scode('!=') -> aeb_fate_ops:neq(?a, ?a, ?a);
|
||||||
op_to_scode('!') -> aeb_fate_ops:not_op(?a, ?a);
|
op_to_scode('!') -> aeb_fate_ops:not_op(?a, ?a);
|
||||||
|
op_to_scode('bnot') -> aeb_fate_ops:bin_not(?a, ?a);
|
||||||
|
op_to_scode('band') -> aeb_fate_ops:bin_and(?a, ?a, ?a);
|
||||||
|
op_to_scode('bor') -> aeb_fate_ops:bin_or(?a, ?a, ?a);
|
||||||
|
op_to_scode('bxor') -> aeb_fate_ops:bin_xor(?a, ?a, ?a);
|
||||||
|
op_to_scode('<<') -> aeb_fate_ops:bin_sl(?a, ?a, ?a);
|
||||||
|
op_to_scode('>>') -> aeb_fate_ops:bin_sr(?a, ?a, ?a);
|
||||||
op_to_scode(map_get) -> aeb_fate_ops:map_lookup(?a, ?a, ?a);
|
op_to_scode(map_get) -> aeb_fate_ops:map_lookup(?a, ?a, ?a);
|
||||||
op_to_scode(map_get_d) -> aeb_fate_ops:map_lookup(?a, ?a, ?a, ?a);
|
op_to_scode(map_get_d) -> aeb_fate_ops:map_lookup(?a, ?a, ?a, ?a);
|
||||||
op_to_scode(map_set) -> aeb_fate_ops:map_update(?a, ?a, ?a, ?a);
|
op_to_scode(map_set) -> aeb_fate_ops:map_update(?a, ?a, ?a, ?a);
|
||||||
@@ -659,7 +712,9 @@ op_to_scode(bits_intersection) -> aeb_fate_ops:bits_and(?a, ?a, ?a);
|
|||||||
op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a);
|
op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a);
|
||||||
op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
|
op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
|
||||||
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
|
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
|
||||||
|
op_to_scode(address_to_bytes) -> aeb_fate_ops:addr_to_bytes(?a, ?a);
|
||||||
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
|
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
|
||||||
|
op_to_scode(int_mulmod) -> aeb_fate_ops:mulmod(?a, ?a, ?a, ?a);
|
||||||
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
|
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
|
||||||
op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a);
|
op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a);
|
||||||
op_to_scode(crypto_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a);
|
op_to_scode(crypto_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a);
|
||||||
@@ -669,6 +724,7 @@ op_to_scode(crypto_ecrecover_secp256k1) -> aeb_fate_ops:ecrecover_secp256k1(?a,
|
|||||||
op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
||||||
op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
||||||
op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
||||||
|
op_to_scode(crypto_poseidon) -> aeb_fate_ops:poseidon(?a, ?a, ?a);
|
||||||
op_to_scode(stringinternal_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
op_to_scode(stringinternal_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
||||||
op_to_scode(stringinternal_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
op_to_scode(stringinternal_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
||||||
op_to_scode(stringinternal_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
op_to_scode(stringinternal_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
||||||
@@ -709,7 +765,7 @@ tuple(N) -> aeb_fate_ops:tuple(?a, N).
|
|||||||
|
|
||||||
optimize_scode(Funs, Options) ->
|
optimize_scode(Funs, Options) ->
|
||||||
maps:map(fun(Name, Def) -> optimize_fun(Funs, Name, Def, Options) end,
|
maps:map(fun(Name, Def) -> optimize_fun(Funs, Name, Def, Options) end,
|
||||||
Funs).
|
Funs).
|
||||||
|
|
||||||
flatten(missing) -> missing;
|
flatten(missing) -> missing;
|
||||||
flatten(Code) -> lists:map(fun flatten_s/1, lists:flatten(Code)).
|
flatten(Code) -> lists:map(fun flatten_s/1, lists:flatten(Code)).
|
||||||
@@ -866,6 +922,13 @@ attributes(I) ->
|
|||||||
{'DIV', A, B, C} -> Pure(A, [B, C]);
|
{'DIV', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'MOD', A, B, C} -> Pure(A, [B, C]);
|
{'MOD', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'POW', A, B, C} -> Pure(A, [B, C]);
|
{'POW', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'MULMOD', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||||
|
{'BAND', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BOR', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BXOR', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BNOT', A, B} -> Pure(A, [B]);
|
||||||
|
{'BSL', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BSR', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'LT', A, B, C} -> Pure(A, [B, C]);
|
{'LT', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'GT', A, B, C} -> Pure(A, [B, C]);
|
{'GT', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'EQ', A, B, C} -> Pure(A, [B, C]);
|
{'EQ', A, B, C} -> Pure(A, [B, C]);
|
||||||
@@ -918,12 +981,14 @@ attributes(I) ->
|
|||||||
{'SHA3', A, B} -> Pure(A, [B]);
|
{'SHA3', A, B} -> Pure(A, [B]);
|
||||||
{'SHA256', A, B} -> Pure(A, [B]);
|
{'SHA256', A, B} -> Pure(A, [B]);
|
||||||
{'BLAKE2B', A, B} -> Pure(A, [B]);
|
{'BLAKE2B', A, B} -> Pure(A, [B]);
|
||||||
|
{'POSEIDON', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'VERIFY_SIG', A, B, C, D} -> Pure(A, [B, C, D]);
|
{'VERIFY_SIG', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||||
{'VERIFY_SIG_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
|
{'VERIFY_SIG_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||||
{'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
|
{'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||||
{'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]);
|
{'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
|
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
|
||||||
{'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]);
|
{'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]);
|
||||||
|
{'ADDRESS_TO_BYTES', A, B} -> Pure(A, [B]);
|
||||||
{'AUTH_TX_HASH', A} -> Pure(A, []);
|
{'AUTH_TX_HASH', A} -> Pure(A, []);
|
||||||
{'AUTH_TX', A} -> Pure(A, []);
|
{'AUTH_TX', A} -> Pure(A, []);
|
||||||
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
|
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
|
||||||
@@ -1092,7 +1157,8 @@ simpl_top(I, Code, Options) ->
|
|||||||
simpl_top(0, I, Code, _Options) ->
|
simpl_top(0, I, Code, _Options) ->
|
||||||
code_error({optimizer_out_of_fuel, I, Code});
|
code_error({optimizer_out_of_fuel, I, Code});
|
||||||
simpl_top(Fuel, I, Code, Options) ->
|
simpl_top(Fuel, I, Code, Options) ->
|
||||||
apply_rules(Fuel, rules(), I, Code, Options).
|
Rules = [R || R = {Rule, _} <- rules(), proplists:get_value(Rule, Options, true)],
|
||||||
|
apply_rules(Fuel, Rules, I, Code, Options).
|
||||||
|
|
||||||
apply_rules(Fuel, Rules, I, Code, Options) ->
|
apply_rules(Fuel, Rules, I, Code, Options) ->
|
||||||
Cons = fun(X, Xs) -> simpl_top(Fuel - 1, X, Xs, Options) end,
|
Cons = fun(X, Xs) -> simpl_top(Fuel - 1, X, Xs, Options) end,
|
||||||
@@ -1119,29 +1185,29 @@ apply_rules_once([{RName, Rule} | Rules], I, Code) ->
|
|||||||
-define(RULE(Name), {Name, fun Name/2}).
|
-define(RULE(Name), {Name, fun Name/2}).
|
||||||
|
|
||||||
merge_rules() ->
|
merge_rules() ->
|
||||||
[?RULE(r_push_consume),
|
[?RULE(optimize_push_consume),
|
||||||
?RULE(r_one_shot_var),
|
?RULE(optimize_one_shot_var),
|
||||||
?RULE(r_write_to_dead_var),
|
?RULE(optimize_write_to_dead_var),
|
||||||
?RULE(r_inline_switch_target)
|
?RULE(optimize_inline_switch_target)
|
||||||
].
|
].
|
||||||
|
|
||||||
rules() ->
|
rules() ->
|
||||||
merge_rules() ++
|
merge_rules() ++
|
||||||
[?RULE(r_swap_push),
|
[?RULE(optimize_swap_push),
|
||||||
?RULE(r_swap_pop),
|
?RULE(optimize_swap_pop),
|
||||||
?RULE(r_swap_write),
|
?RULE(optimize_swap_write),
|
||||||
?RULE(r_constant_propagation),
|
?RULE(optimize_constant_propagation),
|
||||||
?RULE(r_prune_impossible_branches),
|
?RULE(optimize_prune_impossible_branches),
|
||||||
?RULE(r_single_successful_branch),
|
?RULE(optimize_single_successful_branch),
|
||||||
?RULE(r_inline_store),
|
?RULE(optimize_inline_store),
|
||||||
?RULE(r_float_switch_body)
|
?RULE(optimize_float_switch_body)
|
||||||
].
|
].
|
||||||
|
|
||||||
%% Removing pushes that are immediately consumed.
|
%% Removing pushes that are immediately consumed.
|
||||||
r_push_consume({i, Ann1, {'STORE', ?a, A}}, Code) ->
|
optimize_push_consume({i, Ann1, {'STORE', ?a, A}}, Code) ->
|
||||||
inline_push(Ann1, A, 0, Code, []);
|
inline_push(Ann1, A, 0, Code, []);
|
||||||
%% Writing directly to memory instead of going through the accumulator.
|
%% Writing directly to memory instead of going through the accumulator.
|
||||||
r_push_consume({i, Ann1, I}, [{i, Ann2, {'STORE', R, ?a}} | Code]) ->
|
optimize_push_consume({i, Ann1, I}, [{i, Ann2, {'STORE', R, ?a}} | Code]) ->
|
||||||
IsPush =
|
IsPush =
|
||||||
case op_view(I) of
|
case op_view(I) of
|
||||||
{_, ?a, _} -> true;
|
{_, ?a, _} -> true;
|
||||||
@@ -1153,7 +1219,7 @@ r_push_consume({i, Ann1, I}, [{i, Ann2, {'STORE', R, ?a}} | Code]) ->
|
|||||||
end,
|
end,
|
||||||
if IsPush -> {[{i, merge_ann(Ann1, Ann2), setelement(2, I, R)}], Code};
|
if IsPush -> {[{i, merge_ann(Ann1, Ann2), setelement(2, I, R)}], Code};
|
||||||
true -> false end;
|
true -> false end;
|
||||||
r_push_consume(_, _) -> false.
|
optimize_push_consume(_, _) -> false.
|
||||||
|
|
||||||
inline_push(Ann, Arg, Stack, [{i, _, switch_body} = AI | Code], Acc) ->
|
inline_push(Ann, Arg, Stack, [{i, _, switch_body} = AI | Code], Acc) ->
|
||||||
{AI1, {i, Ann1, _}} = swap_instrs({i, Ann, {'STORE', ?a, Arg}}, AI),
|
{AI1, {i, Ann1, _}} = swap_instrs({i, Ann, {'STORE', ?a, Arg}}, AI),
|
||||||
@@ -1186,7 +1252,7 @@ split_stack_arg(N, [A | As], Acc) ->
|
|||||||
split_stack_arg(N1, As, [A | Acc]).
|
split_stack_arg(N1, As, [A | Acc]).
|
||||||
|
|
||||||
%% Move PUSHes past non-stack instructions.
|
%% Move PUSHes past non-stack instructions.
|
||||||
r_swap_push(Push = {i, _, PushI}, [I | Code]) ->
|
optimize_swap_push(Push = {i, _, PushI}, [I | Code]) ->
|
||||||
case op_view(PushI) of
|
case op_view(PushI) of
|
||||||
{_, ?a, _} ->
|
{_, ?a, _} ->
|
||||||
case independent(Push, I) of
|
case independent(Push, I) of
|
||||||
@@ -1197,10 +1263,10 @@ r_swap_push(Push = {i, _, PushI}, [I | Code]) ->
|
|||||||
end;
|
end;
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
r_swap_push(_, _) -> false.
|
optimize_swap_push(_, _) -> false.
|
||||||
|
|
||||||
%% Move non-stack instruction past POPs.
|
%% Move non-stack instruction past POPs.
|
||||||
r_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
|
optimize_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
|
||||||
case independent(IA, JA) of
|
case independent(IA, JA) of
|
||||||
true ->
|
true ->
|
||||||
case {op_view(I), op_view(J)} of
|
case {op_view(I), op_view(J)} of
|
||||||
@@ -1208,7 +1274,7 @@ r_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
|
|||||||
{_, false} -> false;
|
{_, false} -> false;
|
||||||
{{_, IR, IAs}, {_, RJ, JAs}} ->
|
{{_, IR, IAs}, {_, RJ, JAs}} ->
|
||||||
NonStackI = not lists:member(?a, [IR | IAs]),
|
NonStackI = not lists:member(?a, [IR | IAs]),
|
||||||
%% RJ /= ?a to not conflict with r_swap_push
|
%% RJ /= ?a to not conflict with optimize_swap_push
|
||||||
PopJ = RJ /= ?a andalso lists:member(?a, JAs),
|
PopJ = RJ /= ?a andalso lists:member(?a, JAs),
|
||||||
case NonStackI andalso PopJ of
|
case NonStackI andalso PopJ of
|
||||||
false -> false;
|
false -> false;
|
||||||
@@ -1219,22 +1285,22 @@ r_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) ->
|
|||||||
end;
|
end;
|
||||||
false -> false
|
false -> false
|
||||||
end;
|
end;
|
||||||
r_swap_pop(_, _) -> false.
|
optimize_swap_pop(_, _) -> false.
|
||||||
|
|
||||||
%% Match up writes to variables with instructions further down.
|
%% Match up writes to variables with instructions further down.
|
||||||
r_swap_write(I = {i, _, _}, [J | Code]) ->
|
optimize_swap_write(I = {i, _, _}, [J | Code]) ->
|
||||||
case {var_writes(I), independent(I, J)} of
|
case {var_writes(I), independent(I, J)} of
|
||||||
{[_], true} ->
|
{[_], true} ->
|
||||||
{J1, I1} = swap_instrs(I, J),
|
{J1, I1} = swap_instrs(I, J),
|
||||||
r_swap_write([J1], I1, Code);
|
optimize_swap_write([J1], I1, Code);
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
r_swap_write(_, _) -> false.
|
optimize_swap_write(_, _) -> false.
|
||||||
|
|
||||||
r_swap_write(Pre, I, [{i, _, switch_body} = J | Code]) ->
|
optimize_swap_write(Pre, I, [{i, _, switch_body} = J | Code]) ->
|
||||||
{J1, I1} = swap_instrs(I, J),
|
{J1, I1} = swap_instrs(I, J),
|
||||||
r_swap_write([J1 | Pre], I1, Code);
|
optimize_swap_write([J1 | Pre], I1, Code);
|
||||||
r_swap_write(Pre, I, Code0 = [J | Code]) ->
|
optimize_swap_write(Pre, I, Code0 = [J | Code]) ->
|
||||||
case apply_rules_once(merge_rules(), I, Code0) of
|
case apply_rules_once(merge_rules(), I, Code0) of
|
||||||
{_Rule, New, Rest} ->
|
{_Rule, New, Rest} ->
|
||||||
{lists:reverse(Pre) ++ New, Rest};
|
{lists:reverse(Pre) ++ New, Rest};
|
||||||
@@ -1243,27 +1309,27 @@ r_swap_write(Pre, I, Code0 = [J | Code]) ->
|
|||||||
false -> false;
|
false -> false;
|
||||||
true ->
|
true ->
|
||||||
{J1, I1} = swap_instrs(I, J),
|
{J1, I1} = swap_instrs(I, J),
|
||||||
r_swap_write([J1 | Pre], I1, Code)
|
optimize_swap_write([J1 | Pre], I1, Code)
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_swap_write(_, _, _) -> false.
|
optimize_swap_write(_, _, _) -> false.
|
||||||
|
|
||||||
%% Precompute instructions with known values
|
%% Precompute instructions with known values
|
||||||
r_constant_propagation(Cons = {i, Ann1, {'CONS', R, X, Xs}}, [{i, Ann, {'IS_NIL', S, R}} | Code]) ->
|
optimize_constant_propagation(Cons = {i, Ann1, {'CONS', R, X, Xs}}, [{i, Ann, {'IS_NIL', S, R}} | Code]) ->
|
||||||
Store = {i, Ann, {'STORE', S, ?i(false)}},
|
Store = {i, Ann, {'STORE', S, ?i(false)}},
|
||||||
Cons1 = case R of
|
Cons1 = case R of
|
||||||
?a -> {i, Ann1, {'CONS', ?void, X, Xs}};
|
?a -> {i, Ann1, {'CONS', ?void, X, Xs}};
|
||||||
_ -> Cons
|
_ -> Cons
|
||||||
end,
|
end,
|
||||||
{[Cons1, Store], Code};
|
{[Cons1, Store], Code};
|
||||||
r_constant_propagation(Nil = {i, Ann1, {'NIL', R}}, [{i, Ann, {'IS_NIL', S, R}} | Code]) ->
|
optimize_constant_propagation(Nil = {i, Ann1, {'NIL', R}}, [{i, Ann, {'IS_NIL', S, R}} | Code]) ->
|
||||||
Store = {i, Ann, {'STORE', S, ?i(true)}},
|
Store = {i, Ann, {'STORE', S, ?i(true)}},
|
||||||
Nil1 = case R of
|
Nil1 = case R of
|
||||||
?a -> {i, Ann1, {'NIL', ?void}};
|
?a -> {i, Ann1, {'NIL', ?void}};
|
||||||
_ -> Nil
|
_ -> Nil
|
||||||
end,
|
end,
|
||||||
{[Nil1, Store], Code};
|
{[Nil1, Store], Code};
|
||||||
r_constant_propagation({i, Ann, I}, Code) ->
|
optimize_constant_propagation({i, Ann, I}, Code) ->
|
||||||
case op_view(I) of
|
case op_view(I) of
|
||||||
false -> false;
|
false -> false;
|
||||||
{Op, R, As} ->
|
{Op, R, As} ->
|
||||||
@@ -1277,7 +1343,7 @@ r_constant_propagation({i, Ann, I}, Code) ->
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_constant_propagation(_, _) -> false.
|
optimize_constant_propagation(_, _) -> false.
|
||||||
|
|
||||||
eval_op('ADD', [X, Y]) when is_integer(X), is_integer(Y) -> X + Y;
|
eval_op('ADD', [X, Y]) when is_integer(X), is_integer(Y) -> X + Y;
|
||||||
eval_op('SUB', [X, Y]) when is_integer(X), is_integer(Y) -> X - Y;
|
eval_op('SUB', [X, Y]) when is_integer(X), is_integer(Y) -> X - Y;
|
||||||
@@ -1296,12 +1362,12 @@ eval_op('NOT', [false]) -> true;
|
|||||||
eval_op(_, _) -> no_eval. %% TODO: bits?
|
eval_op(_, _) -> no_eval. %% TODO: bits?
|
||||||
|
|
||||||
%% Prune impossible branches from switches
|
%% Prune impossible branches from switches
|
||||||
r_prune_impossible_branches({switch, ?i(V), Type, Alts, missing}, Code) ->
|
optimize_prune_impossible_branches({switch, ?i(V), Type, Alts, missing}, Code) ->
|
||||||
case pick_branch(Type, V, Alts) of
|
case pick_branch(Type, V, Alts) of
|
||||||
false -> false;
|
false -> false;
|
||||||
Alt -> {Alt, Code}
|
Alt -> {Alt, Code}
|
||||||
end;
|
end;
|
||||||
r_prune_impossible_branches({switch, ?i(V), boolean, [False, True] = Alts, Def}, Code) when V == true; V == false ->
|
optimize_prune_impossible_branches({switch, ?i(V), boolean, [False, True] = Alts, Def}, Code) when V == true; V == false ->
|
||||||
Alts1 = [if V -> missing; true -> False end,
|
Alts1 = [if V -> missing; true -> False end,
|
||||||
if V -> True; true -> missing end],
|
if V -> True; true -> missing end],
|
||||||
case Alts == Alts1 of
|
case Alts == Alts1 of
|
||||||
@@ -1312,7 +1378,7 @@ r_prune_impossible_branches({switch, ?i(V), boolean, [False, True] = Alts, Def},
|
|||||||
_ -> {[{switch, ?i(V), boolean, Alts1, Def}], Code}
|
_ -> {[{switch, ?i(V), boolean, Alts1, Def}], Code}
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_prune_impossible_branches(Variant = {i, _, {'VARIANT', R, ?i(_), ?i(Tag), ?i(_)}},
|
optimize_prune_impossible_branches(Variant = {i, _, {'VARIANT', R, ?i(_), ?i(Tag), ?i(_)}},
|
||||||
[{switch, R, Type = {variant, _}, Alts, missing} | Code]) when is_integer(Tag) ->
|
[{switch, R, Type = {variant, _}, Alts, missing} | Code]) when is_integer(Tag) ->
|
||||||
case {R, lists:nth(Tag + 1, Alts)} of
|
case {R, lists:nth(Tag + 1, Alts)} of
|
||||||
{_, missing} ->
|
{_, missing} ->
|
||||||
@@ -1328,7 +1394,7 @@ r_prune_impossible_branches(Variant = {i, _, {'VARIANT', R, ?i(_), ?i(Tag), ?i(_
|
|||||||
false -> {Alt, Code}
|
false -> {Alt, Code}
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_prune_impossible_branches(_, _) -> false.
|
optimize_prune_impossible_branches(_, _) -> false.
|
||||||
|
|
||||||
pick_branch(boolean, V, [False, True]) when V == true; V == false ->
|
pick_branch(boolean, V, [False, True]) when V == true; V == false ->
|
||||||
Alt = if V -> True; true -> False end,
|
Alt = if V -> True; true -> False end,
|
||||||
@@ -1341,7 +1407,7 @@ pick_branch(_Type, _V, _Alts) ->
|
|||||||
|
|
||||||
%% If there's a single branch that doesn't abort we can push the code for that
|
%% If there's a single branch that doesn't abort we can push the code for that
|
||||||
%% out of the switch.
|
%% out of the switch.
|
||||||
r_single_successful_branch({switch, R, Type, Alts, Def}, Code) ->
|
optimize_single_successful_branch({switch, R, Type, Alts, Def}, Code) ->
|
||||||
case push_code_out_of_switch([Def | Alts]) of
|
case push_code_out_of_switch([Def | Alts]) of
|
||||||
{_, none} -> false;
|
{_, none} -> false;
|
||||||
{_, many} -> false;
|
{_, many} -> false;
|
||||||
@@ -1349,7 +1415,7 @@ r_single_successful_branch({switch, R, Type, Alts, Def}, Code) ->
|
|||||||
{[Def1 | Alts1], PushedOut} ->
|
{[Def1 | Alts1], PushedOut} ->
|
||||||
{[{switch, R, Type, Alts1, Def1} | PushedOut], Code}
|
{[{switch, R, Type, Alts1, Def1} | PushedOut], Code}
|
||||||
end;
|
end;
|
||||||
r_single_successful_branch(_, _) -> false.
|
optimize_single_successful_branch(_, _) -> false.
|
||||||
|
|
||||||
push_code_out_of_switch([]) -> {[], none};
|
push_code_out_of_switch([]) -> {[], none};
|
||||||
push_code_out_of_switch([Alt | Alts]) ->
|
push_code_out_of_switch([Alt | Alts]) ->
|
||||||
@@ -1385,7 +1451,7 @@ does_abort({switch, _, _, Alts, Def}) ->
|
|||||||
does_abort(_) -> false.
|
does_abort(_) -> false.
|
||||||
|
|
||||||
%% STORE R A, SWITCH R --> SWITCH A
|
%% STORE R A, SWITCH R --> SWITCH A
|
||||||
r_inline_switch_target({i, Ann, {'STORE', R, A}}, [{switch, R, Type, Alts, Def} | Code]) ->
|
optimize_inline_switch_target({i, Ann, {'STORE', R, A}}, [{switch, R, Type, Alts, Def} | Code]) ->
|
||||||
Ann1 =
|
Ann1 =
|
||||||
case is_reg(A) of
|
case is_reg(A) of
|
||||||
true -> Ann#{ live_out := ordsets:add_element(A, maps:get(live_out, Ann)) };
|
true -> Ann#{ live_out := ordsets:add_element(A, maps:get(live_out, Ann)) };
|
||||||
@@ -1404,18 +1470,18 @@ r_inline_switch_target({i, Ann, {'STORE', R, A}}, [{switch, R, Type, Alts, Def}
|
|||||||
end;
|
end;
|
||||||
_ -> false %% impossible
|
_ -> false %% impossible
|
||||||
end;
|
end;
|
||||||
r_inline_switch_target(_, _) -> false.
|
optimize_inline_switch_target(_, _) -> false.
|
||||||
|
|
||||||
%% Float switch-body to closest switch
|
%% Float switch-body to closest switch
|
||||||
r_float_switch_body(I = {i, _, _}, [J = {i, _, switch_body} | Code]) ->
|
optimize_float_switch_body(I = {i, _, _}, [J = {i, _, switch_body} | Code]) ->
|
||||||
{J1, I1} = swap_instrs(I, J),
|
{J1, I1} = swap_instrs(I, J),
|
||||||
{[], [J1, I1 | Code]};
|
{[], [J1, I1 | Code]};
|
||||||
r_float_switch_body(_, _) -> false.
|
optimize_float_switch_body(_, _) -> false.
|
||||||
|
|
||||||
%% Inline stores
|
%% Inline stores
|
||||||
r_inline_store({i, _, {'STORE', R, R}}, Code) ->
|
optimize_inline_store({i, _, {'STORE', R, R}}, Code) ->
|
||||||
{[], Code};
|
{[], Code};
|
||||||
r_inline_store(I = {i, _, {'STORE', R = {var, _}, A}}, Code) ->
|
optimize_inline_store(I = {i, _, {'STORE', R = {var, _}, A}}, Code) ->
|
||||||
%% Not when A is var unless updating the annotations properly.
|
%% Not when A is var unless updating the annotations properly.
|
||||||
Inline = case A of
|
Inline = case A of
|
||||||
{arg, _} -> true;
|
{arg, _} -> true;
|
||||||
@@ -1423,13 +1489,13 @@ r_inline_store(I = {i, _, {'STORE', R = {var, _}, A}}, Code) ->
|
|||||||
{store, _} -> true;
|
{store, _} -> true;
|
||||||
_ -> false
|
_ -> false
|
||||||
end,
|
end,
|
||||||
if Inline -> r_inline_store([I], false, R, A, Code);
|
if Inline -> optimize_inline_store([I], false, R, A, Code);
|
||||||
true -> false end;
|
true -> false end;
|
||||||
r_inline_store(_, _) -> false.
|
optimize_inline_store(_, _) -> false.
|
||||||
|
|
||||||
r_inline_store(Acc, Progress, R, A, [I = {i, _, switch_body} | Code]) ->
|
optimize_inline_store(Acc, Progress, R, A, [I = {i, _, switch_body} | Code]) ->
|
||||||
r_inline_store([I | Acc], Progress, R, A, Code);
|
optimize_inline_store([I | Acc], Progress, R, A, Code);
|
||||||
r_inline_store(Acc, Progress, R, A, [{i, Ann, I} | Code]) ->
|
optimize_inline_store(Acc, Progress, R, A, [{i, Ann, I} | Code]) ->
|
||||||
#{ write := W } = attributes(I),
|
#{ write := W } = attributes(I),
|
||||||
Inl = fun(X) when X == R -> A; (X) -> X end,
|
Inl = fun(X) when X == R -> A; (X) -> X end,
|
||||||
case live_in(R, Ann) of
|
case live_in(R, Ann) of
|
||||||
@@ -1449,14 +1515,14 @@ r_inline_store(Acc, Progress, R, A, [{i, Ann, I} | Code]) ->
|
|||||||
case lists:member(W, [R, A]) of
|
case lists:member(W, [R, A]) of
|
||||||
true when Progress1 -> {lists:reverse(Acc1), Code};
|
true when Progress1 -> {lists:reverse(Acc1), Code};
|
||||||
true -> false;
|
true -> false;
|
||||||
false -> r_inline_store(Acc1, Progress1, R, A, Code)
|
false -> optimize_inline_store(Acc1, Progress1, R, A, Code)
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
r_inline_store(Acc, true, _, _, Code) -> {lists:reverse(Acc), Code};
|
optimize_inline_store(Acc, true, _, _, Code) -> {lists:reverse(Acc), Code};
|
||||||
r_inline_store(_, false, _, _, _) -> false.
|
optimize_inline_store(_, false, _, _, _) -> false.
|
||||||
|
|
||||||
%% Shortcut write followed by final read
|
%% Shortcut write followed by final read
|
||||||
r_one_shot_var({i, Ann1, I}, [{i, Ann2, J} | Code]) ->
|
optimize_one_shot_var({i, Ann1, I}, [{i, Ann2, J} | Code]) ->
|
||||||
case op_view(I) of
|
case op_view(I) of
|
||||||
{Op, R = {var, _}, As} ->
|
{Op, R = {var, _}, As} ->
|
||||||
Copy = case J of
|
Copy = case J of
|
||||||
@@ -1470,11 +1536,11 @@ r_one_shot_var({i, Ann1, I}, [{i, Ann2, J} | Code]) ->
|
|||||||
end;
|
end;
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
r_one_shot_var(_, _) -> false.
|
optimize_one_shot_var(_, _) -> false.
|
||||||
|
|
||||||
%% Remove writes to dead variables
|
%% Remove writes to dead variables
|
||||||
r_write_to_dead_var({i, _, {'STORE', ?void, ?a}}, _) -> false; %% Avoid looping
|
optimize_write_to_dead_var({i, _, {'STORE', ?void, ?a}}, _) -> false; %% Avoid looping
|
||||||
r_write_to_dead_var({i, Ann, I}, Code) ->
|
optimize_write_to_dead_var({i, Ann, I}, Code) ->
|
||||||
#{ pure := Pure } = attributes(I),
|
#{ pure := Pure } = attributes(I),
|
||||||
case op_view(I) of
|
case op_view(I) of
|
||||||
{_Op, R, As} when R /= ?a, Pure ->
|
{_Op, R, As} when R /= ?a, Pure ->
|
||||||
@@ -1487,7 +1553,7 @@ r_write_to_dead_var({i, Ann, I}, Code) ->
|
|||||||
end;
|
end;
|
||||||
_ -> false
|
_ -> false
|
||||||
end;
|
end;
|
||||||
r_write_to_dead_var(_, _) -> false.
|
optimize_write_to_dead_var(_, _) -> false.
|
||||||
|
|
||||||
op_view({'ABORT', R}) -> {'ABORT', none, [R]};
|
op_view({'ABORT', R}) -> {'ABORT', none, [R]};
|
||||||
op_view({'EXIT', R}) -> {'EXIT', none, [R]};
|
op_view({'EXIT', R}) -> {'EXIT', none, [R]};
|
||||||
|
|||||||
+8
-3
@@ -333,14 +333,19 @@ expr100() ->
|
|||||||
|
|
||||||
expr150() -> infixl(expr200(), binop('|>')).
|
expr150() -> infixl(expr200(), binop('|>')).
|
||||||
expr200() -> infixr(expr300(), binop('||')).
|
expr200() -> infixr(expr300(), binop('||')).
|
||||||
expr300() -> infixr(expr400(), binop('&&')).
|
expr300() -> infixr(expr325(), binop('&&')).
|
||||||
|
expr325() -> infixl(expr350(), binop('bor')).
|
||||||
|
expr350() -> infixl(expr375(), binop('bxor')).
|
||||||
|
expr375() -> infixl(expr400(), binop('band')).
|
||||||
expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])).
|
expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])).
|
||||||
expr500() -> infixr(expr600(), binop(['::', '++'])).
|
expr500() -> infixr(expr550(), binop(['::', '++'])).
|
||||||
|
expr550() -> infixl(expr600(), binop(['<<', '>>'])).
|
||||||
expr600() -> infixl(expr650(), binop(['+', '-'])).
|
expr600() -> infixl(expr650(), binop(['+', '-'])).
|
||||||
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
|
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
|
||||||
expr700() -> infixl(expr750(), binop(['*', '/', mod])).
|
expr700() -> infixl(expr750(), binop(['*', '/', mod])).
|
||||||
expr750() -> infixl(expr800(), binop(['^'])).
|
expr750() -> infixl(expr800(), binop(['^'])).
|
||||||
expr800() -> ?RULE(many(token('!')), expr900(), prefixes(_1, _2)).
|
expr800() -> ?RULE(many(token('!')), expr850(), prefixes(_1, _2)).
|
||||||
|
expr850() -> ?RULE(many(token('bnot')), expr900(), prefixes(_1, _2)).
|
||||||
expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)).
|
expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)).
|
||||||
|
|
||||||
exprAtom() ->
|
exprAtom() ->
|
||||||
|
|||||||
+16
-5
@@ -261,6 +261,8 @@ type(Type, Options) ->
|
|||||||
with_options(Options, fun() -> type(Type) end).
|
with_options(Options, fun() -> type(Type) end).
|
||||||
|
|
||||||
-spec type(aeso_syntax:type()) -> doc().
|
-spec type(aeso_syntax:type()) -> doc().
|
||||||
|
type(F = {fun_t, _, _, var_args, _}) ->
|
||||||
|
type(setelement(4, F, [var_args]));
|
||||||
type({fun_t, _, Named, Args, Ret}) ->
|
type({fun_t, _, Named, Args, Ret}) ->
|
||||||
follow(hsep(args_type(Named ++ Args), text("=>")), type(Ret));
|
follow(hsep(args_type(Named ++ Args), text("=>")), type(Ret));
|
||||||
type({type_sig, _, Named, Args, Ret}) ->
|
type({type_sig, _, Named, Args, Ret}) ->
|
||||||
@@ -288,7 +290,9 @@ type(T = {id, _, _}) -> name(T);
|
|||||||
type(T = {qid, _, _}) -> name(T);
|
type(T = {qid, _, _}) -> name(T);
|
||||||
type(T = {con, _, _}) -> name(T);
|
type(T = {con, _, _}) -> name(T);
|
||||||
type(T = {qcon, _, _}) -> name(T);
|
type(T = {qcon, _, _}) -> name(T);
|
||||||
type(T = {tvar, _, _}) -> name(T).
|
type(T = {tvar, _, _}) -> name(T);
|
||||||
|
|
||||||
|
type(var_args) -> text("var_args").
|
||||||
|
|
||||||
-spec args_type([aeso_syntax:type()]) -> doc().
|
-spec args_type([aeso_syntax:type()]) -> doc().
|
||||||
args_type(Args) ->
|
args_type(Args) ->
|
||||||
@@ -430,16 +434,22 @@ lc_bind(Let) ->
|
|||||||
bin_prec('..') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
bin_prec('..') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||||
bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||||
bin_prec('@') -> { 0, 0, 0}; %% Only in error messages
|
bin_prec('@') -> { 0, 0, 0}; %% Only in error messages
|
||||||
|
bin_prec('|>') -> {150, 150, 200};
|
||||||
bin_prec('||') -> {200, 300, 200};
|
bin_prec('||') -> {200, 300, 200};
|
||||||
bin_prec('&&') -> {300, 400, 300};
|
bin_prec('&&') -> {300, 325, 300};
|
||||||
|
bin_prec('bor') -> {325, 350, 325};
|
||||||
|
bin_prec('bxor') -> {350, 375, 350};
|
||||||
|
bin_prec('band') -> {375, 400, 375};
|
||||||
bin_prec('<') -> {400, 500, 500};
|
bin_prec('<') -> {400, 500, 500};
|
||||||
bin_prec('>') -> {400, 500, 500};
|
bin_prec('>') -> {400, 500, 500};
|
||||||
bin_prec('=<') -> {400, 500, 500};
|
bin_prec('=<') -> {400, 500, 500};
|
||||||
bin_prec('>=') -> {400, 500, 500};
|
bin_prec('>=') -> {400, 500, 500};
|
||||||
bin_prec('==') -> {400, 500, 500};
|
bin_prec('==') -> {400, 500, 500};
|
||||||
bin_prec('!=') -> {400, 500, 500};
|
bin_prec('!=') -> {400, 500, 500};
|
||||||
bin_prec('++') -> {500, 600, 500};
|
bin_prec('++') -> {500, 550, 500};
|
||||||
bin_prec('::') -> {500, 600, 500};
|
bin_prec('::') -> {500, 550, 500};
|
||||||
|
bin_prec('<<') -> {550, 600, 550};
|
||||||
|
bin_prec('>>') -> {550, 600, 550};
|
||||||
bin_prec('+') -> {600, 600, 650};
|
bin_prec('+') -> {600, 600, 650};
|
||||||
bin_prec('-') -> {600, 600, 650};
|
bin_prec('-') -> {600, 600, 650};
|
||||||
bin_prec('*') -> {700, 700, 750};
|
bin_prec('*') -> {700, 700, 750};
|
||||||
@@ -449,7 +459,8 @@ bin_prec('^') -> {750, 750, 800}.
|
|||||||
|
|
||||||
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
|
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
|
||||||
un_prec('-') -> {650, 650};
|
un_prec('-') -> {650, 650};
|
||||||
un_prec('!') -> {800, 800}.
|
un_prec('!') -> {800, 800};
|
||||||
|
un_prec('bnot') -> {850, 850}.
|
||||||
|
|
||||||
equals(Ann, A, B) ->
|
equals(Ann, A, B) ->
|
||||||
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
|
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,7 @@ lexer() ->
|
|||||||
|
|
||||||
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
|
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
|
||||||
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace",
|
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace",
|
||||||
"interface", "main", "using", "as", "for", "hiding"
|
"interface", "main", "using", "as", "for", "hiding", "band", "bor", "bxor", "bnot"
|
||||||
],
|
],
|
||||||
KW = string:join(Keywords, "|"),
|
KW = string:join(Keywords, "|"),
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -13,7 +13,7 @@
|
|||||||
-export_type([ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]).
|
-export_type([ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]).
|
||||||
-export_type([name/0, id/0, con/0, qid/0, qcon/0, tvar/0, op/0]).
|
-export_type([name/0, id/0, con/0, qid/0, qcon/0, tvar/0, op/0]).
|
||||||
-export_type([bin_op/0, un_op/0]).
|
-export_type([bin_op/0, un_op/0]).
|
||||||
-export_type([decl/0, letbind/0, typedef/0, pragma/0]).
|
-export_type([decl/0, letbind/0, typedef/0, pragma/0, fundecl/0]).
|
||||||
-export_type([arg/0, field_t/0, constructor_t/0, named_arg_t/0]).
|
-export_type([arg/0, field_t/0, constructor_t/0, named_arg_t/0]).
|
||||||
-export_type([type/0, constant/0, expr/0, arg_expr/0, field/1, stmt/0, alt/0, lvalue/0, elim/0, pat/0]).
|
-export_type([type/0, constant/0, expr/0, arg_expr/0, field/1, stmt/0, alt/0, lvalue/0, elim/0, pat/0]).
|
||||||
-export_type([ast/0]).
|
-export_type([ast/0]).
|
||||||
@@ -106,8 +106,8 @@
|
|||||||
|
|
||||||
-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^'
|
-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^'
|
||||||
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||||
| '||' | '&&' | '..'.
|
| '||' | '&&' | '..' | 'band' | 'bor' | 'bxor' | '>>' | '<<' | '|>'.
|
||||||
-type un_op() :: '-' | '!'.
|
-type un_op() :: '-' | '!' | 'bnot'.
|
||||||
|
|
||||||
-type expr()
|
-type expr()
|
||||||
:: {lam, ann(), [arg()], expr()}
|
:: {lam, ann(), [arg()], expr()}
|
||||||
|
|||||||
+16
-1
@@ -88,7 +88,7 @@ from_fate_builtin(QType, Val) ->
|
|||||||
|
|
||||||
{["AENS", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} ->
|
{["AENS", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} ->
|
||||||
App(["AENS","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL),
|
App(["AENS","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL),
|
||||||
Chk(Map(Str, Qid(["AENS", "pointee"])), Ptrs)]);
|
Chk(Map(Str, Qid(["AENS", "pointee"])), Ptrs)]);
|
||||||
|
|
||||||
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 0, {Addr}}} ->
|
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 0, {Addr}}} ->
|
||||||
App(["AENS","AccountPt"], [Chk(Adr, Addr)]);
|
App(["AENS","AccountPt"], [Chk(Adr, Addr)]);
|
||||||
@@ -99,6 +99,21 @@ from_fate_builtin(QType, Val) ->
|
|||||||
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 3, {Addr}}} ->
|
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 3, {Addr}}} ->
|
||||||
App(["AENS","ChannelPt"], [Chk(Adr, Addr)]);
|
App(["AENS","ChannelPt"], [Chk(Adr, Addr)]);
|
||||||
|
|
||||||
|
{["AENSv2", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} ->
|
||||||
|
App(["AENSv2","Name"], [Chk(Adr, Addr), Chk(Qid(["Chain", "ttl"]), TTL),
|
||||||
|
Chk(Map(Str, Qid(["AENSv2", "pointee"])), Ptrs)]);
|
||||||
|
|
||||||
|
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 0, {Val}}} ->
|
||||||
|
App(["AENSv2","AccountPt"], [Chk(Adr, Val)]);
|
||||||
|
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 1, {Val}}} ->
|
||||||
|
App(["AENSv2","OraclePt"], [Chk(Adr, Val)]);
|
||||||
|
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 2, {Val}}} ->
|
||||||
|
App(["AENSv2","ContractPt"], [Chk(Adr, Val)]);
|
||||||
|
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 3, {Val}}} ->
|
||||||
|
App(["AENSv2","ChannelPt"], [Chk(Adr, Val)]);
|
||||||
|
{["AENSv2", "pointee"], {variant, [1, 1, 1, 1, 1], 4, {Val}}} ->
|
||||||
|
App(["AENSv2","DataPt"], [Chk(Str, Val)]);
|
||||||
|
|
||||||
{["Chain", "ga_meta_tx"], {variant, [2], 0, {Addr, X}}} ->
|
{["Chain", "ga_meta_tx"], {variant, [2], 0, {Addr, X}}} ->
|
||||||
App(["Chain","GAMetaTx"], [Chk(Adr, Addr), Chk(Int, X)]);
|
App(["Chain","GAMetaTx"], [Chk(Adr, Addr), Chk(Int, X)]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{application, aesophia,
|
{application, aesophia,
|
||||||
[{description, "Compiler for Aeternity Sophia language"},
|
[{description, "Compiler for Aeternity Sophia language"},
|
||||||
{vsn, "7.0.0"},
|
{vsn, "8.0.0"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{applications,
|
{applications,
|
||||||
[kernel,
|
[kernel,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ test_cases(1) ->
|
|||||||
" payable stateful entrypoint a(i : int) = i+1\n">>,
|
" payable stateful entrypoint a(i : int) = i+1\n">>,
|
||||||
MapACI = #{contract =>
|
MapACI = #{contract =>
|
||||||
#{name => <<"C">>,
|
#{name => <<"C">>,
|
||||||
type_defs => [],
|
typedefs => [],
|
||||||
payable => true,
|
payable => true,
|
||||||
kind => contract_main,
|
kind => contract_main,
|
||||||
functions =>
|
functions =>
|
||||||
@@ -43,7 +43,7 @@ test_cases(2) ->
|
|||||||
MapACI = #{contract =>
|
MapACI = #{contract =>
|
||||||
#{name => <<"C">>, payable => false,
|
#{name => <<"C">>, payable => false,
|
||||||
kind => contract_main,
|
kind => contract_main,
|
||||||
type_defs =>
|
typedefs =>
|
||||||
[#{name => <<"allan">>,
|
[#{name => <<"allan">>,
|
||||||
typedef => <<"int">>,
|
typedef => <<"int">>,
|
||||||
vars => []}],
|
vars => []}],
|
||||||
@@ -76,7 +76,7 @@ test_cases(3) ->
|
|||||||
name => <<"C">>, payable => false, kind => contract_main,
|
name => <<"C">>, payable => false, kind => contract_main,
|
||||||
event => #{variant => [#{<<"SingleEventDefined">> => []}]},
|
event => #{variant => [#{<<"SingleEventDefined">> => []}]},
|
||||||
state => <<"unit">>,
|
state => <<"unit">>,
|
||||||
type_defs =>
|
typedefs =>
|
||||||
[#{name => <<"bert">>,
|
[#{name => <<"bert">>,
|
||||||
typedef =>
|
typedef =>
|
||||||
#{variant =>
|
#{variant =>
|
||||||
|
|||||||
@@ -202,6 +202,9 @@ compilable_contracts() ->
|
|||||||
"polymorphism_contract_interface_extensions",
|
"polymorphism_contract_interface_extensions",
|
||||||
"polymorphism_contract_interface_same_decl_multi_interface",
|
"polymorphism_contract_interface_same_decl_multi_interface",
|
||||||
"polymorphism_contract_interface_same_name_same_type",
|
"polymorphism_contract_interface_same_name_same_type",
|
||||||
|
"polymorphism_variance_switching_chain_create",
|
||||||
|
"polymorphism_variance_switching_void_supertype",
|
||||||
|
"polymorphism_variance_switching_unify_with_interface_decls",
|
||||||
"missing_init_fun_state_unit",
|
"missing_init_fun_state_unit",
|
||||||
"complex_compare_leq",
|
"complex_compare_leq",
|
||||||
"complex_compare",
|
"complex_compare",
|
||||||
@@ -214,6 +217,8 @@ compilable_contracts() ->
|
|||||||
"polymorphic_map_keys",
|
"polymorphic_map_keys",
|
||||||
"unapplied_contract_call",
|
"unapplied_contract_call",
|
||||||
"unapplied_named_arg_builtin",
|
"unapplied_named_arg_builtin",
|
||||||
|
"resolve_field_constraint_by_arity",
|
||||||
|
"ceres",
|
||||||
"test" % Custom general-purpose test file. Keep it last on the list.
|
"test" % Custom general-purpose test file. Keep it last on the list.
|
||||||
].
|
].
|
||||||
|
|
||||||
@@ -730,10 +735,22 @@ failing_contracts() ->
|
|||||||
"Conflicting updates for field 'foo'">>])
|
"Conflicting updates for field 'foo'">>])
|
||||||
, ?TYPE_ERROR(factories_type_errors,
|
, ?TYPE_ERROR(factories_type_errors,
|
||||||
[<<?Pos(10,18)
|
[<<?Pos(10,18)
|
||||||
"Chain.clone requires `ref` named argument of contract type.">>,
|
"Chain.clone requires `ref` named argument of contract type.">>,
|
||||||
<<?Pos(11,18)
|
<<?Pos(11,18)
|
||||||
"Cannot unify `(gas : int, value : int, protected : bool) => if(protected, option(void), void)` and `(gas : int, value : int, protected : bool, int, bool) => 'b`\n"
|
"Cannot unify `(gas : int, value : int, protected : bool) => if(protected, option(void), void)` and `(gas : int, value : int, protected : bool, int, bool) => if(protected, option(void), void)`\n"
|
||||||
"when checking contract construction of type\n (gas : int, value : int, protected : bool) =>\n if(protected, option(void), void) (at line 11, column 18)\nagainst the expected type\n (gas : int, value : int, protected : bool, int, bool) => 'b">>,
|
"when checking contract construction of type\n"
|
||||||
|
" (gas : int, value : int, protected : bool) =>\n"
|
||||||
|
" if(protected, option(void), void) (at line 11, column 18)\n"
|
||||||
|
"against the expected type\n"
|
||||||
|
" (gas : int, value : int, protected : bool, int, bool) =>\n"
|
||||||
|
" if(protected, option(void), void)">>,
|
||||||
|
<<?Pos(11,18)
|
||||||
|
"Cannot unify `Bakoom` and `Kaboom`\n"
|
||||||
|
"when checking that contract construction of type\n"
|
||||||
|
" Bakoom\n"
|
||||||
|
"arising from resolution of variadic function `Chain.clone`\n"
|
||||||
|
"matches the expected type\n"
|
||||||
|
" Kaboom">>,
|
||||||
<<?Pos(12,37)
|
<<?Pos(12,37)
|
||||||
"Cannot unify `int` and `bool`\n"
|
"Cannot unify `int` and `bool`\n"
|
||||||
"when checking named argument `gas : int` against inferred type `bool`">>,
|
"when checking named argument `gas : int` against inferred type `bool`">>,
|
||||||
@@ -846,25 +863,25 @@ failing_contracts() ->
|
|||||||
"Trying to implement or extend an undefined interface `Z`">>
|
"Trying to implement or extend an undefined interface `Z`">>
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(polymorphism_contract_interface_same_name_different_type,
|
, ?TYPE_ERROR(polymorphism_contract_interface_same_name_different_type,
|
||||||
[<<?Pos(4,20)
|
[<<?Pos(9,5)
|
||||||
"Unimplemented function `f` from the interface `I1` in the contract `I2`">>])
|
"Duplicate definitions of `f` at\n"
|
||||||
|
" - line 8, column 5\n"
|
||||||
|
" - line 9, column 5">>])
|
||||||
, ?TYPE_ERROR(polymorphism_contract_missing_implementation,
|
, ?TYPE_ERROR(polymorphism_contract_missing_implementation,
|
||||||
[<<?Pos(4,20)
|
[<<?Pos(4,20)
|
||||||
"Unimplemented function `f` from the interface `I1` in the contract `I2`">>
|
"Unimplemented function `f` from the interface `I1` in the contract `I2`">>
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(polymorphism_contract_same_decl_multi_interface,
|
, ?TYPE_ERROR(polymorphism_contract_same_decl_multi_interface,
|
||||||
[<<?Pos(7,10)
|
[<<?Pos(7,10)
|
||||||
"Unimplemented function `f` from the interface `J` in the contract `C`">>
|
"Both interfaces `I` and `J` implemented by the contract `C` have a function called `f`">>
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(polymorphism_contract_undefined_interface,
|
, ?TYPE_ERROR(polymorphism_contract_undefined_interface,
|
||||||
[<<?Pos(1,14)
|
[<<?Pos(1,14)
|
||||||
"Trying to implement or extend an undefined interface `I`">>
|
"Trying to implement or extend an undefined interface `I`">>
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(polymorphism_contract_same_name_different_type_multi_interface,
|
, ?TYPE_ERROR(polymorphism_contract_same_name_different_type_multi_interface,
|
||||||
[<<?Pos(9,5)
|
[<<?Pos(7,10)
|
||||||
"Duplicate definitions of `f` at\n"
|
"Both interfaces `I` and `J` implemented by the contract `C` have a function called `f`">>
|
||||||
" - line 8, column 5\n"
|
|
||||||
" - line 9, column 5">>
|
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(polymorphism_contract_interface_undefined_interface,
|
, ?TYPE_ERROR(polymorphism_contract_interface_undefined_interface,
|
||||||
[<<?Pos(1,24)
|
[<<?Pos(1,24)
|
||||||
@@ -1025,6 +1042,20 @@ failing_contracts() ->
|
|||||||
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
"Cannot unify `Animal` and `Cat` in a covariant context\n"
|
||||||
"when checking the type of the pattern `q15 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Cat, Animal)`">>
|
"when checking the type of the pattern `q15 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Cat, Animal)`">>
|
||||||
])
|
])
|
||||||
|
, ?TYPE_ERROR(polymorphism_variance_switching_chain_create_fail,
|
||||||
|
[<<?Pos(9,22)
|
||||||
|
"I is not implemented.\n"
|
||||||
|
"when resolving arguments of variadic function `Chain.create`">>,
|
||||||
|
<<?Pos(10,13)
|
||||||
|
"Cannot unify `I` and `C` in a covariant context\n"
|
||||||
|
"when checking the type of the pattern `c2 : C` against the expected type `I`">>,
|
||||||
|
<<?Pos(10,22)
|
||||||
|
"I is not implemented.\n"
|
||||||
|
"when resolving arguments of variadic function `Chain.create`">>,
|
||||||
|
<<?Pos(11,22)
|
||||||
|
"I is not implemented.\n"
|
||||||
|
"when resolving arguments of variadic function `Chain.create`">>
|
||||||
|
])
|
||||||
, ?TYPE_ERROR(missing_definition,
|
, ?TYPE_ERROR(missing_definition,
|
||||||
[<<?Pos(2,14)
|
[<<?Pos(2,14)
|
||||||
"Missing definition of function `foo`">>
|
"Missing definition of function `foo`">>
|
||||||
@@ -1065,19 +1096,19 @@ failing_contracts() ->
|
|||||||
])
|
])
|
||||||
, ?TYPE_ERROR(polymorphic_aens_resolve,
|
, ?TYPE_ERROR(polymorphic_aens_resolve,
|
||||||
[<<?Pos(4,5)
|
[<<?Pos(4,5)
|
||||||
"Invalid return type of `AENS.resolve`:\n"
|
"Invalid return type of `AENSv2.resolve`:\n"
|
||||||
" `'a`\n"
|
" `'a`\n"
|
||||||
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
|
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(bad_aens_resolve,
|
, ?TYPE_ERROR(bad_aens_resolve,
|
||||||
[<<?Pos(6,5)
|
[<<?Pos(6,5)
|
||||||
"Invalid return type of `AENS.resolve`:\n"
|
"Invalid return type of `AENSv2.resolve`:\n"
|
||||||
" `list(int)`\n"
|
" `list(int)`\n"
|
||||||
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
|
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(bad_aens_resolve_using,
|
, ?TYPE_ERROR(bad_aens_resolve_using,
|
||||||
[<<?Pos(7,5)
|
[<<?Pos(7,5)
|
||||||
"Invalid return type of `AENS.resolve`:\n"
|
"Invalid return type of `AENSv2.resolve`:\n"
|
||||||
" `list(int)`\n"
|
" `list(int)`\n"
|
||||||
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
|
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
|
||||||
])
|
])
|
||||||
@@ -1109,6 +1140,19 @@ failing_contracts() ->
|
|||||||
" `oracle(string, (int) => int)`\n"
|
" `oracle(string, (int) => int)`\n"
|
||||||
"The response type must not be higher-order (contain function types)">>
|
"The response type must not be higher-order (contain function types)">>
|
||||||
])
|
])
|
||||||
|
, ?TYPE_ERROR(var_args_unify_let,
|
||||||
|
[<<?Pos(3,9)
|
||||||
|
"Cannot infer types for variable argument list.\n"
|
||||||
|
"when checking the type of the pattern `x : 'a` against the expected type `(gas : int, value : int, protected : bool, ref : 'b, var_args) => 'b`">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(var_args_unify_fun_call,
|
||||||
|
[<<?Pos(6,5)
|
||||||
|
"Cannot infer types for variable argument list.\n"
|
||||||
|
"when checking the application of\n"
|
||||||
|
" `g : (() => 'b) => 'b`\n"
|
||||||
|
"to arguments\n"
|
||||||
|
" `Chain.create : (value : int, var_args) => 'c`">>
|
||||||
|
])
|
||||||
].
|
].
|
||||||
|
|
||||||
validation_test_() ->
|
validation_test_() ->
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ simple_contracts_test_() ->
|
|||||||
%% associativity
|
%% associativity
|
||||||
[ RightAssoc(Op) || Op <- ["||", "&&", "::", "++"] ],
|
[ RightAssoc(Op) || Op <- ["||", "&&", "::", "++"] ],
|
||||||
[ NonAssoc(Op) || Op <- ["==", "!=", "<", ">", "=<", ">="] ],
|
[ NonAssoc(Op) || Op <- ["==", "!=", "<", ">", "=<", ">="] ],
|
||||||
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod"] ],
|
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod", "band", "bor", "bxor", "<<", ">>"] ],
|
||||||
|
|
||||||
%% precedence
|
%% precedence
|
||||||
[ Stronger(Op2, Op1) || [T1 , T2 | _] <- tails(Tiers), Op1 <- T1, Op2 <- T2 ],
|
[ Stronger(Op2, Op1) || [T1 , T2 | _] <- tails(Tiers), Op1 <- T1, Op2 <- T2 ],
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ all_tokens() ->
|
|||||||
%% Symbols
|
%% Symbols
|
||||||
lists:map(Lit, [',', '.', ';', '|', ':', '(', ')', '[', ']', '{', '}']) ++
|
lists:map(Lit, [',', '.', ';', '|', ':', '(', ')', '[', ']', '{', '}']) ++
|
||||||
%% Operators
|
%% Operators
|
||||||
lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod, ':', '::', '->', '=>', '||', '&&', '!']) ++
|
lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod,
|
||||||
|
':', '::', '->', '=>', '||', '&&', '!', 'band', 'bor', 'bxor', 'bnot' ,'<<', '>>']) ++
|
||||||
%% Keywords
|
%% Keywords
|
||||||
lists:map(Lit, [contract, type, 'let', switch]) ++
|
lists:map(Lit, [contract, type, 'let', switch]) ++
|
||||||
%% Comment token (not an actual token), just for tests
|
%% Comment token (not an actual token), just for tests
|
||||||
|
|||||||
+17
-17
@@ -6,77 +6,77 @@ main contract AENSTest =
|
|||||||
// Name resolution
|
// Name resolution
|
||||||
|
|
||||||
stateful entrypoint resolve_word(name : string, key : string) : option(address) =
|
stateful entrypoint resolve_word(name : string, key : string) : option(address) =
|
||||||
AENS.resolve(name, key)
|
AENSv2.resolve(name, key)
|
||||||
|
|
||||||
stateful entrypoint resolve_string(name : string, key : string) : option(string) =
|
stateful entrypoint resolve_string(name : string, key : string) : option(string) =
|
||||||
AENS.resolve(name, key)
|
AENSv2.resolve(name, key)
|
||||||
|
|
||||||
stateful entrypoint resolve_contract(name : string, key : string) : option(C) =
|
stateful entrypoint resolve_contract(name : string, key : string) : option(C) =
|
||||||
AENS.resolve(name, key)
|
AENSv2.resolve(name, key)
|
||||||
|
|
||||||
stateful entrypoint resolve_oracle(name : string, key : string) : option(oracle(int, int)) =
|
stateful entrypoint resolve_oracle(name : string, key : string) : option(oracle(int, int)) =
|
||||||
AENS.resolve(name, key)
|
AENSv2.resolve(name, key)
|
||||||
|
|
||||||
stateful entrypoint resolve_oracle_query(name : string, key : string) : option(oracle_query(int, int)) =
|
stateful entrypoint resolve_oracle_query(name : string, key : string) : option(oracle_query(int, int)) =
|
||||||
AENS.resolve(name, key)
|
AENSv2.resolve(name, key)
|
||||||
|
|
||||||
// Transactions
|
// Transactions
|
||||||
|
|
||||||
stateful entrypoint preclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
|
stateful entrypoint preclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
|
||||||
chash : hash) : unit = // Commitment hash
|
chash : hash) : unit = // Commitment hash
|
||||||
AENS.preclaim(addr, chash)
|
AENSv2.preclaim(addr, chash)
|
||||||
|
|
||||||
stateful entrypoint signedPreclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
|
stateful entrypoint signedPreclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
|
||||||
chash : hash, // Commitment hash
|
chash : hash, // Commitment hash
|
||||||
sign : signature) : unit = // Signed by addr (if not Contract.address)
|
sign : signature) : unit = // Signed by addr (if not Contract.address)
|
||||||
AENS.preclaim(addr, chash, signature = sign)
|
AENSv2.preclaim(addr, chash, signature = sign)
|
||||||
|
|
||||||
stateful entrypoint claim(addr : address,
|
stateful entrypoint claim(addr : address,
|
||||||
name : string,
|
name : string,
|
||||||
salt : int,
|
salt : int,
|
||||||
name_fee : int) : unit =
|
name_fee : int) : unit =
|
||||||
AENS.claim(addr, name, salt, name_fee)
|
AENSv2.claim(addr, name, salt, name_fee)
|
||||||
|
|
||||||
stateful entrypoint signedClaim(addr : address,
|
stateful entrypoint signedClaim(addr : address,
|
||||||
name : string,
|
name : string,
|
||||||
salt : int,
|
salt : int,
|
||||||
name_fee : int,
|
name_fee : int,
|
||||||
sign : signature) : unit =
|
sign : signature) : unit =
|
||||||
AENS.claim(addr, name, salt, name_fee, signature = sign)
|
AENSv2.claim(addr, name, salt, name_fee, signature = sign)
|
||||||
|
|
||||||
|
|
||||||
stateful entrypoint update(owner : address,
|
stateful entrypoint update(owner : address,
|
||||||
name : string,
|
name : string,
|
||||||
ttl : option(Chain.ttl),
|
ttl : option(Chain.ttl),
|
||||||
client_ttl : option(int),
|
client_ttl : option(int),
|
||||||
pointers : option(map(string, AENS.pointee))) : unit =
|
pointers : option(map(string, AENSv2.pointee))) : unit =
|
||||||
AENS.update(owner, name, ttl, client_ttl, pointers)
|
AENSv2.update(owner, name, ttl, client_ttl, pointers)
|
||||||
|
|
||||||
stateful entrypoint signedUpdate(owner : address,
|
stateful entrypoint signedUpdate(owner : address,
|
||||||
name : string,
|
name : string,
|
||||||
ttl : option(Chain.ttl),
|
ttl : option(Chain.ttl),
|
||||||
client_ttl : option(int),
|
client_ttl : option(int),
|
||||||
pointers : option(map(string, AENS.pointee)),
|
pointers : option(map(string, AENSv2.pointee)),
|
||||||
sign : signature) : unit =
|
sign : signature) : unit =
|
||||||
AENS.update(owner, name, ttl, client_ttl, pointers, signature = sign)
|
AENSv2.update(owner, name, ttl, client_ttl, pointers, signature = sign)
|
||||||
|
|
||||||
|
|
||||||
stateful entrypoint transfer(owner : address,
|
stateful entrypoint transfer(owner : address,
|
||||||
new_owner : address,
|
new_owner : address,
|
||||||
name : string) : unit =
|
name : string) : unit =
|
||||||
AENS.transfer(owner, new_owner, name)
|
AENSv2.transfer(owner, new_owner, name)
|
||||||
|
|
||||||
stateful entrypoint signedTransfer(owner : address,
|
stateful entrypoint signedTransfer(owner : address,
|
||||||
new_owner : address,
|
new_owner : address,
|
||||||
name : string,
|
name : string,
|
||||||
sign : signature) : unit =
|
sign : signature) : unit =
|
||||||
AENS.transfer(owner, new_owner, name, signature = sign)
|
AENSv2.transfer(owner, new_owner, name, signature = sign)
|
||||||
|
|
||||||
stateful entrypoint revoke(owner : address,
|
stateful entrypoint revoke(owner : address,
|
||||||
name : string) : unit =
|
name : string) : unit =
|
||||||
AENS.revoke(owner, name)
|
AENSv2.revoke(owner, name)
|
||||||
|
|
||||||
stateful entrypoint signedRevoke(owner : address,
|
stateful entrypoint signedRevoke(owner : address,
|
||||||
name : string,
|
name : string,
|
||||||
sign : signature) : unit =
|
sign : signature) : unit =
|
||||||
AENS.revoke(owner, name, signature = sign)
|
AENSv2.revoke(owner, name, signature = sign)
|
||||||
|
|||||||
@@ -1,17 +1,30 @@
|
|||||||
contract AENSUpdate =
|
include "Option.aes"
|
||||||
|
include "AENSCompat.aes"
|
||||||
|
contract interface OldAENSContract =
|
||||||
|
entrypoint set : (string, string, AENS.pointee) => unit
|
||||||
|
entrypoint lookup : (string, string) => AENS.pointee
|
||||||
|
|
||||||
|
main contract AENSUpdate =
|
||||||
stateful entrypoint update_name(owner : address, name : string) =
|
stateful entrypoint update_name(owner : address, name : string) =
|
||||||
let p1 : AENS.pointee = AENS.AccountPt(Call.caller)
|
let p1 : AENSv2.pointee = AENSv2.AccountPt(Call.caller)
|
||||||
let p2 : AENS.pointee = AENS.OraclePt(Call.caller)
|
let p2 : AENSv2.pointee = AENSv2.OraclePt(Call.caller)
|
||||||
let p3 : AENS.pointee = AENS.ContractPt(Call.caller)
|
let p3 : AENSv2.pointee = AENSv2.ContractPt(Call.caller)
|
||||||
let p4 : AENS.pointee = AENS.ChannelPt(Call.caller)
|
let p4 : AENSv2.pointee = AENSv2.ChannelPt(Call.caller)
|
||||||
AENS.update(owner, name, None, None,
|
let p5 : AENSv2.pointee = AENSv2.DataPt("any something will do")
|
||||||
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
|
AENSv2.update(owner, name, None, None,
|
||||||
["contract_pubkey"] = p3, ["misc"] = p4 }))
|
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
|
||||||
|
["contract_pubkey"] = p3, ["misc"] = p4, ["data"] = p5 }))
|
||||||
|
|
||||||
|
stateful entrypoint old_interaction(c : OldAENSContract, owner : address, name : string) =
|
||||||
|
let p : AENS.pointee = c.lookup(name, "key1")
|
||||||
|
AENSv2.update(owner, name, None, None, Some({ ["key1"] = AENSCompat.pointee_to_V2(p) }))
|
||||||
|
switch(AENSv2.lookup(name))
|
||||||
|
Some(AENSv2.Name(_, _, pt_map)) =>
|
||||||
|
c.set(name, "key2", Option.force(AENSCompat.pointee_from_V2(pt_map["key1"])))
|
||||||
|
|
||||||
entrypoint get_ttl(name : string) =
|
entrypoint get_ttl(name : string) =
|
||||||
switch(AENS.lookup(name))
|
switch(AENSv2.lookup(name))
|
||||||
Some(AENS.Name(_, FixedTTL(ttl), _)) => ttl
|
Some(AENSv2.Name(_, FixedTTL(ttl), _)) => ttl
|
||||||
|
|
||||||
entrypoint expiry(o : oracle(int, int)) : int =
|
entrypoint expiry(o : oracle(int, int)) : int =
|
||||||
Oracle.expiry(o)
|
Oracle.expiry(o)
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ contract BadAENSresolve =
|
|||||||
type t('a) = option(list('a))
|
type t('a) = option(list('a))
|
||||||
|
|
||||||
function fail() : t(int) =
|
function fail() : t(int) =
|
||||||
AENS.resolve("foo.aet", "whatever")
|
AENSv2.resolve("foo.aet", "whatever")
|
||||||
|
|
||||||
entrypoint main_fun() = ()
|
entrypoint main_fun() = ()
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
contract BadAENSresolve =
|
contract BadAENSresolve =
|
||||||
using AENS
|
using AENSv2
|
||||||
|
|
||||||
type t('a) = option(list('a))
|
type t('a) = option(list('a))
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
contract C =
|
||||||
|
entrypoint test() =
|
||||||
|
let a : int = 23
|
||||||
|
let b : int = 52
|
||||||
|
let c = a bor b
|
||||||
|
let d = c bxor b
|
||||||
|
let e = d band b
|
||||||
|
let f = bnot a
|
||||||
|
let g = f << 2
|
||||||
|
let h = g >> 2
|
||||||
|
let i = Int.mulmod(a, b, h)
|
||||||
|
let j = Crypto.poseidon(i, a)
|
||||||
|
let k : bytes(32) = Address.to_bytes(Call.origin)
|
||||||
|
(a bor b band c bxor a << bnot b >> a, k)
|
||||||
@@ -1,16 +1,7 @@
|
|||||||
|
|
||||||
contract interface Remote =
|
|
||||||
entrypoint up_to : (int) => list(int)
|
|
||||||
entrypoint sum : (list(int)) => int
|
|
||||||
entrypoint some_string : () => string
|
|
||||||
entrypoint pair : (int, string) => int * string
|
|
||||||
entrypoint squares : (int) => list(int * int)
|
|
||||||
entrypoint filter_some : (list(option(int))) => list(int)
|
|
||||||
entrypoint all_some : (list(option(int))) => option(list(int))
|
|
||||||
|
|
||||||
contract ComplexTypes =
|
contract ComplexTypes =
|
||||||
|
|
||||||
record state = { worker : Remote }
|
record state = { worker : ComplexTypes }
|
||||||
|
|
||||||
entrypoint init(worker) = {worker = worker}
|
entrypoint init(worker) = {worker = worker}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
|
|
||||||
// Testing primitives for accessing the block chain environment
|
// Testing primitives for accessing the block chain environment
|
||||||
contract interface Interface =
|
|
||||||
entrypoint contract_address : () => address
|
|
||||||
entrypoint call_origin : () => address
|
|
||||||
entrypoint call_caller : () => address
|
|
||||||
entrypoint call_value : () => int
|
|
||||||
|
|
||||||
contract Environment =
|
contract Environment =
|
||||||
|
|
||||||
record state = {remote : Interface}
|
record state = {remote : Environment}
|
||||||
|
|
||||||
entrypoint init(remote) = {remote = remote}
|
entrypoint init(remote) = {remote = remote}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
contract PolymorphicAENSresolve =
|
contract PolymorphicAENSresolve =
|
||||||
|
|
||||||
function fail() : option('a) =
|
function fail() : option('a) =
|
||||||
AENS.resolve("foo.aet", "whatever")
|
AENSv2.resolve("foo.aet", "whatever")
|
||||||
|
|
||||||
entrypoint main_fun() = ()
|
entrypoint main_fun() = ()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
contract interface I =
|
||||||
|
entrypoint f : () => int
|
||||||
|
|
||||||
|
contract C1 : I =
|
||||||
|
entrypoint f() = 123
|
||||||
|
|
||||||
|
contract C2 : I =
|
||||||
|
entrypoint f() = 888
|
||||||
|
|
||||||
|
namespace Make =
|
||||||
|
stateful function new1() : I = Chain.create() : C1
|
||||||
|
stateful function new2() : I = Chain.create() : C2
|
||||||
|
|
||||||
|
stateful function new(c : I) : int = c.f()
|
||||||
|
|
||||||
|
main contract Main =
|
||||||
|
stateful entrypoint test1() =
|
||||||
|
let c = Make.new1()
|
||||||
|
Make.new(c)
|
||||||
|
|
||||||
|
stateful entrypoint test2() =
|
||||||
|
let c = Make.new2()
|
||||||
|
Make.new(c)
|
||||||
|
|
||||||
|
stateful entrypoint test3() =
|
||||||
|
let c1 = Chain.create() : C1 // succeeds
|
||||||
|
let c2 : I = Chain.create() : C1 // succeeds
|
||||||
|
let c3 : C1 = Chain.create() // succeeds
|
||||||
|
()
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
contract interface I =
|
||||||
|
entrypoint f : () => int
|
||||||
|
|
||||||
|
contract C : I =
|
||||||
|
entrypoint f() = 123
|
||||||
|
|
||||||
|
main contract Main =
|
||||||
|
stateful entrypoint test() =
|
||||||
|
let c1 : I = Chain.create() // fails
|
||||||
|
let c2 : C = Chain.create() : I // fails
|
||||||
|
let c3 = Chain.create() : I // fails
|
||||||
|
()
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
payable contract interface SalesOffer =
|
||||||
|
entrypoint init : (address, address, int, int) => unit
|
||||||
|
|
||||||
|
payable contract Test : SalesOffer =
|
||||||
|
entrypoint init(_, _, _, _) = ()
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
payable contract interface SalesOffer =
|
||||||
|
entrypoint init : (address, address, int, int) => void
|
||||||
|
|
||||||
|
payable contract Test : SalesOffer =
|
||||||
|
entrypoint init(_ : address, _ : address, _ : int, _ : int) = ()
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
contract First =
|
||||||
|
entrypoint print_num(x) = 1 + x
|
||||||
|
|
||||||
|
contract Second =
|
||||||
|
entrypoint print_num() = 1
|
||||||
|
|
||||||
|
main contract Test =
|
||||||
|
entrypoint f(c) = c.print_num(1)
|
||||||
|
entrypoint g(c) = c.print_num()
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
|
|
||||||
contract interface SpendContract =
|
|
||||||
entrypoint withdraw : (int) => int
|
|
||||||
|
|
||||||
contract SpendTest =
|
contract SpendTest =
|
||||||
|
|
||||||
stateful entrypoint spend(to, amount) =
|
stateful entrypoint spend(to, amount) =
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
// Named argument builtins are:
|
// Named argument builtins are:
|
||||||
// Oracle.register
|
// Oracle.register
|
||||||
// Oracle.respond
|
// Oracle.respond
|
||||||
// AENS.preclaim
|
// AENSv2.preclaim
|
||||||
// AENS.claim
|
// AENSv2.claim
|
||||||
// AENS.transfer
|
// AENSv2.transfer
|
||||||
// AENS.revoke
|
// AENSv2.revoke
|
||||||
// Oracle.extend
|
// Oracle.extend
|
||||||
include "String.aes"
|
include "String.aes"
|
||||||
contract UnappliedBuiltins =
|
contract UnappliedBuiltins =
|
||||||
@@ -28,7 +28,7 @@ contract UnappliedBuiltins =
|
|||||||
function oracle_get_answer() = Oracle.get_answer : (o, _) => _
|
function oracle_get_answer() = Oracle.get_answer : (o, _) => _
|
||||||
function oracle_check() = Oracle.check : o => _
|
function oracle_check() = Oracle.check : o => _
|
||||||
function oracle_check_query() = Oracle.check_query : (o, _) => _
|
function oracle_check_query() = Oracle.check_query : (o, _) => _
|
||||||
function aens_resolve() = AENS.resolve : (_, _) => option(string)
|
function aens_resolve() = AENSv2.resolve : (_, _) => option(string)
|
||||||
function map_lookup() = Map.lookup : (_, m) => _
|
function map_lookup() = Map.lookup : (_, m) => _
|
||||||
function map_lookup_default() = Map.lookup_default : (_, m, _) => _
|
function map_lookup_default() = Map.lookup_default : (_, m, _) => _
|
||||||
function map_member() = Map.member : (_, m) => _
|
function map_member() = Map.member : (_, m) => _
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
contract C =
|
||||||
|
stateful function g(h) =
|
||||||
|
h()
|
||||||
|
|
||||||
|
stateful entrypoint f() =
|
||||||
|
g(Chain.create)
|
||||||
|
123
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
main contract C =
|
||||||
|
stateful entrypoint f() =
|
||||||
|
let x = Chain.clone
|
||||||
|
123
|
||||||
Reference in New Issue
Block a user