Compare commits

..

6 Commits

Author SHA1 Message Date
Hans Svensson 8e191b0c88 [Ceres]: Document generic all names delegation signatures (#440) 2023-03-22 08:55:57 +01:00
Hans Svensson f70fc56df8 Ceres: document changes to Auth.tx_hash (#439) 2023-03-03 10:06:45 +01:00
Hans Svensson 4ae24722f4 Remove unused variable in AENSCompat 2022-12-01 08:33:59 +01:00
Hans Svensson 55a97852ed Introduce AENSv2 namespace to introduce raw data pointers (#426) 2022-11-16 21:31:44 +01:00
Hans Svensson 1380142082 Add bitwise operations, Address.to_bytes and Crypto.poseidon 2022-11-11 16:15:05 +01:00
Hans Svensson 1754763e23 Let CERES compiler be v8.0.0 tentatively 2022-11-04 10:30:04 +01:00
44 changed files with 836 additions and 2111 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
mkdocs==1.4.2 mkdocs==1.2.4
mkdocs-simple-hooks==0.1.5 mkdocs-simple-hooks==0.1.5
mkdocs-material==9.0.9 mkdocs-material==7.3.6
mike==1.1.2 mike==1.1.2
pygments==2.14.0 pygments==2.12.0
+2 -38
View File
@@ -18,42 +18,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
only contains the old datatypes, that can be used to interface existing only contains the old datatypes, that can be used to interface existing
contracts. Standard library `AENSCompat` is added to convert between old and contracts. Standard library `AENSCompat` is added to convert between old and
new pointers. new pointers.
- Introduce arbitrary sized binary arrays (type `bytes()`); adding `Bytes.split_any`,
`Bytes.to_fixed_size`, `Bytes.to_any_size`, `Bytes.size`, `String.to_bytes`,
and `Int.to_bytes`; and adjust `Bytes.concat` to allow both fixed and arbitrary
sized byte arrays.
- `Chain.network_id` - a function to get hold of the Chain's network id.
### Changed ### Changed
### Removed ### Removed
- `Bitwise.aes` standard library is removed - the builtin operations are superior. - `Bitwise.aes` standard library is removed - the builtin operations are superior.
## [Unreleased] ## [Unreleased]
### Added ### Added
### Changed
### Removed
### Fixed
## [7.2.1]
### Fixed
- Fixed bugs with the newly added debugging symbols
## [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. - Options to enable/disable certain optimizations.
- The ability to call a different instance of the current contract - The ability to call a different instance of the current contract
``` ```
@@ -62,12 +32,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
entrypoint f(c : Main) : int = c.spend(10) entrypoint f(c : Main) : int = c.spend(10)
``` ```
- Return a mapping from variables to FATE registers in the compilation output. - Return a mapping from variables to FATE registers in the compilation output.
- Hole expression.
### Changed ### Changed
- Type definitions serialised to ACI as `typedefs` field instead of `type_defs` to increase compatibility. - Type definitions serialised to ACI as `typedefs` field instead of `type_defs` to increase compatibility.
- Check contracts and entrypoints modifiers when implementing interfaces. ### Removed
- 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 ### Fixed
- Typechecker crashes if Chain.create or Chain.clone are used without arguments. - Typechecker crashes if Chain.create or Chain.clone are used without arguments.
@@ -422,10 +389,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Simplify calldata creation - instead of passing a compiled contract, simply - Simplify calldata creation - instead of passing a compiled contract, simply
pass a (stubbed) contract string. pass a (stubbed) contract string.
[Unreleased]: https://github.com/aeternity/aesophia/compare/v7.2.1...HEAD [Unreleased]: https://github.com/aeternity/aesophia/compare/v7.0.1...HEAD
[7.2.1]: https://github.com/aeternity/aesophia/compare/v7.2.0...v7.2.1
[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.1]: https://github.com/aeternity/aesophia/compare/v7.0.0...v7.0.1
[7.0.0]: https://github.com/aeternity/aesophia/compare/v6.1.0...v7.0.0 [7.0.0]: https://github.com/aeternity/aesophia/compare/v6.1.0...v7.0.0
[6.1.0]: https://github.com/aeternity/aesophia/compare/v6.0.2...v6.1.0 [6.1.0]: https://github.com/aeternity/aesophia/compare/v6.0.2...v6.1.0
+2
View File
@@ -53,6 +53,8 @@ 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 `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: #### Options to control which compiler optimizations should run:
By default all optimizations are turned on, to disable an optimization, it should be By default all optimizations are turned on, to disable an optimization, it should be
+4 -54
View File
@@ -191,17 +191,6 @@ contract interface X : Z =
entrypoint z() = 1 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 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)), 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)),
@@ -256,10 +245,10 @@ datatype bi('a) = Bi // bi is bivariant on 'a
The following facts apply here: The following facts apply here:
- `co('a)` is a subtype of `co('b)` when `'a` is a subtype of `'b` - `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` - `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` - `in('a)` is a subtype of `in('b) when `'a` is equal to `'b`
- `bi('a)` is a subtype of `bi('b)` always - `bi('a)` is a subtype of `bi('b) always
That altogether induce the following rules of subtyping in Sophia: That altogether induce the following rules of subtyping in Sophia:
@@ -560,45 +549,6 @@ Sophia has the following types:
| oracle_query('a, 'b) | `oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY` | | oracle_query('a, 'b) | `oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY` |
| contract | `ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ` | | 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 ## Arithmetic
Sophia integers (`int`) are represented by arbitrary-sized signed words and support the following Sophia integers (`int`) are represented by arbitrary-sized signed words and support the following
+40 -108
View File
@@ -242,7 +242,7 @@ signature), i.e. over `network id` + `owner address` + `string "AENS"` +
##### update ##### update
``` ```
AENSv2.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int), AENSv2.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
new_ptrs : option(map(string, AENSv2.pointee)), <signature : signature>) : unit new_ptrs : map(string, AENSv2.pointee), <signature : signature>) : unit
``` ```
Updates the name. If the optional parameters are set to `None` that parameter Updates the name. If the optional parameters are set to `None` that parameter
@@ -371,7 +371,7 @@ Each bit is true if and only if it was 1 in `a` and 0 in `b`
### Bytes ### Bytes
#### to\_int #### to_int
``` ```
Bytes.to_int(b : bytes(n)) : int Bytes.to_int(b : bytes(n)) : int
``` ```
@@ -379,7 +379,7 @@ Bytes.to_int(b : bytes(n)) : int
Interprets the byte array as a big endian integer Interprets the byte array as a big endian integer
#### to\_str #### to_str
``` ```
Bytes.to_str(b : bytes(n)) : string Bytes.to_str(b : bytes(n)) : string
``` ```
@@ -392,8 +392,7 @@ Returns the hexadecimal representation of the byte array
Bytes.concat : (a : bytes(m), b : bytes(n)) => bytes(m + n) Bytes.concat : (a : bytes(m), b : bytes(n)) => bytes(m + n)
``` ```
Concatenates two byte arrays. If `m` and `n` are known at compile time, the Concatenates two byte arrays
result can be used as a fixed size byte array, otherwise it has type `bytes()`.
#### split #### split
@@ -403,38 +402,6 @@ Bytes.split(a : bytes(m + n)) : bytes(m) * bytes(n)
Splits a byte array at given index Splits a byte array at given index
#### split\_any
```
Bytes.split_any(a : bytes(), at : int) : option(bytes() * bytes(n))
```
Splits an arbitrary size byte array at index `at`. If `at` is positive split
from the beginning of the array, if `at` is negative, split `abs(at)` from the
_end_ of the array. If the array is shorter than `abs(at)` then `None` is
returned.
#### to\_fixed\_size
```
Bytes.to_fixed_size(a : bytes()) : option(bytes(n))
```
Converts an arbitrary size byte array to a fix size byte array. If `a` is
`n` bytes, `None` is returned.
#### to\_any\_size
```
Bytes.to_any_size(a : bytes(n)) : bytes()
```
Converts a fixed size byte array to an arbitrary size byte array. This is a
no-op at run-time, and only used during type checking.
#### size
```
Bytes.size(a : bytes()) : int
```
Computes the lenght/size of a byte array.
### Call ### Call
@@ -572,6 +539,38 @@ Address.to_bytes(a : address) : bytes(32)
The binary representation of the address. The binary representation of the address.
##### coinbase
```
Chain.coinbase : address
```
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.
##### bytecode_hash ##### bytecode_hash
``` ```
Chain.bytecode_hash : 'c => option(hash) Chain.bytecode_hash : 'c => option(hash)
@@ -642,7 +641,6 @@ main contract Market =
The typechecker must be certain about the created contract's type, so it is The typechecker must be certain about the created contract's type, so it is
worth writing it explicitly as shown in the example. worth writing it explicitly as shown in the example.
##### clone ##### clone
``` ```
Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ... Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ...
@@ -701,62 +699,11 @@ implementation of the `init` function does not actually return `state`, but
calls `put` instead. Moreover, FATE prevents even handcrafted calls to `init`. 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 ##### event
``` ```
Chain.event(e : event) : unit 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.
##### network\_id
```
Chain.network\_id : string
```
The network id of the chain.
##### 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 ### Char
@@ -871,20 +818,12 @@ Verifies a standard 64-byte ECDSA signature (`R || S`).
### Int ### Int
#### to\_str #### to_str
``` ```
Int.to_str(n : int) : string Int.to_str : int => string
``` ```
Casts the integer to a string (in decimal representation). Casts integer to string using decimal representation
#### to\_bytes
```
Int.to_bytes(n : int, size : int) : bytes()
```
Casts the integer to a byte array with `size` bytes (big endian, truncating if
necessary).
### Map ### Map
@@ -2461,13 +2400,6 @@ to_int(s : string) : option(int)
Converts a decimal ("123", "-253") or a hexadecimal ("0xa2f", "-0xBBB") string into Converts a decimal ("123", "-253") or a hexadecimal ("0xa2f", "-0xBBB") string into
an integer. If the string doesn't contain a valid number `None` is returned. an integer. If the string doesn't contain a valid number `None` is returned.
#### to\_bytes
```
to_bytes(s : string) : bytes()
```
Converts string into byte array.
#### sha3 #### sha3
``` ```
sha3(s : string) : hash sha3(s : string) : hash
-2
View File
@@ -104,7 +104,6 @@ Implement ::= ':' Sep1(Con, ',')
Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
| 'record' Id ['(' TVar* ')'] '=' RecordType | 'record' Id ['(' TVar* ')'] '=' RecordType
| 'datatype' Id ['(' TVar* ')'] '=' DataType | 'datatype' Id ['(' TVar* ')'] '=' DataType
| 'let' Id [':' Type] '=' Expr
| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl) | (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
| Using | Using
@@ -239,7 +238,6 @@ Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x +
| Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%' | Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%'
| AccountAddress | ContractAddress // Chain identifiers | AccountAddress | ContractAddress // Chain identifiers
| OracleAddress | OracleQueryId // Chain identifiers | OracleAddress | OracleQueryId // Chain identifiers
| '???' // Hole expression 1 + ???
Generator ::= Pattern '<-' Expr // Generator Generator ::= Pattern '<-' Expr // Generator
| 'if' '(' Expr ')' // Guard | 'if' '(' Expr ')' // Guard
-3
View File
@@ -1,8 +1,5 @@
include "List.aes" include "List.aes"
namespace String = namespace String =
// Gives a bytes() representation of the string
function to_bytes(s : string) : bytes() = StringInternal.to_bytes(s)
// Computes the SHA3/Keccak hash of the string // Computes the SHA3/Keccak hash of the string
function sha3(s : string) : hash = StringInternal.sha3(s) function sha3(s : string) : hash = StringInternal.sha3(s)
// Computes the SHA256 hash of the string. // Computes the SHA256 hash of the string.
+3 -2
View File
@@ -2,7 +2,8 @@
{erl_opts, [debug_info]}. {erl_opts, [debug_info]}.
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.4.0"}}} {deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {tag, "v3.2.0"}}}
, {getopt, "1.0.1"}
, {eblake2, "1.0.0"} , {eblake2, "1.0.0"}
, {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}} , {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}}
]}. ]}.
@@ -14,7 +15,7 @@
]}. ]}.
{relx, [{release, {aesophia, "8.0.0"}, {relx, [{release, {aesophia, "8.0.0"},
[aesophia, aebytecode]}, [aesophia, aebytecode, getopt]},
{dev_mode, true}, {dev_mode, true},
{include_erts, false}, {include_erts, false},
+3 -3
View File
@@ -1,11 +1,11 @@
{"1.2.0", {"1.2.0",
[{<<"aebytecode">>, [{<<"aebytecode">>,
{git,"https://github.com/aeternity/aebytecode.git", {git,"https://github.com/aeternity/aebytecode.git",
{ref,"009e0361922037f978f9c0ef357d4d1be8559928"}}, {ref,"2a0a397afad6b45da52572170f718194018bf33c"}},
0}, 0},
{<<"aeserialization">>, {<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git", {git,"https://github.com/aeternity/aeserialization.git",
{ref,"177bf604b2a05e940f92cf00e96e6e269e708245"}}, {ref,"eb68fe331bd476910394966b7f5ede7a74d37e35"}},
1}, 1},
{<<"base58">>, {<<"base58">>,
{git,"https://github.com/aeternity/erl-base58.git", {git,"https://github.com/aeternity/erl-base58.git",
@@ -16,7 +16,7 @@
{git,"https://github.com/aeternity/enacl.git", {git,"https://github.com/aeternity/enacl.git",
{ref,"793ddb502f7fe081302e1c42227dca70b09f8e17"}}, {ref,"793ddb502f7fe081302e1c42227dca70b09f8e17"}},
2}, 2},
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},1}, {<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0},
{<<"jsx">>, {<<"jsx">>,
{git,"https://github.com/talentdeficit/jsx.git", {git,"https://github.com/talentdeficit/jsx.git",
{ref,"3074d4865b3385a050badf7828ad31490d860df5"}}, {ref,"3074d4865b3385a050badf7828ad31490d860df5"}},
-2
View File
@@ -282,8 +282,6 @@ decode_type(#{list := [Et]}) ->
decode_type(#{map := Ets}) -> decode_type(#{map := Ets}) ->
Ts = decode_types(Ets), Ts = decode_types(Ets),
["map",$(,lists:join(",", Ts),$)]; ["map",$(,lists:join(",", Ts),$)];
decode_type(#{bytes := any}) ->
["bytes()"];
decode_type(#{bytes := Len}) -> decode_type(#{bytes := Len}) ->
["bytes(", integer_to_list(Len), ")"]; ["bytes(", integer_to_list(Len), ")"];
decode_type(#{variant := Ets}) -> decode_type(#{variant := Ets}) ->
+90 -439
View File
@@ -89,9 +89,8 @@
-type field_constraint() :: #field_constraint{} | #record_create_constraint{} | #is_contract_constraint{}. -type field_constraint() :: #field_constraint{} | #record_create_constraint{} | #is_contract_constraint{}.
-type byte_constraint() :: {is_bytes, term(), utype()} -type byte_constraint() :: {is_bytes, utype()}
| {is_fixed_bytes, term(), utype()} | {add_bytes, aeso_syntax:ann(), concat | split, utype(), utype(), utype()}.
| {add_bytes, aeso_syntax:ann(), concat | split | split_any, utype(), utype(), utype()}.
-type aens_resolve_constraint() :: {aens_resolve_type, utype()}. -type aens_resolve_constraint() :: {aens_resolve_type, utype()}.
-type oracle_type_constraint() :: {oracle_type, aeso_syntax:ann(), utype()}. -type oracle_type_constraint() :: {oracle_type, aeso_syntax:ann(), utype()}.
@@ -125,18 +124,15 @@
-type variance() :: invariant | covariant | contravariant | bivariant. -type variance() :: invariant | covariant | contravariant | bivariant.
-type fun_info() :: {aeso_syntax:ann(), typesig() | type()}. -type fun_info() :: {aeso_syntax:ann(), typesig() | type()}.
-type type_info() :: {aeso_syntax:ann(), typedef()}. -type type_info() :: {aeso_syntax:ann(), typedef()}.
-type const_info() :: {aeso_syntax:ann(), type()}. -type var_info() :: {aeso_syntax:ann(), utype()}.
-type var_info() :: {aeso_syntax:ann(), utype()}.
-type fun_env() :: [{name(), fun_info()}]. -type fun_env() :: [{name(), fun_info()}].
-type type_env() :: [{name(), type_info()}]. -type type_env() :: [{name(), type_info()}].
-type const_env() :: [{name(), const_info()}].
-record(scope, { funs = [] :: fun_env() -record(scope, { funs = [] :: fun_env()
, types = [] :: type_env() , types = [] :: type_env()
, consts = [] :: const_env()
, access = public :: access() , access = public :: access()
, kind = namespace :: namespace | contract , kind = namespace :: namespace | contract
, ann = [{origin, system}] :: aeso_syntax:ann() , ann = [{origin, system}] :: aeso_syntax:ann()
@@ -156,7 +152,6 @@
, in_guard = false :: boolean() , in_guard = false :: boolean()
, stateful = false :: boolean() , stateful = false :: boolean()
, unify_throws = true :: boolean() , unify_throws = true :: boolean()
, current_const = none :: none | aeso_syntax:id()
, current_function = none :: none | aeso_syntax:id() , current_function = none :: none | aeso_syntax:id()
, what = top :: top | namespace | contract | contract_interface , what = top :: top | namespace | contract | contract_interface
}). }).
@@ -188,13 +183,9 @@ pop_scope(Env) ->
get_scope(#env{ scopes = Scopes }, Name) -> get_scope(#env{ scopes = Scopes }, Name) ->
maps:get(Name, Scopes, false). 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(). -spec on_current_scope(env(), fun((scope()) -> scope())) -> env().
on_current_scope(Env = #env{ namespace = NS, scopes = Scopes }, Fun) -> on_current_scope(Env = #env{ namespace = NS, scopes = Scopes }, Fun) ->
Scope = get_current_scope(Env), Scope = maps:get(NS, Scopes),
Env#env{ scopes = Scopes#{ NS => Fun(Scope) } }. Env#env{ scopes = Scopes#{ NS => Fun(Scope) } }.
-spec on_scopes(env(), fun((scope()) -> scope())) -> env(). -spec on_scopes(env(), fun((scope()) -> scope())) -> env().
@@ -202,8 +193,8 @@ on_scopes(Env = #env{ scopes = Scopes }, Fun) ->
Env#env{ scopes = maps:map(fun(_, Scope) -> Fun(Scope) end, Scopes) }. Env#env{ scopes = maps:map(fun(_, Scope) -> Fun(Scope) end, Scopes) }.
-spec bind_var(aeso_syntax:id(), utype(), env()) -> env(). -spec bind_var(aeso_syntax:id(), utype(), env()) -> env().
bind_var({id, Ann, X}, T, Env) -> bind_var({id, Ann, X}, T, Env = #env{ vars = Vars }) ->
when_warning(warn_shadowing, fun() -> warn_potential_shadowing(Env, Ann, X) end), when_warning(warn_shadowing, fun() -> warn_potential_shadowing(Ann, X, Vars) end),
Env#env{ vars = [{X, {Ann, T}} | Env#env.vars] }. Env#env{ vars = [{X, {Ann, T}} | Env#env.vars] }.
-spec bind_vars([{aeso_syntax:id(), utype()}], env()) -> env(). -spec bind_vars([{aeso_syntax:id(), utype()}], env()) -> env().
@@ -238,7 +229,7 @@ force_bind_fun(X, Type, Env = #env{ what = What }) ->
NoCode = get_option(no_code, false), NoCode = get_option(no_code, false),
Entry = if X == "init", What == contract, not NoCode -> Entry = if X == "init", What == contract, not NoCode ->
{reserved_init, Ann, Type}; {reserved_init, Ann, Type};
What == contract; What == contract_interface -> {contract_fun, Ann, Type}; What == contract_interface -> {contract_fun, Ann, Type};
true -> {Ann, Type} true -> {Ann, Type}
end, end,
on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) -> on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) ->
@@ -256,37 +247,6 @@ bind_type(X, Ann, Def, Env) ->
Scope#scope{ types = [{X, {Ann, Def}} | Types] } Scope#scope{ types = [{X, {Ann, Def}} | Types] }
end). 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 %% Bind state primitives
-spec bind_state(env()) -> env(). -spec bind_state(env()) -> env().
bind_state(Env) -> bind_state(Env) ->
@@ -352,11 +312,11 @@ bind_contract(Typing, {Contract, Ann, Id, _Impls, Contents}, Env)
Sys = [{origin, system}], Sys = [{origin, system}],
TypeOrFresh = fun({typed, _, _, Type}) -> Type; (_) -> fresh_uvar(Sys) end, TypeOrFresh = fun({typed, _, _, Type}) -> Type; (_) -> fresh_uvar(Sys) end,
Fields = Fields =
[ {field_t, AnnF, Entrypoint, contract_call_type(aeso_syntax:set_ann(Sys, Type))} [ {field_t, AnnF, Entrypoint, contract_call_type(Type)}
|| {fun_decl, AnnF, Entrypoint, Type = {fun_t, _, _, _, _}} <- Contents ] ++ || {fun_decl, AnnF, Entrypoint, Type = {fun_t, _, _, _, _}} <- Contents ] ++
[ {field_t, AnnF, Entrypoint, [ {field_t, AnnF, Entrypoint,
contract_call_type( contract_call_type(
{fun_t, Sys, [], [TypeOrFresh(Arg) || Arg <- Args], TypeOrFresh(Ret)}) {fun_t, AnnF, [], [TypeOrFresh(Arg) || Arg <- Args], TypeOrFresh(Ret)})
} }
|| {letfun, AnnF, Entrypoint = {id, _, Name}, Args, _Type, [{guarded, _, [], Ret}]} <- Contents, || {letfun, AnnF, Entrypoint = {id, _, Name}, Args, _Type, [{guarded, _, [], Ret}]} <- Contents,
Name =/= "init" Name =/= "init"
@@ -466,35 +426,23 @@ lookup_env(Env, Kind, Ann, Name) ->
lookup_env1(#env{ namespace = Current, used_namespaces = UsedNamespaces, scopes = Scopes }, Kind, Ann, QName) -> lookup_env1(#env{ namespace = Current, used_namespaces = UsedNamespaces, scopes = Scopes }, Kind, Ann, QName) ->
Qual = lists:droplast(QName), Qual = lists:droplast(QName),
Name = lists:last(QName), Name = lists:last(QName),
QNameIsEvent = lists:suffix(["Chain", "event"], QName),
AllowPrivate = lists:prefix(Qual, Current), AllowPrivate = lists:prefix(Qual, Current),
%% Get the scope %% Get the scope
case maps:get(Qual, Scopes, false) of case maps:get(Qual, Scopes, false) of
false -> false; %% TODO: return reason for not in scope false -> false; %% TODO: return reason for not in scope
#scope{ funs = Funs, types = Types, consts = Consts, kind = ScopeKind } -> #scope{ funs = Funs, types = Types } ->
Defs = case Kind of Defs = case Kind of
type -> Types; type -> Types;
term -> Funs term -> Funs
end, end,
%% Look up the unqualified name %% Look up the unqualified name
case proplists:get_value(Name, Defs, false) of 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} -> {reserved_init, Ann1, Type} ->
type_error({cannot_call_init_function, Ann}), type_error({cannot_call_init_function, Ann}),
{QName, {Ann1, Type}}; %% Return the type to avoid an extra not-in-scope error {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} -> {contract_fun, Ann1, Type} ->
type_error({contract_treated_as_namespace_entrypoint, Ann, QName}), type_error({contract_treated_as_namespace, Ann, QName}),
{QName, {Ann1, Type}}; {QName, {Ann1, Type}};
{Ann1, _} = E -> {Ann1, _} = E ->
%% Check that it's not private (or we can see private funs) %% Check that it's not private (or we can see private funs)
@@ -535,11 +483,8 @@ qname({qid, _, Xs}) -> Xs;
qname({con, _, X}) -> [X]; qname({con, _, X}) -> [X];
qname({qcon, _, Xs}) -> Xs. qname({qcon, _, Xs}) -> Xs.
-spec name(Named | {typed, _, Named, _}) -> name() when -spec name(aeso_syntax:id() | aeso_syntax:con()) -> name().
Named :: aeso_syntax:id() | aeso_syntax:con(). name({_, _, X}) -> X.
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(). -spec qid(aeso_syntax:ann(), qname()) -> aeso_syntax:id() | aeso_syntax:qid().
qid(Ann, [X]) -> {id, Ann, X}; qid(Ann, [X]) -> {id, Ann, X};
@@ -654,7 +599,6 @@ global_env() ->
{"block_height", Int}, {"block_height", Int},
{"difficulty", Int}, {"difficulty", Int},
{"gas_limit", Int}, {"gas_limit", Int},
{"network_id", String},
{"bytecode_hash",FunC1(bytecode_hash, A, Option(Hash))}, {"bytecode_hash",FunC1(bytecode_hash, A, Option(Hash))},
{"create", Stateful( {"create", Stateful(
FunN([ {named_arg_t, Ann, {id, Ann, "value"}, Int, {typed, Ann, {int, Ann, 0}, Int}} FunN([ {named_arg_t, Ann, {id, Ann, "value"}, Int, {typed, Ann, {int, Ann, 0}, Int}}
@@ -831,7 +775,6 @@ global_env() ->
[{"length", Fun1(String, Int)}, [{"length", Fun1(String, Int)},
{"concat", Fun([String, String], String)}, {"concat", Fun([String, String], String)},
{"to_list", Fun1(String, List(Char))}, {"to_list", Fun1(String, List(Char))},
{"to_bytes", Fun1(String, Bytes(any))},
{"from_list", Fun1(List(Char), String)}, {"from_list", Fun1(List(Char), String)},
{"to_upper", Fun1(String, String)}, {"to_upper", Fun1(String, String)},
{"to_lower", Fun1(String, String)}, {"to_lower", Fun1(String, String)},
@@ -862,20 +805,15 @@ global_env() ->
%% Bytes %% Bytes
BytesScope = #scope BytesScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"to_int", Fun1(Bytes('_'), Int)}, [{"to_int", Fun1(Bytes(any), Int)},
{"to_str", Fun1(Bytes('_'), String)}, {"to_str", Fun1(Bytes(any), String)},
{"to_fixed_size", Fun1(Bytes(any), Option(Bytes(fixed)))}, {"concat", FunC(bytes_concat, [Bytes(any), Bytes(any)], Bytes(any))},
{"to_any_size", Fun1(Bytes(fixed), Bytes(any))}, {"split", FunC(bytes_split, [Bytes(any)], Pair(Bytes(any), Bytes(any)))}
{"size", Fun1(Bytes('_'), Int)},
{"concat", FunC(bytes_concat, [Bytes('_'), Bytes('_')], Bytes('_'))},
{"split", FunC1(bytes_split, Bytes(fixed), Pair(Bytes(fixed), Bytes(fixed)))},
{"split_any", Fun([Bytes(any), Int], Option(Pair(Bytes(any), Bytes(any))))}
]) }, ]) },
%% Conversion %% Conversion
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}, IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)},
{"to_bytes", Fun([Int, Int], Bytes(any))}, {"mulmod", Fun([Int, Int, Int], Int)}]) },
{"mulmod", Fun([Int, Int, Int], Int)}]) },
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)}, AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)},
{"to_bytes", Fun1(Address, Bytes(32))}, {"to_bytes", Fun1(Address, Bytes(32))},
@@ -987,7 +925,6 @@ infer1(Env0, [Contract0 = {Contract, Ann, ConName, Impls, Code} | Rest], Acc, Op
contract -> ets_insert(defined_contracts, {qname(ConName)}); contract -> ets_insert(defined_contracts, {qname(ConName)});
contract_interface -> ok contract_interface -> ok
end, end,
check_contract_preserved_payability(Env, ConName, Ann, Impls, Acc, What),
populate_functions_to_implement(Env, ConName, Impls, Acc), populate_functions_to_implement(Env, ConName, Impls, Acc),
Env1 = bind_contract(untyped, Contract0, Env), Env1 = bind_contract(untyped, Contract0, Env),
{Env2, Code1} = infer_contract_top(push_scope(contract, ConName, Env1), What, Code, Options), {Env2, Code1} = infer_contract_top(push_scope(contract, ConName, Env1), What, Code, Options),
@@ -1013,25 +950,6 @@ infer1(Env, [{pragma, _, _} | Rest], Acc, Options) ->
%% Pragmas are checked in check_modifiers %% Pragmas are checked in check_modifiers
infer1(Env, Rest, Acc, Options). infer1(Env, Rest, Acc, Options).
-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. %% Report all functions that were not implemented by the contract ContractName.
-spec report_unimplemented_functions(env(), ContractName) -> ok | no_return() when -spec report_unimplemented_functions(env(), ContractName) -> ok | no_return() when
ContractName :: aeso_syntax:con(). ContractName :: aeso_syntax:con().
@@ -1132,7 +1050,6 @@ infer_contract(Env0, What, Defs0, Options) ->
({fun_clauses, _, _, _, _}) -> function; ({fun_clauses, _, _, _, _}) -> function;
({fun_decl, _, _, _}) -> prototype; ({fun_decl, _, _, _}) -> prototype;
({using, _, _, _, _}) -> using; ({using, _, _, _, _}) -> using;
({letval, _, _, _}) -> constant;
(_) -> unexpected (_) -> unexpected
end, end,
Get = fun(K, In) -> [ Def || Def <- In, Kind(Def) == K ] end, Get = fun(K, In) -> [ Def || Def <- In, Kind(Def) == K ] end,
@@ -1148,12 +1065,11 @@ infer_contract(Env0, What, Defs0, Options) ->
contract_interface -> Env1; contract_interface -> Env1;
contract -> bind_state(Env1) %% bind state and put contract -> bind_state(Env1) %% bind state and put
end, end,
{Env2C, Consts} = check_constants(Env2, Get(constant, Defs)), {ProtoSigs, Decls} = lists:unzip([ check_fundecl(Env1, Decl) || Decl <- Get(prototype, Defs) ]),
{ProtoSigs, Decls} = lists:unzip([ check_fundecl(Env2C, Decl) || Decl <- Get(prototype, Defs) ]),
[ type_error({missing_definition, Id}) || {fun_decl, _, Id, _} <- Decls, [ type_error({missing_definition, Id}) || {fun_decl, _, Id, _} <- Decls,
What =:= contract, What =:= contract,
get_option(no_code, false) =:= false ], get_option(no_code, false) =:= false ],
Env3 = bind_funs(ProtoSigs, Env2C), Env3 = bind_funs(ProtoSigs, Env2),
Functions = Get(function, Defs), Functions = Get(function, Defs),
%% Check for duplicates in Functions (we turn it into a map below) %% Check for duplicates in Functions (we turn it into a map below)
FunBind = fun({letfun, Ann, {id, _, Fun}, _, _, _}) -> {Fun, {tuple_t, Ann, []}}; FunBind = fun({letfun, Ann, {id, _, Fun}, _, _, _}) -> {Fun, {tuple_t, Ann, []}};
@@ -1173,7 +1089,7 @@ infer_contract(Env0, What, Defs0, Options) ->
check_entrypoints(Defs1), check_entrypoints(Defs1),
destroy_and_report_type_errors(Env4), destroy_and_report_type_errors(Env4),
%% Add inferred types of definitions %% Add inferred types of definitions
{Env5, TypeDefs ++ Decls ++ Consts ++ Defs1}. {Env5, TypeDefs ++ Decls ++ Defs1}.
%% Restructure blocks into multi-clause fundefs (`fun_clauses`). %% Restructure blocks into multi-clause fundefs (`fun_clauses`).
-spec process_blocks([aeso_syntax:decl()]) -> [aeso_syntax:decl()]. -spec process_blocks([aeso_syntax:decl()]) -> [aeso_syntax:decl()].
@@ -1323,21 +1239,6 @@ opposite_variance(covariant) -> contravariant;
opposite_variance(contravariant) -> covariant; opposite_variance(contravariant) -> covariant;
opposite_variance(bivariant) -> bivariant. 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, []) -> check_usings(Env, []) ->
Env; Env;
check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con, Alias, Parts} | Rest]) -> check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con, Alias, Parts} | Rest]) ->
@@ -1352,10 +1253,6 @@ check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con,
create_type_errors(), create_type_errors(),
type_error({using_undefined_namespace, Ann, qname(Con)}), type_error({using_undefined_namespace, Ann, qname(Con)}),
destroy_and_report_type_errors(Env); 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 -> Scope ->
Nsp = case Parts of Nsp = case Parts of
none -> none ->
@@ -1609,37 +1506,19 @@ check_reserved_entrypoints(Funs) ->
check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type = {fun_t, _, _, _, _}}) -> check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type = {fun_t, _, _, _, _}}) ->
Type1 = {fun_t, _, Named, Args, Ret} = check_type(Env, Type), Type1 = {fun_t, _, Named, Args, Ret} = check_type(Env, Type),
TypeSig = {type_sig, Ann, none, Named, Args, Ret}, TypeSig = {type_sig, Ann, none, Named, Args, Ret},
register_implementation(Id, TypeSig), register_implementation(Name),
{{Name, TypeSig}, {fun_decl, Ann, Id, Type1}}; {{Name, TypeSig}, {fun_decl, Ann, Id, Type1}};
check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type}) -> check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type}) ->
type_error({fundecl_must_have_funtype, Ann, Id, Type}), type_error({fundecl_must_have_funtype, Ann, Id, Type}),
{{Name, {type_sig, Ann, none, [], [], Type}}, check_type(Env, Type)}. {{Name, {type_sig, Ann, none, [], [], Type}}, check_type(Env, Type)}.
%% Register the function FunId as implemented by deleting it from the functions %% Register the function FunName as implemented by deleting it from the functions
%% to be implemented table if it is included there, or return true otherwise. %% to be implemented table if it is included there, or return true otherwise.
-spec register_implementation(FunId, FunSig) -> true | no_return() when -spec register_implementation(FunName) -> true | no_return() when
FunId :: aeso_syntax:id(), FunName :: string().
FunSig :: typesig(). register_implementation(Name) ->
register_implementation(Id, Sig) ->
Name = name(Id),
case ets_lookup(functions_to_implement, Name) of case ets_lookup(functions_to_implement, Name) of
[{Name, Interface, Decl = {fun_decl, _, DeclId, _}}] -> [{Name, _, {fun_decl, _, _, _}}] ->
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); ets_delete(functions_to_implement, Name);
[] -> [] ->
true; true;
@@ -1649,9 +1528,9 @@ register_implementation(Id, Sig) ->
infer_nonrec(Env, LetFun) -> infer_nonrec(Env, LetFun) ->
create_constraints(), create_constraints(),
NewLetFun = {{_, Sig}, _} = infer_letfun(Env, LetFun), NewLetFun = {{FunName, _}, _} = infer_letfun(Env, LetFun),
check_special_funs(Env, NewLetFun), check_special_funs(Env, NewLetFun),
register_implementation(get_letfun_id(LetFun), Sig), register_implementation(FunName),
solve_then_destroy_and_report_unsolved_constraints(Env), solve_then_destroy_and_report_unsolved_constraints(Env),
Result = {TypeSig, _} = instantiate(NewLetFun), Result = {TypeSig, _} = instantiate(NewLetFun),
print_typesig(TypeSig), print_typesig(TypeSig),
@@ -1680,8 +1559,8 @@ infer_letrec(Env, Defs) ->
ExtendEnv = bind_funs(Funs, Env), ExtendEnv = bind_funs(Funs, Env),
Inferred = Inferred =
[ begin [ begin
Res = {{Name, TypeSig}, LetFun} = infer_letfun(ExtendEnv, LF), Res = {{Name, TypeSig}, _} = infer_letfun(ExtendEnv, LF),
register_implementation(get_letfun_id(LetFun), TypeSig), register_implementation(Name),
Got = proplists:get_value(Name, Funs), Got = proplists:get_value(Name, Funs),
Expect = typesig_to_fun_t(TypeSig), Expect = typesig_to_fun_t(TypeSig),
unify(Env, Got, Expect, {check_typesig, Name, Got, Expect}), unify(Env, Got, Expect, {check_typesig, Name, Got, Expect}),
@@ -1733,9 +1612,6 @@ infer_letfun1(Env0 = #env{ namespace = NS }, {letfun, Attrib, Fun = {id, NameAtt
{{Name, TypeSig}, {{Name, TypeSig},
{letfun, Attrib, {id, NameAttrib, Name}, TypedArgs, ResultType, NewGuardedBodies}}. {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) -> desugar_clauses(Ann, Fun, {type_sig, _, _, _, ArgTypes, RetType}, Clauses) ->
NeedDesugar = NeedDesugar =
case Clauses of case Clauses of
@@ -1782,24 +1658,14 @@ lookup_name(Env = #env{ namespace = NS, current_function = CurFn }, As, Id, Opti
type_error({unbound_variable, Id}), type_error({unbound_variable, Id}),
{Id, fresh_uvar(As)}; {Id, fresh_uvar(As)};
{QId, {_, Ty}} -> {QId, {_, Ty}} ->
%% Variables and functions cannot be used when CurFn is `none`. when_warning(warn_unused_variables, fun() -> used_variable(NS, name(CurFn), QId) end),
%% i.e. they cannot be used in toplevel constants when_warning(warn_unused_functions,
[ begin fun() -> register_function_call(NS ++ qname(CurFn), QId) end),
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), Freshen = proplists:get_value(freshen, Options, false),
check_stateful(Env, Id, Ty), check_stateful(Env, Id, Ty),
Ty1 = case Ty of Ty1 = case Ty of
{type_sig, _, _, _, _, _} -> freshen_type_sig(As, Ty, [{fun_name, Id}]); {type_sig, _, _, _, _, _} -> freshen_type_sig(As, Ty);
_ when Freshen -> freshen_type(As, Ty, [{fun_name, Id}]); _ when Freshen -> freshen_type(As, Ty);
_ -> Ty _ -> Ty
end, end,
{set_qname(QId, Id), Ty1} {set_qname(QId, Id), Ty1}
@@ -1823,14 +1689,10 @@ check_stateful(#env { current_function = Fun }, _Id, _Type) ->
%% Hack: don't allow passing the 'value' named arg if not stateful. This only %% 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. %% works since the user can't create functions with named arguments.
check_stateful_named_arg(#env{ stateful = Stateful, current_function = Fun }, {id, _, "value"}, Default) -> check_stateful_named_arg(#env{ stateful = false, current_function = Fun }, {id, _, "value"}, Default) ->
case Default of case Default of
{int, _, 0} -> ok; {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; end;
check_stateful_named_arg(_, _, _) -> ok. check_stateful_named_arg(_, _, _) -> ok.
@@ -1948,10 +1810,6 @@ infer_expr(_Env, Body={contract_pubkey, As, _}) ->
{typed, As, Body, Con}; {typed, As, Body, Con};
infer_expr(_Env, Body={id, As, "_"}) -> infer_expr(_Env, Body={id, As, "_"}) ->
{typed, As, Body, fresh_uvar(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 -> infer_expr(Env, Id = {Tag, As, _}) when Tag == id; Tag == qid ->
{QName, Type} = lookup_name(Env, As, Id), {QName, Type} = lookup_name(Env, As, Id),
{typed, As, QName, Type}; {typed, As, QName, Type};
@@ -2159,81 +2017,6 @@ infer_expr(Env, Let = {letfun, Attrs, _, _, _, _}) ->
type_error({missing_body_for_let, Attrs}), type_error({missing_body_for_let, Attrs}),
infer_expr(Env, {block, Attrs, [Let, abort_expr(Attrs, "missing body")]}). 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) -> infer_var_args_fun(Env, {typed, Ann, Fun, FunType0}, NamedArgs, ArgTypes) ->
FunType = FunType =
case Fun of case Fun of
@@ -2358,14 +2141,9 @@ infer_pattern(Env, Pattern) ->
NewPattern = infer_expr(NewEnv, Pattern), NewPattern = infer_expr(NewEnv, Pattern),
{NewEnv#env{ in_pattern = Env#env.in_pattern }, NewPattern}. {NewEnv#env{ in_pattern = Env#env.in_pattern }, NewPattern}.
infer_case(Env = #env{ namespace = NS, current_function = FunId }, Attrs, Pattern, ExprType, GuardedBranches, SwitchType) -> infer_case(Env = #env{ namespace = NS, current_function = {id, _, Fun} }, Attrs, Pattern, ExprType, GuardedBranches, SwitchType) ->
{NewEnv, NewPattern = {typed, _, _, PatType}} = infer_pattern(Env, Pattern), {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}) -> InferGuardedBranches = fun({guarded, Ann, Guards, Branch}) ->
NewGuards = lists:map(fun(Guard) -> NewGuards = lists:map(fun(Guard) ->
check_expr(NewEnv#env{ in_guard = true }, Guard, {id, Attrs, "bool"}) check_expr(NewEnv#env{ in_guard = true }, Guard, {id, Attrs, "bool"})
@@ -2399,19 +2177,6 @@ infer_block(Env, Attrs, [E|Rest], BlockType) ->
when_warning(warn_unused_return_value, fun() -> potential_unused_return_value(NewE) end), when_warning(warn_unused_return_value, fun() -> potential_unused_return_value(NewE) end),
[NewE|infer_block(Env, Attrs, Rest, BlockType)]. [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}) infer_infix({BoolOp, As})
when BoolOp =:= '&&'; BoolOp =:= '||' -> when BoolOp =:= '&&'; BoolOp =:= '||' ->
Bool = {id, As, "bool"}, Bool = {id, As, "bool"},
@@ -2680,8 +2445,7 @@ destroy_and_report_unsolved_constraints(Env) ->
(_) -> false (_) -> false
end, OtherCs2), end, OtherCs2),
{BytesCs, OtherCs4} = {BytesCs, OtherCs4} =
lists:partition(fun({is_bytes, _, _}) -> true; lists:partition(fun({is_bytes, _}) -> true;
({is_fixed_bytes, _, _}) -> true;
({add_bytes, _, _, _, _, _}) -> true; ({add_bytes, _, _, _, _, _}) -> true;
(_) -> false (_) -> false
end, OtherCs3), end, OtherCs3),
@@ -2810,21 +2574,15 @@ solve_constraint(Env, C = #dependent_type_constraint{}) ->
check_named_argument_constraint(Env, C); check_named_argument_constraint(Env, C);
solve_constraint(Env, C = #named_argument_constraint{}) -> solve_constraint(Env, C = #named_argument_constraint{}) ->
check_named_argument_constraint(Env, C); check_named_argument_constraint(Env, C);
solve_constraint(_Env, {is_bytes, _, _}) -> ok; solve_constraint(_Env, {is_bytes, _}) -> ok;
solve_constraint(_Env, {is_fixed_bytes, _, _}) -> ok; solve_constraint(Env, {add_bytes, Ann, _, A0, B0, C0}) ->
solve_constraint(Env, {add_bytes, Ann, Action, A0, B0, C0}) ->
A = unfold_types_in_type(Env, dereference(A0)), A = unfold_types_in_type(Env, dereference(A0)),
B = unfold_types_in_type(Env, dereference(B0)), B = unfold_types_in_type(Env, dereference(B0)),
C = unfold_types_in_type(Env, dereference(C0)), C = unfold_types_in_type(Env, dereference(C0)),
case {A, B, C} of case {A, B, C} of
{{bytes_t, _, M}, {bytes_t, _, N}, _} when is_integer(M), is_integer(N) -> {{bytes_t, _, M}, {bytes_t, _, N}, _} -> unify(Env, {bytes_t, Ann, M + N}, C, {at, Ann});
unify(Env, {bytes_t, Ann, M + N}, C, {at, Ann}); {{bytes_t, _, M}, _, {bytes_t, _, R}} when R >= M -> unify(Env, {bytes_t, Ann, R - M}, B, {at, Ann});
{{bytes_t, _, M}, _, {bytes_t, _, R}} when is_integer(M), is_integer(R), R >= M -> {_, {bytes_t, _, N}, {bytes_t, _, R}} when R >= N -> unify(Env, {bytes_t, Ann, R - N}, A, {at, Ann});
unify(Env, {bytes_t, Ann, R - M}, B, {at, Ann});
{_, {bytes_t, _, N}, {bytes_t, _, R}} when is_integer(N), is_integer(R), R >= N ->
unify(Env, {bytes_t, Ann, R - N}, A, {at, Ann});
{{bytes_t, _, _}, {bytes_t, _, _}, _} when Action == concat ->
unify(Env, {bytes_t, Ann, any}, C, {at, Ann});
_ -> ok _ -> ok
end; end;
solve_constraint(_, _) -> ok. solve_constraint(_, _) -> ok.
@@ -2833,29 +2591,18 @@ check_bytes_constraints(Env, Constraints) ->
InAddConstraint = [ T || {add_bytes, _, _, A, B, C} <- Constraints, InAddConstraint = [ T || {add_bytes, _, _, A, B, C} <- Constraints,
T <- [A, B, C], T <- [A, B, C],
element(1, T) /= bytes_t ], element(1, T) /= bytes_t ],
InSplitConstraint = [ T || {add_bytes, _, split, A, B, C} <- Constraints,
T <- [A, B, C],
element(1, T) /= bytes_t ],
%% Skip is_bytes constraints for types that occur in add_bytes constraints %% Skip is_bytes constraints for types that occur in add_bytes constraints
%% (no need to generate error messages for both is_bytes and add_bytes). %% (no need to generate error messages for both is_bytes and add_bytes).
Skip = fun({is_bytes, _, T}) -> lists:member(T, InAddConstraint); Skip = fun({is_bytes, T}) -> lists:member(T, InAddConstraint);
({is_fixed_bytes, _, T}) -> lists:member(T, InSplitConstraint);
(_) -> false end, (_) -> false end,
[ check_bytes_constraint(Env, C) || C <- Constraints, not Skip(C) ]. [ check_bytes_constraint(Env, C) || C <- Constraints, not Skip(C) ].
check_bytes_constraint(Env, {is_bytes, Ann, Type}) -> check_bytes_constraint(Env, {is_bytes, Type}) ->
Type1 = unfold_types_in_type(Env, instantiate(Type)), Type1 = unfold_types_in_type(Env, instantiate(Type)),
case Type1 of case Type1 of
{bytes_t, _, N} when is_integer(N); N == any -> ok; {bytes_t, _, _} -> ok;
_ -> _ ->
type_error({unknown_byte_type, Ann, Type}) type_error({unknown_byte_length, Type})
end;
check_bytes_constraint(Env, {is_fixed_bytes, Ann, Type}) ->
Type1 = unfold_types_in_type(Env, instantiate(Type)),
case Type1 of
{bytes_t, _, N} when is_integer(N) -> ok;
_ ->
type_error({unknown_byte_length, Ann, Type})
end; end;
check_bytes_constraint(Env, {add_bytes, Ann, Fun, A0, B0, C0}) -> check_bytes_constraint(Env, {add_bytes, Ann, Fun, A0, B0, C0}) ->
A = unfold_types_in_type(Env, instantiate(A0)), A = unfold_types_in_type(Env, instantiate(A0)),
@@ -3156,9 +2903,9 @@ unify1(Env, T, {uvar, A, R}, Variance, When) ->
unify1(Env, {uvar, A, R}, T, Variance, When); unify1(Env, {uvar, A, R}, T, Variance, When);
unify1(_Env, {tvar, _, X}, {tvar, _, X}, _Variance, _When) -> true; %% Rigid type variables unify1(_Env, {tvar, _, X}, {tvar, _, X}, _Variance, _When) -> true; %% Rigid type variables
unify1(Env, [A|B], [C|D], [V|Variances], When) -> unify1(Env, [A|B], [C|D], [V|Variances], When) ->
unify0(Env, A, C, V, When) and unify0(Env, B, D, Variances, When); unify0(Env, A, C, V, When) andalso unify0(Env, B, D, Variances, When);
unify1(Env, [A|B], [C|D], Variance, When) -> unify1(Env, [A|B], [C|D], Variance, When) ->
unify0(Env, A, C, Variance, When) and unify0(Env, B, D, Variance, When); unify0(Env, A, C, Variance, When) andalso unify0(Env, B, D, Variance, When);
unify1(_Env, X, X, _Variance, _When) -> unify1(_Env, X, X, _Variance, _When) ->
true; true;
unify1(_Env, _A, {id, _, "void"}, Variance, _When) unify1(_Env, _A, {id, _, "void"}, Variance, _When)
@@ -3203,8 +2950,8 @@ unify1(_Env, {fun_t, _, _, var_args, _}, {fun_t, _, _, _, _}, _Variance, When) -
type_error({unify_varargs, When}); type_error({unify_varargs, When});
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, Variance, When) unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, Variance, When)
when length(Args1) == length(Args2) -> when length(Args1) == length(Args2) ->
unify0(Env, Named1, Named2, opposite_variance(Variance), When) and unify0(Env, Named1, Named2, opposite_variance(Variance), When) andalso
unify0(Env, Args1, Args2, opposite_variance(Variance), When) and unify0(Env, Args1, Args2, opposite_variance(Variance), When) andalso
unify0(Env, Result1, Result2, Variance, When); unify0(Env, Result1, Result2, Variance, When);
unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, Variance, When) unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, Variance, When)
when length(Args1) == length(Args2), Tag == id orelse Tag == qid -> when length(Args1) == length(Args2), Tag == id orelse Tag == qid ->
@@ -3318,59 +3065,49 @@ create_freshen_tvars() ->
destroy_freshen_tvars() -> destroy_freshen_tvars() ->
ets_delete(freshen_tvars). ets_delete(freshen_tvars).
freshen_type(Ann, Type, Ctx) -> freshen_type(Ann, Type) ->
create_freshen_tvars(), create_freshen_tvars(),
Type1 = freshen(Ann, Type, Ctx), Type1 = freshen(Ann, Type),
destroy_freshen_tvars(), destroy_freshen_tvars(),
Type1. Type1.
freshen(Type) -> freshen(Type) ->
freshen(aeso_syntax:get_ann(Type), Type, none). freshen(aeso_syntax:get_ann(Type), Type).
freshen(Ann, {tvar, _, Name}, _Ctx) -> freshen(Ann, {tvar, _, Name}) ->
NewT = case ets_lookup(freshen_tvars, Name) of NewT = case ets_lookup(freshen_tvars, Name) of
[] -> fresh_uvar(Ann); [] -> fresh_uvar(Ann);
[{Name, T}] -> T [{Name, T}] -> T
end, end,
ets_insert(freshen_tvars, {Name, NewT}), ets_insert(freshen_tvars, {Name, NewT}),
NewT; NewT;
freshen(Ann, {bytes_t, _, '_'}, Ctx) -> freshen(Ann, {bytes_t, _, any}) ->
X = fresh_uvar(Ann), X = fresh_uvar(Ann),
add_constraint({is_bytes, Ctx, X}), add_constraint({is_bytes, X}),
X; X;
freshen(Ann, {bytes_t, _, fixed}, Ctx) -> freshen(Ann, T) when is_tuple(T) ->
X = fresh_uvar(Ann), list_to_tuple(freshen(Ann, tuple_to_list(T)));
add_constraint({is_fixed_bytes, Ctx, X}), freshen(Ann, [A | B]) ->
X; [freshen(Ann, A) | freshen(Ann, B)];
freshen(Ann, {fun_t, FAnn, NamedArgs, Args, Result}, Ctx) when is_list(Args) -> freshen(_, X) ->
{fun_t, FAnn, freshen(Ann, NamedArgs, Ctx),
[ freshen(Ann, Arg, [{arg, Ix} | Ctx]) || {Arg, Ix} <- lists:zip(Args, lists:seq(1, length(Args))) ],
freshen(Ann, Result, [result | Ctx])};
freshen(Ann, {fun_t, FAnn, NamedArgs, Arg, Result}, Ctx) ->
{fun_t, FAnn, freshen(Ann, NamedArgs, Ctx), freshen(Ann, Arg, Ctx), freshen(Ann, Result, [result | Ctx])};
freshen(Ann, T, Ctx) when is_tuple(T) ->
list_to_tuple(freshen(Ann, tuple_to_list(T), Ctx));
freshen(Ann, [A | B], Ctx) ->
[freshen(Ann, A, Ctx) | freshen(Ann, B, Ctx)];
freshen(_, X, _Ctx) ->
X. X.
freshen_type_sig(Ann, TypeSig = {type_sig, _, Constr, _, _, _}, Ctx) -> freshen_type_sig(Ann, TypeSig = {type_sig, _, Constr, _, _, _}) ->
FunT = freshen_type(Ann, typesig_to_fun_t(TypeSig), Ctx), FunT = freshen_type(Ann, typesig_to_fun_t(TypeSig)),
apply_typesig_constraint(Ann, Constr, FunT), apply_typesig_constraint(Ann, Constr, FunT),
FunT. FunT.
apply_typesig_constraint(_Ann, none, _FunT) -> ok; apply_typesig_constraint(_Ann, none, _FunT) -> ok;
apply_typesig_constraint(Ann, address_to_contract, {fun_t, _, [], [_], Type}) -> apply_typesig_constraint(Ann, address_to_contract, {fun_t, _, [], [_], Type}) ->
add_constraint([#is_contract_constraint{ contract_t = Type, add_constraint([#is_contract_constraint{ contract_t = Type,
context = {address_to_contract, Ann}}]); context = {address_to_contract, Ann}}]);
apply_typesig_constraint(Ann, bytes_concat, {fun_t, _, [], [A, B], C}) -> apply_typesig_constraint(Ann, bytes_concat, {fun_t, _, [], [A, B], C}) ->
add_constraint({add_bytes, Ann, concat, A, B, C}); add_constraint({add_bytes, Ann, concat, A, B, C});
apply_typesig_constraint(Ann, bytes_split, {fun_t, _, [], [C], {tuple_t, _, [A, B]}}) -> apply_typesig_constraint(Ann, bytes_split, {fun_t, _, [], [C], {tuple_t, _, [A, B]}}) ->
add_constraint({add_bytes, Ann, split, A, B, C}); add_constraint({add_bytes, Ann, split, A, B, C});
apply_typesig_constraint(Ann, bytecode_hash, {fun_t, _, _, [Con], _}) -> apply_typesig_constraint(Ann, bytecode_hash, {fun_t, _, _, [Con], _}) ->
add_constraint([#is_contract_constraint{ contract_t = Con, add_constraint([#is_contract_constraint{ contract_t = Con,
context = {bytecode_hash, Ann} }]). context = {bytecode_hash, Ann} }]).
%% Dereferences all uvars and replaces the uninstantiated ones with a %% Dereferences all uvars and replaces the uninstantiated ones with a
@@ -3411,7 +3148,6 @@ all_warnings() ->
[ warn_unused_includes [ warn_unused_includes
, warn_unused_stateful , warn_unused_stateful
, warn_unused_variables , warn_unused_variables
, warn_unused_constants
, warn_unused_typedefs , warn_unused_typedefs
, warn_unused_return_value , warn_unused_return_value
, warn_unused_functions , warn_unused_functions
@@ -3442,14 +3178,9 @@ when_warning(Warn, Do) ->
%% Warnings (Unused includes) %% Warnings (Unused includes)
potential_unused_include(Ann, SrcFile) -> potential_unused_include(Ann, SrcFile) ->
IsIncluded = aeso_syntax:get_ann(include_type, Ann, none) =/= none, case aeso_syntax:get_ann(file, Ann, no_file) of
case IsIncluded of no_file -> ok;
false -> ok; File -> ets_insert(warnings, {unused_include, File, SrcFile})
true ->
case aeso_syntax:get_ann(file, Ann, no_file) of
no_file -> ok;
File -> ets_insert(warnings, {unused_include, File, SrcFile})
end
end. end.
used_include(Ann) -> used_include(Ann) ->
@@ -3489,17 +3220,6 @@ used_variable(Namespace, Fun, [VarName]) ->
ets_match_delete(warnings, {unused_variable, '_', Namespace, Fun, VarName}); ets_match_delete(warnings, {unused_variable, '_', Namespace, Fun, VarName});
used_variable(_, _, _) -> ok. 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) %% Warnings (Unused return value)
potential_unused_return_value({typed, Ann, {app, _, {typed, _, _, {fun_t, _, _, _, {id, _, Type}}}, _}, _}) when Type /= "unit" -> potential_unused_return_value({typed, Ann, {app, _, {typed, _, _, {fun_t, _, _, _, {id, _, Type}}}, _}, _}) when Type /= "unit" ->
@@ -3545,11 +3265,9 @@ destroy_and_report_unused_functions() ->
%% Warnings (Shadowing) %% Warnings (Shadowing)
warn_potential_shadowing(_, _, "_") -> ok; warn_potential_shadowing(_, "_", _) -> ok;
warn_potential_shadowing(Env = #env{ vars = Vars }, Ann, Name) -> warn_potential_shadowing(Ann, Name, Vars) ->
CurrentScope = get_current_scope(Env), case proplists:get_value(Name, Vars, false) of
Consts = CurrentScope#scope.consts,
case proplists:get_value(Name, Vars ++ Consts, false) of
false -> ok; false -> ok;
{AnnOld, _} -> ets_insert(warnings, {shadowing, Ann, Name, AnnOld}) {AnnOld, _} -> ets_insert(warnings, {shadowing, Ann, Name, AnnOld})
end. end.
@@ -3653,9 +3371,6 @@ mk_error({cannot_unify, A, B, Cxt, When}) ->
[pp(instantiate(A)), pp(instantiate(B))]), [pp(instantiate(A)), pp(instantiate(B))]),
{Pos, Ctxt} = pp_when(When), {Pos, Ctxt} = pp_when(When),
mk_t_err(Pos, Msg, Ctxt); 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}) -> mk_error({unbound_variable, Id}) ->
Msg = io_lib:format("Unbound variable `~s`", [pp(Id)]), Msg = io_lib:format("Unbound variable `~s`", [pp(Id)]),
case Id of case Id of
@@ -3791,6 +3506,10 @@ mk_error({type_decl, _, {id, Pos, Name}, _}) ->
Msg = io_lib:format("Empty type declarations are not supported. Type `~s` lacks a definition", Msg = io_lib:format("Empty type declarations are not supported. Type `~s` lacks a definition",
[Name]), [Name]),
mk_t_err(pos(Pos), Msg); 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}) -> mk_error({stateful_not_allowed, Id, Fun}) ->
Msg = io_lib:format("Cannot reference stateful function `~s` in the definition of non-stateful function `~s`.", Msg = io_lib:format("Cannot reference stateful function `~s` in the definition of non-stateful function `~s`.",
[pp(Id), pp(Fun)]), [pp(Id), pp(Fun)]),
@@ -3874,14 +3593,10 @@ mk_error({cannot_call_init_function, Ann}) ->
Msg = "The 'init' function is called exclusively by the create contract transaction " Msg = "The 'init' function is called exclusively by the create contract transaction "
"and cannot be called from the contract code.", "and cannot be called from the contract code.",
mk_t_err(pos(Ann), Msg); mk_t_err(pos(Ann), Msg);
mk_error({contract_treated_as_namespace_entrypoint, Ann, [Con, Fun] = QName}) -> mk_error({contract_treated_as_namespace, Ann, [Con, Fun] = QName}) ->
Msg = io_lib:format("Invalid call to contract entrypoint `~s`.", [string:join(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]), 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_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}) -> mk_error({bad_top_level_decl, Decl}) ->
What = case element(1, Decl) of What = case element(1, Decl) of
letval -> "function or entrypoint"; letval -> "function or entrypoint";
@@ -3891,11 +3606,8 @@ mk_error({bad_top_level_decl, Decl}) ->
Msg = io_lib:format("The definition of '~s' must appear inside a ~s.", Msg = io_lib:format("The definition of '~s' must appear inside a ~s.",
[pp_expr(Id), What]), [pp_expr(Id), What]),
mk_t_err(pos(Decl), Msg); mk_t_err(pos(Decl), Msg);
mk_error({unknown_byte_type, Ctx, Type}) -> mk_error({unknown_byte_length, Type}) ->
Msg = io_lib:format("Cannot resolve type of byte array in\n ~s", [pp_context(Ctx)]), Msg = io_lib:format("Cannot resolve length of byte array.", []),
mk_t_err(pos(Type), Msg);
mk_error({unknown_byte_length, Ctx, Type}) ->
Msg = io_lib:format("Cannot resolve length of byte array in\n ~s", [pp_context(Ctx)]),
mk_t_err(pos(Type), Msg); mk_t_err(pos(Type), Msg);
mk_error({unsolved_bytes_constraint, Ann, concat, A, B, C}) -> mk_error({unsolved_bytes_constraint, Ann, concat, A, B, C}) ->
Msg = io_lib:format("Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n" Msg = io_lib:format("Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n"
@@ -3909,12 +3621,6 @@ mk_error({unsolved_bytes_constraint, Ann, split, A, B, C}) ->
[ pp_type(" - ", C), pp_loc(C), pp_type(" - ", A), pp_loc(A), [ pp_type(" - ", C), pp_loc(C), pp_type(" - ", A), pp_loc(A),
pp_type(" - ", B), pp_loc(B)]), pp_type(" - ", B), pp_loc(B)]),
mk_t_err(pos(Ann), Msg); mk_t_err(pos(Ann), Msg);
mk_error({unsolved_bytes_constraint, Ann, split_any, A, B, C}) ->
Msg = io_lib:format("Failed to resolve byte arrays in call to Bytes.split_any with argument of type\n"
"~s (at ~s)\nand result types\n~s (at ~s)\n~s (at ~s)",
[ pp_type(" - ", C), pp_loc(C), pp_type(" - ", A), pp_loc(A),
pp_type(" - ", B), pp_loc(B)]),
mk_t_err(pos(Ann), Msg);
mk_error({failed_to_get_compiler_version, Err}) -> mk_error({failed_to_get_compiler_version, Err}) ->
Msg = io_lib:format("Failed to get compiler version. Error: ~p", [Err]), Msg = io_lib:format("Failed to get compiler version. Error: ~p", [Err]),
mk_t_err(pos(0, 0), Msg); mk_t_err(pos(0, 0), Msg);
@@ -3980,7 +3686,7 @@ mk_error({empty_record_definition, Ann, Name}) ->
Msg = io_lib:format("Empty record definitions are not allowed. Cannot define the record `~s`", [Name]), Msg = io_lib:format("Empty record definitions are not allowed. Cannot define the record `~s`", [Name]),
mk_t_err(pos(Ann), Msg); mk_t_err(pos(Ann), Msg);
mk_error({unimplemented_interface_function, ConId, InterfaceName, FunName}) -> mk_error({unimplemented_interface_function, ConId, InterfaceName, FunName}) ->
Msg = io_lib:format("Unimplemented entrypoint `~s` from the interface `~s` in the contract `~s`", [FunName, InterfaceName, pp(ConId)]), Msg = io_lib:format("Unimplemented function `~s` from the interface `~s` in the contract `~s`", [FunName, InterfaceName, pp(ConId)]),
mk_t_err(pos(ConId), Msg); mk_t_err(pos(ConId), Msg);
mk_error({referencing_undefined_interface, InterfaceId}) -> mk_error({referencing_undefined_interface, InterfaceId}) ->
Msg = io_lib:format("Trying to implement or extend an undefined interface `~s`", [pp(InterfaceId)]), Msg = io_lib:format("Trying to implement or extend an undefined interface `~s`", [pp(InterfaceId)]),
@@ -4028,46 +3734,6 @@ mk_error({interface_implementation_conflict, Contract, I1, I2, Fun}) ->
"the contract `~s` have a function called `~s`", "the contract `~s` have a function called `~s`",
[name(I1), name(I2), name(Contract), name(Fun)]), [name(I1), name(I2), name(Contract), name(Fun)]),
mk_t_err(pos(Contract), Msg); 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) -> mk_error(Err) ->
Msg = io_lib:format("Unknown error: ~p", [Err]), Msg = io_lib:format("Unknown error: ~p", [Err]),
mk_t_err(pos(0, 0), Msg). mk_t_err(pos(0, 0), Msg).
@@ -4081,9 +3747,6 @@ mk_warning({unused_stateful, Ann, FunName}) ->
mk_warning({unused_variable, Ann, _Namespace, _Fun, VarName}) -> mk_warning({unused_variable, Ann, _Namespace, _Fun, VarName}) ->
Msg = io_lib:format("The variable `~s` is defined but never used.", [VarName]), Msg = io_lib:format("The variable `~s` is defined but never used.", [VarName]),
aeso_warnings:new(pos(Ann), Msg); 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}) -> mk_warning({unused_typedef, Ann, QName, _Arity}) ->
Msg = io_lib:format("The type `~s` is defined but never used.", [lists:last(QName)]), Msg = io_lib:format("The type `~s` is defined but never used.", [lists:last(QName)]),
aeso_warnings:new(pos(Ann), Msg); aeso_warnings:new(pos(Ann), Msg);
@@ -4281,18 +3944,6 @@ pp_type(Type) ->
pp_type(Label, Type) -> pp_type(Label, Type) ->
prettypr:format(prettypr:beside(prettypr:text(Label), aeso_pretty:type(Type, [show_generated])), 80, 80). prettypr:format(prettypr:beside(prettypr:text(Label), aeso_pretty:type(Type, [show_generated])), 80, 80).
pp_context([{fun_name, Id}]) -> ["a call to ", pp(Id)];
pp_context([result | Ctx]) -> ["the result of ", pp_context(Ctx)];
pp_context([{arg, N} | Ctx]) ->
Cnt = fun(1) -> "first";
(2) -> "second";
(3) -> "third";
(I) -> io_lib:format("~pth", [I])
end,
["the ", Cnt(N), " argument of ", pp_context(Ctx)];
pp_context(none) -> "unknown context".
src_file(T) -> aeso_syntax:get_ann(file, T, no_file). src_file(T) -> aeso_syntax:get_ann(file, T, no_file).
include_type(T) -> aeso_syntax:get_ann(include_type, T, none). include_type(T) -> aeso_syntax:get_ann(include_type, T, none).
line_number(T) -> aeso_syntax:get_ann(line, T, 0). line_number(T) -> aeso_syntax:get_ann(line, T, 0).
@@ -4344,7 +3995,7 @@ pp({tuple_t, _, []}) ->
"unit"; "unit";
pp({tuple_t, _, Cpts}) -> pp({tuple_t, _, Cpts}) ->
["(", string:join(lists:map(fun pp/1, Cpts), " * "), ")"]; ["(", string:join(lists:map(fun pp/1, Cpts), " * "), ")"];
pp({bytes_t, _, any}) -> "bytes()"; pp({bytes_t, _, any}) -> "bytes(_)";
pp({bytes_t, _, Len}) -> pp({bytes_t, _, Len}) ->
["bytes(", integer_to_list(Len), ")"]; ["bytes(", integer_to_list(Len), ")"];
pp({app_t, _, T, []}) -> pp({app_t, _, T, []}) ->
+516 -725
View File
File diff suppressed because it is too large Load Diff
+33 -74
View File
@@ -12,8 +12,6 @@
, file/2 , file/2
, from_string/2 , from_string/2
, check_call/4 , check_call/4
, decode_value/4
, encode_value/4
, create_calldata/3 , create_calldata/3
, create_calldata/4 , create_calldata/4
, version/0 , version/0
@@ -119,7 +117,7 @@ from_string1(ContractString, Options) ->
, warnings := Warnings } = string_to_code(ContractString, Options), , warnings := Warnings } = string_to_code(ContractString, Options),
#{ child_con_env := ChildContracts } = FCodeEnv, #{ child_con_env := ChildContracts } = FCodeEnv,
SavedFreshNames = maps:get(saved_fresh_names, FCodeEnv, #{}), SavedFreshNames = maps:get(saved_fresh_names, FCodeEnv, #{}),
FateCode = aeso_fcode_to_fate:compile(ChildContracts, FCode, SavedFreshNames, Options), {FateCode, VarsRegs} = aeso_fcode_to_fate:compile(ChildContracts, FCode, SavedFreshNames, Options),
pp_assembler(FateCode, Options), pp_assembler(FateCode, Options),
ByteCode = aeb_fate_code:serialize(FateCode, []), ByteCode = aeb_fate_code:serialize(FateCode, []),
{ok, Version} = version(), {ok, Version} = version(),
@@ -132,7 +130,13 @@ from_string1(ContractString, Options) ->
payable => maps:get(payable, FCode), payable => maps:get(payable, FCode),
warnings => Warnings warnings => Warnings
}, },
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)}. ResDbg = Res#{variables_registers => VarsRegs},
FinalRes =
case proplists:get_value(debug_info, Options, false) of
true -> ResDbg;
false -> Res
end,
{ok, maybe_generate_aci(FinalRes, FoldedTypedAst, Options)}.
maybe_generate_aci(Result, FoldedTypedAst, Options) -> maybe_generate_aci(Result, FoldedTypedAst, Options) ->
case proplists:get_value(aci, Options) of case proplists:get_value(aci, Options) of
@@ -184,55 +188,30 @@ check_call(Source, FunName, Args, Options) ->
check_call1(Source, FunName, Args, Options). check_call1(Source, FunName, Args, Options).
check_call1(ContractString0, 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 try
%% First check the contract without the __call function %% First check the contract without the __call function
#{fcode := OrgFcode #{fcode := OrgFcode
, fcode_env := #{child_con_env := ChildContracts} , fcode_env := #{child_con_env := ChildContracts}
, ast := Ast} = string_to_code(Contract0, Options), , ast := Ast} = string_to_code(ContractString0, Options),
FateCode = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []), {FateCode, _} = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, #{}, []),
%% collect all hashes and compute the first name without hash collision to %% collect all hashes and compute the first name without hash collision to
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)), SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
CallName = first_none_match(?CALL_NAME, SymbolHashes, CallName = first_none_match(?CALL_NAME, SymbolHashes,
lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)), lists:seq($1, $9) ++ lists:seq($A, $Z) ++ lists:seq($a, $z)),
Contract = insert_call_function(Ast, Contract0, CallName, Call), ContractString = insert_call_function(Ast, ContractString0, CallName, FunName, Args),
{ok, CallName, string_to_code(Contract, Options)} #{fcode := Fcode} = string_to_code(ContractString, Options),
CallArgs = arguments_of_body(CallName, FunName, Fcode),
{ok, FunName, CallArgs}
catch catch
throw:{error, Errors} -> {error, Errors} throw:{error, Errors} -> {error, Errors}
end. end.
get_call_body(CallName, #{fcode := Fcode}) -> arguments_of_body(CallName, _FunName, Fcode) ->
#{body := Body} = maps:get({entrypoint, list_to_binary(CallName)}, maps:get(functions, Fcode)), #{body := Body} = maps:get({entrypoint, list_to_binary(CallName)}, maps:get(functions, Fcode)),
Body. {def, _FName, Args} = Body,
%% FName is either {entrypoint, list_to_binary(FunName)} or 'init'
encode_value(Contract0, Type, Value, Options) -> [ aeso_fcode_to_fate:term_to_fate(A) || A <- Args ].
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, []) -> first_none_match(_CallName, _Hashes, []) ->
error(unable_to_find_unique_call_name); error(unable_to_find_unique_call_name);
@@ -245,31 +224,14 @@ first_none_match(CallName, Hashes, [Char|Chars]) ->
end. end.
%% Add the __call function to a contract. %% Add the __call function to a contract.
-spec insert_call_function(aeso_syntax:ast(), string(), string(), -spec insert_call_function(aeso_syntax:ast(), string(), string(), string(), [string()]) -> string().
{call, string(), [string()]} | {value, string(), string()} | {type, string()}) -> string(). insert_call_function(Ast, Code, Call, FunName, Args) ->
insert_call_function(Ast, Code, Call, {call, FunName, Args}) ->
Ind = last_contract_indent(Ast), Ind = last_contract_indent(Ast),
lists:flatten( lists:flatten(
[ Code, [ Code,
"\n\n", "\n\n",
lists:duplicate(Ind, " "), lists:duplicate(Ind, " "),
"stateful entrypoint ", Call, "() = ", FunName, "(", string:join(Args, ","), ")\n" "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(). -spec insert_init_function(string(), options()) -> string().
@@ -312,25 +274,22 @@ to_sophia_value(ContractString, FunName, ok, Data, Options0) ->
{ok, _, Type0} = get_decode_type(FunName, TypedAst), {ok, _, Type0} = get_decode_type(FunName, TypedAst),
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]), Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),
fate_data_to_sophia_value(Type0, Type, Data) 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
catch catch
throw:{error, Errors} -> {error, Errors} throw:{error, Errors} -> {error, Errors}
end. 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()]) -> -spec create_calldata(string(), string(), [string()]) ->
{ok, binary()} | {error, [aeso_errors:error()]}. {ok, binary()} | {error, [aeso_errors:error()]}.
create_calldata(Code, Fun, Args) -> create_calldata(Code, Fun, Args) ->
+117 -211
View File
@@ -52,8 +52,7 @@
tailpos = true, tailpos = true,
child_contracts = #{}, child_contracts = #{},
saved_fresh_names = #{}, saved_fresh_names = #{},
options = [], options = [] }).
debug_info = false }).
%% -- Debugging -------------------------------------------------------------- %% -- Debugging --------------------------------------------------------------
@@ -82,16 +81,24 @@ code_error(Err) ->
compile(FCode, SavedFreshNames, Options) -> compile(FCode, SavedFreshNames, Options) ->
compile(#{}, FCode, SavedFreshNames, Options). compile(#{}, FCode, SavedFreshNames, Options).
compile(ChildContracts, 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, #{ contract_name := ContractName,
functions := Functions } = FCode, functions := Functions } = FCode,
SFuns = functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options), SFuns = functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Options),
SFuns1 = optimize_scode(SFuns, Options), SFuns1 = optimize_scode(SFuns, Options),
FateCode = to_basic_blocks(SFuns1), FateCode = to_basic_blocks(SFuns1),
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]), ?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
case proplists:get_value(include_child_contract_symbols, Options, false) of FateCode1 = case proplists:get_value(include_child_contract_symbols, Options, false) of
false -> FateCode; false -> FateCode;
true -> add_child_symbols(ChildContracts, FateCode) true -> add_child_symbols(ChildContracts, FateCode)
end. end,
{FateCode1, get_variables_registers()}.
make_function_id(X) -> make_function_id(X) ->
aeb_fate_code:symbol_identifier(make_function_name(X)). aeb_fate_code:symbol_identifier(make_function_name(X)).
@@ -116,15 +123,31 @@ functions_to_scode(ChildContracts, ContractName, Functions, SavedFreshNames, Opt
function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, SavedFreshNames, Options) -> function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, SavedFreshNames, Options) ->
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType), {ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
Attrs = [ A || A <- Attrs0, A == private orelse A == payable ], Attrs = Attrs0 -- [stateful], %% Only track private and payable from here.
Env = init_env(ChildContracts, ContractName, Functions, Name, Args, SavedFreshNames, Options), Env = init_env(ChildContracts, ContractName, Functions, Name, Args, SavedFreshNames, Options),
ArgsNames = [ X || {X, _} <- lists:reverse(Env#env.vars) ], [ add_variables_register(Env, Arg, Register) ||
proplists:get_value(debug_info, Options, false),
%% DBG_LOC is added before the function body to make it possible to break {Arg, Register} <- Env#env.vars ],
%% at the function signature
SCode = to_scode(Env, Body), SCode = to_scode(Env, Body),
DbgSCode = dbg_contract(Env) ++ dbg_loc(Env, Attrs0) ++ dbg_scoped_vars(Env, ArgsNames, SCode), {Attrs, {ArgTypes, ResType1}, SCode}.
{Attrs, {ArgTypes, ResType1}, DbgSCode}.
get_variables_registers() ->
case get(variables_registers) of
undefined -> #{};
Vs -> Vs
end.
add_variables_register(Env = #env{saved_fresh_names = SavedFreshNames}, Name, Register) ->
Olds = get_variables_registers(),
RealName = maps:get(Name, SavedFreshNames, Name),
FunName =
case Env#env.current_function of
event -> "Chain.event";
{entrypoint, BinName} -> binary_to_list(BinName);
{local_fun, QualName} -> lists:last(QualName)
end,
New = {Env#env.contract, FunName, RealName},
put(variables_registers, Olds#{New => Register}).
-define(tvars, '$tvars'). -define(tvars, '$tvars').
@@ -171,20 +194,20 @@ types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts).
%% -- Environment functions -- %% -- Environment functions --
init_env(ChildContracts, ContractName, FunNames, Name, Args, SavedFreshNames, Options) -> init_env(ChildContracts, ContractName, FunNames, Name, Args, SavedFreshNames, Options) ->
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ], #env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
contract = ContractName, contract = ContractName,
child_contracts = ChildContracts, child_contracts = ChildContracts,
locals = FunNames, locals = FunNames,
current_function = Name, current_function = Name,
options = Options, options = Options,
tailpos = true, tailpos = true,
saved_fresh_names = SavedFreshNames, saved_fresh_names = SavedFreshNames }.
debug_info = proplists:get_value(debug_info, Options, false) }.
next_var(#env{ vars = Vars }) -> next_var(#env{ vars = Vars }) ->
1 + lists:max([-1 | [J || {_, {var, J}} <- Vars]]). 1 + lists:max([-1 | [J || {_, {var, J}} <- Vars]]).
bind_var(Name, Var, Env = #env{ vars = Vars }) -> bind_var(Name, Var, Env = #env{ vars = Vars }) ->
proplists:get_value(debug_info, Env#env.options, false) andalso add_variables_register(Env, Name, Var),
Env#env{ vars = [{Name, Var} | Vars] }. Env#env{ vars = [{Name, Var} | Vars] }.
bind_local(Name, Env) -> bind_local(Name, Env) ->
@@ -211,7 +234,7 @@ serialize_contract_code(Env, C) ->
Options = Env#env.options, Options = Env#env.options,
SavedFreshNames = Env#env.saved_fresh_names, SavedFreshNames = Env#env.saved_fresh_names,
FCode = maps:get(C, Env#env.child_contracts), FCode = maps:get(C, Env#env.child_contracts),
FateCode = compile(Env#env.child_contracts, FCode, SavedFreshNames, Options), {FateCode, _} = compile1(Env#env.child_contracts, FCode, SavedFreshNames, Options),
ByteCode = aeb_fate_code:serialize(FateCode, []), ByteCode = aeb_fate_code:serialize(FateCode, []),
{ok, Version} = aeso_compiler:version(), {ok, Version} = aeso_compiler:version(),
OriginalSourceCode = proplists:get_value(original_src, Options, ""), OriginalSourceCode = proplists:get_value(original_src, Options, ""),
@@ -245,44 +268,44 @@ lit_to_fate(Env, L) ->
term_to_fate(E) -> term_to_fate(#env{}, #{}, E). term_to_fate(E) -> term_to_fate(#env{}, #{}, E).
term_to_fate(GlobEnv, E) -> term_to_fate(GlobEnv, #{}, 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); lit_to_fate(GlobEnv, L);
%% negative literals are parsed as 0 - N %% 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); aeb_fate_data:make_integer(-N);
term_to_fate(_GlobEnv, _Env, {nil, _}) -> term_to_fate(_GlobEnv, _Env, nil) ->
aeb_fate_data:make_list([]); 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 %% 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, 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])); 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 ], FateAs = [ term_to_fate(GlobEnv, Env, A) || A <- As ],
aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs)); 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); 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); 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), {bits, N} = term_to_fate(GlobEnv, B),
J = term_to_fate(GlobEnv, I), J = term_to_fate(GlobEnv, I),
{bits, N bor (1 bsl J)}; {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), {bits, N} = term_to_fate(GlobEnv, B),
J = term_to_fate(GlobEnv, I), J = term_to_fate(GlobEnv, I),
{bits, N band bnot (1 bsl J)}; {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) }, Env1 = Env#{ X => term_to_fate(GlobEnv, Env, E) },
term_to_fate(GlobEnv, Env1, Body); 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 case maps:get(X, Env, undefined) of
undefined -> throw(not_a_fate_value); undefined -> throw(not_a_fate_value);
V -> V V -> V
end; end;
term_to_fate(_GlobEnv, _Env, {builtin, _, map_empty, []}) -> term_to_fate(_GlobEnv, _Env, {builtin, map_empty, []}) ->
aeb_fate_data:make_map(#{}); 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, M),
Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)}; Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)};
term_to_fate(_GlobEnv, _Env, _) -> term_to_fate(_GlobEnv, _Env, _) ->
@@ -290,59 +313,52 @@ term_to_fate(_GlobEnv, _Env, _) ->
to_scode(Env, T) -> to_scode(Env, T) ->
try term_to_fate(Env, T) of try term_to_fate(Env, T) of
V -> V -> [push(?i(V))]
FAnn = element(2, T),
[dbg_loc(Env, FAnn), push(?i(V))]
catch throw:not_a_fate_value -> catch throw:not_a_fate_value ->
to_scode1(Env, T) to_scode1(Env, T)
end. end.
to_scode1(Env, {lit, Ann, L}) -> to_scode1(Env, {lit, L}) ->
[ dbg_loc(Env, Ann), push(?i(lit_to_fate(Env, L))) ]; [push(?i(lit_to_fate(Env, L)))];
to_scode1(Env, {nil, Ann}) -> to_scode1(_Env, nil) ->
[ dbg_loc(Env, Ann), aeb_fate_ops:nil(?a) ]; [aeb_fate_ops:nil(?a)];
to_scode1(Env, {var, Ann, X}) -> to_scode1(Env, {var, X}) ->
[ dbg_loc(Env, Ann), push(lookup_var(Env, X)) ]; [push(lookup_var(Env, X))];
to_scode1(Env, {con, Ann, Ar, I, As}) -> to_scode1(Env, {con, Ar, I, As}) ->
N = length(As), N = length(As),
[ dbg_loc(Env, Ann), [[to_scode(notail(Env), A) || A <- As],
[to_scode(notail(Env), A) || A <- As], aeb_fate_ops:variant(?a, ?i(Ar), ?i(I), ?i(N))];
aeb_fate_ops:variant(?a, ?i(Ar), ?i(I), ?i(N)) ];
to_scode1(Env, {tuple, Ann, As}) -> to_scode1(Env, {tuple, As}) ->
N = length(As), N = length(As),
[ dbg_loc(Env, Ann), [[ to_scode(notail(Env), A) || A <- As ],
[ to_scode(notail(Env), A) || A <- As ], tuple(N)];
tuple(N) ];
to_scode1(Env, {proj, Ann, E, I}) -> to_scode1(Env, {proj, E, I}) ->
[ dbg_loc(Env, Ann), [to_scode(notail(Env), E),
to_scode(notail(Env), E), aeb_fate_ops:element_op(?a, ?i(I), ?a)];
aeb_fate_ops:element_op(?a, ?i(I), ?a) ];
to_scode1(Env, {set_proj, Ann, R, I, E}) -> to_scode1(Env, {set_proj, R, I, E}) ->
[ dbg_loc(Env, Ann), [to_scode(notail(Env), E),
to_scode(notail(Env), E), to_scode(notail(Env), R),
to_scode(notail(Env), R), aeb_fate_ops:setelement(?a, ?i(I), ?a, ?a)];
aeb_fate_ops:setelement(?a, ?i(I), ?a, ?a) ];
to_scode1(Env, {op, Ann, Op, Args}) -> to_scode1(Env, {op, Op, Args}) ->
[ dbg_loc(Env, Ann) | call_to_scode(Env, op_to_scode(Op), Args) ]; call_to_scode(Env, op_to_scode(Op), Args);
to_scode1(Env, {'let', Ann, X, {var, _, Y}, Body}) -> to_scode1(Env, {'let', X, {var, Y}, Body}) ->
Env1 = bind_var(X, lookup_var(Env, Y), Env), Env1 = bind_var(X, lookup_var(Env, Y), Env),
[ dbg_loc(Env, Ann) | dbg_scoped_vars(Env1, [X], to_scode(Env1, Body)) ]; to_scode(Env1, Body);
to_scode1(Env, {'let', Ann, X, Expr, Body}) -> to_scode1(Env, {'let', X, Expr, Body}) ->
{I, Env1} = bind_local(X, Env), {I, Env1} = bind_local(X, Env),
SCode = [ to_scode(notail(Env), Expr), [ to_scode(notail(Env), Expr),
aeb_fate_ops:store({var, I}, {stack, 0}), aeb_fate_ops:store({var, I}, {stack, 0}),
to_scode(Env1, Body) ], to_scode(Env1, Body) ];
[ dbg_loc(Env, Ann) | dbg_scoped_vars(Env1, [X], SCode) ];
to_scode1(Env = #env{ current_function = Fun, tailpos = true, debug_info = false }, {def, Ann, Fun, Args}) -> to_scode1(Env = #env{ current_function = Fun, tailpos = true }, {def, Fun, Args}) ->
%% Tail-call to current function, f(e0..en). Compile to %% Tail-call to current function, f(e0..en). Compile to
%% [ let xi = ei ] %% [ let xi = ei ]
%% [ STORE argi xi ] %% [ STORE argi xi ]
@@ -355,62 +371,61 @@ to_scode1(Env = #env{ current_function = Fun, tailpos = true, debug_info = false
aeb_fate_ops:store({var, I}, ?a)], aeb_fate_ops:store({var, I}, ?a)],
{[I | Is], Acc1, Env2} {[I | Is], Acc1, Env2}
end, {[], [], Env}, Args), end, {[], [], Env}, Args),
[ dbg_loc(Env, Ann), [ Code,
Code,
[ aeb_fate_ops:store({arg, I}, {var, J}) [ aeb_fate_ops:store({arg, I}, {var, J})
|| {I, J} <- lists:zip(lists:seq(0, length(Vars) - 1), || {I, J} <- lists:zip(lists:seq(0, length(Vars) - 1),
lists:reverse(Vars)) ], lists:reverse(Vars)) ],
loop ]; loop ];
to_scode1(Env, {def, Ann, Fun, Args}) -> to_scode1(Env, {def, Fun, Args}) ->
FName = make_function_id(Fun), FName = make_function_id(Fun),
Lbl = aeb_fate_data:make_string(FName), Lbl = aeb_fate_data:make_string(FName),
[ dbg_loc(Env, Ann) | call_to_scode(Env, local_call(Env, ?i(Lbl)), Args) ]; call_to_scode(Env, local_call(Env, ?i(Lbl)), Args);
to_scode1(Env, {funcall, Ann, Fun, Args}) -> to_scode1(Env, {funcall, Fun, Args}) ->
[ dbg_loc(Env, Ann) | call_to_scode(Env, [to_scode(Env, Fun), local_call(Env, ?a)], Args) ]; call_to_scode(Env, [to_scode(Env, Fun), local_call(Env, ?a)], Args);
to_scode1(Env, {builtin, Ann, B, Args}) -> to_scode1(Env, {builtin, B, Args}) ->
[ dbg_loc(Env, Ann) | builtin_to_scode(Env, B, Args) ]; builtin_to_scode(Env, B, Args);
to_scode1(Env, {remote, Ann, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) -> to_scode1(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) ->
Lbl = make_function_id(Fun), Lbl = make_function_id(Fun),
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT), {ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})), ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
RetType = ?i(aeb_fate_data:make_typerep(RetType0)), RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
SCode = case Protected of case Protected of
{lit, _, {bool, false}} -> {lit, {bool, false}} ->
case Gas of case Gas of
{builtin, _, call_gas_left, _} -> {builtin, call_gas_left, _} ->
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a), Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
call_to_scode(Env, Call, [Ct, Value | Args]); call_to_scode(Env, Call, [Ct, Value | Args]);
_ -> _ ->
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a), Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
call_to_scode(Env, Call, [Ct, Value, Gas | Args]) call_to_scode(Env, Call, [Ct, Value, Gas | Args])
end; end;
{lit, _, {bool, true}} -> {lit, {bool, true}} ->
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?i(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_to_scode(Env, Call, [Ct, Value, Gas | Args]);
_ -> _ ->
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a), Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a),
call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args]) call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args])
end, end;
[ dbg_loc(Env, Ann) | SCode ];
to_scode1(Env, {get_state, Ann, Reg}) -> to_scode1(_Env, {get_state, Reg}) ->
[ dbg_loc(Env, Ann), push(?s(Reg)) ]; [push(?s(Reg))];
to_scode1(Env, {set_state, Ann, Reg, Val}) -> to_scode1(Env, {set_state, Reg, Val}) ->
[ dbg_loc(Env, Ann) | call_to_scode(Env, [{'STORE', ?s(Reg), ?a}, tuple(0)], [Val]) ]; call_to_scode(Env, [{'STORE', ?s(Reg), ?a},
tuple(0)], [Val]);
to_scode1(Env, {closure, Ann, Fun, FVs}) -> to_scode1(Env, {closure, Fun, FVs}) ->
[ to_scode(Env, {tuple, Ann, [{lit, Ann, {string, make_function_id(Fun)}}, FVs]}) ]; to_scode(Env, {tuple, [{lit, {string, make_function_id(Fun)}}, FVs]});
to_scode1(Env, {switch, Ann, Case}) -> to_scode1(Env, {switch, Case}) ->
[ dbg_loc(Env, Ann) | split_to_scode(Env, Case) ]. split_to_scode(Env, Case).
local_call( Env = #env{debug_info = false}, Fun) when Env#env.tailpos -> aeb_fate_ops:call_t(Fun); 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, Fun) -> aeb_fate_ops:call(Fun).
split_to_scode(Env, {nosplit, Renames, Expr}) -> split_to_scode(Env, {nosplit, Expr}) ->
[switch_body, dbg_scoped_vars(Env, Renames, to_scode(Env, Expr))]; [switch_body, to_scode(Env, Expr)];
split_to_scode(Env, {split, {tuple, _}, X, Alts}) -> split_to_scode(Env, {split, {tuple, _}, X, Alts}) ->
{Def, Alts1} = catchall_to_scode(Env, X, Alts), {Def, Alts1} = catchall_to_scode(Env, X, Alts),
Arg = lookup_var(Env, X), Arg = lookup_var(Env, X),
@@ -539,14 +554,6 @@ builtin_to_scode(Env, bytes_concat, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_concat(?a, ?a, ?a), Args); call_to_scode(Env, aeb_fate_ops:bytes_concat(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_split, [_, _] = Args) -> builtin_to_scode(Env, bytes_split, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_split(?a, ?a, ?a), Args); call_to_scode(Env, aeb_fate_ops:bytes_split(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_split_any, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_split_any(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_to_fixed_size, [_, _] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_to_fixed_size(?a, ?a, ?a), Args);
builtin_to_scode(Env, bytes_to_any_size, [A]) ->
[to_scode(Env, A)]; %% no_op!
builtin_to_scode(Env, bytes_size, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:bytes_size(?a, ?a), Args);
builtin_to_scode(Env, abort, [_] = Args) -> builtin_to_scode(Env, abort, [_] = Args) ->
call_to_scode(Env, aeb_fate_ops:abort(?a), Args); call_to_scode(Env, aeb_fate_ops:abort(?a), Args);
builtin_to_scode(Env, exit, [_] = Args) -> builtin_to_scode(Env, exit, [_] = Args) ->
@@ -568,8 +575,6 @@ builtin_to_scode(_Env, chain_difficulty, []) ->
[aeb_fate_ops:difficulty(?a)]; [aeb_fate_ops:difficulty(?a)];
builtin_to_scode(_Env, chain_gas_limit, []) -> builtin_to_scode(_Env, chain_gas_limit, []) ->
[aeb_fate_ops:gaslimit(?a)]; [aeb_fate_ops:gaslimit(?a)];
builtin_to_scode(_Env, chain_network_id, []) ->
[aeb_fate_ops:network_id(?a)];
builtin_to_scode(_Env, contract_balance, []) -> builtin_to_scode(_Env, contract_balance, []) ->
[aeb_fate_ops:balance(?a)]; [aeb_fate_ops:balance(?a)];
builtin_to_scode(_Env, contract_address, []) -> builtin_to_scode(_Env, contract_address, []) ->
@@ -644,7 +649,7 @@ builtin_to_scode(Env, chain_bytecode_hash, [_Addr] = Args) ->
builtin_to_scode(Env, chain_clone, builtin_to_scode(Env, chain_clone,
[InitArgsT, GasCap, Value, Prot, Contract | InitArgs]) -> [InitArgsT, GasCap, Value, Prot, Contract | InitArgs]) ->
case GasCap of case GasCap of
{builtin, _, call_gas_left, _} -> {builtin, call_gas_left, _} ->
call_to_scode(Env, aeb_fate_ops:clone(?a, ?a, ?a, ?a), call_to_scode(Env, aeb_fate_ops:clone(?a, ?a, ?a, ?a),
[Contract, InitArgsT, Value, Prot | InitArgs] [Contract, InitArgsT, Value, Prot | InitArgs]
); );
@@ -693,7 +698,6 @@ op_to_scode(map_member) -> aeb_fate_ops:map_member(?a, ?a, ?a);
op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a); op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a);
op_to_scode(stringinternal_length) -> aeb_fate_ops:str_length(?a, ?a); op_to_scode(stringinternal_length) -> aeb_fate_ops:str_length(?a, ?a);
op_to_scode(stringinternal_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a); op_to_scode(stringinternal_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a);
op_to_scode(stringinternal_to_bytes) -> aeb_fate_ops:str_to_bytes(?a, ?a);
op_to_scode(stringinternal_to_list) -> aeb_fate_ops:str_to_list(?a, ?a); op_to_scode(stringinternal_to_list) -> aeb_fate_ops:str_to_list(?a, ?a);
op_to_scode(stringinternal_from_list) -> aeb_fate_ops:str_from_list(?a, ?a); op_to_scode(stringinternal_from_list) -> aeb_fate_ops:str_from_list(?a, ?a);
op_to_scode(stringinternal_to_lower) -> aeb_fate_ops:str_to_lower(?a, ?a); op_to_scode(stringinternal_to_lower) -> aeb_fate_ops:str_to_lower(?a, ?a);
@@ -710,7 +714,6 @@ op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a); op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
op_to_scode(address_to_bytes) -> aeb_fate_ops:addr_to_bytes(?a, ?a); op_to_scode(address_to_bytes) -> aeb_fate_ops:addr_to_bytes(?a, ?a);
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a); op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
op_to_scode(int_to_bytes) -> aeb_fate_ops:int_to_bytes(?a, ?a, ?a);
op_to_scode(int_mulmod) -> aeb_fate_ops:mulmod(?a, ?a, ?a, ?a); op_to_scode(int_mulmod) -> aeb_fate_ops:mulmod(?a, ?a, ?a, ?a);
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a); op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a); op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a);
@@ -757,77 +760,6 @@ push(A) -> {'STORE', ?a, A}.
tuple(0) -> push(?i({tuple, {}})); tuple(0) -> push(?i({tuple, {}}));
tuple(N) -> aeb_fate_ops:tuple(?a, N). 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 --------------------------------------------------------------- %% -- Phase II ---------------------------------------------------------------
%% Optimize %% Optimize
@@ -963,10 +895,6 @@ attributes(I) ->
loop -> Impure(pc, []); loop -> Impure(pc, []);
switch_body -> Pure(none, []); switch_body -> Pure(none, []);
'RETURN' -> Impure(pc, []); 'RETURN' -> Impure(pc, []);
{'DBG_LOC', _, _} -> Impure(none, []);
{'DBG_DEF', _, _} -> Impure(none, []);
{'DBG_UNDEF', _, _} -> Impure(none, []);
{'DBG_CONTRACT', _} -> Impure(none, []);
{'RETURNR', A} -> Impure(pc, A); {'RETURNR', A} -> Impure(pc, A);
{'CALL', A} -> Impure(?a, [A]); {'CALL', A} -> Impure(?a, [A]);
{'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]); {'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]);
@@ -1031,11 +959,9 @@ attributes(I) ->
{'APPEND', A, B, C} -> Pure(A, [B, C]); {'APPEND', A, B, C} -> Pure(A, [B, C]);
{'STR_JOIN', A, B, C} -> Pure(A, [B, C]); {'STR_JOIN', A, B, C} -> Pure(A, [B, C]);
{'INT_TO_STR', A, B} -> Pure(A, B); {'INT_TO_STR', A, B} -> Pure(A, B);
{'INT_TO_BYTES', A, B, C} -> Pure(A, [B, C]);
{'ADDR_TO_STR', A, B} -> Pure(A, B); {'ADDR_TO_STR', A, B} -> Pure(A, B);
{'STR_REVERSE', A, B} -> Pure(A, B); {'STR_REVERSE', A, B} -> Pure(A, B);
{'STR_LENGTH', A, B} -> Pure(A, B); {'STR_LENGTH', A, B} -> Pure(A, B);
{'STR_TO_BYTES', A, B} -> Pure(A, B);
{'INT_TO_ADDR', A, B} -> Pure(A, B); {'INT_TO_ADDR', A, B} -> Pure(A, B);
{'VARIANT', A, B, C, D} -> Pure(A, [?a, B, C, D]); {'VARIANT', A, B, C, D} -> Pure(A, [?a, B, C, D]);
{'VARIANT_TEST', A, B, C} -> Pure(A, [B, C]); {'VARIANT_TEST', A, B, C} -> Pure(A, [B, C]);
@@ -1069,9 +995,6 @@ attributes(I) ->
{'BYTES_TO_STR', A, B} -> Pure(A, [B]); {'BYTES_TO_STR', A, B} -> Pure(A, [B]);
{'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]); {'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]);
{'BYTES_SPLIT', A, B, C} -> Pure(A, [B, C]); {'BYTES_SPLIT', A, B, C} -> Pure(A, [B, C]);
{'BYTES_SPLIT_ANY', A, B, C} -> Pure(A, [B, C]);
{'BYTES_SIZE', A, B} -> Pure(A, B);
{'BYTES_TO_FIXED_SIZE', A, B, C} -> Pure(A, [B, C]);
{'ORACLE_CHECK', A, B, C, D} -> Pure(A, [B, C, D]); {'ORACLE_CHECK', A, B, C, D} -> Pure(A, [B, C, D]);
{'ORACLE_CHECK_QUERY', A, B, C, D, E} -> Pure(A, [B, C, D, E]); {'ORACLE_CHECK_QUERY', A, B, C, D, E} -> Pure(A, [B, C, D, E]);
{'IS_ORACLE', A, B} -> Pure(A, [B]); {'IS_ORACLE', A, B} -> Pure(A, [B]);
@@ -1092,7 +1015,6 @@ attributes(I) ->
{'MICROBLOCK', A} -> Pure(A, []); {'MICROBLOCK', A} -> Pure(A, []);
{'DIFFICULTY', A} -> Pure(A, []); {'DIFFICULTY', A} -> Pure(A, []);
{'GASLIMIT', A} -> Pure(A, []); {'GASLIMIT', A} -> Pure(A, []);
{'NETWORK_ID', A} -> Pure(A, []);
{'GAS', A} -> Pure(A, []); {'GAS', A} -> Pure(A, []);
{'LOG0', A} -> Impure(none, [A]); {'LOG0', A} -> Impure(none, [A]);
{'LOG1', A, B} -> Impure(none, [A, B]); {'LOG1', A, B} -> Impure(none, [A, B]);
@@ -1701,23 +1623,7 @@ bb(_Name, Code) ->
Blocks = lists:flatmap(fun split_calls/1, Blocks1), Blocks = lists:flatmap(fun split_calls/1, Blocks1),
Labels = maps:from_list([ {Ref, I} || {I, {Ref, _}} <- with_ixs(Blocks) ]), Labels = maps:from_list([ {Ref, I} || {I, {Ref, _}} <- with_ixs(Blocks) ]),
BBs = [ set_labels(Labels, B) || B <- Blocks ], BBs = [ set_labels(Labels, B) || B <- Blocks ],
maps:from_list(dbg_loc_filter(BBs)). maps:from_list(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 -- %% -- Break up scode into basic blocks --
+4 -12
View File
@@ -264,11 +264,10 @@ type300() ->
type400() -> type400() ->
choice( choice(
[?RULE(typeAtom(), optional(type_args()), [?RULE(typeAtom(), optional(type_args()),
any_bytes( case _2 of
case _2 of none -> _1;
none -> _1; {ok, Args} -> {app_t, get_ann(_1), _1, Args}
{ok, Args} -> {app_t, get_ann(_1), _1, Args} end),
end)),
?RULE(id("bytes"), parens(token(int)), ?RULE(id("bytes"), parens(token(int)),
{bytes_t, get_ann(_1), element(3, _2)}) {bytes_t, get_ann(_1), element(3, _2)})
]). ]).
@@ -365,12 +364,9 @@ exprAtom() ->
, ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4)) , ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4))
, ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2)) , ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2))
, letpat() , letpat()
, hole()
]) ])
end). end).
hole() -> ?RULE(token('???'), {id, get_ann(_1), "???"}).
comprehension_exp() -> comprehension_exp() ->
?LAZY_P(choice( ?LAZY_P(choice(
[ comprehension_bind() [ comprehension_bind()
@@ -793,7 +789,3 @@ auto_imports(L) when is_list(L) ->
auto_imports(T) when is_tuple(T) -> auto_imports(T) when is_tuple(T) ->
auto_imports(tuple_to_list(T)); auto_imports(tuple_to_list(T));
auto_imports(_) -> []. auto_imports(_) -> [].
any_bytes({id, Ann, "bytes"}) -> {bytes_t, Ann, any};
any_bytes({app_t, _, {id, Ann, "bytes"}, []}) -> {bytes_t, Ann, any};
any_bytes(Type) -> Type.
+1 -3
View File
@@ -275,9 +275,7 @@ type({tuple_t, _, Args}) ->
tuple_type(Args); tuple_type(Args);
type({args_t, _, Args}) -> type({args_t, _, Args}) ->
args_type(Args); args_type(Args);
type({bytes_t, _, any}) -> text("bytes()"); type({bytes_t, _, any}) -> text("bytes(_)");
type({bytes_t, _, '_'}) -> text("bytes(_)");
type({bytes_t, _, fixed}) -> text("bytes(_)");
type({bytes_t, _, Len}) -> type({bytes_t, _, Len}) ->
text(lists:concat(["bytes(", Len, ")"])); text(lists:concat(["bytes(", Len, ")"]));
type({if_t, _, Id, Then, Else}) -> type({if_t, _, Id, Then, Else}) ->
+2 -3
View File
@@ -10,7 +10,7 @@
-export([get_ann/1, get_ann/2, get_ann/3, set_ann/2, qualify/2]). -export([get_ann/1, get_ann/2, get_ann/3, set_ann/2, qualify/2]).
-export_type([ann_file/0, ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]). -export_type([ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]).
-export_type([name/0, id/0, con/0, qid/0, qcon/0, tvar/0, op/0]). -export_type([name/0, id/0, con/0, qid/0, qcon/0, tvar/0, op/0]).
-export_type([bin_op/0, un_op/0]). -export_type([bin_op/0, un_op/0]).
-export_type([decl/0, letbind/0, typedef/0, pragma/0, fundecl/0]). -export_type([decl/0, letbind/0, typedef/0, pragma/0, fundecl/0]).
@@ -24,9 +24,8 @@
-type ann_col() :: integer(). -type ann_col() :: integer().
-type ann_origin() :: system | user. -type ann_origin() :: system | user.
-type ann_format() :: '?:' | hex | infix | prefix | elif. -type ann_format() :: '?:' | hex | infix | prefix | elif.
-type ann_file() :: string() | no_file.
-type ann() :: [ {file, ann_file()} | {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()} -type ann() :: [ {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()}
| stateful | private | payable | main | interface | entrypoint]. | stateful | private | payable | main | interface | entrypoint].
-type name() :: string(). -type name() :: string().
+3 -4
View File
@@ -31,13 +31,11 @@
| aeso_syntax:field(aeso_syntax:expr()) | aeso_syntax:field(aeso_syntax:expr())
| aeso_syntax:stmt(). | aeso_syntax:stmt().
fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) -> 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, Sum = fun(Xs) -> lists:foldl(Plus, Zero, Xs) end,
Same = fun(A) -> fold(Alg, Fun, K, A) end, Same = fun(A) -> fold(Alg, Fun, K, A) end,
Decl = fun(D) -> fold(Alg, Fun, decl, D) end, Decl = fun(D) -> fold(Alg, Fun, decl, D) end,
Type = fun(T) -> fold(Alg, Fun, TypeKind, T) end, Type = fun(T) -> fold(Alg, Fun, type, T) end,
Expr = fun(E) -> fold(Alg, Fun, ExprKind, E) end, Expr = fun(E) -> fold(Alg, Fun, expr, E) end,
BindExpr = fun(P) -> fold(Alg, Fun, bind_expr, P) end, BindExpr = fun(P) -> fold(Alg, Fun, bind_expr, P) end,
BindType = fun(T) -> fold(Alg, Fun, bind_type, T) end, BindType = fun(T) -> fold(Alg, Fun, bind_type, T) end,
Top = Fun(K, X), Top = Fun(K, X),
@@ -157,3 +155,4 @@ used(D) ->
(_, _) -> #{} (_, _) -> #{}
end, decl, D)), end, decl, D)),
lists:filter(NotBound, Xs). lists:filter(NotBound, Xs).
+8 -155
View File
@@ -69,7 +69,6 @@ simple_compile_test_() ->
[ {"Testing warning messages", [ {"Testing warning messages",
fun() -> fun() ->
#{ warnings := Warnings } = compile("warnings", [warn_all]), #{ warnings := Warnings } = compile("warnings", [warn_all]),
#{ warnings := [] } = compile("warning_unused_include_no_include", [warn_all]),
check_warnings(warnings(), Warnings) check_warnings(warnings(), Warnings)
end} ] ++ end} ] ++
[]. [].
@@ -170,7 +169,6 @@ compilable_contracts() ->
"namespace_bug", "namespace_bug",
"bytes_to_x", "bytes_to_x",
"bytes_concat", "bytes_concat",
"bytes_misc",
"aens", "aens",
"aens_update", "aens_update",
"tuple_match", "tuple_match",
@@ -207,9 +205,6 @@ compilable_contracts() ->
"polymorphism_variance_switching_chain_create", "polymorphism_variance_switching_chain_create",
"polymorphism_variance_switching_void_supertype", "polymorphism_variance_switching_void_supertype",
"polymorphism_variance_switching_unify_with_interface_decls", "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", "missing_init_fun_state_unit",
"complex_compare_leq", "complex_compare_leq",
"complex_compare", "complex_compare",
@@ -223,7 +218,6 @@ compilable_contracts() ->
"unapplied_contract_call", "unapplied_contract_call",
"unapplied_named_arg_builtin", "unapplied_named_arg_builtin",
"resolve_field_constraint_by_arity", "resolve_field_constraint_by_arity",
"toplevel_constants",
"ceres", "ceres",
"test" % Custom general-purpose test file. Keep it last on the list. "test" % Custom general-purpose test file. Keep it last on the list.
]. ].
@@ -289,11 +283,7 @@ warnings() ->
<<?PosW(48, 5) <<?PosW(48, 5)
"Unused return value.">>, "Unused return value.">>,
<<?PosW(60, 5) <<?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() -> failing_contracts() ->
@@ -449,10 +439,6 @@ failing_contracts() ->
[<<?Pos(12, 42) [<<?Pos(12, 42)
"Cannot unify `int` and `string`\n" "Cannot unify `int` and `string`\n"
"when checking the type of the expression `r.foo() : map(int, string)` " "when checking the type of the expression `r.foo() : map(int, string)` "
"against the expected type `map(string, int)`">>,
<<?Pos(12, 42)
"Cannot unify `string` and `int`\n"
"when checking the type of the expression `r.foo() : map(int, string)` "
"against the expected type `map(string, int)`">>]) "against the expected type `map(string, int)`">>])
, ?TYPE_ERROR(not_toplevel_include, , ?TYPE_ERROR(not_toplevel_include,
[<<?Pos(2, 11) [<<?Pos(2, 11)
@@ -610,21 +596,6 @@ failing_contracts() ->
[<<?Pos(3, 5) [<<?Pos(3, 5)
"Unbound variable `Chain.event`\n" "Unbound variable `Chain.event`\n"
"Did you forget to define the event type?">>]) "Did you forget to define the event type?">>])
, ?TYPE_ERROR(bad_bytes_to_x,
[<<?Pos(3, 35)
"Cannot resolve length of byte array in\n"
" the result of a call to Bytes.to_fixed_size">>,
<<?Pos(4, 36)
"Cannot unify `bytes()` and `bytes(4)`\nwhen checking the application of\n"
" `Bytes.to_fixed_size : (bytes()) => option('a)`\n"
"to arguments\n"
" `b : bytes(4)`">>,
<<?Pos(4, 36)
"Cannot resolve length of byte array in\n"
" the result of a call to Bytes.to_fixed_size">>,
<<?Pos(5, 35)
"Cannot resolve length of byte array in\n"
" the first argument of a call to Bytes.to_any_size">>])
, ?TYPE_ERROR(bad_bytes_concat, , ?TYPE_ERROR(bad_bytes_concat,
[<<?Pos(12, 40) [<<?Pos(12, 40)
"Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n" "Failed to resolve byte array lengths in call to Bytes.concat with arguments of type\n"
@@ -649,8 +620,7 @@ failing_contracts() ->
"and result type\n" "and result type\n"
" - 'c (at line 16, column 39)">>, " - 'c (at line 16, column 39)">>,
<<?Pos(19, 25) <<?Pos(19, 25)
"Cannot resolve type of byte array in\n" "Cannot resolve length of byte array.">>])
" the first argument of a call to Bytes.to_str">>])
, ?TYPE_ERROR(bad_bytes_split, , ?TYPE_ERROR(bad_bytes_split,
[<<?Pos(13, 5) [<<?Pos(13, 5)
"Failed to resolve byte array lengths in call to Bytes.split with argument of type\n" "Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
@@ -685,6 +655,10 @@ failing_contracts() ->
[<<?Pos(5, 28) [<<?Pos(5, 28)
"Invalid call to contract entrypoint `Foo.foo`.\n" "Invalid call to contract entrypoint `Foo.foo`.\n"
"It must be called as `c.foo` for some `c : Foo`.">>]) "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, , ?TYPE_ERROR(empty_typedecl,
[<<?Pos(2, 8) [<<?Pos(2, 8)
"Empty type declarations are not supported. " "Empty type declarations are not supported. "
@@ -882,11 +856,7 @@ failing_contracts() ->
<<?Pos(48, 5) <<?Pos(48, 5)
"Unused return value.">>, "Unused return value.">>,
<<?Pos(60, 5) <<?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, , ?TYPE_ERROR(polymorphism_contract_interface_recursive,
[<<?Pos(1,24) [<<?Pos(1,24)
@@ -899,7 +869,7 @@ failing_contracts() ->
" - line 9, column 5">>]) " - line 9, column 5">>])
, ?TYPE_ERROR(polymorphism_contract_missing_implementation, , ?TYPE_ERROR(polymorphism_contract_missing_implementation,
[<<?Pos(4,20) [<<?Pos(4,20)
"Unimplemented entrypoint `f` from the interface `I1` in the contract `I2`">> "Unimplemented function `f` from the interface `I1` in the contract `I2`">>
]) ])
, ?TYPE_ERROR(polymorphism_contract_same_decl_multi_interface, , ?TYPE_ERROR(polymorphism_contract_same_decl_multi_interface,
[<<?Pos(7,10) [<<?Pos(7,10)
@@ -950,9 +920,6 @@ failing_contracts() ->
<<?Pos(67,36) <<?Pos(67,36)
"Cannot unify `Cat` and `Animal` in a contravariant context\n" "Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the application of\n `DT_INV : ((Cat) => Cat) => dt_inv(Cat)`\nto arguments\n `f_c_to_a : (Cat) => Animal`">>, "when checking the application of\n `DT_INV : ((Cat) => Cat) => dt_inv(Cat)`\nto arguments\n `f_c_to_a : (Cat) => Animal`">>,
<<?Pos(67,36)
"Cannot unify `Cat` and `Animal` in a invariant context\n"
"when checking the type of the expression `DT_INV(f_c_to_a) : dt_inv(Cat)` against the expected type `dt_inv(Animal)`">>,
<<?Pos(68,36) <<?Pos(68,36)
"Cannot unify `Cat` and `Animal` in a invariant context\n" "Cannot unify `Cat` and `Animal` in a invariant context\n"
"when checking the type of the expression `DT_INV(f_c_to_c) : dt_inv(Cat)` against the expected type `dt_inv(Animal)`">>, "when checking the type of the expression `DT_INV(f_c_to_c) : dt_inv(Cat)` against the expected type `dt_inv(Animal)`">>,
@@ -1001,9 +968,6 @@ failing_contracts() ->
<<?Pos(116,59) <<?Pos(116,59)
"Cannot unify `Cat` and `Animal` in a contravariant context\n" "Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) : dt_a_contra_b_contra(Cat, Cat)` against the expected type `dt_a_contra_b_contra(Animal, Animal)`">>, "when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) : dt_a_contra_b_contra(Cat, Cat)` against the expected type `dt_a_contra_b_contra(Animal, Animal)`">>,
<<?Pos(116,59)
"Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_c_to_u) : dt_a_contra_b_contra(Cat, Cat)` against the expected type `dt_a_contra_b_contra(Animal, Animal)`">>,
<<?Pos(119,59) <<?Pos(119,59)
"Cannot unify `Cat` and `Animal` in a contravariant context\n" "Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_a_to_u) : dt_a_contra_b_contra(Cat, Animal)` against the expected type `dt_a_contra_b_contra(Animal, Cat)`">>, "when checking the type of the expression `DT_A_CONTRA_B_CONTRA(f_c_to_a_to_u) : dt_a_contra_b_contra(Cat, Animal)` against the expected type `dt_a_contra_b_contra(Animal, Cat)`">>,
@@ -1047,9 +1011,6 @@ failing_contracts() ->
<<?Pos(19,13) <<?Pos(19,13)
"Cannot unify `Cat` and `Animal` in a contravariant context\n" "Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the pattern `o07 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Animal)`">>, "when checking the type of the pattern `o07 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Animal)`">>,
<<?Pos(19,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `o07 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Animal)`">>,
<<?Pos(20,13) <<?Pos(20,13)
"Cannot unify `Cat` and `Animal` in a contravariant context\n" "Cannot unify `Cat` and `Animal` in a contravariant context\n"
"when checking the type of the pattern `o08 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Cat)`">>, "when checking the type of the pattern `o08 : oracle(Animal, Cat)` against the expected type `oracle(Cat, Cat)`">>,
@@ -1074,9 +1035,6 @@ failing_contracts() ->
<<?Pos(42,13) <<?Pos(42,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n" "Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q13 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Animal)`">>, "when checking the type of the pattern `q13 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Animal)`">>,
<<?Pos(42,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q13 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Animal)`">>,
<<?Pos(43,13) <<?Pos(43,13)
"Cannot unify `Animal` and `Cat` in a covariant context\n" "Cannot unify `Animal` and `Cat` in a covariant context\n"
"when checking the type of the pattern `q14 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Cat)`">>, "when checking the type of the pattern `q14 : oracle_query(Cat, Cat)` against the expected type `oracle_query(Animal, Cat)`">>,
@@ -1195,111 +1153,6 @@ failing_contracts() ->
"to arguments\n" "to arguments\n"
" `Chain.create : (value : int, var_args) => 'c`">> " `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_() -> validation_test_() ->
-3
View File
@@ -6,7 +6,6 @@
namespace Ns = namespace Ns =
datatype d('a) = D | S(int) | M('a, list('a), int) datatype d('a) = D | S(int) | M('a, list('a), int)
private function fff() = 123 private function fff() = 123
let const = 1
stateful entrypoint stateful entrypoint
f (1, x) = (_) => x f (1, x) = (_) => x
@@ -34,8 +33,6 @@ contract AllSyntax =
type state = shakespeare(int) type state = shakespeare(int)
let cc = "str"
entrypoint init() = { entrypoint init() = {
johann = 1000, johann = 1000,
wolfgang = -10, wolfgang = -10,
-5
View File
@@ -1,5 +0,0 @@
// include "String.aes"
contract BytesToX =
entrypoint fail1(b : bytes()) = Bytes.to_fixed_size(b)
entrypoint fail2(b : bytes(4)) = Bytes.to_fixed_size(b)
entrypoint fail3(b : bytes()) = Bytes.to_any_size(b)
-27
View File
@@ -1,27 +0,0 @@
include "String.aes"
contract BytesMisc =
entrypoint sizeFixed(b : bytes(4)) : int = Bytes.size(b)
entrypoint sizeAny(b : bytes()) : int = Bytes.size(b)
entrypoint int_to_bytes(i : int) : bytes() = Int.to_bytes(i, 16)
entrypoint test(b3 : bytes(3), b7 : bytes(7), bX : bytes, i : int, s : string) =
let bi = Int.to_bytes(i, 8)
let bs = String.to_bytes(s)
let b10 = Bytes.concat(b3, b7)
let (b4, b6 : bytes(6)) = Bytes.split(b10)
let Some((b8, b2)) = Bytes.split_any(bX, 8)
let bX7 = Bytes.concat(bX, b7)
let Some((b5, bX2)) = Bytes.split_any(bX7, 5)
let Some((b7b, b0)) = Bytes.split_any(bX, Bytes.size(b7))
let Some(b5b : bytes(5)) = Bytes.to_fixed_size(b5)
let (b1 : bytes(1), _) = Bytes.split(b5b)
[bi, bs, b0, Bytes.to_any_size(b1), b2, Bytes.to_any_size(b4), Bytes.to_any_size(b6), b7b, b8, bX2]
-2
View File
@@ -6,5 +6,3 @@ contract BytesToX =
String.concat(Bytes.to_str(b), Bytes.to_str(#ffff)) String.concat(Bytes.to_str(b), Bytes.to_str(#ffff))
entrypoint to_str_big(b : bytes(65)) : string = entrypoint to_str_big(b : bytes(65)) : string =
Bytes.to_str(b) Bytes.to_str(b)
entrypoint to_fixed(b : bytes()) : option(bytes(4)) = Bytes.to_fixed_size(b)
entrypoint to_any(b : bytes(4)) = Bytes.to_any_size(b)
@@ -1,5 +0,0 @@
contract F =
entrypoint g() = 1
main contract C =
entrypoint f() = F.g()
+1 -5
View File
@@ -2,8 +2,7 @@
contract ChainTest = contract ChainTest =
record state = { last_bf : address record state = { last_bf : address }
, nw_id : string }
function init() : state = function init() : state =
{last_bf = Contract.address} {last_bf = Contract.address}
@@ -12,6 +11,3 @@ contract ChainTest =
function save_coinbase() = function save_coinbase() =
put(state{last_bf = Chain.coinbase}) put(state{last_bf = Chain.coinbase})
function save_network_id() =
put(state{nw_id = Chain.network_id})
-13
View File
@@ -1,13 +0,0 @@
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,5 +0,0 @@
contract interface I =
entrypoint f : () => int
contract C : I =
stateful entrypoint f() = 1
@@ -1,6 +0,0 @@
contract interface I =
entrypoint f : () => int
contract C : I =
entrypoint init() = ()
function f() = 1
@@ -1,5 +0,0 @@
payable contract interface I =
payable entrypoint f : () => int
contract C : I =
entrypoint f() = 123
@@ -1,8 +0,0 @@
payable contract interface I =
payable entrypoint f : () => int
contract interface H : I =
payable entrypoint f : () => int
payable contract C : H =
entrypoint f() = 123
@@ -1,14 +0,0 @@
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
@@ -1,7 +0,0 @@
contract interface I =
payable entrypoint f : () => int
entrypoint g : () => int
contract C : I =
payable entrypoint f() = 1
payable entrypoint g() = 2
@@ -1,7 +0,0 @@
contract interface I =
stateful entrypoint f : () => int
stateful entrypoint g : () => int
contract C : I =
stateful entrypoint f() = 1
entrypoint g() = 2
@@ -1,5 +0,0 @@
contract interface I =
payable entrypoint f : () => int
contract C : I =
entrypoint f() = 1
-64
View File
@@ -1,64 +0,0 @@
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
@@ -1,11 +0,0 @@
contract G =
let const = 1
main contract C =
let c = G.const
stateful entrypoint f() =
let g = Chain.create() : G
g.const
g.const()
@@ -1,6 +0,0 @@
contract C =
let selfcycle = selfcycle
let cycle1 = cycle2
let cycle2 = cycle3
let cycle3 = cycle1
@@ -1,7 +0,0 @@
contract interface I =
let (x::y::_) = [1,2,3]
let c = 10
let d = 10
contract C =
entrypoint init() = ()
@@ -1,21 +0,0 @@
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
@@ -1,3 +0,0 @@
contract C =
let x::_ = [1,2,3,4]
let y::(p = z::_) = [1,2,3,4]
+3
View File
@@ -0,0 +1,3 @@
contract C =
let this_is_illegal = 2/0
entrypoint this_is_legal() = 2/0
@@ -1,7 +0,0 @@
contract F =
entrypoint g() = 1
main contract C =
using F for [g]
entrypoint f() = g()
@@ -1,5 +0,0 @@
namespace N =
function nconst() = 1
main contract C =
entrypoint f() = N.nconst()
+1 -29
View File
@@ -12,7 +12,7 @@ namespace UnusedNamespace =
// Unused // Unused
private function h() = 3 private function h() = 3
main contract Warnings = contract Warnings =
type state = int type state = int
@@ -58,31 +58,3 @@ namespace FunctionsAsArgs =
private function inc(n : int) : int = n + 1 private function inc(n : int) : int = n + 1
// Never used // Never used
private function dec(n : int) : int = n - 1 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