Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 311bf49505 | |||
| 0e3bcba07d | |||
| 699d1f7ab8 | |||
| 1a40a93157 | |||
| c078119bc4 | |||
| 31fd8fe24f | |||
| 9ad8e26e88 | |||
| 5adeb6c93e | |||
| 256df25af4 |
@@ -1,5 +1,5 @@
|
|||||||
mkdocs==1.2.4
|
mkdocs==1.4.2
|
||||||
mkdocs-simple-hooks==0.1.5
|
mkdocs-simple-hooks==0.1.5
|
||||||
mkdocs-material==7.3.6
|
mkdocs-material==9.0.9
|
||||||
mike==1.1.2
|
mike==1.1.2
|
||||||
pygments==2.12.0
|
pygments==2.14.0
|
||||||
+12
-2
@@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
|
### Changed
|
||||||
|
### Removed
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
## [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
|
||||||
```
|
```
|
||||||
@@ -14,9 +20,12 @@ 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.
|
||||||
### Removed
|
- Check contracts and entrypoints modifiers when implementing interfaces.
|
||||||
|
- Contracts can no longer be used as namespaces.
|
||||||
|
- Do not show unused stateful warning for functions that call other contracts with a non-zero value argument.
|
||||||
### Fixed
|
### 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.
|
||||||
|
|
||||||
@@ -371,7 +380,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Simplify calldata creation - instead of passing a compiled contract, simply
|
- Simplify calldata creation - instead of passing a compiled contract, simply
|
||||||
pass a (stubbed) contract string.
|
pass a (stubbed) contract string.
|
||||||
|
|
||||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v7.0.1...HEAD
|
[Unreleased]: https://github.com/aeternity/aesophia/compare/v7.1.0...HEAD
|
||||||
|
[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
|
||||||
|
|||||||
+28
-4
@@ -191,6 +191,17 @@ 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)),
|
||||||
@@ -245,10 +256,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:
|
||||||
|
|
||||||
@@ -549,6 +560,19 @@ 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.
|
||||||
|
|
||||||
## 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
|
||||||
|
|||||||
@@ -483,7 +483,7 @@ The address of the account that mined the current block.
|
|||||||
Chain.timestamp : int
|
Chain.timestamp : int
|
||||||
```
|
```
|
||||||
|
|
||||||
The timestamp of the current block.
|
The timestamp of the current block (unix time, milliseconds).
|
||||||
|
|
||||||
|
|
||||||
##### difficulty
|
##### difficulty
|
||||||
|
|||||||
@@ -238,6 +238,7 @@ 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
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@
|
|||||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{relx, [{release, {aesophia, "7.0.1"},
|
{relx, [{release, {aesophia, "7.1.0"},
|
||||||
[aesophia, aebytecode, getopt]},
|
[aesophia, aebytecode, getopt]},
|
||||||
|
|
||||||
{dev_mode, true},
|
{dev_mode, true},
|
||||||
|
|||||||
@@ -229,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_interface -> {contract_fun, Ann, Type};
|
What == contract; 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 }) ->
|
||||||
@@ -426,6 +426,7 @@ 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
|
||||||
@@ -441,6 +442,8 @@ lookup_env1(#env{ namespace = Current, used_namespaces = UsedNamespaces, scopes
|
|||||||
{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, Ann, QName}),
|
type_error({contract_treated_as_namespace, Ann, QName}),
|
||||||
{QName, {Ann1, Type}};
|
{QName, {Ann1, Type}};
|
||||||
@@ -906,6 +909,7 @@ 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),
|
||||||
@@ -931,6 +935,25 @@ 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().
|
||||||
@@ -1234,6 +1257,10 @@ 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 ->
|
||||||
@@ -1487,19 +1514,37 @@ 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(Name),
|
register_implementation(Id, TypeSig),
|
||||||
{{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 FunName as implemented by deleting it from the functions
|
%% Register the function FunId as implemented by deleting it from the functions
|
||||||
%% to be implemented table if it is included there, or return true otherwise.
|
%% to be implemented table if it is included there, or return true otherwise.
|
||||||
-spec register_implementation(FunName) -> true | no_return() when
|
-spec register_implementation(FunId, FunSig) -> true | no_return() when
|
||||||
FunName :: string().
|
FunId :: aeso_syntax:id(),
|
||||||
register_implementation(Name) ->
|
FunSig :: typesig().
|
||||||
|
register_implementation(Id, Sig) ->
|
||||||
|
Name = name(Id),
|
||||||
case ets_lookup(functions_to_implement, Name) of
|
case ets_lookup(functions_to_implement, Name) of
|
||||||
[{Name, _, {fun_decl, _, _, _}}] ->
|
[{Name, Interface, Decl = {fun_decl, _, DeclId, _}}] ->
|
||||||
|
DeclStateful = aeso_syntax:get_ann(stateful, Decl, false),
|
||||||
|
DeclPayable = aeso_syntax:get_ann(payable, Decl, false),
|
||||||
|
|
||||||
|
SigEntrypoint = aeso_syntax:get_ann(entrypoint, Sig, false),
|
||||||
|
SigStateful = aeso_syntax:get_ann(stateful, Sig, false),
|
||||||
|
SigPayable = aeso_syntax:get_ann(payable, Sig, false),
|
||||||
|
|
||||||
|
[ type_error({function_should_be_entrypoint, Id, DeclId, Interface})
|
||||||
|
|| not SigEntrypoint ],
|
||||||
|
|
||||||
|
[ type_error({entrypoint_cannot_be_stateful, Id, DeclId, Interface})
|
||||||
|
|| SigStateful andalso not DeclStateful ],
|
||||||
|
|
||||||
|
[ type_error({entrypoint_must_be_payable, Id, DeclId, Interface})
|
||||||
|
|| not SigPayable andalso DeclPayable ],
|
||||||
|
|
||||||
ets_delete(functions_to_implement, Name);
|
ets_delete(functions_to_implement, Name);
|
||||||
[] ->
|
[] ->
|
||||||
true;
|
true;
|
||||||
@@ -1509,9 +1554,9 @@ register_implementation(Name) ->
|
|||||||
|
|
||||||
infer_nonrec(Env, LetFun) ->
|
infer_nonrec(Env, LetFun) ->
|
||||||
create_constraints(),
|
create_constraints(),
|
||||||
NewLetFun = {{FunName, _}, _} = infer_letfun(Env, LetFun),
|
NewLetFun = {{_, Sig}, _} = infer_letfun(Env, LetFun),
|
||||||
check_special_funs(Env, NewLetFun),
|
check_special_funs(Env, NewLetFun),
|
||||||
register_implementation(FunName),
|
register_implementation(get_letfun_id(LetFun), Sig),
|
||||||
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),
|
||||||
@@ -1540,8 +1585,8 @@ infer_letrec(Env, Defs) ->
|
|||||||
ExtendEnv = bind_funs(Funs, Env),
|
ExtendEnv = bind_funs(Funs, Env),
|
||||||
Inferred =
|
Inferred =
|
||||||
[ begin
|
[ begin
|
||||||
Res = {{Name, TypeSig}, _} = infer_letfun(ExtendEnv, LF),
|
Res = {{Name, TypeSig}, LetFun} = infer_letfun(ExtendEnv, LF),
|
||||||
register_implementation(Name),
|
register_implementation(get_letfun_id(LetFun), TypeSig),
|
||||||
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}),
|
||||||
@@ -1593,6 +1638,9 @@ 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
|
||||||
@@ -1670,10 +1718,14 @@ 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 = false, current_function = Fun }, {id, _, "value"}, Default) ->
|
check_stateful_named_arg(#env{ stateful = Stateful, 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.
|
||||||
|
|
||||||
@@ -1791,6 +1843,10 @@ 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};
|
||||||
@@ -3344,6 +3400,9 @@ 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
|
||||||
@@ -3659,7 +3718,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 function `~s` from the interface `~s` in the contract `~s`", [FunName, InterfaceName, pp(ConId)]),
|
Msg = io_lib:format("Unimplemented entrypoint `~s` from the interface `~s` in the contract `~s`", [FunName, InterfaceName, pp(ConId)]),
|
||||||
mk_t_err(pos(ConId), Msg);
|
mk_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)]),
|
||||||
@@ -3707,6 +3766,29 @@ 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(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).
|
||||||
|
|||||||
@@ -359,9 +359,12 @@ 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()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{application, aesophia,
|
{application, aesophia,
|
||||||
[{description, "Compiler for Aeternity Sophia language"},
|
[{description, "Compiler for Aeternity Sophia language"},
|
||||||
{vsn, "7.0.1"},
|
{vsn, "7.1.0"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{applications,
|
{applications,
|
||||||
[kernel,
|
[kernel,
|
||||||
|
|||||||
@@ -205,6 +205,9 @@ 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",
|
||||||
@@ -868,7 +871,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 function `f` from the interface `I1` in the contract `I2`">>
|
"Unimplemented entrypoint `f` from the interface `I1` in the contract `I2`">>
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(polymorphism_contract_same_decl_multi_interface,
|
, ?TYPE_ERROR(polymorphism_contract_same_decl_multi_interface,
|
||||||
[<<?Pos(7,10)
|
[<<?Pos(7,10)
|
||||||
@@ -1152,6 +1155,43 @@ 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`">>
|
||||||
|
])
|
||||||
].
|
].
|
||||||
|
|
||||||
validation_test_() ->
|
validation_test_() ->
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
contract F =
|
||||||
|
entrypoint g() = 1
|
||||||
|
|
||||||
|
main contract C =
|
||||||
|
entrypoint f() = F.g()
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
include "List.aes"
|
||||||
|
|
||||||
|
contract C =
|
||||||
|
entrypoint f() =
|
||||||
|
let ??? = true
|
||||||
|
let v = ???
|
||||||
|
let q = v == "str"
|
||||||
|
let xs = [1, 2, 3, 4]
|
||||||
|
switch (List.first(List.map(???, xs)))
|
||||||
|
Some(x) => x + 1
|
||||||
|
None => 0
|
||||||
|
|
||||||
|
function g() = ???
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
contract interface I =
|
||||||
|
entrypoint f : () => int
|
||||||
|
|
||||||
|
contract C : I =
|
||||||
|
stateful entrypoint f() = 1
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
contract interface I =
|
||||||
|
entrypoint f : () => int
|
||||||
|
|
||||||
|
contract C : I =
|
||||||
|
entrypoint init() = ()
|
||||||
|
function f() = 1
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
payable contract interface I =
|
||||||
|
payable entrypoint f : () => int
|
||||||
|
|
||||||
|
contract C : I =
|
||||||
|
entrypoint f() = 123
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
payable contract interface I =
|
||||||
|
payable entrypoint f : () => int
|
||||||
|
|
||||||
|
contract interface H : I =
|
||||||
|
payable entrypoint f : () => int
|
||||||
|
|
||||||
|
payable contract C : H =
|
||||||
|
entrypoint f() = 123
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
contract interface F =
|
||||||
|
entrypoint f : () => int
|
||||||
|
|
||||||
|
payable contract interface G : F =
|
||||||
|
payable entrypoint f : () => int
|
||||||
|
entrypoint g : () => int
|
||||||
|
|
||||||
|
payable contract interface H =
|
||||||
|
payable entrypoint h : () => int
|
||||||
|
|
||||||
|
payable contract C : G, H =
|
||||||
|
payable entrypoint f() = 1
|
||||||
|
payable entrypoint g() = 2
|
||||||
|
payable entrypoint h() = 3
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
contract interface I =
|
||||||
|
payable entrypoint f : () => int
|
||||||
|
entrypoint g : () => int
|
||||||
|
|
||||||
|
contract C : I =
|
||||||
|
payable entrypoint f() = 1
|
||||||
|
payable entrypoint g() = 2
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
contract interface I =
|
||||||
|
stateful entrypoint f : () => int
|
||||||
|
stateful entrypoint g : () => int
|
||||||
|
|
||||||
|
contract C : I =
|
||||||
|
stateful entrypoint f() = 1
|
||||||
|
entrypoint g() = 2
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
contract interface I =
|
||||||
|
payable entrypoint f : () => int
|
||||||
|
|
||||||
|
contract C : I =
|
||||||
|
entrypoint f() = 1
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
contract F =
|
||||||
|
entrypoint g() = 1
|
||||||
|
|
||||||
|
main contract C =
|
||||||
|
using F for [g]
|
||||||
|
|
||||||
|
entrypoint f() = g()
|
||||||
@@ -12,7 +12,7 @@ namespace UnusedNamespace =
|
|||||||
// Unused
|
// Unused
|
||||||
private function h() = 3
|
private function h() = 3
|
||||||
|
|
||||||
contract Warnings =
|
main contract Warnings =
|
||||||
|
|
||||||
type state = int
|
type state = int
|
||||||
|
|
||||||
@@ -58,3 +58,10 @@ 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)
|
||||||
|
|||||||
Reference in New Issue
Block a user