Rebase new_ceres branch on top of new master #954
6
.github/workflows/requirements.txt
vendored
6
.github/workflows/requirements.txt
vendored
@ -1,5 +1,5 @@
|
||||
mkdocs==1.2.4
|
||||
mkdocs==1.4.2
|
||||
mkdocs-simple-hooks==0.1.5
|
||||
mkdocs-material==7.3.6
|
||||
mkdocs-material==9.0.9
|
||||
mike==1.1.2
|
||||
pygments==2.12.0
|
||||
pygments==2.14.0
|
48
CHANGELOG.md
48
CHANGELOG.md
@ -4,8 +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/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [CERES 8.0.0]
|
||||
### 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
|
||||
### Removed
|
||||
- `Bitwise.aes` standard library is removed - the builtin operations are superior.
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
### Changed
|
||||
### Removed
|
||||
### Fixed
|
||||
|
||||
## [7.2.0]
|
||||
### Added
|
||||
- Toplevel compile-time constants
|
||||
```
|
||||
namespace N =
|
||||
let nc = 1
|
||||
contract C =
|
||||
let cc = 2
|
||||
```
|
||||
- API functions for encoding/decoding Sophia values to/from FATE.
|
||||
### Removed
|
||||
- Remove the mapping from variables to FATE registers from the compilation output.
|
||||
### Fixed
|
||||
- Warning about unused include when there is no include.
|
||||
|
||||
## [7.1.0]
|
||||
### Added
|
||||
- Options to enable/disable certain optimizations.
|
||||
- The ability to call a different instance of the current contract
|
||||
```
|
||||
@ -14,9 +53,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
entrypoint f(c : Main) : int = c.spend(10)
|
||||
```
|
||||
- Return a mapping from variables to FATE registers in the compilation output.
|
||||
- Hole expression.
|
||||
### Changed
|
||||
- Type definitions serialised to ACI as `typedefs` field instead of `type_defs` to increase compatibility.
|
||||
### Removed
|
||||
- Check contracts and entrypoints modifiers when implementing interfaces.
|
||||
- Contracts can no longer be used as namespaces.
|
||||
- Do not show unused stateful warning for functions that call other contracts with a non-zero value argument.
|
||||
### Fixed
|
||||
- Typechecker crashes if Chain.create or Chain.clone are used without arguments.
|
||||
|
||||
@ -371,7 +413,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Simplify calldata creation - instead of passing a compiled contract, simply
|
||||
pass a (stubbed) contract string.
|
||||
|
||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v7.0.1...HEAD
|
||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v7.2.0...HEAD
|
||||
[7.2.0]: https://github.com/aeternity/aesophia/compare/v7.1.0...v7.2.0
|
||||
[7.1.0]: https://github.com/aeternity/aesophia/compare/v7.0.1...v7.1.0
|
||||
[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
|
||||
[6.1.0]: https://github.com/aeternity/aesophia/compare/v6.0.2...v6.1.0
|
||||
|
@ -53,8 +53,6 @@ The **pp_** options all print to standard output the following:
|
||||
|
||||
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
|
||||
|
@ -191,6 +191,17 @@ contract interface X : Z =
|
||||
entrypoint z() = 1
|
||||
```
|
||||
|
||||
#### Adding or removing modifiers
|
||||
|
||||
When a `contract` or a `contract interface` implements another `contract interface`, the `payable` and `stateful` modifiers can be kept or changed, both in the contract and in the entrypoints, according to the following rules:
|
||||
|
||||
1. A `payable` contract or interface can implement a `payable` interface or a non-`payable` interface.
|
||||
2. A non-`payable` contract or interface can only implement a non-`payable` interface, and cannot implement a `payable` interface.
|
||||
3. A `payable` entrypoint can implement a `payable` entrypoint or a non-`payable` entrypoint.
|
||||
4. A non-`payable` entrypoint can only implement a non-`payable` entrypoint, and cannot implement a `payable` entrypoint.
|
||||
5. A non-`stateful` entrypoint can implement a `stateful` entrypoint or a non-`stateful` entrypoint.
|
||||
6. A `stateful` entrypoint can only implement a `stateful` entrypoint, and cannot implement a non-`stateful` entrypoint.
|
||||
|
||||
#### Subtyping and variance
|
||||
|
||||
Subtyping in Sophia follows common rules that take type variance into account. As described by [Wikipedia](https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)),
|
||||
@ -245,10 +256,10 @@ datatype bi('a) = Bi // bi is bivariant on 'a
|
||||
|
||||
The following facts apply here:
|
||||
|
||||
- `co('a)` is a subtype of `co('b) when `'a` is a subtype of `'b`
|
||||
- `ct('a)` is a subtype of `ct('b) when `'b` is a subtype of `'a`
|
||||
- `in('a)` is a subtype of `in('b) when `'a` is equal to `'b`
|
||||
- `bi('a)` is a subtype of `bi('b) always
|
||||
- `co('a)` is a subtype of `co('b)` when `'a` is a subtype of `'b`
|
||||
- `ct('a)` is a subtype of `ct('b)` when `'b` is a subtype of `'a`
|
||||
- `in('a)` is a subtype of `in('b)` when `'a` is equal to `'b`
|
||||
- `bi('a)` is a subtype of `bi('b)` always
|
||||
|
||||
That altogether induce the following rules of subtyping in Sophia:
|
||||
|
||||
@ -549,6 +560,45 @@ Sophia has the following types:
|
||||
| oracle_query('a, 'b) | `oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY` |
|
||||
| contract | `ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ` |
|
||||
|
||||
## Hole expression
|
||||
|
||||
Hole expressions, written as `???`, are expressions that are used as a placeholder. During compilation, the compiler will generate a type error indication the type of the hole expression.
|
||||
|
||||
```
|
||||
include "List.aes"
|
||||
contract C =
|
||||
entrypoint f() =
|
||||
List.sum(List.map(???, [1,2,3]))
|
||||
```
|
||||
|
||||
A hole expression found in the example above will generate the error `` Found a hole of type `(int) => int` ``. This says that the compiler expects a function from `int` to `int` in place of the `???` placeholder.
|
||||
|
||||
## Constants
|
||||
|
||||
Constants in Sophia are contract-level bindings that can be used in either contracts or namespaces. The value of a constant can be a literal, another constant, or arithmetic operations applied to other constants. Lists, tuples, maps, and records can also be used to define a constant as long as their elements are also constants.
|
||||
|
||||
The following visibility rules apply to constants:
|
||||
* Constants defined inside a contract are private in that contract. Thus, cannot be accessed through instances of their defining contract.
|
||||
* Constants defined inside a namespace are public. Thus, can be used in other contracts or namespaces.
|
||||
* Constants cannot be defined inside a contract interface.
|
||||
|
||||
When a constant is shadowed, it can be accessed using its qualified name:
|
||||
|
||||
```
|
||||
contract C =
|
||||
let c = 1
|
||||
entrypoint f() =
|
||||
let c = 2
|
||||
c + C.c // the result is 3
|
||||
```
|
||||
|
||||
The name of the constant must be an id; therefore, no pattern matching is allowed when defining a constant:
|
||||
|
||||
```
|
||||
contract C
|
||||
let x::y::_ = [1,2,3] // this will result in an error
|
||||
```
|
||||
|
||||
## Arithmetic
|
||||
|
||||
Sophia integers (`int`) are represented by arbitrary-sized signed words and support the following
|
||||
@ -564,6 +614,14 @@ All operations are *safe* with respect to overflow and underflow.
|
||||
The division and modulo operations throw an arithmetic error if the
|
||||
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
|
||||
|
||||
Sophia integers do not support bit arithmetic. Instead there is a separate
|
||||
|
@ -14,6 +14,7 @@ The out-of-the-box namespaces are:
|
||||
|
||||
- [Address](#address)
|
||||
- [AENS](#aens)
|
||||
- [AENSv2](#aensv2)
|
||||
- [Auth](#auth)
|
||||
- [Bits](#bits)
|
||||
- [Bytes](#bytes)
|
||||
@ -31,6 +32,7 @@ The following ones need to be included as regular files with `.aes` suffix, for
|
||||
include "List.aes"
|
||||
```
|
||||
|
||||
- [AENSCompat](#aenscompat)
|
||||
- [Bitwise](#bitwise)
|
||||
- [BLS12_381](#bls12_381)
|
||||
- [Func](#func)
|
||||
@ -90,13 +92,10 @@ Cast address to contract type C (where `C` is a contract)
|
||||
|
||||
### AENS
|
||||
|
||||
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.
|
||||
The old AENS namespace, kept in the compiler to be able to interact with
|
||||
contracts from before Ceres, compiled using aesophia compiler version 7.x and
|
||||
earlier. Used in [AENSCompat](#aenscompat) when converting between old and new
|
||||
pointers.
|
||||
|
||||
#### Types
|
||||
|
||||
@ -113,12 +112,41 @@ datatype pointee = AccountPt(address) | OraclePt(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
|
||||
|
||||
##### 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
|
||||
@ -129,41 +157,53 @@ type checked against this type at run time.
|
||||
|
||||
##### 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
|
||||
`pointees` for the name. Note: the expiry of the name is always a fixed TTL.
|
||||
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
|
||||
```
|
||||
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
|
||||
`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
|
||||
```
|
||||
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
|
||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
||||
(concatenated as byte arrays)
|
||||
using the private key of the `owner` account for signing.
|
||||
`network id` + `owner address` + `name_hash` + `Contract.address` (concatenated
|
||||
as byte arrays) 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
|
||||
```
|
||||
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.
|
||||
@ -173,10 +213,16 @@ The [signature](./sophia_features.md#delegation-signature) should be over
|
||||
(concatenated as byte arrays)
|
||||
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
|
||||
```
|
||||
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.
|
||||
@ -186,17 +232,24 @@ The [signature](./sophia_features.md#delegation-signature) should be over
|
||||
(concatenated as byte arrays)
|
||||
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
|
||||
```
|
||||
AENS.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
|
||||
new_ptrs : map(string, AENS.pointee), <signature : signature>) : unit
|
||||
AENSv2.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
|
||||
new_ptrs : option(map(string, AENSv2.pointee)), <signature : signature>) : unit
|
||||
```
|
||||
|
||||
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
|
||||
block of the name is not changed.
|
||||
|
||||
Note: Changed to consume `AENSv2.pointee` in v8.0 (Ceres protocol upgrade).
|
||||
|
||||
### Auth
|
||||
|
||||
@ -236,7 +289,10 @@ namespace Chain =
|
||||
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
|
||||
@ -381,6 +437,12 @@ Call.gas_price : int
|
||||
|
||||
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
|
||||
```
|
||||
@ -469,37 +531,12 @@ Chain.block_height : int"
|
||||
|
||||
The height of the current block (i.e. the block in which the current call will be included).
|
||||
|
||||
|
||||
##### coinbase
|
||||
#### to_bytes
|
||||
```
|
||||
Chain.coinbase : address
|
||||
Address.to_bytes(a : address) : bytes(32)
|
||||
```
|
||||
|
||||
The address of the account that mined the current block.
|
||||
|
||||
|
||||
##### timestamp
|
||||
```
|
||||
Chain.timestamp : int
|
||||
```
|
||||
|
||||
The timestamp of the current block.
|
||||
|
||||
|
||||
##### difficulty
|
||||
```
|
||||
Chain.difficulty : int
|
||||
```
|
||||
|
||||
The difficulty of the current block.
|
||||
|
||||
|
||||
##### gas
|
||||
```
|
||||
Chain.gas_limit : int
|
||||
```
|
||||
|
||||
The gas limit of the current block.
|
||||
The binary representation of the address.
|
||||
|
||||
|
||||
##### bytecode_hash
|
||||
@ -538,6 +575,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
|
||||
`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.
|
||||
|
||||
@ -565,6 +609,7 @@ main contract Market =
|
||||
The typechecker must be certain about the created contract's type, so it is
|
||||
worth writing it explicitly as shown in the example.
|
||||
|
||||
|
||||
##### clone
|
||||
```
|
||||
Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ...
|
||||
@ -623,11 +668,54 @@ implementation of the `init` function does not actually return `state`, but
|
||||
calls `put` instead. Moreover, FATE prevents even handcrafted calls to `init`.
|
||||
|
||||
|
||||
##### coinbase
|
||||
```
|
||||
Chain.coinbase : address
|
||||
```
|
||||
|
||||
The address of the account that mined the current block.
|
||||
|
||||
|
||||
##### difficulty
|
||||
```
|
||||
Chain.difficulty : int
|
||||
```
|
||||
|
||||
The difficulty of the current block.
|
||||
|
||||
|
||||
##### event
|
||||
```
|
||||
Chain.event(e : event) : unit
|
||||
```
|
||||
Emits the event. To use this function one needs to define the `event` type as a `datatype` in the contract.
|
||||
|
||||
Emits the event. To use this function one needs to define the `event` type as a
|
||||
`datatype` in the contract.
|
||||
|
||||
|
||||
##### gas\_limit
|
||||
```
|
||||
Chain.gas_limit : int
|
||||
```
|
||||
|
||||
The gas limit of the current block.
|
||||
|
||||
|
||||
##### spend
|
||||
```
|
||||
Chain.spend(to : address, amount : int) : unit
|
||||
```
|
||||
|
||||
Spend `amount` tokens to `to`. Will fail (and abort the contract) if contract
|
||||
doesn't have `amount` tokens to transfer, or, if `to` is not `payable`.
|
||||
|
||||
|
||||
##### timestamp
|
||||
```
|
||||
Chain.timestamp : int
|
||||
```
|
||||
|
||||
The timestamp of the current block (unix time, milliseconds).
|
||||
|
||||
|
||||
### Char
|
||||
@ -914,88 +1002,21 @@ It returns `true` iff the oracle query exist and has the expected type.
|
||||
These need to be explicitly included (with `.aes` suffix)
|
||||
|
||||
|
||||
### Bitwise
|
||||
### AENSCompat
|
||||
|
||||
Bitwise operations on arbitrary precision integers.
|
||||
|
||||
#### bsr
|
||||
#### pointee\_to\_V2
|
||||
```
|
||||
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.
|
||||
|
||||
|
||||
#### bsl
|
||||
#### pointee\_from\_V2
|
||||
```
|
||||
Bitwise.bsl(n : int, x : int) : int
|
||||
AENSCompat.pointee_from_V2(p2 : AENSv2.pointee) : option(AENS.pointee)
|
||||
```
|
||||
|
||||
Logical bit shift `x` left `n` positions.
|
||||
|
||||
|
||||
#### 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`.
|
||||
Translate new pointee format to old, `DataPt` can't be translated, so `None` is returned in this case.
|
||||
|
||||
|
||||
### BLS12\_381
|
||||
|
@ -104,6 +104,7 @@ Implement ::= ':' Sep1(Con, ',')
|
||||
Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
|
||||
| 'record' Id ['(' TVar* ')'] '=' RecordType
|
||||
| 'datatype' Id ['(' TVar* ')'] '=' DataType
|
||||
| 'let' Id [':' Type] '=' Expr
|
||||
| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
|
||||
| Using
|
||||
|
||||
@ -238,6 +239,7 @@ Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x +
|
||||
| Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%'
|
||||
| AccountAddress | ContractAddress // Chain identifiers
|
||||
| OracleAddress | OracleQueryId // Chain identifiers
|
||||
| '???' // Hole expression 1 + ???
|
||||
|
||||
Generator ::= Pattern '<-' Expr // Generator
|
||||
| 'if' '(' Expr ')' // Guard
|
||||
@ -254,8 +256,8 @@ Path ::= Id // Record field
|
||||
|
||||
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
|
||||
| '|>'
|
||||
UnOp ::= '-' | '!'
|
||||
| 'band' | 'bor' | 'bxor' | '<<' | '>>' | '|>'
|
||||
UnOp ::= '-' | '!' | 'bnot'
|
||||
```
|
||||
|
||||
## Operators types
|
||||
@ -264,6 +266,7 @@ UnOp ::= '-' | '!'
|
||||
| --- | ---
|
||||
| `-` `+` `*` `/` `mod` `^` | arithmetic operators
|
||||
| `!` `&&` `||` | logical operators
|
||||
| `band` `bor` `bxor` `bnot` `<<` `>>` | bitwise operators
|
||||
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
|
||||
| `::` `++` | list operators
|
||||
| `|>` | functional operators
|
||||
@ -274,13 +277,17 @@ In order of highest to lowest precedence.
|
||||
|
||||
| Operators | Associativity
|
||||
| --- | ---
|
||||
| `!` | right
|
||||
| `!` `bnot`| right
|
||||
| `^` | left
|
||||
| `*` `/` `mod` | left
|
||||
| `-` (unary) | right
|
||||
| `+` `-` | left
|
||||
| `<<` `>>` | left
|
||||
| `::` `++` | right
|
||||
| `<` `>` `=<` `>=` `==` `!=` | none
|
||||
| `band` | left
|
||||
| `bxor` | left
|
||||
| `bor` | left
|
||||
| `&&` | right
|
||||
| `||` | right
|
||||
| `|>` | left
|
||||
|
17
priv/stdlib/AENSCompat.aes
Normal file
17
priv/stdlib/AENSCompat.aes
Normal file
@ -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,8 +2,7 @@
|
||||
|
||||
{erl_opts, [debug_info]}.
|
||||
|
||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.2.0"}}}
|
||||
, {getopt, "1.0.1"}
|
||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.3.0"}}}
|
||||
, {eblake2, "1.0.0"}
|
||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}
|
||||
]}.
|
||||
@ -14,7 +13,7 @@
|
||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||
]}.
|
||||
|
||||
{relx, [{release, {aesophia, "7.0.1"},
|
||||
{relx, [{release, {aesophia, "8.0.0"},
|
||||
[aesophia, aebytecode, getopt]},
|
||||
|
||||
{dev_mode, true},
|
||||
|
@ -1,11 +1,11 @@
|
||||
{"1.2.0",
|
||||
[{<<"aebytecode">>,
|
||||
{git,"https://github.com/aeternity/aebytecode.git",
|
||||
{ref,"2a0a397afad6b45da52572170f718194018bf33c"}},
|
||||
{ref,"b38349274fc2bed98d7fe86877e6e1a2df302109"}},
|
||||
0},
|
||||
{<<"aeserialization">>,
|
||||
{git,"https://github.com/aeternity/aeserialization.git",
|
||||
{ref,"eb68fe331bd476910394966b7f5ede7a74d37e35"}},
|
||||
{ref,"177bf604b2a05e940f92cf00e96e6e269e708245"}},
|
||||
1},
|
||||
{<<"base58">>,
|
||||
{git,"https://github.com/aeternity/erl-base58.git",
|
||||
@ -16,7 +16,7 @@
|
||||
{git,"https://github.com/aeternity/enacl.git",
|
||||
{ref,"793ddb502f7fe081302e1c42227dca70b09f8e17"}},
|
||||
2},
|
||||
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0},
|
||||
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},1},
|
||||
{<<"jsx">>,
|
||||
{git,"https://github.com/talentdeficit/jsx.git",
|
||||
{ref,"3074d4865b3385a050badf7828ad31490d860df5"}},
|
||||
|
@ -124,15 +124,18 @@
|
||||
|
||||
-type variance() :: invariant | covariant | contravariant | bivariant.
|
||||
|
||||
-type fun_info() :: {aeso_syntax:ann(), typesig() | type()}.
|
||||
-type type_info() :: {aeso_syntax:ann(), typedef()}.
|
||||
-type var_info() :: {aeso_syntax:ann(), utype()}.
|
||||
-type fun_info() :: {aeso_syntax:ann(), typesig() | type()}.
|
||||
-type type_info() :: {aeso_syntax:ann(), typedef()}.
|
||||
-type const_info() :: {aeso_syntax:ann(), type()}.
|
||||
-type var_info() :: {aeso_syntax:ann(), utype()}.
|
||||
|
||||
-type fun_env() :: [{name(), fun_info()}].
|
||||
-type type_env() :: [{name(), type_info()}].
|
||||
-type fun_env() :: [{name(), fun_info()}].
|
||||
-type type_env() :: [{name(), type_info()}].
|
||||
-type const_env() :: [{name(), const_info()}].
|
||||
|
||||
-record(scope, { funs = [] :: fun_env()
|
||||
, types = [] :: type_env()
|
||||
, consts = [] :: const_env()
|
||||
, access = public :: access()
|
||||
, kind = namespace :: namespace | contract
|
||||
, ann = [{origin, system}] :: aeso_syntax:ann()
|
||||
@ -152,6 +155,7 @@
|
||||
, in_guard = false :: boolean()
|
||||
, stateful = false :: boolean()
|
||||
, unify_throws = true :: boolean()
|
||||
, current_const = none :: none | aeso_syntax:id()
|
||||
, current_function = none :: none | aeso_syntax:id()
|
||||
, what = top :: top | namespace | contract | contract_interface
|
||||
}).
|
||||
@ -183,9 +187,13 @@ pop_scope(Env) ->
|
||||
get_scope(#env{ scopes = Scopes }, Name) ->
|
||||
maps:get(Name, Scopes, false).
|
||||
|
||||
-spec get_current_scope(env()) -> scope().
|
||||
get_current_scope(#env{ namespace = NS, scopes = Scopes }) ->
|
||||
maps:get(NS, Scopes).
|
||||
|
||||
-spec on_current_scope(env(), fun((scope()) -> scope())) -> env().
|
||||
on_current_scope(Env = #env{ namespace = NS, scopes = Scopes }, Fun) ->
|
||||
Scope = maps:get(NS, Scopes),
|
||||
Scope = get_current_scope(Env),
|
||||
Env#env{ scopes = Scopes#{ NS => Fun(Scope) } }.
|
||||
|
||||
-spec on_scopes(env(), fun((scope()) -> scope())) -> env().
|
||||
@ -193,8 +201,8 @@ on_scopes(Env = #env{ scopes = Scopes }, Fun) ->
|
||||
Env#env{ scopes = maps:map(fun(_, Scope) -> Fun(Scope) end, Scopes) }.
|
||||
|
||||
-spec bind_var(aeso_syntax:id(), utype(), env()) -> env().
|
||||
bind_var({id, Ann, X}, T, Env = #env{ vars = Vars }) ->
|
||||
when_warning(warn_shadowing, fun() -> warn_potential_shadowing(Ann, X, Vars) end),
|
||||
bind_var({id, Ann, X}, T, Env) ->
|
||||
when_warning(warn_shadowing, fun() -> warn_potential_shadowing(Env, Ann, X) end),
|
||||
Env#env{ vars = [{X, {Ann, T}} | Env#env.vars] }.
|
||||
|
||||
-spec bind_vars([{aeso_syntax:id(), utype()}], env()) -> env().
|
||||
@ -229,7 +237,7 @@ force_bind_fun(X, Type, Env = #env{ what = What }) ->
|
||||
NoCode = get_option(no_code, false),
|
||||
Entry = if X == "init", What == contract, not NoCode ->
|
||||
{reserved_init, Ann, Type};
|
||||
What == contract_interface -> {contract_fun, Ann, Type};
|
||||
What == contract; What == contract_interface -> {contract_fun, Ann, Type};
|
||||
true -> {Ann, Type}
|
||||
end,
|
||||
on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) ->
|
||||
@ -247,6 +255,37 @@ bind_type(X, Ann, Def, Env) ->
|
||||
Scope#scope{ types = [{X, {Ann, Def}} | Types] }
|
||||
end).
|
||||
|
||||
-spec bind_const(name(), aeso_syntax:ann(), type(), env()) -> env().
|
||||
bind_const(X, Ann, Type, Env) ->
|
||||
case lookup_env(Env, term, Ann, [X]) of
|
||||
false ->
|
||||
on_current_scope(Env, fun(Scope = #scope{ consts = Consts }) ->
|
||||
Scope#scope{ consts = [{X, {Ann, Type}} | Consts] }
|
||||
end);
|
||||
_ ->
|
||||
type_error({duplicate_definition, X, [Ann, aeso_syntax:get_ann(Type)]}),
|
||||
Env
|
||||
end.
|
||||
|
||||
-spec bind_consts(env(), #{ name() => aeso_syntax:decl() }, [{acyclic, name()} | {cyclic, [name()]}], [aeso_syntax:decl()]) ->
|
||||
{env(), [aeso_syntax:decl()]}.
|
||||
bind_consts(Env, _Consts, [], Acc) ->
|
||||
{Env, lists:reverse(Acc)};
|
||||
bind_consts(Env, Consts, [{cyclic, Xs} | _SCCs], _Acc) ->
|
||||
ConstDecls = [ maps:get(X, Consts) || X <- Xs ],
|
||||
type_error({mutually_recursive_constants, lists:reverse(ConstDecls)}),
|
||||
{Env, []};
|
||||
bind_consts(Env, Consts, [{acyclic, X} | SCCs], Acc) ->
|
||||
case maps:get(X, Consts, undefined) of
|
||||
Const = {letval, Ann, Id, _} ->
|
||||
NewConst = {letval, _, {typed, _, _, Type}, _} = infer_const(Env, Const),
|
||||
NewEnv = bind_const(name(Id), Ann, Type, Env),
|
||||
bind_consts(NewEnv, Consts, SCCs, [NewConst | Acc]);
|
||||
undefined ->
|
||||
%% When a used id is not a letval, a type error will be thrown
|
||||
bind_consts(Env, Consts, SCCs, Acc)
|
||||
end.
|
||||
|
||||
%% Bind state primitives
|
||||
-spec bind_state(env()) -> env().
|
||||
bind_state(Env) ->
|
||||
@ -312,11 +351,11 @@ bind_contract(Typing, {Contract, Ann, Id, _Impls, Contents}, Env)
|
||||
Sys = [{origin, system}],
|
||||
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(aeso_syntax:set_ann(Sys, Type))}
|
||||
|| {fun_decl, AnnF, Entrypoint, Type = {fun_t, _, _, _, _}} <- Contents ] ++
|
||||
[ {field_t, AnnF, Entrypoint,
|
||||
contract_call_type(
|
||||
{fun_t, AnnF, [], [TypeOrFresh(Arg) || Arg <- Args], TypeOrFresh(Ret)})
|
||||
{fun_t, Sys, [], [TypeOrFresh(Arg) || Arg <- Args], TypeOrFresh(Ret)})
|
||||
}
|
||||
|| {letfun, AnnF, Entrypoint = {id, _, Name}, Args, _Type, [{guarded, _, [], Ret}]} <- Contents,
|
||||
Name =/= "init"
|
||||
@ -426,23 +465,35 @@ lookup_env(Env, Kind, Ann, Name) ->
|
||||
lookup_env1(#env{ namespace = Current, used_namespaces = UsedNamespaces, scopes = Scopes }, Kind, Ann, QName) ->
|
||||
Qual = lists:droplast(QName),
|
||||
Name = lists:last(QName),
|
||||
QNameIsEvent = lists:suffix(["Chain", "event"], QName),
|
||||
AllowPrivate = lists:prefix(Qual, Current),
|
||||
%% Get the scope
|
||||
case maps:get(Qual, Scopes, false) of
|
||||
false -> false; %% TODO: return reason for not in scope
|
||||
#scope{ funs = Funs, types = Types } ->
|
||||
#scope{ funs = Funs, types = Types, consts = Consts, kind = ScopeKind } ->
|
||||
Defs = case Kind of
|
||||
type -> Types;
|
||||
term -> Funs
|
||||
end,
|
||||
%% Look up the unqualified name
|
||||
case proplists:get_value(Name, Defs, false) of
|
||||
false -> false;
|
||||
false ->
|
||||
case proplists:get_value(Name, Consts, false) of
|
||||
false ->
|
||||
false;
|
||||
Const when AllowPrivate; ScopeKind == namespace ->
|
||||
{QName, Const};
|
||||
Const ->
|
||||
type_error({contract_treated_as_namespace_constant, Ann, QName}),
|
||||
{QName, Const}
|
||||
end;
|
||||
{reserved_init, Ann1, Type} ->
|
||||
type_error({cannot_call_init_function, Ann}),
|
||||
{QName, {Ann1, Type}}; %% Return the type to avoid an extra not-in-scope error
|
||||
{contract_fun, Ann1, Type} when AllowPrivate orelse QNameIsEvent ->
|
||||
{QName, {Ann1, Type}};
|
||||
{contract_fun, Ann1, Type} ->
|
||||
type_error({contract_treated_as_namespace, Ann, QName}),
|
||||
type_error({contract_treated_as_namespace_entrypoint, Ann, QName}),
|
||||
{QName, {Ann1, Type}};
|
||||
{Ann1, _} = E ->
|
||||
%% Check that it's not private (or we can see private funs)
|
||||
@ -483,8 +534,11 @@ qname({qid, _, Xs}) -> Xs;
|
||||
qname({con, _, X}) -> [X];
|
||||
qname({qcon, _, Xs}) -> Xs.
|
||||
|
||||
-spec name(aeso_syntax:id() | aeso_syntax:con()) -> name().
|
||||
name({_, _, X}) -> X.
|
||||
-spec name(Named | {typed, _, Named, _}) -> name() when
|
||||
Named :: aeso_syntax:id() | aeso_syntax:con().
|
||||
name({typed, _, X, _}) -> name(X);
|
||||
name({id, _, X}) -> X;
|
||||
name({con, _, X}) -> X.
|
||||
|
||||
-spec qid(aeso_syntax:ann(), qname()) -> aeso_syntax:id() | aeso_syntax:qid().
|
||||
qid(Ann, [X]) -> {id, Ann, X};
|
||||
@ -540,6 +594,8 @@ global_env() ->
|
||||
TTL = {qid, Ann, ["Chain", "ttl"]},
|
||||
Pointee = {qid, Ann, ["AENS", "pointee"]},
|
||||
AENSName = {qid, Ann, ["AENS", "name"]},
|
||||
PointeeV2 = {qid, Ann, ["AENSv2", "pointee"]},
|
||||
AENSNameV2 = {qid, Ann, ["AENSv2", "name"]},
|
||||
Fr = {qid, Ann, ["MCL_BLS12_381", "fr"]},
|
||||
Fp = {qid, Ann, ["MCL_BLS12_381", "fp"]},
|
||||
Fp2 = {tuple_t, Ann, [Fp, Fp]},
|
||||
@ -675,14 +731,7 @@ global_env() ->
|
||||
|
||||
AENSScope = #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, Pointee))], Unit)},
|
||||
{"lookup", Fun([String], option_t(Ann, AENSName))},
|
||||
%% AENS pointee constructors
|
||||
[%% AENS pointee constructors
|
||||
{"AccountPt", Fun1(Address, Pointee)},
|
||||
{"OraclePt", Fun1(Address, Pointee)},
|
||||
{"ContractPt", Fun1(Address, Pointee)},
|
||||
@ -692,6 +741,26 @@ global_env() ->
|
||||
])
|
||||
, 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
|
||||
{ funs = MkDefs(
|
||||
[{"from_list", Fun1(List(Pair(K, V)), Map(K, V))},
|
||||
@ -711,7 +780,8 @@ global_env() ->
|
||||
{"ecrecover_secp256k1", Fun([Hash, Bytes(65)], Option(Bytes(20)))},
|
||||
{"sha3", 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
|
||||
MCL_BLS12_381_Scope = #scope
|
||||
@ -796,14 +866,16 @@ global_env() ->
|
||||
]) },
|
||||
|
||||
%% 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)},
|
||||
{"to_bytes", Fun1(Address, Bytes(32))},
|
||||
{"to_contract", FunC(address_to_contract, [Address], A)},
|
||||
{"is_oracle", Fun1(Address, Bool)},
|
||||
{"is_contract", Fun1(Address, Bool)},
|
||||
{"is_payable", Fun1(Address, Bool)}]) },
|
||||
|
||||
|
||||
#env{ scopes =
|
||||
#{ [] => TopScope
|
||||
, ["Chain"] => ChainScope
|
||||
@ -811,6 +883,7 @@ global_env() ->
|
||||
, ["Call"] => CallScope
|
||||
, ["Oracle"] => OracleScope
|
||||
, ["AENS"] => AENSScope
|
||||
, ["AENSv2"] => AENSv2Scope
|
||||
, ["Map"] => MapScope
|
||||
, ["Auth"] => AuthScope
|
||||
, ["Crypto"] => CryptoScope
|
||||
@ -906,6 +979,7 @@ infer1(Env0, [Contract0 = {Contract, Ann, ConName, Impls, Code} | Rest], Acc, Op
|
||||
contract -> ets_insert(defined_contracts, {qname(ConName)});
|
||||
contract_interface -> ok
|
||||
end,
|
||||
check_contract_preserved_payability(Env, ConName, Ann, Impls, Acc, What),
|
||||
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),
|
||||
@ -931,6 +1005,25 @@ infer1(Env, [{pragma, _, _} | Rest], Acc, Options) ->
|
||||
%% Pragmas are checked in check_modifiers
|
||||
infer1(Env, Rest, Acc, Options).
|
||||
|
||||
-spec check_contract_preserved_payability(env(), Con, Ann, Impls, Contracts, Kind) -> ok | no_return() when
|
||||
Con :: aeso_syntax:con(),
|
||||
Ann :: aeso_syntax:ann(),
|
||||
Impls :: [Con],
|
||||
Contracts :: [aeso_syntax:decl()],
|
||||
Kind :: contract | contract_interface.
|
||||
check_contract_preserved_payability(Env, ContractName, ContractAnn, Impls, DefinedContracts, Kind) ->
|
||||
Payable = proplists:get_value(payable, ContractAnn, false),
|
||||
ImplsNames = [ name(I) || I <- Impls ],
|
||||
Interfaces = [ Con || I = {contract_interface, _, Con, _, _} <- DefinedContracts,
|
||||
lists:member(name(Con), ImplsNames),
|
||||
aeso_syntax:get_ann(payable, I, false) ],
|
||||
|
||||
create_type_errors(),
|
||||
[ type_error({unpreserved_payablity, Kind, ContractName, I}) || I <- Interfaces, Payable == false ],
|
||||
destroy_and_report_type_errors(Env),
|
||||
|
||||
ok.
|
||||
|
||||
%% 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().
|
||||
@ -1031,6 +1124,7 @@ infer_contract(Env0, What, Defs0, Options) ->
|
||||
({fun_clauses, _, _, _, _}) -> function;
|
||||
({fun_decl, _, _, _}) -> prototype;
|
||||
({using, _, _, _, _}) -> using;
|
||||
({letval, _, _, _}) -> constant;
|
||||
(_) -> unexpected
|
||||
end,
|
||||
Get = fun(K, In) -> [ Def || Def <- In, Kind(Def) == K ] end,
|
||||
@ -1046,11 +1140,12 @@ infer_contract(Env0, What, Defs0, Options) ->
|
||||
contract_interface -> Env1;
|
||||
contract -> bind_state(Env1) %% bind state and put
|
||||
end,
|
||||
{ProtoSigs, Decls} = lists:unzip([ check_fundecl(Env1, Decl) || Decl <- Get(prototype, Defs) ]),
|
||||
{Env2C, Consts} = check_constants(Env2, Get(constant, Defs)),
|
||||
{ProtoSigs, Decls} = lists:unzip([ check_fundecl(Env2C, Decl) || Decl <- Get(prototype, Defs) ]),
|
||||
[ type_error({missing_definition, Id}) || {fun_decl, _, Id, _} <- Decls,
|
||||
What =:= contract,
|
||||
get_option(no_code, false) =:= false ],
|
||||
Env3 = bind_funs(ProtoSigs, Env2),
|
||||
Env3 = bind_funs(ProtoSigs, Env2C),
|
||||
Functions = Get(function, Defs),
|
||||
%% Check for duplicates in Functions (we turn it into a map below)
|
||||
FunBind = fun({letfun, Ann, {id, _, Fun}, _, _, _}) -> {Fun, {tuple_t, Ann, []}};
|
||||
@ -1070,7 +1165,7 @@ infer_contract(Env0, What, Defs0, Options) ->
|
||||
check_entrypoints(Defs1),
|
||||
destroy_and_report_type_errors(Env4),
|
||||
%% Add inferred types of definitions
|
||||
{Env5, TypeDefs ++ Decls ++ Defs1}.
|
||||
{Env5, TypeDefs ++ Decls ++ Consts ++ Defs1}.
|
||||
|
||||
%% Restructure blocks into multi-clause fundefs (`fun_clauses`).
|
||||
-spec process_blocks([aeso_syntax:decl()]) -> [aeso_syntax:decl()].
|
||||
@ -1220,6 +1315,21 @@ opposite_variance(covariant) -> contravariant;
|
||||
opposite_variance(contravariant) -> covariant;
|
||||
opposite_variance(bivariant) -> bivariant.
|
||||
|
||||
-spec check_constants(env(), [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}.
|
||||
check_constants(Env = #env{ what = What }, Consts) ->
|
||||
HasValidId = fun({letval, _, {id, _, _}, _}) -> true;
|
||||
({letval, _, {typed, _, {id, _, _}, _}, _}) -> true;
|
||||
(_) -> false
|
||||
end,
|
||||
{Valid, Invalid} = lists:partition(HasValidId, Consts),
|
||||
[ type_error({invalid_const_id, aeso_syntax:get_ann(Pat)}) || {letval, _, Pat, _} <- Invalid ],
|
||||
[ type_error({illegal_const_in_interface, Ann}) || {letval, Ann, _, _} <- Valid, What == contract_interface ],
|
||||
when_warning(warn_unused_constants, fun() -> potential_unused_constants(Env, Valid) end),
|
||||
ConstMap = maps:from_list([ {name(Id), Const} || Const = {letval, _, Id, _} <- Valid ]),
|
||||
DepGraph = maps:map(fun(_, Const) -> aeso_syntax_utils:used_ids(Const) end, ConstMap),
|
||||
SCCs = aeso_utils:scc(DepGraph),
|
||||
bind_consts(Env, ConstMap, SCCs, []).
|
||||
|
||||
check_usings(Env, []) ->
|
||||
Env;
|
||||
check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con, Alias, Parts} | Rest]) ->
|
||||
@ -1234,6 +1344,10 @@ check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con,
|
||||
create_type_errors(),
|
||||
type_error({using_undefined_namespace, Ann, qname(Con)}),
|
||||
destroy_and_report_type_errors(Env);
|
||||
#scope{kind = contract} ->
|
||||
create_type_errors(),
|
||||
type_error({using_undefined_namespace, Ann, qname(Con)}),
|
||||
destroy_and_report_type_errors(Env);
|
||||
Scope ->
|
||||
Nsp = case Parts of
|
||||
none ->
|
||||
@ -1487,19 +1601,37 @@ check_reserved_entrypoints(Funs) ->
|
||||
check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type = {fun_t, _, _, _, _}}) ->
|
||||
Type1 = {fun_t, _, Named, Args, Ret} = check_type(Env, Type),
|
||||
TypeSig = {type_sig, Ann, none, Named, Args, Ret},
|
||||
register_implementation(Name),
|
||||
register_implementation(Id, TypeSig),
|
||||
{{Name, TypeSig}, {fun_decl, Ann, Id, Type1}};
|
||||
check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type}) ->
|
||||
type_error({fundecl_must_have_funtype, Ann, Id, Type}),
|
||||
{{Name, {type_sig, Ann, none, [], [], Type}}, check_type(Env, Type)}.
|
||||
|
||||
%% Register the function FunName as implemented by deleting it from the functions
|
||||
%% Register the function FunId 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) ->
|
||||
-spec register_implementation(FunId, FunSig) -> true | no_return() when
|
||||
FunId :: aeso_syntax:id(),
|
||||
FunSig :: typesig().
|
||||
register_implementation(Id, Sig) ->
|
||||
Name = name(Id),
|
||||
case ets_lookup(functions_to_implement, Name) of
|
||||
[{Name, _, {fun_decl, _, _, _}}] ->
|
||||
[{Name, Interface, Decl = {fun_decl, _, DeclId, _}}] ->
|
||||
DeclStateful = aeso_syntax:get_ann(stateful, Decl, false),
|
||||
DeclPayable = aeso_syntax:get_ann(payable, Decl, false),
|
||||
|
||||
SigEntrypoint = aeso_syntax:get_ann(entrypoint, Sig, false),
|
||||
SigStateful = aeso_syntax:get_ann(stateful, Sig, false),
|
||||
SigPayable = aeso_syntax:get_ann(payable, Sig, false),
|
||||
|
||||
[ type_error({function_should_be_entrypoint, Id, DeclId, Interface})
|
||||
|| not SigEntrypoint ],
|
||||
|
||||
[ type_error({entrypoint_cannot_be_stateful, Id, DeclId, Interface})
|
||||
|| SigStateful andalso not DeclStateful ],
|
||||
|
||||
[ type_error({entrypoint_must_be_payable, Id, DeclId, Interface})
|
||||
|| not SigPayable andalso DeclPayable ],
|
||||
|
||||
ets_delete(functions_to_implement, Name);
|
||||
[] ->
|
||||
true;
|
||||
@ -1509,9 +1641,9 @@ register_implementation(Name) ->
|
||||
|
||||
infer_nonrec(Env, LetFun) ->
|
||||
create_constraints(),
|
||||
NewLetFun = {{FunName, _}, _} = infer_letfun(Env, LetFun),
|
||||
NewLetFun = {{_, Sig}, _} = infer_letfun(Env, LetFun),
|
||||
check_special_funs(Env, NewLetFun),
|
||||
register_implementation(FunName),
|
||||
register_implementation(get_letfun_id(LetFun), Sig),
|
||||
solve_then_destroy_and_report_unsolved_constraints(Env),
|
||||
Result = {TypeSig, _} = instantiate(NewLetFun),
|
||||
print_typesig(TypeSig),
|
||||
@ -1540,8 +1672,8 @@ infer_letrec(Env, Defs) ->
|
||||
ExtendEnv = bind_funs(Funs, Env),
|
||||
Inferred =
|
||||
[ begin
|
||||
Res = {{Name, TypeSig}, _} = infer_letfun(ExtendEnv, LF),
|
||||
register_implementation(Name),
|
||||
Res = {{Name, TypeSig}, LetFun} = infer_letfun(ExtendEnv, LF),
|
||||
register_implementation(get_letfun_id(LetFun), TypeSig),
|
||||
Got = proplists:get_value(Name, Funs),
|
||||
Expect = typesig_to_fun_t(TypeSig),
|
||||
unify(Env, Got, Expect, {check_typesig, Name, Got, Expect}),
|
||||
@ -1593,6 +1725,9 @@ infer_letfun1(Env0 = #env{ namespace = NS }, {letfun, Attrib, Fun = {id, NameAtt
|
||||
{{Name, TypeSig},
|
||||
{letfun, Attrib, {id, NameAttrib, Name}, TypedArgs, ResultType, NewGuardedBodies}}.
|
||||
|
||||
get_letfun_id({fun_clauses, _, Id, _, _}) -> Id;
|
||||
get_letfun_id({letfun, _, Id, _, _, _}) -> Id.
|
||||
|
||||
desugar_clauses(Ann, Fun, {type_sig, _, _, _, ArgTypes, RetType}, Clauses) ->
|
||||
NeedDesugar =
|
||||
case Clauses of
|
||||
@ -1639,9 +1774,19 @@ lookup_name(Env = #env{ namespace = NS, current_function = CurFn }, As, Id, Opti
|
||||
type_error({unbound_variable, Id}),
|
||||
{Id, fresh_uvar(As)};
|
||||
{QId, {_, Ty}} ->
|
||||
when_warning(warn_unused_variables, fun() -> used_variable(NS, name(CurFn), QId) end),
|
||||
when_warning(warn_unused_functions,
|
||||
fun() -> register_function_call(NS ++ qname(CurFn), QId) end),
|
||||
%% Variables and functions cannot be used when CurFn is `none`.
|
||||
%% i.e. they cannot be used in toplevel constants
|
||||
[ begin
|
||||
when_warning(
|
||||
warn_unused_variables,
|
||||
fun() -> used_variable(NS, name(CurFn), QId) end),
|
||||
when_warning(
|
||||
warn_unused_functions,
|
||||
fun() -> register_function_call(NS ++ qname(CurFn), QId) end)
|
||||
end || CurFn =/= none ],
|
||||
|
||||
when_warning(warn_unused_constants, fun() -> used_constant(NS, QId) end),
|
||||
|
||||
Freshen = proplists:get_value(freshen, Options, false),
|
||||
check_stateful(Env, Id, Ty),
|
||||
Ty1 = case Ty of
|
||||
@ -1670,10 +1815,14 @@ check_stateful(#env { current_function = Fun }, _Id, _Type) ->
|
||||
|
||||
%% Hack: don't allow passing the 'value' named arg if not stateful. This only
|
||||
%% works since the user can't create functions with named arguments.
|
||||
check_stateful_named_arg(#env{ stateful = false, current_function = Fun }, {id, _, "value"}, Default) ->
|
||||
check_stateful_named_arg(#env{ stateful = Stateful, current_function = Fun }, {id, _, "value"}, Default) ->
|
||||
case Default of
|
||||
{int, _, 0} -> ok;
|
||||
_ -> type_error({value_arg_not_allowed, Default, Fun})
|
||||
_ ->
|
||||
case Stateful of
|
||||
true -> when_warning(warn_unused_stateful, fun() -> used_stateful(Fun) end);
|
||||
false -> type_error({value_arg_not_allowed, Default, Fun})
|
||||
end
|
||||
end;
|
||||
check_stateful_named_arg(_, _, _) -> ok.
|
||||
|
||||
@ -1791,6 +1940,10 @@ infer_expr(_Env, Body={contract_pubkey, As, _}) ->
|
||||
{typed, As, Body, Con};
|
||||
infer_expr(_Env, Body={id, As, "_"}) ->
|
||||
{typed, As, Body, fresh_uvar(As)};
|
||||
infer_expr(_Env, Body={id, As, "???"}) ->
|
||||
T = fresh_uvar(As),
|
||||
type_error({hole_found, As, T}),
|
||||
{typed, As, Body, T};
|
||||
infer_expr(Env, Id = {Tag, As, _}) when Tag == id; Tag == qid ->
|
||||
{QName, Type} = lookup_name(Env, As, Id),
|
||||
{typed, As, QName, Type};
|
||||
@ -1880,7 +2033,7 @@ infer_expr(Env, {app, Ann, Fun, Args0} = App) ->
|
||||
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When),
|
||||
when_warning(warn_negative_spend, fun() -> warn_potential_negative_spend(Ann, NewFun1, NewArgs) end),
|
||||
[ add_constraint({aens_resolve_type, GeneralResultType})
|
||||
|| element(3, FunName) =:= ["AENS", "resolve"] ],
|
||||
|| element(3, FunName) =:= ["AENSv2", "resolve"] ],
|
||||
[ add_constraint({oracle_type, Ann, OType})
|
||||
|| OType <- [get_oracle_type(FunName, ArgTypes, GeneralResultType)],
|
||||
OType =/= false ],
|
||||
@ -1998,6 +2151,81 @@ infer_expr(Env, Let = {letfun, Attrs, _, _, _, _}) ->
|
||||
type_error({missing_body_for_let, Attrs}),
|
||||
infer_expr(Env, {block, Attrs, [Let, abort_expr(Attrs, "missing body")]}).
|
||||
|
||||
check_valid_const_expr({bool, _, _}) ->
|
||||
true;
|
||||
check_valid_const_expr({int, _, _}) ->
|
||||
true;
|
||||
check_valid_const_expr({char, _, _}) ->
|
||||
true;
|
||||
check_valid_const_expr({string, _, _}) ->
|
||||
true;
|
||||
check_valid_const_expr({bytes, _, _}) ->
|
||||
true;
|
||||
check_valid_const_expr({account_pubkey, _, _}) ->
|
||||
true;
|
||||
check_valid_const_expr({oracle_pubkey, _, _}) ->
|
||||
true;
|
||||
check_valid_const_expr({oracle_query_id, _, _}) ->
|
||||
true;
|
||||
check_valid_const_expr({contract_pubkey, _, _}) ->
|
||||
true;
|
||||
check_valid_const_expr({id, _, "_"}) ->
|
||||
true;
|
||||
check_valid_const_expr({Tag, _, _}) when Tag == id; Tag == qid; Tag == con; Tag == qcon ->
|
||||
true;
|
||||
check_valid_const_expr({tuple, _, Cpts}) ->
|
||||
lists:all(fun(X) -> X end, [ check_valid_const_expr(C) || C <- Cpts ]);
|
||||
check_valid_const_expr({list, _, Elems}) ->
|
||||
lists:all(fun(X) -> X end, [ check_valid_const_expr(Elem) || Elem <- Elems ]);
|
||||
check_valid_const_expr({list_comp, _, _, _}) ->
|
||||
false;
|
||||
check_valid_const_expr({typed, _, Body, _}) ->
|
||||
check_valid_const_expr(Body);
|
||||
check_valid_const_expr({app, Ann, Fun, Args0}) ->
|
||||
{_, Args} = split_args(Args0),
|
||||
case aeso_syntax:get_ann(format, Ann) of
|
||||
infix ->
|
||||
lists:all(fun(X) -> X end, [ check_valid_const_expr(Arg) || Arg <- Args ]);
|
||||
prefix ->
|
||||
lists:all(fun(X) -> X end, [ check_valid_const_expr(Arg) || Arg <- Args ]);
|
||||
_ ->
|
||||
%% Applications of data constructors are allowed in constants
|
||||
lists:member(element(1, Fun), [con, qcon])
|
||||
end;
|
||||
check_valid_const_expr({'if', _, _, _, _}) ->
|
||||
false;
|
||||
check_valid_const_expr({switch, _, _, _}) ->
|
||||
false;
|
||||
check_valid_const_expr({record, _, Fields}) ->
|
||||
lists:all(fun(X) -> X end, [ check_valid_const_expr(Expr) || {field, _, _, Expr} <- Fields ]);
|
||||
check_valid_const_expr({record, _, _, _}) ->
|
||||
false;
|
||||
check_valid_const_expr({proj, _, Record, _}) ->
|
||||
check_valid_const_expr(Record);
|
||||
% Maps
|
||||
check_valid_const_expr({map_get, _, _, _}) -> %% map lookup
|
||||
false;
|
||||
check_valid_const_expr({map_get, _, _, _, _}) -> %% map lookup with default
|
||||
false;
|
||||
check_valid_const_expr({map, _, KVs}) -> %% map construction
|
||||
lists:all(fun(X) -> X end, [ check_valid_const_expr(K) andalso check_valid_const_expr(V) || {K, V} <- KVs ]);
|
||||
check_valid_const_expr({map, _, _, _}) -> %% map update
|
||||
false;
|
||||
check_valid_const_expr({block, _, _}) ->
|
||||
false;
|
||||
check_valid_const_expr({record_or_map_error, _, Fields}) ->
|
||||
lists:all(fun(X) -> X end, [ check_valid_const_expr(Expr) || {field, _, _, Expr} <- Fields ]);
|
||||
check_valid_const_expr({record_or_map_error, _, _, _}) ->
|
||||
false;
|
||||
check_valid_const_expr({lam, _, _, _}) ->
|
||||
false;
|
||||
check_valid_const_expr({letpat, _, _, _}) ->
|
||||
false;
|
||||
check_valid_const_expr({letval, _, _, _}) ->
|
||||
false;
|
||||
check_valid_const_expr({letfun, _, _, _, _, _}) ->
|
||||
false.
|
||||
|
||||
infer_var_args_fun(Env, {typed, Ann, Fun, FunType0}, NamedArgs, ArgTypes) ->
|
||||
FunType =
|
||||
case Fun of
|
||||
@ -2122,9 +2350,14 @@ infer_pattern(Env, Pattern) ->
|
||||
NewPattern = infer_expr(NewEnv, Pattern),
|
||||
{NewEnv#env{ in_pattern = Env#env.in_pattern }, NewPattern}.
|
||||
|
||||
infer_case(Env = #env{ namespace = NS, current_function = {id, _, Fun} }, Attrs, Pattern, ExprType, GuardedBranches, SwitchType) ->
|
||||
infer_case(Env = #env{ namespace = NS, current_function = FunId }, Attrs, Pattern, ExprType, GuardedBranches, SwitchType) ->
|
||||
{NewEnv, NewPattern = {typed, _, _, PatType}} = infer_pattern(Env, Pattern),
|
||||
when_warning(warn_unused_variables, fun() -> potential_unused_variables(NS, Fun, free_vars(Pattern)) end),
|
||||
|
||||
%% Make sure we are inside a function before warning about potentially unused var
|
||||
[ when_warning(warn_unused_variables,
|
||||
fun() -> potential_unused_variables(NS, Fun, free_vars(Pattern)) end)
|
||||
|| {id, _, Fun} <- [FunId] ],
|
||||
|
||||
InferGuardedBranches = fun({guarded, Ann, Guards, Branch}) ->
|
||||
NewGuards = lists:map(fun(Guard) ->
|
||||
check_expr(NewEnv#env{ in_guard = true }, Guard, {id, Attrs, "bool"})
|
||||
@ -2158,6 +2391,19 @@ infer_block(Env, Attrs, [E|Rest], BlockType) ->
|
||||
when_warning(warn_unused_return_value, fun() -> potential_unused_return_value(NewE) end),
|
||||
[NewE|infer_block(Env, Attrs, Rest, BlockType)].
|
||||
|
||||
infer_const(Env, {letval, Ann, TypedId = {typed, _, Id = {id, _, _}, Type}, Expr}) ->
|
||||
check_valid_const_expr(Expr) orelse type_error({invalid_const_expr, Id}),
|
||||
NewExpr = check_expr(Env#env{ current_const = Id }, Expr, Type),
|
||||
{letval, Ann, TypedId, NewExpr};
|
||||
infer_const(Env, {letval, Ann, Id = {id, AnnId, _}, Expr}) ->
|
||||
check_valid_const_expr(Expr) orelse type_error({invalid_const_expr, Id}),
|
||||
create_constraints(),
|
||||
NewExpr = {typed, _, _, Type} = infer_expr(Env#env{ current_const = Id }, Expr),
|
||||
solve_then_destroy_and_report_unsolved_constraints(Env),
|
||||
IdType = setelement(2, Type, AnnId),
|
||||
NewId = {typed, aeso_syntax:get_ann(Id), Id, IdType},
|
||||
instantiate({letval, Ann, NewId, NewExpr}).
|
||||
|
||||
infer_infix({BoolOp, As})
|
||||
when BoolOp =:= '&&'; BoolOp =:= '||' ->
|
||||
Bool = {id, As, "bool"},
|
||||
@ -2167,6 +2413,11 @@ infer_infix({IntOp, As})
|
||||
IntOp == '^'; IntOp == 'mod' ->
|
||||
Int = {id, As, "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})
|
||||
when RelOp == '=='; RelOp == '!=';
|
||||
RelOp == '<'; RelOp == '>';
|
||||
@ -2194,6 +2445,9 @@ infer_infix({'|>', As}) ->
|
||||
infer_prefix({'!',As}) ->
|
||||
Bool = {id, As, "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 =:= '-' ->
|
||||
Int = {id, As, "int"},
|
||||
{fun_t, As, [], [Int], Int}.
|
||||
@ -3121,6 +3375,7 @@ all_warnings() ->
|
||||
[ warn_unused_includes
|
||||
, warn_unused_stateful
|
||||
, warn_unused_variables
|
||||
, warn_unused_constants
|
||||
, warn_unused_typedefs
|
||||
, warn_unused_return_value
|
||||
, warn_unused_functions
|
||||
@ -3151,9 +3406,14 @@ when_warning(Warn, Do) ->
|
||||
%% Warnings (Unused includes)
|
||||
|
||||
potential_unused_include(Ann, SrcFile) ->
|
||||
case aeso_syntax:get_ann(file, Ann, no_file) of
|
||||
no_file -> ok;
|
||||
File -> ets_insert(warnings, {unused_include, File, SrcFile})
|
||||
IsIncluded = aeso_syntax:get_ann(include_type, Ann, none) =/= none,
|
||||
case IsIncluded of
|
||||
false -> ok;
|
||||
true ->
|
||||
case aeso_syntax:get_ann(file, Ann, no_file) of
|
||||
no_file -> ok;
|
||||
File -> ets_insert(warnings, {unused_include, File, SrcFile})
|
||||
end
|
||||
end.
|
||||
|
||||
used_include(Ann) ->
|
||||
@ -3193,6 +3453,17 @@ used_variable(Namespace, Fun, [VarName]) ->
|
||||
ets_match_delete(warnings, {unused_variable, '_', Namespace, Fun, VarName});
|
||||
used_variable(_, _, _) -> ok.
|
||||
|
||||
%% Warnings (Unused constants)
|
||||
|
||||
potential_unused_constants(#env{ what = namespace }, _Consts) ->
|
||||
[];
|
||||
potential_unused_constants(#env{ namespace = Namespace }, Consts) ->
|
||||
[ ets_insert(warnings, {unused_constant, Ann, Namespace, Name}) || {letval, _, {id, Ann, Name}, _} <- Consts ].
|
||||
|
||||
used_constant(Namespace = [Contract], [Contract, ConstName]) ->
|
||||
ets_match_delete(warnings, {unused_constant, '_', Namespace, ConstName});
|
||||
used_constant(_, _) -> ok.
|
||||
|
||||
%% Warnings (Unused return value)
|
||||
|
||||
potential_unused_return_value({typed, Ann, {app, _, {typed, _, _, {fun_t, _, _, _, {id, _, Type}}}, _}, _}) when Type /= "unit" ->
|
||||
@ -3238,9 +3509,11 @@ destroy_and_report_unused_functions() ->
|
||||
|
||||
%% Warnings (Shadowing)
|
||||
|
||||
warn_potential_shadowing(_, "_", _) -> ok;
|
||||
warn_potential_shadowing(Ann, Name, Vars) ->
|
||||
case proplists:get_value(Name, Vars, false) of
|
||||
warn_potential_shadowing(_, _, "_") -> ok;
|
||||
warn_potential_shadowing(Env = #env{ vars = Vars }, Ann, Name) ->
|
||||
CurrentScope = get_current_scope(Env),
|
||||
Consts = CurrentScope#scope.consts,
|
||||
case proplists:get_value(Name, Vars ++ Consts, false) of
|
||||
false -> ok;
|
||||
{AnnOld, _} -> ets_insert(warnings, {shadowing, Ann, Name, AnnOld})
|
||||
end.
|
||||
@ -3344,6 +3617,9 @@ mk_error({cannot_unify, A, B, Cxt, When}) ->
|
||||
[pp(instantiate(A)), pp(instantiate(B))]),
|
||||
{Pos, Ctxt} = pp_when(When),
|
||||
mk_t_err(Pos, Msg, Ctxt);
|
||||
mk_error({hole_found, Ann, Type}) ->
|
||||
Msg = io_lib:format("Found a hole of type `~s`", [pp(instantiate(Type))]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({unbound_variable, Id}) ->
|
||||
Msg = io_lib:format("Unbound variable `~s`", [pp(Id)]),
|
||||
case Id of
|
||||
@ -3479,10 +3755,6 @@ mk_error({type_decl, _, {id, Pos, Name}, _}) ->
|
||||
Msg = io_lib:format("Empty type declarations are not supported. Type `~s` lacks a definition",
|
||||
[Name]),
|
||||
mk_t_err(pos(Pos), Msg);
|
||||
mk_error({letval, _Pos, {id, Pos, Name}, _Def}) ->
|
||||
Msg = io_lib:format("Toplevel \"let\" definitions are not supported. Value `~s` could be replaced by 0-argument function.",
|
||||
[Name]),
|
||||
mk_t_err(pos(Pos), Msg);
|
||||
mk_error({stateful_not_allowed, Id, Fun}) ->
|
||||
Msg = io_lib:format("Cannot reference stateful function `~s` in the definition of non-stateful function `~s`.",
|
||||
[pp(Id), pp(Fun)]),
|
||||
@ -3566,10 +3838,14 @@ mk_error({cannot_call_init_function, Ann}) ->
|
||||
Msg = "The 'init' function is called exclusively by the create contract transaction "
|
||||
"and cannot be called from the contract code.",
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({contract_treated_as_namespace, Ann, [Con, Fun] = QName}) ->
|
||||
mk_error({contract_treated_as_namespace_entrypoint, Ann, [Con, Fun] = QName}) ->
|
||||
Msg = io_lib:format("Invalid call to contract entrypoint `~s`.", [string:join(QName, ".")]),
|
||||
Cxt = io_lib:format("It must be called as `c.~s` for some `c : ~s`.", [Fun, Con]),
|
||||
mk_t_err(pos(Ann), Msg, Cxt);
|
||||
mk_error({contract_treated_as_namespace_constant, Ann, QName}) ->
|
||||
Msg = io_lib:format("Invalid use of the contract constant `~s`.", [string:join(QName, ".")]),
|
||||
Cxt = "Toplevel contract constants can only be used in the contracts where they are defined.",
|
||||
mk_t_err(pos(Ann), Msg, Cxt);
|
||||
mk_error({bad_top_level_decl, Decl}) ->
|
||||
What = case element(1, Decl) of
|
||||
letval -> "function or entrypoint";
|
||||
@ -3659,7 +3935,7 @@ mk_error({empty_record_definition, Ann, Name}) ->
|
||||
Msg = io_lib:format("Empty record definitions are not allowed. Cannot define the record `~s`", [Name]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({unimplemented_interface_function, ConId, InterfaceName, FunName}) ->
|
||||
Msg = io_lib:format("Unimplemented function `~s` from the interface `~s` in the contract `~s`", [FunName, InterfaceName, pp(ConId)]),
|
||||
Msg = io_lib:format("Unimplemented entrypoint `~s` from the interface `~s` in the contract `~s`", [FunName, InterfaceName, pp(ConId)]),
|
||||
mk_t_err(pos(ConId), Msg);
|
||||
mk_error({referencing_undefined_interface, InterfaceId}) ->
|
||||
Msg = io_lib:format("Trying to implement or extend an undefined interface `~s`", [pp(InterfaceId)]),
|
||||
@ -3691,7 +3967,7 @@ mk_error({higher_order_entrypoint, Ann, {id, _, Name}, Thing}) ->
|
||||
[ThingS, Name, Bad]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
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"
|
||||
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)",
|
||||
[pp_type(" `", T)]),
|
||||
@ -3707,6 +3983,46 @@ mk_error({interface_implementation_conflict, Contract, I1, I2, Fun}) ->
|
||||
"the contract `~s` have a function called `~s`",
|
||||
[name(I1), name(I2), name(Contract), name(Fun)]),
|
||||
mk_t_err(pos(Contract), Msg);
|
||||
mk_error({function_should_be_entrypoint, Impl, Base, Interface}) ->
|
||||
Msg = io_lib:format("`~s` must be declared as an entrypoint instead of a function "
|
||||
"in order to implement the entrypoint `~s` from the interface `~s`",
|
||||
[name(Impl), name(Base), name(Interface)]),
|
||||
mk_t_err(pos(Impl), Msg);
|
||||
mk_error({entrypoint_cannot_be_stateful, Impl, Base, Interface}) ->
|
||||
Msg = io_lib:format("`~s` cannot be stateful because the entrypoint `~s` in the "
|
||||
"interface `~s` is not stateful",
|
||||
[name(Impl), name(Base), name(Interface)]),
|
||||
mk_t_err(pos(Impl), Msg);
|
||||
mk_error({entrypoint_must_be_payable, Impl, Base, Interface}) ->
|
||||
Msg = io_lib:format("`~s` must be payable because the entrypoint `~s` in the "
|
||||
"interface `~s` is payable",
|
||||
[name(Impl), name(Base), name(Interface)]),
|
||||
mk_t_err(pos(Impl), Msg);
|
||||
mk_error({unpreserved_payablity, Kind, ContractCon, InterfaceCon}) ->
|
||||
KindStr = case Kind of
|
||||
contract -> "contract";
|
||||
contract_interface -> "interface"
|
||||
end,
|
||||
Msg = io_lib:format("Non-payable ~s `~s` cannot implement payable interface `~s`",
|
||||
[KindStr, name(ContractCon), name(InterfaceCon)]),
|
||||
mk_t_err(pos(ContractCon), Msg);
|
||||
mk_error({mutually_recursive_constants, Consts}) ->
|
||||
Msg = [ "Mutual recursion detected between the constants",
|
||||
[ io_lib:format("\n - `~s` at ~s", [name(Id), pp_loc(Ann)])
|
||||
|| {letval, Ann, Id, _} <- Consts ] ],
|
||||
[{letval, Ann, _, _} | _] = Consts,
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({invalid_const_id, Ann}) ->
|
||||
Msg = "The name of the compile-time constant cannot have pattern matching",
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({invalid_const_expr, ConstId}) ->
|
||||
Msg = io_lib:format("Invalid expression in the definition of the constant `~s`", [name(ConstId)]),
|
||||
Cxt = "You can only use the following expressions as constants: "
|
||||
"literals, lists, tuples, maps, and other constants",
|
||||
mk_t_err(pos(aeso_syntax:get_ann(ConstId)), Msg, Cxt);
|
||||
mk_error({illegal_const_in_interface, Ann}) ->
|
||||
Msg = "Cannot define toplevel constants inside a contract interface",
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error(Err) ->
|
||||
Msg = io_lib:format("Unknown error: ~p", [Err]),
|
||||
mk_t_err(pos(0, 0), Msg).
|
||||
@ -3720,6 +4036,9 @@ mk_warning({unused_stateful, Ann, FunName}) ->
|
||||
mk_warning({unused_variable, Ann, _Namespace, _Fun, VarName}) ->
|
||||
Msg = io_lib:format("The variable `~s` is defined but never used.", [VarName]),
|
||||
aeso_warnings:new(pos(Ann), Msg);
|
||||
mk_warning({unused_constant, Ann, _Namespace, ConstName}) ->
|
||||
Msg = io_lib:format("The constant `~s` is defined but never used.", [ConstName]),
|
||||
aeso_warnings:new(pos(Ann), Msg);
|
||||
mk_warning({unused_typedef, Ann, QName, _Arity}) ->
|
||||
Msg = io_lib:format("The type `~s` is defined but never used.", [lists:last(QName)]),
|
||||
aeso_warnings:new(pos(Ann), Msg);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,8 @@
|
||||
, file/2
|
||||
, from_string/2
|
||||
, check_call/4
|
||||
, decode_value/4
|
||||
, encode_value/4
|
||||
, create_calldata/3
|
||||
, create_calldata/4
|
||||
, version/0
|
||||
@ -117,7 +119,7 @@ from_string1(ContractString, Options) ->
|
||||
, warnings := Warnings } = string_to_code(ContractString, Options),
|
||||
#{ child_con_env := ChildContracts } = FCodeEnv,
|
||||
SavedFreshNames = maps:get(saved_fresh_names, FCodeEnv, #{}),
|
||||
{FateCode, VarsRegs} = aeso_fcode_to_fate:compile(ChildContracts, FCode, SavedFreshNames, Options),
|
||||
FateCode = aeso_fcode_to_fate:compile(ChildContracts, FCode, SavedFreshNames, Options),
|
||||
pp_assembler(FateCode, Options),
|
||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||
{ok, Version} = version(),
|
||||
@ -130,13 +132,7 @@ from_string1(ContractString, Options) ->
|
||||
payable => maps:get(payable, FCode),
|
||||
warnings => Warnings
|
||||
},
|
||||
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)}.
|
||||
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)}.
|
||||
|
||||
maybe_generate_aci(Result, FoldedTypedAst, Options) ->
|
||||
case proplists:get_value(aci, Options) of
|
||||
@ -188,30 +184,55 @@ check_call(Source, FunName, Args, Options) ->
|
||||
check_call1(Source, FunName, Args, Options).
|
||||
|
||||
check_call1(ContractString0, FunName, Args, Options) ->
|
||||
case add_extra_call(ContractString0, {call, FunName, Args}, Options) of
|
||||
{ok, CallName, Code} ->
|
||||
{def, _, _, FcodeArgs} = get_call_body(CallName, Code),
|
||||
{ok, FunName, [ aeso_fcode_to_fate:term_to_fate(A) || A <- FcodeArgs ]};
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
end.
|
||||
|
||||
add_extra_call(Contract0, Call, Options) ->
|
||||
try
|
||||
%% First check the contract without the __call function
|
||||
#{fcode := OrgFcode
|
||||
, fcode_env := #{child_con_env := ChildContracts}
|
||||
, ast := Ast} = string_to_code(ContractString0, Options),
|
||||
{FateCode, _} = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []),
|
||||
, ast := Ast} = string_to_code(Contract0, Options),
|
||||
FateCode = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []),
|
||||
%% collect all hashes and compute the first name without hash collision to
|
||||
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
||||
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
||||
lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)),
|
||||
ContractString = insert_call_function(Ast, ContractString0, CallName, FunName, Args),
|
||||
#{fcode := Fcode} = string_to_code(ContractString, Options),
|
||||
CallArgs = arguments_of_body(CallName, FunName, Fcode),
|
||||
|
||||
{ok, FunName, CallArgs}
|
||||
Contract = insert_call_function(Ast, Contract0, CallName, Call),
|
||||
{ok, CallName, string_to_code(Contract, Options)}
|
||||
catch
|
||||
throw:{error, Errors} -> {error, Errors}
|
||||
end.
|
||||
|
||||
arguments_of_body(CallName, _FunName, Fcode) ->
|
||||
get_call_body(CallName, #{fcode := Fcode}) ->
|
||||
#{body := Body} = maps:get({entrypoint, list_to_binary(CallName)}, maps:get(functions, Fcode)),
|
||||
{def, _FName, Args} = Body,
|
||||
%% FName is either {entrypoint, list_to_binary(FunName)} or 'init'
|
||||
[ aeso_fcode_to_fate:term_to_fate(A) || A <- Args ].
|
||||
Body.
|
||||
|
||||
encode_value(Contract0, Type, Value, Options) ->
|
||||
case add_extra_call(Contract0, {value, Type, Value}, Options) of
|
||||
{ok, CallName, Code} ->
|
||||
Body = get_call_body(CallName, Code),
|
||||
{ok, aeb_fate_encoding:serialize(aeso_fcode_to_fate:term_to_fate(Body))};
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
end.
|
||||
|
||||
decode_value(Contract0, Type, FateValue, Options) ->
|
||||
case add_extra_call(Contract0, {type, Type}, Options) of
|
||||
{ok, CallName, Code} ->
|
||||
#{ unfolded_typed_ast := TypedAst
|
||||
, type_env := TypeEnv} = Code,
|
||||
{ok, _, Type0} = get_decode_type(CallName, TypedAst),
|
||||
Type1 = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
||||
fate_data_to_sophia_value(Type0, Type1, FateValue);
|
||||
Err = {error, _} ->
|
||||
Err
|
||||
end.
|
||||
|
||||
first_none_match(_CallName, _Hashes, []) ->
|
||||
error(unable_to_find_unique_call_name);
|
||||
@ -224,14 +245,31 @@ first_none_match(CallName, Hashes, [Char|Chars]) ->
|
||||
end.
|
||||
|
||||
%% Add the __call function to a contract.
|
||||
-spec insert_call_function(aeso_syntax:ast(), string(), string(), string(), [string()]) -> string().
|
||||
insert_call_function(Ast, Code, Call, FunName, Args) ->
|
||||
-spec insert_call_function(aeso_syntax:ast(), string(), string(),
|
||||
{call, string(), [string()]} | {value, string(), string()} | {type, string()}) -> string().
|
||||
insert_call_function(Ast, Code, Call, {call, FunName, Args}) ->
|
||||
Ind = last_contract_indent(Ast),
|
||||
lists:flatten(
|
||||
[ Code,
|
||||
"\n\n",
|
||||
lists:duplicate(Ind, " "),
|
||||
"stateful entrypoint ", Call, "() = ", FunName, "(", string:join(Args, ","), ")\n"
|
||||
]);
|
||||
insert_call_function(Ast, Code, Call, {value, Type, Value}) ->
|
||||
Ind = last_contract_indent(Ast),
|
||||
lists:flatten(
|
||||
[ Code,
|
||||
"\n\n",
|
||||
lists:duplicate(Ind, " "),
|
||||
"entrypoint ", Call, "() : ", Type, " = ", Value, "\n"
|
||||
]);
|
||||
insert_call_function(Ast, Code, Call, {type, Type}) ->
|
||||
Ind = last_contract_indent(Ast),
|
||||
lists:flatten(
|
||||
[ Code,
|
||||
"\n\n",
|
||||
lists:duplicate(Ind, " "),
|
||||
"entrypoint ", Call, "(val : ", Type, ") = val\n"
|
||||
]).
|
||||
|
||||
-spec insert_init_function(string(), options()) -> string().
|
||||
@ -274,22 +312,25 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
|
||||
{ok, _, Type0} = get_decode_type(FunName, TypedAst),
|
||||
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
|
||||
|
||||
try
|
||||
{ok, aeso_vm_decode:from_fate(Type, aeb_fate_encoding:deserialize(Data))}
|
||||
catch throw:cannot_translate_to_sophia ->
|
||||
Type1 = prettypr:format(aeso_pretty:type(Type0)),
|
||||
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s",
|
||||
[aeb_fate_encoding:deserialize(Data), Type1]),
|
||||
{error, [aeso_errors:new(data_error, Msg)]};
|
||||
_:_ ->
|
||||
Type1 = prettypr:format(aeso_pretty:type(Type0)),
|
||||
Msg = io_lib:format("Failed to decode binary as type ~s", [Type1]),
|
||||
{error, [aeso_errors:new(data_error, Msg)]}
|
||||
end
|
||||
fate_data_to_sophia_value(Type0, Type, Data)
|
||||
catch
|
||||
throw:{error, Errors} -> {error, Errors}
|
||||
end.
|
||||
|
||||
fate_data_to_sophia_value(Type, UnfoldedType, FateData) ->
|
||||
try
|
||||
{ok, aeso_vm_decode:from_fate(UnfoldedType, aeb_fate_encoding:deserialize(FateData))}
|
||||
catch throw:cannot_translate_to_sophia ->
|
||||
Type1 = prettypr:format(aeso_pretty:type(Type)),
|
||||
Msg = io_lib:format("Cannot translate FATE value ~p\n of Sophia type ~s",
|
||||
[aeb_fate_encoding:deserialize(FateData), Type1]),
|
||||
{error, [aeso_errors:new(data_error, Msg)]};
|
||||
_:_ ->
|
||||
Type1 = prettypr:format(aeso_pretty:type(Type)),
|
||||
Msg = io_lib:format("Failed to decode binary as type ~s", [Type1]),
|
||||
{error, [aeso_errors:new(data_error, Msg)]}
|
||||
end.
|
||||
|
||||
-spec create_calldata(string(), string(), [string()]) ->
|
||||
{ok, binary()} | {error, [aeso_errors:error()]}.
|
||||
create_calldata(Code, Fun, Args) ->
|
||||
|
@ -52,7 +52,8 @@
|
||||
tailpos = true,
|
||||
child_contracts = #{},
|
||||
saved_fresh_names = #{},
|
||||
options = [] }).
|
||||
options = [],
|
||||
debug_info = false }).
|
||||
|
||||
%% -- Debugging --------------------------------------------------------------
|
||||
|
||||
@ -81,24 +82,16 @@ code_error(Err) ->
|
||||
compile(FCode, SavedFreshNames, Options) ->
|
||||
compile(#{}, FCode, SavedFreshNames, 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,
|
||||
functions := Functions } = FCode,
|
||||
SFuns = functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options),
|
||||
SFuns1 = optimize_scode(SFuns, Options),
|
||||
FateCode = to_basic_blocks(SFuns1),
|
||||
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(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()}.
|
||||
case proplists:get_value(include_child_contract_symbols, Options, false) of
|
||||
false -> FateCode;
|
||||
true -> add_child_symbols(ChildContracts, FateCode)
|
||||
end.
|
||||
|
||||
make_function_id(X) ->
|
||||
aeb_fate_code:symbol_identifier(make_function_name(X)).
|
||||
@ -123,31 +116,15 @@ functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Opt
|
||||
|
||||
function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, SavedFreshNames, Options) ->
|
||||
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
|
||||
Attrs = Attrs0 -- [stateful], %% Only track private and payable from here.
|
||||
Attrs = [ A || A <- Attrs0, A == private orelse A == payable ],
|
||||
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 ],
|
||||
ArgsNames = [ X || {X, _} <- lists:reverse(Env#env.vars) ],
|
||||
|
||||
%% DBG_LOC is added before the function body to make it possible to break
|
||||
%% at the function signature
|
||||
SCode = to_scode(Env, Body),
|
||||
{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}).
|
||||
DbgSCode = dbg_contract(Env) ++ dbg_loc(Env, Attrs0) ++ dbg_scoped_vars(Env, ArgsNames, SCode),
|
||||
{Attrs, {ArgTypes, ResType1}, DbgSCode}.
|
||||
|
||||
-define(tvars, '$tvars').
|
||||
|
||||
@ -194,20 +171,20 @@ types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts).
|
||||
%% -- Environment functions --
|
||||
|
||||
init_env(ChildContracts, ContractName, FunNames, Name, Args, SavedFreshNames, Options) ->
|
||||
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
|
||||
contract = ContractName,
|
||||
child_contracts = ChildContracts,
|
||||
locals = FunNames,
|
||||
current_function = Name,
|
||||
options = Options,
|
||||
tailpos = true,
|
||||
saved_fresh_names = SavedFreshNames }.
|
||||
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
|
||||
contract = ContractName,
|
||||
child_contracts = ChildContracts,
|
||||
locals = FunNames,
|
||||
current_function = Name,
|
||||
options = Options,
|
||||
tailpos = true,
|
||||
saved_fresh_names = SavedFreshNames,
|
||||
debug_info = proplists:get_value(debug_info, Options, false) }.
|
||||
|
||||
next_var(#env{ vars = Vars }) ->
|
||||
1 + lists:max([-1 | [J || {_, {var, J}} <- 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] }.
|
||||
|
||||
bind_local(Name, Env) ->
|
||||
@ -234,7 +211,7 @@ serialize_contract_code(Env, C) ->
|
||||
Options = Env#env.options,
|
||||
SavedFreshNames = Env#env.saved_fresh_names,
|
||||
FCode = maps:get(C, Env#env.child_contracts),
|
||||
{FateCode, _} = compile1(Env#env.child_contracts, FCode, SavedFreshNames, Options),
|
||||
FateCode = compile(Env#env.child_contracts, FCode, SavedFreshNames, Options),
|
||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||
{ok, Version} = aeso_compiler:version(),
|
||||
OriginalSourceCode = proplists:get_value(original_src, Options, ""),
|
||||
@ -268,44 +245,44 @@ lit_to_fate(Env, L) ->
|
||||
term_to_fate(E) -> term_to_fate(#env{}, #{}, E).
|
||||
term_to_fate(GlobEnv, E) -> term_to_fate(GlobEnv, #{}, E).
|
||||
|
||||
term_to_fate(GlobEnv, _Env, {lit, L}) ->
|
||||
term_to_fate(GlobEnv, _Env, {lit, _, L}) ->
|
||||
lit_to_fate(GlobEnv, L);
|
||||
%% negative literals are parsed as 0 - N
|
||||
term_to_fate(_GlobEnv, _Env, {op, '-', [{lit, {int, 0}}, {lit, {int, N}}]}) ->
|
||||
term_to_fate(_GlobEnv, _Env, {op, _, '-', [{lit, _, {int, 0}}, {lit, _, {int, N}}]}) ->
|
||||
aeb_fate_data:make_integer(-N);
|
||||
term_to_fate(_GlobEnv, _Env, nil) ->
|
||||
term_to_fate(_GlobEnv, _Env, {nil, _}) ->
|
||||
aeb_fate_data:make_list([]);
|
||||
term_to_fate(GlobEnv, Env, {op, '::', [Hd, Tl]}) ->
|
||||
term_to_fate(GlobEnv, Env, {op, _, '::', [Hd, Tl]}) ->
|
||||
%% The Tl will translate into a list, because FATE lists are just lists
|
||||
[term_to_fate(GlobEnv, Env, Hd) | term_to_fate(GlobEnv, Env, Tl)];
|
||||
term_to_fate(GlobEnv, Env, {tuple, As}) ->
|
||||
term_to_fate(GlobEnv, Env, {tuple, _, As}) ->
|
||||
aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(GlobEnv, Env, A) || A<-As]));
|
||||
term_to_fate(GlobEnv, Env, {con, Ar, I, As}) ->
|
||||
term_to_fate(GlobEnv, Env, {con, _, Ar, I, As}) ->
|
||||
FateAs = [ term_to_fate(GlobEnv, Env, A) || A <- As ],
|
||||
aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs));
|
||||
term_to_fate(_GlobEnv, _Env, {builtin, bits_all, []}) ->
|
||||
term_to_fate(_GlobEnv, _Env, {builtin, _, bits_all, []}) ->
|
||||
aeb_fate_data:make_bits(-1);
|
||||
term_to_fate(_GlobEnv, _Env, {builtin, bits_none, []}) ->
|
||||
term_to_fate(_GlobEnv, _Env, {builtin, _, bits_none, []}) ->
|
||||
aeb_fate_data:make_bits(0);
|
||||
term_to_fate(GlobEnv, _Env, {op, bits_set, [B, I]}) ->
|
||||
term_to_fate(GlobEnv, _Env, {op, _, bits_set, [B, I]}) ->
|
||||
{bits, N} = term_to_fate(GlobEnv, B),
|
||||
J = term_to_fate(GlobEnv, I),
|
||||
{bits, N bor (1 bsl J)};
|
||||
term_to_fate(GlobEnv, _Env, {op, bits_clear, [B, I]}) ->
|
||||
term_to_fate(GlobEnv, _Env, {op, _, bits_clear, [B, I]}) ->
|
||||
{bits, N} = term_to_fate(GlobEnv, B),
|
||||
J = term_to_fate(GlobEnv, I),
|
||||
{bits, N band bnot (1 bsl J)};
|
||||
term_to_fate(GlobEnv, Env, {'let', X, E, Body}) ->
|
||||
term_to_fate(GlobEnv, Env, {'let', _, X, E, Body}) ->
|
||||
Env1 = Env#{ X => term_to_fate(GlobEnv, Env, E) },
|
||||
term_to_fate(GlobEnv, Env1, Body);
|
||||
term_to_fate(_GlobEnv, Env, {var, X}) ->
|
||||
term_to_fate(_GlobEnv, Env, {var, _, X}) ->
|
||||
case maps:get(X, Env, undefined) of
|
||||
undefined -> throw(not_a_fate_value);
|
||||
V -> V
|
||||
end;
|
||||
term_to_fate(_GlobEnv, _Env, {builtin, map_empty, []}) ->
|
||||
term_to_fate(_GlobEnv, _Env, {builtin, _, map_empty, []}) ->
|
||||
aeb_fate_data:make_map(#{});
|
||||
term_to_fate(GlobEnv, Env, {op, map_set, [M, K, V]}) ->
|
||||
term_to_fate(GlobEnv, Env, {op, _, map_set, [M, K, V]}) ->
|
||||
Map = term_to_fate(GlobEnv, Env, M),
|
||||
Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)};
|
||||
term_to_fate(_GlobEnv, _Env, _) ->
|
||||
@ -313,52 +290,59 @@ term_to_fate(_GlobEnv, _Env, _) ->
|
||||
|
||||
to_scode(Env, T) ->
|
||||
try term_to_fate(Env, T) of
|
||||
V -> [push(?i(V))]
|
||||
V ->
|
||||
FAnn = element(2, T),
|
||||
[dbg_loc(Env, FAnn), push(?i(V))]
|
||||
catch throw:not_a_fate_value ->
|
||||
to_scode1(Env, T)
|
||||
end.
|
||||
|
||||
to_scode1(Env, {lit, L}) ->
|
||||
[push(?i(lit_to_fate(Env, L)))];
|
||||
to_scode1(Env, {lit, Ann, L}) ->
|
||||
[ dbg_loc(Env, Ann), push(?i(lit_to_fate(Env, L))) ];
|
||||
|
||||
to_scode1(_Env, nil) ->
|
||||
[aeb_fate_ops:nil(?a)];
|
||||
to_scode1(Env, {nil, Ann}) ->
|
||||
[ dbg_loc(Env, Ann), aeb_fate_ops:nil(?a) ];
|
||||
|
||||
to_scode1(Env, {var, X}) ->
|
||||
[push(lookup_var(Env, X))];
|
||||
to_scode1(Env, {var, Ann, X}) ->
|
||||
[ dbg_loc(Env, Ann), push(lookup_var(Env, X)) ];
|
||||
|
||||
to_scode1(Env, {con, Ar, I, As}) ->
|
||||
to_scode1(Env, {con, Ann, Ar, I, As}) ->
|
||||
N = length(As),
|
||||
[[to_scode(notail(Env), A) || A <- As],
|
||||
aeb_fate_ops:variant(?a, ?i(Ar), ?i(I), ?i(N))];
|
||||
[ dbg_loc(Env, Ann),
|
||||
[to_scode(notail(Env), A) || A <- As],
|
||||
aeb_fate_ops:variant(?a, ?i(Ar), ?i(I), ?i(N)) ];
|
||||
|
||||
to_scode1(Env, {tuple, As}) ->
|
||||
to_scode1(Env, {tuple, Ann, As}) ->
|
||||
N = length(As),
|
||||
[[ to_scode(notail(Env), A) || A <- As ],
|
||||
tuple(N)];
|
||||
[ dbg_loc(Env, Ann),
|
||||
[ to_scode(notail(Env), A) || A <- As ],
|
||||
tuple(N) ];
|
||||
|
||||
to_scode1(Env, {proj, E, I}) ->
|
||||
[to_scode(notail(Env), E),
|
||||
aeb_fate_ops:element_op(?a, ?i(I), ?a)];
|
||||
to_scode1(Env, {proj, Ann, E, I}) ->
|
||||
[ dbg_loc(Env, Ann),
|
||||
to_scode(notail(Env), E),
|
||||
aeb_fate_ops:element_op(?a, ?i(I), ?a) ];
|
||||
|
||||
to_scode1(Env, {set_proj, R, I, E}) ->
|
||||
[to_scode(notail(Env), E),
|
||||
to_scode(notail(Env), R),
|
||||
aeb_fate_ops:setelement(?a, ?i(I), ?a, ?a)];
|
||||
to_scode1(Env, {set_proj, Ann, R, I, E}) ->
|
||||
[ dbg_loc(Env, Ann),
|
||||
to_scode(notail(Env), E),
|
||||
to_scode(notail(Env), R),
|
||||
aeb_fate_ops:setelement(?a, ?i(I), ?a, ?a) ];
|
||||
|
||||
to_scode1(Env, {op, Op, Args}) ->
|
||||
call_to_scode(Env, op_to_scode(Op), Args);
|
||||
to_scode1(Env, {op, Ann, Op, Args}) ->
|
||||
[ dbg_loc(Env, Ann) | call_to_scode(Env, op_to_scode(Op), Args) ];
|
||||
|
||||
to_scode1(Env, {'let', X, {var, Y}, Body}) ->
|
||||
to_scode1(Env, {'let', Ann, X, {var, _, Y}, Body}) ->
|
||||
Env1 = bind_var(X, lookup_var(Env, Y), Env),
|
||||
to_scode(Env1, Body);
|
||||
to_scode1(Env, {'let', X, Expr, Body}) ->
|
||||
[ dbg_loc(Env, Ann) | dbg_scoped_vars(Env1, [X], to_scode(Env1, Body)) ];
|
||||
to_scode1(Env, {'let', Ann, X, Expr, Body}) ->
|
||||
{I, Env1} = bind_local(X, Env),
|
||||
[ to_scode(notail(Env), Expr),
|
||||
aeb_fate_ops:store({var, I}, {stack, 0}),
|
||||
to_scode(Env1, Body) ];
|
||||
SCode = [ to_scode(notail(Env), Expr),
|
||||
aeb_fate_ops:store({var, I}, {stack, 0}),
|
||||
to_scode(Env1, Body) ],
|
||||
[ dbg_loc(Env, Ann) | dbg_scoped_vars(Env1, [X], SCode) ];
|
||||
|
||||
to_scode1(Env = #env{ current_function = Fun, tailpos = true }, {def, Fun, Args}) ->
|
||||
to_scode1(Env = #env{ current_function = Fun, tailpos = true, debug_info = false }, {def, Ann, Fun, Args}) ->
|
||||
%% Tail-call to current function, f(e0..en). Compile to
|
||||
%% [ let xi = ei ]
|
||||
%% [ STORE argi xi ]
|
||||
@ -371,61 +355,62 @@ to_scode1(Env = #env{ current_function = Fun, tailpos = true }, {def, Fun, Args}
|
||||
aeb_fate_ops:store({var, I}, ?a)],
|
||||
{[I | Is], Acc1, Env2}
|
||||
end, {[], [], Env}, Args),
|
||||
[ Code,
|
||||
[ dbg_loc(Env, Ann),
|
||||
Code,
|
||||
[ aeb_fate_ops:store({arg, I}, {var, J})
|
||||
|| {I, J} <- lists:zip(lists:seq(0, length(Vars) - 1),
|
||||
lists:reverse(Vars)) ],
|
||||
loop ];
|
||||
to_scode1(Env, {def, Fun, Args}) ->
|
||||
to_scode1(Env, {def, Ann, Fun, Args}) ->
|
||||
FName = make_function_id(Fun),
|
||||
Lbl = aeb_fate_data:make_string(FName),
|
||||
call_to_scode(Env, local_call(Env, ?i(Lbl)), Args);
|
||||
to_scode1(Env, {funcall, Fun, Args}) ->
|
||||
call_to_scode(Env, [to_scode(Env, Fun), local_call(Env, ?a)], Args);
|
||||
[ dbg_loc(Env, Ann) | call_to_scode(Env, local_call(Env, ?i(Lbl)), Args) ];
|
||||
to_scode1(Env, {funcall, Ann, Fun, Args}) ->
|
||||
[ dbg_loc(Env, Ann) | call_to_scode(Env, [to_scode(Env, Fun), local_call(Env, ?a)], Args) ];
|
||||
|
||||
to_scode1(Env, {builtin, B, Args}) ->
|
||||
builtin_to_scode(Env, B, Args);
|
||||
to_scode1(Env, {builtin, Ann, B, Args}) ->
|
||||
[ dbg_loc(Env, Ann) | builtin_to_scode(Env, B, Args) ];
|
||||
|
||||
to_scode1(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) ->
|
||||
to_scode1(Env, {remote, Ann, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) ->
|
||||
Lbl = make_function_id(Fun),
|
||||
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
|
||||
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
|
||||
RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
|
||||
case Protected of
|
||||
{lit, {bool, false}} ->
|
||||
SCode = case Protected of
|
||||
{lit, _, {bool, false}} ->
|
||||
case Gas of
|
||||
{builtin, call_gas_left, _} ->
|
||||
{builtin, _, call_gas_left, _} ->
|
||||
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
|
||||
call_to_scode(Env, Call, [Ct, Value | Args]);
|
||||
_ ->
|
||||
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
|
||||
call_to_scode(Env, Call, [Ct, Value, Gas | Args])
|
||||
end;
|
||||
{lit, {bool, true}} ->
|
||||
{lit, _, {bool, true}} ->
|
||||
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?i(true)),
|
||||
call_to_scode(Env, Call, [Ct, Value, Gas | Args]);
|
||||
_ ->
|
||||
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a),
|
||||
call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args])
|
||||
end;
|
||||
end,
|
||||
[ dbg_loc(Env, Ann) | SCode ];
|
||||
|
||||
to_scode1(_Env, {get_state, Reg}) ->
|
||||
[push(?s(Reg))];
|
||||
to_scode1(Env, {set_state, Reg, Val}) ->
|
||||
call_to_scode(Env, [{'STORE', ?s(Reg), ?a},
|
||||
tuple(0)], [Val]);
|
||||
to_scode1(Env, {get_state, Ann, Reg}) ->
|
||||
[ dbg_loc(Env, Ann), push(?s(Reg)) ];
|
||||
to_scode1(Env, {set_state, Ann, Reg, Val}) ->
|
||||
[ dbg_loc(Env, Ann) | call_to_scode(Env, [{'STORE', ?s(Reg), ?a}, tuple(0)], [Val]) ];
|
||||
|
||||
to_scode1(Env, {closure, Fun, FVs}) ->
|
||||
to_scode(Env, {tuple, [{lit, {string, make_function_id(Fun)}}, FVs]});
|
||||
to_scode1(Env, {closure, Ann, Fun, FVs}) ->
|
||||
[ to_scode(Env, {tuple, Ann, [{lit, Ann, {string, make_function_id(Fun)}}, FVs]}) ];
|
||||
|
||||
to_scode1(Env, {switch, Case}) ->
|
||||
split_to_scode(Env, Case).
|
||||
to_scode1(Env, {switch, Ann, Case}) ->
|
||||
[ dbg_loc(Env, Ann) | split_to_scode(Env, Case) ].
|
||||
|
||||
local_call( Env, Fun) when Env#env.tailpos -> aeb_fate_ops:call_t(Fun);
|
||||
local_call(_Env, Fun) -> aeb_fate_ops:call(Fun).
|
||||
local_call( Env = #env{debug_info = false}, Fun) when Env#env.tailpos -> aeb_fate_ops:call_t(Fun);
|
||||
local_call(_Env, Fun) -> aeb_fate_ops:call(Fun).
|
||||
|
||||
split_to_scode(Env, {nosplit, Expr}) ->
|
||||
[switch_body, to_scode(Env, Expr)];
|
||||
split_to_scode(Env, {nosplit, Renames, Expr}) ->
|
||||
[switch_body, dbg_scoped_vars(Env, Renames, to_scode(Env, Expr))];
|
||||
split_to_scode(Env, {split, {tuple, _}, X, Alts}) ->
|
||||
{Def, Alts1} = catchall_to_scode(Env, X, Alts),
|
||||
Arg = lookup_var(Env, X),
|
||||
@ -649,7 +634,7 @@ builtin_to_scode(Env, chain_bytecode_hash, [_Addr] = Args) ->
|
||||
builtin_to_scode(Env, chain_clone,
|
||||
[InitArgsT, GasCap, Value, Prot, Contract | InitArgs]) ->
|
||||
case GasCap of
|
||||
{builtin, call_gas_left, _} ->
|
||||
{builtin, _, call_gas_left, _} ->
|
||||
call_to_scode(Env, aeb_fate_ops:clone(?a, ?a, ?a, ?a),
|
||||
[Contract, InitArgsT, Value, Prot | InitArgs]
|
||||
);
|
||||
@ -682,6 +667,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:neq(?a, ?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_d) -> aeb_fate_ops:map_lookup(?a, ?a, ?a, ?a);
|
||||
op_to_scode(map_set) -> aeb_fate_ops:map_update(?a, ?a, ?a, ?a);
|
||||
@ -706,7 +697,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_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_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_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(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);
|
||||
@ -716,6 +709,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_sha256) -> aeb_fate_ops:sha256(?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_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
||||
op_to_scode(stringinternal_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
||||
@ -751,6 +745,77 @@ push(A) -> {'STORE', ?a, A}.
|
||||
tuple(0) -> push(?i({tuple, {}}));
|
||||
tuple(N) -> aeb_fate_ops:tuple(?a, N).
|
||||
|
||||
%% -- Debug info functions --
|
||||
|
||||
dbg_contract(#env{debug_info = false}) ->
|
||||
[];
|
||||
dbg_contract(#env{contract = Contract}) ->
|
||||
[{'DBG_CONTRACT', {immediate, Contract}}].
|
||||
|
||||
dbg_loc(#env{debug_info = false}, _) ->
|
||||
[];
|
||||
dbg_loc(_Env, Ann) ->
|
||||
File = case proplists:get_value(file, Ann, no_file) of
|
||||
no_file -> "";
|
||||
F -> F
|
||||
end,
|
||||
Line = proplists:get_value(line, Ann, undefined),
|
||||
case Line of
|
||||
undefined -> [];
|
||||
_ -> [{'DBG_LOC', {immediate, File}, {immediate, Line}}]
|
||||
end.
|
||||
|
||||
dbg_scoped_vars(#env{debug_info = false}, _, SCode) ->
|
||||
SCode;
|
||||
dbg_scoped_vars(_Env, [], SCode) ->
|
||||
SCode;
|
||||
dbg_scoped_vars(Env, [{SavedVarName, Var} | Rest], SCode) ->
|
||||
dbg_scoped_vars(Env, Rest, dbg_scoped_var(Env, SavedVarName, Var, SCode));
|
||||
dbg_scoped_vars(Env = #env{saved_fresh_names = SavedFreshNames}, [Var | Rest], SCode) ->
|
||||
SavedVarName = maps:get(Var, SavedFreshNames, Var),
|
||||
dbg_scoped_vars(Env, Rest, dbg_scoped_var(Env, SavedVarName, Var, SCode)).
|
||||
|
||||
dbg_scoped_var(Env, SavedVarName, Var, SCode) ->
|
||||
case SavedVarName == "_" orelse is_fresh_name(SavedVarName) of
|
||||
true ->
|
||||
SCode;
|
||||
false ->
|
||||
Register = lookup_var(Env, Var),
|
||||
Def = [{'DBG_DEF', {immediate, SavedVarName}, Register}],
|
||||
Undef = [{'DBG_UNDEF', {immediate, SavedVarName}, Register}],
|
||||
Def ++ dbg_undef(Undef, SCode)
|
||||
end.
|
||||
|
||||
is_fresh_name([$% | _]) ->
|
||||
true;
|
||||
is_fresh_name(_) ->
|
||||
false.
|
||||
|
||||
dbg_undef(_Undef, missing) ->
|
||||
missing;
|
||||
dbg_undef(Undef, loop) ->
|
||||
[Undef, loop];
|
||||
dbg_undef(Undef, switch_body) ->
|
||||
[switch_body, Undef];
|
||||
dbg_undef(Undef, {switch, Arg, Type, Alts, Catch}) ->
|
||||
NewAlts = [ dbg_undef(Undef, Alt) || Alt <- Alts ],
|
||||
NewCatch = dbg_undef(Undef, Catch),
|
||||
NewSwitch = {switch, Arg, Type, NewAlts, NewCatch},
|
||||
NewSwitch;
|
||||
dbg_undef(Undef, SCode) when is_list(SCode) ->
|
||||
lists:droplast(SCode) ++ [dbg_undef(Undef, lists:last(SCode))];
|
||||
dbg_undef(Undef, SCode) when is_tuple(SCode); is_atom(SCode) ->
|
||||
[Mnemonic | _] =
|
||||
case is_tuple(SCode) of
|
||||
true -> tuple_to_list(SCode);
|
||||
false -> [SCode]
|
||||
end,
|
||||
Op = aeb_fate_opcodes:m_to_op(Mnemonic),
|
||||
case aeb_fate_opcodes:end_bb(Op) of
|
||||
true -> [Undef, SCode];
|
||||
false -> [SCode, Undef]
|
||||
end.
|
||||
|
||||
%% -- Phase II ---------------------------------------------------------------
|
||||
%% Optimize
|
||||
|
||||
@ -886,6 +951,10 @@ attributes(I) ->
|
||||
loop -> Impure(pc, []);
|
||||
switch_body -> Pure(none, []);
|
||||
'RETURN' -> Impure(pc, []);
|
||||
{'DBG_LOC', _, _} -> Impure(none, []);
|
||||
{'DBG_DEF', _, _} -> Impure(none, []);
|
||||
{'DBG_UNDEF', _, _} -> Impure(none, []);
|
||||
{'DBG_CONTRACT', _} -> Impure(none, []);
|
||||
{'RETURNR', A} -> Impure(pc, A);
|
||||
{'CALL', A} -> Impure(?a, [A]);
|
||||
{'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]);
|
||||
@ -913,6 +982,13 @@ attributes(I) ->
|
||||
{'DIV', A, B, C} -> Pure(A, [B, C]);
|
||||
{'MOD', 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]);
|
||||
{'GT', A, B, C} -> Pure(A, [B, C]);
|
||||
{'EQ', A, B, C} -> Pure(A, [B, C]);
|
||||
@ -965,12 +1041,14 @@ attributes(I) ->
|
||||
{'SHA3', A, B} -> Pure(A, [B]);
|
||||
{'SHA256', 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_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]);
|
||||
{'CONTRACT_TO_ADDRESS', 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', A} -> Pure(A, []);
|
||||
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
|
||||
@ -1605,7 +1683,23 @@ bb(_Name, Code) ->
|
||||
Blocks = lists:flatmap(fun split_calls/1, Blocks1),
|
||||
Labels = maps:from_list([ {Ref, I} || {I, {Ref, _}} <- with_ixs(Blocks) ]),
|
||||
BBs = [ set_labels(Labels, B) || B <- Blocks ],
|
||||
maps:from_list(BBs).
|
||||
maps:from_list(dbg_loc_filter(BBs)).
|
||||
|
||||
%% Filter DBG_LOC instructions to keep one instruction per line
|
||||
dbg_loc_filter(BBs) ->
|
||||
dbg_loc_filter(BBs, [], [], sets:new()).
|
||||
|
||||
dbg_loc_filter([], _, AllBlocks, _) ->
|
||||
lists:reverse(AllBlocks);
|
||||
dbg_loc_filter([{I, []} | Rest], AllOps, AllBlocks, DbgLocs) ->
|
||||
dbg_loc_filter(Rest, [], [{I, lists:reverse(AllOps)} | AllBlocks], DbgLocs);
|
||||
dbg_loc_filter([{I, [Op = {'DBG_LOC', _, _} | Ops]} | Rest], AllOps, AllBlocks, DbgLocs) ->
|
||||
case sets:is_element(Op, DbgLocs) of
|
||||
true -> dbg_loc_filter([{I, Ops} | Rest], AllOps, AllBlocks, DbgLocs);
|
||||
false -> dbg_loc_filter([{I, Ops} | Rest], [Op | AllOps], AllBlocks, sets:add_element(Op, DbgLocs))
|
||||
end;
|
||||
dbg_loc_filter([{I, [Op | Ops]} | Rest], AllOps, AllBlocks, DbgLocs) ->
|
||||
dbg_loc_filter([{I, Ops} | Rest], [Op | AllOps], AllBlocks, DbgLocs).
|
||||
|
||||
%% -- Break up scode into basic blocks --
|
||||
|
||||
|
@ -333,14 +333,19 @@ expr100() ->
|
||||
|
||||
expr150() -> infixl(expr200(), 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(['<', '>', '=<', '>=', '==', '!='])).
|
||||
expr500() -> infixr(expr600(), binop(['::', '++'])).
|
||||
expr500() -> infixr(expr550(), binop(['::', '++'])).
|
||||
expr550() -> infixl(expr600(), binop(['<<', '>>'])).
|
||||
expr600() -> infixl(expr650(), binop(['+', '-'])).
|
||||
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
|
||||
expr700() -> infixl(expr750(), binop(['*', '/', mod])).
|
||||
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)).
|
||||
|
||||
exprAtom() ->
|
||||
@ -359,9 +364,12 @@ exprAtom() ->
|
||||
, ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4))
|
||||
, ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2))
|
||||
, letpat()
|
||||
, hole()
|
||||
])
|
||||
end).
|
||||
|
||||
hole() -> ?RULE(token('???'), {id, get_ann(_1), "???"}).
|
||||
|
||||
comprehension_exp() ->
|
||||
?LAZY_P(choice(
|
||||
[ comprehension_bind()
|
||||
|
@ -436,15 +436,20 @@ bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||
bin_prec('@') -> { 0, 0, 0}; %% Only in error messages
|
||||
bin_prec('|>') -> {150, 150, 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('++') -> {500, 600, 500};
|
||||
bin_prec('::') -> {500, 600, 500};
|
||||
bin_prec('++') -> {500, 550, 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('*') -> {700, 700, 750};
|
||||
@ -454,7 +459,8 @@ bin_prec('^') -> {750, 750, 800}.
|
||||
|
||||
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
|
||||
un_prec('-') -> {650, 650};
|
||||
un_prec('!') -> {800, 800}.
|
||||
un_prec('!') -> {800, 800};
|
||||
un_prec('bnot') -> {850, 850}.
|
||||
|
||||
equals(Ann, A, B) ->
|
||||
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
|
||||
|
@ -45,7 +45,7 @@ lexer() ->
|
||||
|
||||
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
|
||||
"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, "|"),
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
-export([get_ann/1, get_ann/2, get_ann/3, set_ann/2, qualify/2]).
|
||||
|
||||
-export_type([ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]).
|
||||
-export_type([ann_file/0, 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([bin_op/0, un_op/0]).
|
||||
-export_type([decl/0, letbind/0, typedef/0, pragma/0, fundecl/0]).
|
||||
@ -24,8 +24,9 @@
|
||||
-type ann_col() :: integer().
|
||||
-type ann_origin() :: system | user.
|
||||
-type ann_format() :: '?:' | hex | infix | prefix | elif.
|
||||
-type ann_file() :: string() | no_file.
|
||||
|
||||
-type ann() :: [ {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()}
|
||||
-type ann() :: [ {file, ann_file()} | {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()}
|
||||
| stateful | private | payable | main | interface | entrypoint].
|
||||
|
||||
-type name() :: string().
|
||||
@ -106,8 +107,8 @@
|
||||
|
||||
-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^'
|
||||
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||
| '||' | '&&' | '..' | '|>'.
|
||||
-type un_op() :: '-' | '!'.
|
||||
| '||' | '&&' | '..' | 'band' | 'bor' | 'bxor' | '>>' | '<<' | '|>'.
|
||||
-type un_op() :: '-' | '!' | 'bnot'.
|
||||
|
||||
-type expr()
|
||||
:: {lam, ann(), [arg()], expr()}
|
||||
|
@ -31,11 +31,13 @@
|
||||
| aeso_syntax:field(aeso_syntax:expr())
|
||||
| aeso_syntax:stmt().
|
||||
fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
|
||||
ExprKind = if K == bind_expr -> bind_expr; true -> expr end,
|
||||
TypeKind = if K == bind_type -> bind_type; true -> type end,
|
||||
Sum = fun(Xs) -> lists:foldl(Plus, Zero, Xs) end,
|
||||
Same = fun(A) -> fold(Alg, Fun, K, A) end,
|
||||
Decl = fun(D) -> fold(Alg, Fun, decl, D) end,
|
||||
Type = fun(T) -> fold(Alg, Fun, type, T) end,
|
||||
Expr = fun(E) -> fold(Alg, Fun, expr, E) end,
|
||||
Type = fun(T) -> fold(Alg, Fun, TypeKind, T) end,
|
||||
Expr = fun(E) -> fold(Alg, Fun, ExprKind, E) end,
|
||||
BindExpr = fun(P) -> fold(Alg, Fun, bind_expr, P) end,
|
||||
BindType = fun(T) -> fold(Alg, Fun, bind_type, T) end,
|
||||
Top = Fun(K, X),
|
||||
@ -155,4 +157,3 @@ used(D) ->
|
||||
(_, _) -> #{}
|
||||
end, decl, D)),
|
||||
lists:filter(NotBound, Xs).
|
||||
|
||||
|
@ -88,7 +88,7 @@ from_fate_builtin(QType, Val) ->
|
||||
|
||||
{["AENS", "name"], {variant, [3], 0, {Addr, TTL, Ptrs}}} ->
|
||||
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}}} ->
|
||||
App(["AENS","AccountPt"], [Chk(Adr, Addr)]);
|
||||
@ -99,6 +99,21 @@ from_fate_builtin(QType, Val) ->
|
||||
{["AENS", "pointee"], {variant, [1, 1, 1, 1], 3, {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}}} ->
|
||||
App(["Chain","GAMetaTx"], [Chk(Adr, Addr), Chk(Int, X)]);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{application, aesophia,
|
||||
[{description, "Compiler for Aeternity Sophia language"},
|
||||
{vsn, "7.0.1"},
|
||||
{vsn, "8.0.0"},
|
||||
{registered, []},
|
||||
{applications,
|
||||
[kernel,
|
||||
|
@ -69,6 +69,7 @@ simple_compile_test_() ->
|
||||
[ {"Testing warning messages",
|
||||
fun() ->
|
||||
#{ warnings := Warnings } = compile("warnings", [warn_all]),
|
||||
#{ warnings := [] } = compile("warning_unused_include_no_include", [warn_all]),
|
||||
check_warnings(warnings(), Warnings)
|
||||
end} ] ++
|
||||
[].
|
||||
@ -205,6 +206,9 @@ compilable_contracts() ->
|
||||
"polymorphism_variance_switching_chain_create",
|
||||
"polymorphism_variance_switching_void_supertype",
|
||||
"polymorphism_variance_switching_unify_with_interface_decls",
|
||||
"polymorphism_preserve_or_add_payable_contract",
|
||||
"polymorphism_preserve_or_add_payable_entrypoint",
|
||||
"polymorphism_preserve_or_remove_stateful_entrypoint",
|
||||
"missing_init_fun_state_unit",
|
||||
"complex_compare_leq",
|
||||
"complex_compare",
|
||||
@ -218,6 +222,8 @@ compilable_contracts() ->
|
||||
"unapplied_contract_call",
|
||||
"unapplied_named_arg_builtin",
|
||||
"resolve_field_constraint_by_arity",
|
||||
"toplevel_constants",
|
||||
"ceres",
|
||||
"test" % Custom general-purpose test file. Keep it last on the list.
|
||||
].
|
||||
|
||||
@ -282,7 +288,11 @@ warnings() ->
|
||||
<<?PosW(48, 5)
|
||||
"Unused return value.">>,
|
||||
<<?PosW(60, 5)
|
||||
"The function `dec` is defined but never used.">>
|
||||
"The function `dec` is defined but never used.">>,
|
||||
<<?PosW(73, 9)
|
||||
"The definition of `const` shadows an older definition at line 70, column 3.">>,
|
||||
<<?PosW(84, 7)
|
||||
"The constant `c` is defined but never used.">>
|
||||
]).
|
||||
|
||||
failing_contracts() ->
|
||||
@ -654,10 +664,6 @@ failing_contracts() ->
|
||||
[<<?Pos(5, 28)
|
||||
"Invalid call to contract entrypoint `Foo.foo`.\n"
|
||||
"It must be called as `c.foo` for some `c : Foo`.">>])
|
||||
, ?TYPE_ERROR(toplevel_let,
|
||||
[<<?Pos(2, 7)
|
||||
"Toplevel \"let\" definitions are not supported. "
|
||||
"Value `this_is_illegal` could be replaced by 0-argument function.">>])
|
||||
, ?TYPE_ERROR(empty_typedecl,
|
||||
[<<?Pos(2, 8)
|
||||
"Empty type declarations are not supported. "
|
||||
@ -855,7 +861,11 @@ failing_contracts() ->
|
||||
<<?Pos(48, 5)
|
||||
"Unused return value.">>,
|
||||
<<?Pos(60, 5)
|
||||
"The function `dec` is defined but never used.">>
|
||||
"The function `dec` is defined but never used.">>,
|
||||
<<?Pos(73, 9)
|
||||
"The definition of `const` shadows an older definition at line 70, column 3.">>,
|
||||
<<?Pos(84, 7)
|
||||
"The constant `c` is defined but never used.">>
|
||||
])
|
||||
, ?TYPE_ERROR(polymorphism_contract_interface_recursive,
|
||||
[<<?Pos(1,24)
|
||||
@ -868,7 +878,7 @@ failing_contracts() ->
|
||||
" - line 9, column 5">>])
|
||||
, ?TYPE_ERROR(polymorphism_contract_missing_implementation,
|
||||
[<<?Pos(4,20)
|
||||
"Unimplemented function `f` from the interface `I1` in the contract `I2`">>
|
||||
"Unimplemented entrypoint `f` from the interface `I1` in the contract `I2`">>
|
||||
])
|
||||
, ?TYPE_ERROR(polymorphism_contract_same_decl_multi_interface,
|
||||
[<<?Pos(7,10)
|
||||
@ -1095,19 +1105,19 @@ failing_contracts() ->
|
||||
])
|
||||
, ?TYPE_ERROR(polymorphic_aens_resolve,
|
||||
[<<?Pos(4,5)
|
||||
"Invalid return type of `AENS.resolve`:\n"
|
||||
"Invalid return type of `AENSv2.resolve`:\n"
|
||||
" `'a`\n"
|
||||
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
|
||||
])
|
||||
, ?TYPE_ERROR(bad_aens_resolve,
|
||||
[<<?Pos(6,5)
|
||||
"Invalid return type of `AENS.resolve`:\n"
|
||||
"Invalid return type of `AENSv2.resolve`:\n"
|
||||
" `list(int)`\n"
|
||||
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
|
||||
])
|
||||
, ?TYPE_ERROR(bad_aens_resolve_using,
|
||||
[<<?Pos(7,5)
|
||||
"Invalid return type of `AENS.resolve`:\n"
|
||||
"Invalid return type of `AENSv2.resolve`:\n"
|
||||
" `list(int)`\n"
|
||||
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
|
||||
])
|
||||
@ -1152,6 +1162,111 @@ failing_contracts() ->
|
||||
"to arguments\n"
|
||||
" `Chain.create : (value : int, var_args) => 'c`">>
|
||||
])
|
||||
, ?TYPE_ERROR(polymorphism_add_stateful_entrypoint,
|
||||
[<<?Pos(5,25)
|
||||
"`f` cannot be stateful because the entrypoint `f` in the interface `I` is not stateful">>
|
||||
])
|
||||
, ?TYPE_ERROR(polymorphism_change_entrypoint_to_function,
|
||||
[<<?Pos(6,14)
|
||||
"`f` must be declared as an entrypoint instead of a function in order to implement the entrypoint `f` from the interface `I`">>
|
||||
])
|
||||
, ?TYPE_ERROR(polymorphism_non_payable_contract_implement_payable,
|
||||
[<<?Pos(4,10)
|
||||
"Non-payable contract `C` cannot implement payable interface `I`">>
|
||||
])
|
||||
, ?TYPE_ERROR(polymorphism_non_payable_interface_implement_payable,
|
||||
[<<?Pos(4,20)
|
||||
"Non-payable interface `H` cannot implement payable interface `I`">>
|
||||
])
|
||||
, ?TYPE_ERROR(polymorphism_remove_payable_entrypoint,
|
||||
[<<?Pos(5,16)
|
||||
"`f` must be payable because the entrypoint `f` in the interface `I` is payable">>
|
||||
])
|
||||
, ?TYPE_ERROR(calling_child_contract_entrypoint,
|
||||
[<<?Pos(5,20)
|
||||
"Invalid call to contract entrypoint `F.g`.\n"
|
||||
"It must be called as `c.g` for some `c : F`.">>])
|
||||
, ?TYPE_ERROR(using_contract_as_namespace,
|
||||
[<<?Pos(5,3)
|
||||
"Cannot use undefined namespace F">>])
|
||||
, ?TYPE_ERROR(hole_expression,
|
||||
[<<?Pos(5,13)
|
||||
"Found a hole of type `bool`">>,
|
||||
<<?Pos(6,17)
|
||||
"Found a hole of type `string`">>,
|
||||
<<?Pos(9,37)
|
||||
"Found a hole of type `(int) => int`">>,
|
||||
<<?Pos(13,20)
|
||||
"Found a hole of type `'a`">>
|
||||
])
|
||||
, ?TYPE_ERROR(toplevel_constants_contract_as_namespace,
|
||||
[<<?Pos(5,13)
|
||||
"Invalid use of the contract constant `G.const`.\n"
|
||||
"Toplevel contract constants can only be used in the contracts where they are defined.">>,
|
||||
<<?Pos(10,11)
|
||||
"Record type `G` does not have field `const`">>,
|
||||
<<?Pos(10,11)
|
||||
"Unbound field const">>,
|
||||
<<?Pos(11,11)
|
||||
"Record type `G` does not have field `const`">>,
|
||||
<<?Pos(11,11)
|
||||
"Unbound field const">>
|
||||
])
|
||||
, ?TYPE_ERROR(toplevel_constants_cycles,
|
||||
[<<?Pos(2,21)
|
||||
"Unbound variable `selfcycle`">>,
|
||||
<<?Pos(4,5)
|
||||
"Mutual recursion detected between the constants\n"
|
||||
" - `cycle1` at line 4, column 5\n"
|
||||
" - `cycle2` at line 5, column 5\n"
|
||||
" - `cycle3` at line 6, column 5">>
|
||||
])
|
||||
, ?TYPE_ERROR(toplevel_constants_in_interface,
|
||||
[<<?Pos(2,10)
|
||||
"The name of the compile-time constant cannot have pattern matching">>,
|
||||
<<?Pos(3,5)
|
||||
"Cannot define toplevel constants inside a contract interface">>,
|
||||
<<?Pos(4,5)
|
||||
"Cannot define toplevel constants inside a contract interface">>
|
||||
])
|
||||
, ?TYPE_ERROR(toplevel_constants_invalid_expr,
|
||||
[<<?Pos(10,9)
|
||||
"Invalid expression in the definition of the constant `c01`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(11,9)
|
||||
"Invalid expression in the definition of the constant `c02`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(12,9)
|
||||
"Invalid expression in the definition of the constant `c03`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(13,9)
|
||||
"Invalid expression in the definition of the constant `c04`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(14,9)
|
||||
"Invalid expression in the definition of the constant `c05`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(17,9)
|
||||
"Invalid expression in the definition of the constant `c07`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(18,9)
|
||||
"Invalid expression in the definition of the constant `c08`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(19,9)
|
||||
"Invalid expression in the definition of the constant `c09`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(20,9)
|
||||
"Invalid expression in the definition of the constant `c10`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>,
|
||||
<<?Pos(21,9)
|
||||
"Invalid expression in the definition of the constant `c11`\n"
|
||||
"You can only use the following expressions as constants: literals, lists, tuples, maps, and other constants">>
|
||||
])
|
||||
, ?TYPE_ERROR(toplevel_constants_invalid_id,
|
||||
[<<?Pos(2,9)
|
||||
"The name of the compile-time constant cannot have pattern matching">>,
|
||||
<<?Pos(3,9)
|
||||
"The name of the compile-time constant cannot have pattern matching">>
|
||||
])
|
||||
].
|
||||
|
||||
validation_test_() ->
|
||||
|
@ -53,7 +53,7 @@ simple_contracts_test_() ->
|
||||
%% associativity
|
||||
[ RightAssoc(Op) || Op <- ["||", "&&", "::", "++"] ],
|
||||
[ NonAssoc(Op) || Op <- ["==", "!=", "<", ">", "=<", ">="] ],
|
||||
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod"] ],
|
||||
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod", "band", "bor", "bxor", "<<", ">>"] ],
|
||||
|
||||
%% precedence
|
||||
[ Stronger(Op2, Op1) || [T1 , T2 | _] <- tails(Tiers), Op1 <- T1, Op2 <- T2 ],
|
||||
|
@ -39,7 +39,8 @@ all_tokens() ->
|
||||
%% Symbols
|
||||
lists:map(Lit, [',', '.', ';', '|', ':', '(', ')', '[', ']', '{', '}']) ++
|
||||
%% Operators
|
||||
lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod, ':', '::', '->', '=>', '||', '&&', '!']) ++
|
||||
lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod,
|
||||
':', '::', '->', '=>', '||', '&&', '!', 'band', 'bor', 'bxor', 'bnot' ,'<<', '>>']) ++
|
||||
%% Keywords
|
||||
lists:map(Lit, [contract, type, 'let', switch]) ++
|
||||
%% Comment token (not an actual token), just for tests
|
||||
|
@ -6,77 +6,77 @@ main contract AENSTest =
|
||||
// Name resolution
|
||||
|
||||
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) =
|
||||
AENS.resolve(name, key)
|
||||
AENSv2.resolve(name, key)
|
||||
|
||||
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)) =
|
||||
AENS.resolve(name, key)
|
||||
AENSv2.resolve(name, key)
|
||||
|
||||
stateful entrypoint resolve_oracle_query(name : string, key : string) : option(oracle_query(int, int)) =
|
||||
AENS.resolve(name, key)
|
||||
AENSv2.resolve(name, key)
|
||||
|
||||
// Transactions
|
||||
|
||||
stateful entrypoint preclaim(addr : address, // Claim on behalf of this account (can be Contract.address)
|
||||
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)
|
||||
chash : hash, // Commitment hash
|
||||
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,
|
||||
name : string,
|
||||
salt : int,
|
||||
name_fee : int) : unit =
|
||||
AENS.claim(addr, name, salt, name_fee)
|
||||
AENSv2.claim(addr, name, salt, name_fee)
|
||||
|
||||
stateful entrypoint signedClaim(addr : address,
|
||||
name : string,
|
||||
salt : int,
|
||||
name_fee : int,
|
||||
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,
|
||||
name : string,
|
||||
ttl : option(Chain.ttl),
|
||||
client_ttl : option(int),
|
||||
pointers : option(map(string, AENS.pointee))) : unit =
|
||||
AENS.update(owner, name, ttl, client_ttl, pointers)
|
||||
pointers : option(map(string, AENSv2.pointee))) : unit =
|
||||
AENSv2.update(owner, name, ttl, client_ttl, pointers)
|
||||
|
||||
stateful entrypoint signedUpdate(owner : address,
|
||||
name : string,
|
||||
ttl : option(Chain.ttl),
|
||||
client_ttl : option(int),
|
||||
pointers : option(map(string, AENS.pointee)),
|
||||
pointers : option(map(string, AENSv2.pointee)),
|
||||
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,
|
||||
new_owner : address,
|
||||
name : string) : unit =
|
||||
AENS.transfer(owner, new_owner, name)
|
||||
AENSv2.transfer(owner, new_owner, name)
|
||||
|
||||
stateful entrypoint signedTransfer(owner : address,
|
||||
new_owner : address,
|
||||
name : string,
|
||||
sign : signature) : unit =
|
||||
AENS.transfer(owner, new_owner, name, signature = sign)
|
||||
AENSv2.transfer(owner, new_owner, name, signature = sign)
|
||||
|
||||
stateful entrypoint revoke(owner : address,
|
||||
name : string) : unit =
|
||||
AENS.revoke(owner, name)
|
||||
AENSv2.revoke(owner, name)
|
||||
|
||||
stateful entrypoint signedRevoke(owner : address,
|
||||
name : string,
|
||||
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) =
|
||||
let p1 : AENS.pointee = AENS.AccountPt(Call.caller)
|
||||
let p2 : AENS.pointee = AENS.OraclePt(Call.caller)
|
||||
let p3 : AENS.pointee = AENS.ContractPt(Call.caller)
|
||||
let p4 : AENS.pointee = AENS.ChannelPt(Call.caller)
|
||||
AENS.update(owner, name, None, None,
|
||||
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
|
||||
["contract_pubkey"] = p3, ["misc"] = p4 }))
|
||||
let p1 : AENSv2.pointee = AENSv2.AccountPt(Call.caller)
|
||||
let p2 : AENSv2.pointee = AENSv2.OraclePt(Call.caller)
|
||||
let p3 : AENSv2.pointee = AENSv2.ContractPt(Call.caller)
|
||||
let p4 : AENSv2.pointee = AENSv2.ChannelPt(Call.caller)
|
||||
let p5 : AENSv2.pointee = AENSv2.DataPt("any something will do")
|
||||
AENSv2.update(owner, name, None, None,
|
||||
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) =
|
||||
switch(AENS.lookup(name))
|
||||
Some(AENS.Name(_, FixedTTL(ttl), _)) => ttl
|
||||
switch(AENSv2.lookup(name))
|
||||
Some(AENSv2.Name(_, FixedTTL(ttl), _)) => ttl
|
||||
|
||||
entrypoint expiry(o : oracle(int, int)) : int =
|
||||
Oracle.expiry(o)
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
namespace Ns =
|
||||
datatype d('a) = D | S(int) | M('a, list('a), int)
|
||||
private function fff() = 123
|
||||
let const = 1
|
||||
|
||||
stateful entrypoint
|
||||
f (1, x) = (_) => x
|
||||
@ -33,6 +34,8 @@ contract AllSyntax =
|
||||
|
||||
type state = shakespeare(int)
|
||||
|
||||
let cc = "str"
|
||||
|
||||
entrypoint init() = {
|
||||
johann = 1000,
|
||||
wolfgang = -10,
|
||||
|
@ -3,7 +3,7 @@ contract BadAENSresolve =
|
||||
type t('a) = option(list('a))
|
||||
|
||||
function fail() : t(int) =
|
||||
AENS.resolve("foo.aet", "whatever")
|
||||
AENSv2.resolve("foo.aet", "whatever")
|
||||
|
||||
entrypoint main_fun() = ()
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
contract BadAENSresolve =
|
||||
using AENS
|
||||
using AENSv2
|
||||
|
||||
type t('a) = option(list('a))
|
||||
|
||||
|
5
test/contracts/calling_child_contract_entrypoint.aes
Normal file
5
test/contracts/calling_child_contract_entrypoint.aes
Normal file
@ -0,0 +1,5 @@
|
||||
contract F =
|
||||
entrypoint g() = 1
|
||||
|
||||
main contract C =
|
||||
entrypoint f() = F.g()
|
14
test/contracts/ceres.aes
Normal file
14
test/contracts/ceres.aes
Normal file
@ -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)
|
13
test/contracts/hole_expression.aes
Normal file
13
test/contracts/hole_expression.aes
Normal file
@ -0,0 +1,13 @@
|
||||
include "List.aes"
|
||||
|
||||
contract C =
|
||||
entrypoint f() =
|
||||
let ??? = true
|
||||
let v = ???
|
||||
let q = v == "str"
|
||||
let xs = [1, 2, 3, 4]
|
||||
switch (List.first(List.map(???, xs)))
|
||||
Some(x) => x + 1
|
||||
None => 0
|
||||
|
||||
function g() = ???
|
@ -1,7 +1,7 @@
|
||||
contract PolymorphicAENSresolve =
|
||||
|
||||
function fail() : option('a) =
|
||||
AENS.resolve("foo.aet", "whatever")
|
||||
AENSv2.resolve("foo.aet", "whatever")
|
||||
|
||||
entrypoint main_fun() = ()
|
||||
|
||||
|
5
test/contracts/polymorphism_add_stateful_entrypoint.aes
Normal file
5
test/contracts/polymorphism_add_stateful_entrypoint.aes
Normal file
@ -0,0 +1,5 @@
|
||||
contract interface I =
|
||||
entrypoint f : () => int
|
||||
|
||||
contract C : I =
|
||||
stateful entrypoint f() = 1
|
@ -0,0 +1,6 @@
|
||||
contract interface I =
|
||||
entrypoint f : () => int
|
||||
|
||||
contract C : I =
|
||||
entrypoint init() = ()
|
||||
function f() = 1
|
@ -0,0 +1,5 @@
|
||||
payable contract interface I =
|
||||
payable entrypoint f : () => int
|
||||
|
||||
contract C : I =
|
||||
entrypoint f() = 123
|
@ -0,0 +1,8 @@
|
||||
payable contract interface I =
|
||||
payable entrypoint f : () => int
|
||||
|
||||
contract interface H : I =
|
||||
payable entrypoint f : () => int
|
||||
|
||||
payable contract C : H =
|
||||
entrypoint f() = 123
|
@ -0,0 +1,14 @@
|
||||
contract interface F =
|
||||
entrypoint f : () => int
|
||||
|
||||
payable contract interface G : F =
|
||||
payable entrypoint f : () => int
|
||||
entrypoint g : () => int
|
||||
|
||||
payable contract interface H =
|
||||
payable entrypoint h : () => int
|
||||
|
||||
payable contract C : G, H =
|
||||
payable entrypoint f() = 1
|
||||
payable entrypoint g() = 2
|
||||
payable entrypoint h() = 3
|
@ -0,0 +1,7 @@
|
||||
contract interface I =
|
||||
payable entrypoint f : () => int
|
||||
entrypoint g : () => int
|
||||
|
||||
contract C : I =
|
||||
payable entrypoint f() = 1
|
||||
payable entrypoint g() = 2
|
@ -0,0 +1,7 @@
|
||||
contract interface I =
|
||||
stateful entrypoint f : () => int
|
||||
stateful entrypoint g : () => int
|
||||
|
||||
contract C : I =
|
||||
stateful entrypoint f() = 1
|
||||
entrypoint g() = 2
|
@ -0,0 +1,5 @@
|
||||
contract interface I =
|
||||
payable entrypoint f : () => int
|
||||
|
||||
contract C : I =
|
||||
entrypoint f() = 1
|
64
test/contracts/toplevel_constants.aes
Normal file
64
test/contracts/toplevel_constants.aes
Normal file
@ -0,0 +1,64 @@
|
||||
namespace N0 =
|
||||
let nsconst = 1
|
||||
|
||||
namespace N =
|
||||
let nsconst = N0.nsconst
|
||||
|
||||
contract C =
|
||||
datatype event = EventX(int, string)
|
||||
|
||||
record account = { name : string,
|
||||
balance : int }
|
||||
|
||||
let c01 = 2425
|
||||
let c02 = -5
|
||||
let c03 = ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
|
||||
let c04 = true
|
||||
let c05 = Bits.none
|
||||
let c06 = #fedcba9876543210
|
||||
let c07 = "str"
|
||||
let c08 = [1, 2, 3]
|
||||
let c09 = [(true, 24), (false, 19), (false, -42)]
|
||||
let c10 = (42, "Foo", true)
|
||||
let c11 = { name = "str", balance = 100000000 }
|
||||
let c12 = {["foo"] = 19, ["bar"] = 42}
|
||||
let c13 = Some(42)
|
||||
let c14 = 11 : int
|
||||
let c15 = EventX(0, "Hello")
|
||||
let c16 = #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
|
||||
let c17 = #000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
|
||||
let c18 = RelativeTTL(50)
|
||||
let c19 = ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5
|
||||
let c20 = oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
|
||||
let c21 = ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ : C
|
||||
let c22 = N.nsconst
|
||||
let c23 = c01
|
||||
let c24 = c11.name
|
||||
let c25 : int = 1
|
||||
|
||||
entrypoint f01() = c01
|
||||
entrypoint f02() = c02
|
||||
entrypoint f03() = c03
|
||||
entrypoint f04() = c04
|
||||
entrypoint f05() = c05
|
||||
entrypoint f06() = c06
|
||||
entrypoint f07() = c07
|
||||
entrypoint f08() = c08
|
||||
entrypoint f09() = c09
|
||||
entrypoint f10() = c10
|
||||
entrypoint f11() = c11
|
||||
entrypoint f12() = c12
|
||||
entrypoint f13() = c13
|
||||
entrypoint f14() = c14
|
||||
entrypoint f15() = c15
|
||||
entrypoint f16() = c16
|
||||
entrypoint f17() = c17
|
||||
entrypoint f18() = c18
|
||||
entrypoint f19() = c19
|
||||
entrypoint f20() = c20
|
||||
entrypoint f21() = c21
|
||||
entrypoint f22() = c22
|
||||
entrypoint f23() = c23
|
||||
entrypoint f24() = c24
|
||||
entrypoint f25() = c25
|
||||
entrypoint fqual() = C.c01
|
11
test/contracts/toplevel_constants_contract_as_namespace.aes
Normal file
11
test/contracts/toplevel_constants_contract_as_namespace.aes
Normal file
@ -0,0 +1,11 @@
|
||||
contract G =
|
||||
let const = 1
|
||||
|
||||
main contract C =
|
||||
let c = G.const
|
||||
|
||||
stateful entrypoint f() =
|
||||
let g = Chain.create() : G
|
||||
|
||||
g.const
|
||||
g.const()
|
6
test/contracts/toplevel_constants_cycles.aes
Normal file
6
test/contracts/toplevel_constants_cycles.aes
Normal file
@ -0,0 +1,6 @@
|
||||
contract C =
|
||||
let selfcycle = selfcycle
|
||||
|
||||
let cycle1 = cycle2
|
||||
let cycle2 = cycle3
|
||||
let cycle3 = cycle1
|
7
test/contracts/toplevel_constants_in_interface.aes
Normal file
7
test/contracts/toplevel_constants_in_interface.aes
Normal file
@ -0,0 +1,7 @@
|
||||
contract interface I =
|
||||
let (x::y::_) = [1,2,3]
|
||||
let c = 10
|
||||
let d = 10
|
||||
|
||||
contract C =
|
||||
entrypoint init() = ()
|
21
test/contracts/toplevel_constants_invalid_expr.aes
Normal file
21
test/contracts/toplevel_constants_invalid_expr.aes
Normal file
@ -0,0 +1,21 @@
|
||||
main contract C =
|
||||
record account = { name : string,
|
||||
balance : int }
|
||||
|
||||
let one = 1
|
||||
let opt = Some(5)
|
||||
let acc = { name = "str", balance = 100000 }
|
||||
let mpp = {["foo"] = 19, ["bar"] = 42}
|
||||
|
||||
let c01 = [x | x <- [1,2,3,4,5]]
|
||||
let c02 = [x + k | x <- [1,2,3,4,5], let k = x*x]
|
||||
let c03 = [x + y | x <- [1,2,3,4,5], let k = x*x, if (k > 5), y <- [k, k+1, k+2]]
|
||||
let c04 = if (one > 2) 3 else 4
|
||||
let c05 = switch (opt)
|
||||
Some(x) => x
|
||||
None => 2
|
||||
let c07 = acc{ balance = one }
|
||||
let c08 = mpp["foo"]
|
||||
let c09 = mpp["non" = 10]
|
||||
let c10 = mpp{["foo"] = 20}
|
||||
let c11 = (x) => x + 1
|
3
test/contracts/toplevel_constants_invalid_id.aes
Normal file
3
test/contracts/toplevel_constants_invalid_id.aes
Normal file
@ -0,0 +1,3 @@
|
||||
contract C =
|
||||
let x::_ = [1,2,3,4]
|
||||
let y::(p = z::_) = [1,2,3,4]
|
@ -1,3 +0,0 @@
|
||||
contract C =
|
||||
let this_is_illegal = 2/0
|
||||
entrypoint this_is_legal() = 2/0
|
@ -2,10 +2,10 @@
|
||||
// Named argument builtins are:
|
||||
// Oracle.register
|
||||
// Oracle.respond
|
||||
// AENS.preclaim
|
||||
// AENS.claim
|
||||
// AENS.transfer
|
||||
// AENS.revoke
|
||||
// AENSv2.preclaim
|
||||
// AENSv2.claim
|
||||
// AENSv2.transfer
|
||||
// AENSv2.revoke
|
||||
// Oracle.extend
|
||||
include "String.aes"
|
||||
contract UnappliedBuiltins =
|
||||
@ -28,7 +28,7 @@ contract UnappliedBuiltins =
|
||||
function oracle_get_answer() = Oracle.get_answer : (o, _) => _
|
||||
function oracle_check() = Oracle.check : 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_default() = Map.lookup_default : (_, m, _) => _
|
||||
function map_member() = Map.member : (_, m) => _
|
||||
|
7
test/contracts/using_contract_as_namespace.aes
Normal file
7
test/contracts/using_contract_as_namespace.aes
Normal file
@ -0,0 +1,7 @@
|
||||
contract F =
|
||||
entrypoint g() = 1
|
||||
|
||||
main contract C =
|
||||
using F for [g]
|
||||
|
||||
entrypoint f() = g()
|
5
test/contracts/warning_unused_include_no_include.aes
Normal file
5
test/contracts/warning_unused_include_no_include.aes
Normal file
@ -0,0 +1,5 @@
|
||||
namespace N =
|
||||
function nconst() = 1
|
||||
|
||||
main contract C =
|
||||
entrypoint f() = N.nconst()
|
@ -12,7 +12,7 @@ namespace UnusedNamespace =
|
||||
// Unused
|
||||
private function h() = 3
|
||||
|
||||
contract Warnings =
|
||||
main contract Warnings =
|
||||
|
||||
type state = int
|
||||
|
||||
@ -58,3 +58,31 @@ namespace FunctionsAsArgs =
|
||||
private function inc(n : int) : int = n + 1
|
||||
// Never used
|
||||
private function dec(n : int) : int = n - 1
|
||||
|
||||
contract Remote =
|
||||
entrypoint id(_) = 0
|
||||
|
||||
contract C =
|
||||
payable stateful entrypoint
|
||||
call_missing_con() : int = (ct_1111111111111111111111111111112JF6Dz72 : Remote).id(value = 1, 0)
|
||||
|
||||
namespace ShadowingConst =
|
||||
let const = 1
|
||||
|
||||
function f() =
|
||||
let const = 2
|
||||
const
|
||||
|
||||
namespace UnusedConstNamespace =
|
||||
// No warnings should be shown even though const is not used
|
||||
let const = 1
|
||||
|
||||
contract UnusedConstContract =
|
||||
// Only `c` should show a warning because it is never used in the contract
|
||||
let a = 1
|
||||
let b = 2
|
||||
let c = 3
|
||||
|
||||
entrypoint f() =
|
||||
// Both normal access and qualified access should prevent the unused const warning
|
||||
a + UnusedConstContract.b
|
||||
|
Loading…
x
Reference in New Issue
Block a user