Compare commits
35 Commits
v4.0.0-rc3
...
v4.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| b81312a714 | |||
| 63c0b714d0 | |||
| d018cc5819 | |||
| f5b2732b04 | |||
| f86f7984f4 | |||
| 1ae0a42071 | |||
| 18ae801333 | |||
| 32d52f0abc | |||
| 5e6ff6c9a7 | |||
| 2d6d506d63 | |||
| 482d22d46b | |||
| a333888fb9 | |||
| 5fc6e18cd2 | |||
| dd94a6bd67 | |||
| 7f86b7d301 | |||
| e018c31ce1 | |||
| 9234690d31 | |||
| 214a5f0a91 | |||
| d4d3a9650a | |||
| b752965443 | |||
| 0019d92e45 | |||
| 29f2168827 | |||
| f81dc88526 | |||
| a21715a657 | |||
| 048c2ca98d | |||
| 662e5e70ef | |||
| 8e3483ced4 | |||
| 6efc390bb6 | |||
| 981027b2e7 | |||
| 11d998b739 | |||
| b481b3254b | |||
| 01a2efb7b8 | |||
| a730fcc366 | |||
| 457f9cf4ea | |||
| f34b6ed982 |
+27
-1
@@ -9,6 +9,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Changed
|
||||
### Removed
|
||||
|
||||
## [4.0.0] - 2019-10-11
|
||||
### Added
|
||||
- `Address.to_contract` - casts an address to a (any) contract type.
|
||||
- Pragma to check compiler version, e.g. `@compiler >= 4.0`.
|
||||
### Changed
|
||||
- Nice type error if contract function is called as from a namespace.
|
||||
- Fail on function definitions in contracts other than the main contract.
|
||||
- Bug fix in variable optimization - don't discard writes to the store/state.
|
||||
### Removed
|
||||
|
||||
## [4.0.0-rc5] - 2019-09-27
|
||||
### Added
|
||||
### Changed
|
||||
- Bug fixes in error reporting.
|
||||
- Bug fix in variable liveness analysis for FATE.
|
||||
### Removed
|
||||
|
||||
## [4.0.0-rc4] - 2019-09-13
|
||||
### Added
|
||||
- Handle numeric escapes, i.e. `"\x19Ethereum Signed Message:\n"`, and similar strings.
|
||||
### Changed
|
||||
### Removed
|
||||
|
||||
## [4.0.0-rc3] - 2019-09-12
|
||||
### Added
|
||||
- `Bytes.concat` and `Bytes.split` are added to be able to
|
||||
@@ -154,7 +177,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Simplify calldata creation - instead of passing a compiled contract, simply
|
||||
pass a (stubbed) contract string.
|
||||
|
||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc3...HEAD
|
||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v4.0.0...HEAD
|
||||
[4.0.0]: https://github.com/aeternity/aesophia/compare/v4.0.0...v3.2.0
|
||||
[4.0.0-rc5]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc4...v4.0.0-rc5
|
||||
[4.0.0-rc4]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc3...v4.0.0-rc4
|
||||
[4.0.0-rc3]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc1...v4.0.0-rc3
|
||||
[4.0.0-rc1]: https://github.com/aeternity/aesophia/compare/v3.2.0...v4.0.0-rc1
|
||||
[3.2.0]: https://github.com/aeternity/aesophia/compare/v3.1.0...v3.2.0
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
{erl_opts, [debug_info]}.
|
||||
|
||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"a66dc0a"}}}
|
||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"4f4d6d3"}}}
|
||||
, {getopt, "1.0.1"}
|
||||
, {eblake2, "1.0.0"}
|
||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
||||
@@ -15,7 +15,7 @@
|
||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||
]}.
|
||||
|
||||
{relx, [{release, {aesophia, "4.0.0-rc3"},
|
||||
{relx, [{release, {aesophia, "4.0.0"},
|
||||
[aesophia, aebytecode, getopt]},
|
||||
|
||||
{dev_mode, true},
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{"1.1.0",
|
||||
[{<<"aebytecode">>,
|
||||
{git,"https://github.com/aeternity/aebytecode.git",
|
||||
{ref,"a66dc0a97facdeaad7e5403018ad195d989e4793"}},
|
||||
{ref,"4f4d6d30cd2c46b3830454d650a424d513f69134"}},
|
||||
0},
|
||||
{<<"aeserialization">>,
|
||||
{git,"https://github.com/aeternity/aeserialization.git",
|
||||
|
||||
+108
-35
@@ -60,7 +60,8 @@
|
||||
|
||||
-record(is_contract_constraint,
|
||||
{ contract_t :: utype(),
|
||||
context :: aeso_syntax:expr() %% The address literal
|
||||
context :: {contract_literal, aeso_syntax:expr()} |
|
||||
{address_to_contract, aeso_syntax:ann()}
|
||||
}).
|
||||
|
||||
-type field_constraint() :: #field_constraint{} | #record_create_constraint{} | #is_contract_constraint{}.
|
||||
@@ -83,7 +84,7 @@
|
||||
-type qname() :: [string()].
|
||||
-type typesig() :: {type_sig, aeso_syntax:ann(), type_constraints(), [aeso_syntax:named_arg_t()], [type()], type()}.
|
||||
|
||||
-type type_constraints() :: none | bytes_concat | bytes_split.
|
||||
-type type_constraints() :: none | bytes_concat | bytes_split | address_to_contract.
|
||||
|
||||
-type fun_info() :: {aeso_syntax:ann(), typesig() | type()}.
|
||||
-type type_info() :: {aeso_syntax:ann(), typedef()}.
|
||||
@@ -110,7 +111,7 @@
|
||||
, in_pattern = false :: boolean()
|
||||
, stateful = false :: boolean()
|
||||
, current_function = none :: none | aeso_syntax:id()
|
||||
, what = top :: top | namespace | contract
|
||||
, what = top :: top | namespace | contract | main_contract
|
||||
}).
|
||||
|
||||
-type env() :: #env{}.
|
||||
@@ -175,12 +176,13 @@ bind_fun(X, Type, Env) ->
|
||||
end.
|
||||
|
||||
-spec force_bind_fun(name(), type() | typesig(), env()) -> env().
|
||||
force_bind_fun(X, Type, Env) ->
|
||||
force_bind_fun(X, Type, Env = #env{ what = What }) ->
|
||||
Ann = aeso_syntax:get_ann(Type),
|
||||
NoCode = get_option(no_code, false),
|
||||
Entry = case X == "init" andalso Env#env.what == contract andalso not NoCode of
|
||||
true -> {reserved_init, Ann, Type};
|
||||
false -> {Ann, Type}
|
||||
Entry = if X == "init", What == main_contract, not NoCode ->
|
||||
{reserved_init, Ann, Type};
|
||||
What == contract -> {contract_fun, Ann, Type};
|
||||
true -> {Ann, Type}
|
||||
end,
|
||||
on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) ->
|
||||
Scope#scope{ funs = [{X, Entry} | Funs] }
|
||||
@@ -306,6 +308,9 @@ lookup_env1(#env{ namespace = Current, scopes = Scopes }, Kind, Ann, QName) ->
|
||||
{reserved_init, Ann1, Type} ->
|
||||
type_error({cannot_call_init_function, Ann}),
|
||||
{QName, {Ann1, Type}}; %% Return the type to avoid an extra not-in-scope error
|
||||
{contract_fun, Ann1, Type} ->
|
||||
type_error({contract_treated_as_namespace, Ann, QName}),
|
||||
{QName, {Ann1, Type}};
|
||||
{Ann1, _} = E ->
|
||||
%% Check that it's not private (or we can see private funs)
|
||||
case not is_private(Ann1) orelse AllowPrivate of
|
||||
@@ -519,6 +524,7 @@ global_env() ->
|
||||
%% Conversion
|
||||
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) },
|
||||
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)},
|
||||
{"to_contract", FunC(address_to_contract, [Address], A)},
|
||||
{"is_oracle", Fun1(Address, Bool)},
|
||||
{"is_contract", Fun1(Address, Bool)},
|
||||
{"is_payable", Fun1(Address, Bool)}]) },
|
||||
@@ -582,7 +588,8 @@ infer1(Env, [], Acc, _Options) -> {Env, lists:reverse(Acc)};
|
||||
infer1(Env, [{contract, Ann, ConName, Code} | Rest], Acc, Options) ->
|
||||
%% do type inference on each contract independently.
|
||||
check_scope_name_clash(Env, contract, ConName),
|
||||
{Env1, Code1} = infer_contract_top(push_scope(contract, ConName, Env), contract, Code, Options),
|
||||
What = if Rest == [] -> main_contract; true -> contract end,
|
||||
{Env1, Code1} = infer_contract_top(push_scope(contract, ConName, Env), What, Code, Options),
|
||||
Contract1 = {contract, Ann, ConName, Code1},
|
||||
Env2 = pop_scope(Env1),
|
||||
Env3 = bind_contract(Contract1, Env2),
|
||||
@@ -591,7 +598,10 @@ infer1(Env, [{namespace, Ann, Name, Code} | Rest], Acc, Options) ->
|
||||
check_scope_name_clash(Env, namespace, Name),
|
||||
{Env1, Code1} = infer_contract_top(push_scope(namespace, Name, Env), namespace, Code, Options),
|
||||
Namespace1 = {namespace, Ann, Name, Code1},
|
||||
infer1(pop_scope(Env1), Rest, [Namespace1 | Acc], Options).
|
||||
infer1(pop_scope(Env1), Rest, [Namespace1 | Acc], Options);
|
||||
infer1(Env, [{pragma, _, _} | Rest], Acc, Options) ->
|
||||
%% Pragmas are checked in check_modifiers
|
||||
infer1(Env, Rest, Acc, Options).
|
||||
|
||||
check_scope_name_clash(Env, Kind, Name) ->
|
||||
case get_scope(Env, qname(Name)) of
|
||||
@@ -602,7 +612,7 @@ check_scope_name_clash(Env, Kind, Name) ->
|
||||
destroy_and_report_type_errors(Env)
|
||||
end.
|
||||
|
||||
-spec infer_contract_top(env(), contract | namespace, [aeso_syntax:decl()], list(option())) ->
|
||||
-spec infer_contract_top(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) ->
|
||||
{env(), [aeso_syntax:decl()]}.
|
||||
infer_contract_top(Env, Kind, Defs0, _Options) ->
|
||||
Defs = desugar(Defs0),
|
||||
@@ -610,7 +620,7 @@ infer_contract_top(Env, Kind, Defs0, _Options) ->
|
||||
|
||||
%% infer_contract takes a proplist mapping global names to types, and
|
||||
%% a list of definitions.
|
||||
-spec infer_contract(env(), contract | namespace, [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}.
|
||||
-spec infer_contract(env(), main_contract | contract | namespace, [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}.
|
||||
infer_contract(Env0, What, Defs) ->
|
||||
Env = Env0#env{ what = What },
|
||||
Kind = fun({type_def, _, _, _, _}) -> type;
|
||||
@@ -625,7 +635,8 @@ infer_contract(Env0, What, Defs) ->
|
||||
Env2 =
|
||||
case What of
|
||||
namespace -> Env1;
|
||||
contract -> bind_state(Env1) %% bind state and put
|
||||
contract -> Env1;
|
||||
main_contract -> bind_state(Env1) %% bind state and put
|
||||
end,
|
||||
{ProtoSigs, Decls} = lists:unzip([ check_fundecl(Env1, Decl) || Decl <- Get(prototype) ]),
|
||||
Env3 = bind_funs(ProtoSigs, Env2),
|
||||
@@ -704,18 +715,45 @@ check_unexpected(Xs) ->
|
||||
|
||||
check_modifiers(Env, Contracts) ->
|
||||
create_type_errors(),
|
||||
[ case C of
|
||||
{contract, _, Con, Decls} ->
|
||||
check_modifiers_(Env, Contracts),
|
||||
destroy_and_report_type_errors(Env).
|
||||
|
||||
check_modifiers_(Env, [{contract, _, Con, Decls} | Rest]) ->
|
||||
IsMain = Rest == [],
|
||||
check_modifiers1(contract, Decls),
|
||||
case {lists:keymember(letfun, 1, Decls),
|
||||
[ D || D <- Decls, aeso_syntax:get_ann(entrypoint, D, false) ]} of
|
||||
{true, []} -> type_error({contract_has_no_entrypoints, Con});
|
||||
_ -> ok
|
||||
_ when not IsMain ->
|
||||
case [ {Ann, Id} || {letfun, Ann, Id, _, _, _} <- Decls ] of
|
||||
[{Ann, Id} | _] -> type_error({definition_in_non_main_contract, Ann, Id});
|
||||
[] -> ok
|
||||
end;
|
||||
{namespace, _, _, Decls} -> check_modifiers1(namespace, Decls);
|
||||
Decl -> type_error({bad_top_level_decl, Decl})
|
||||
end || C <- Contracts ],
|
||||
destroy_and_report_type_errors(Env).
|
||||
_ -> ok
|
||||
end,
|
||||
check_modifiers_(Env, Rest);
|
||||
check_modifiers_(Env, [{namespace, _, _, Decls} | Rest]) ->
|
||||
check_modifiers1(namespace, Decls),
|
||||
check_modifiers_(Env, Rest);
|
||||
check_modifiers_(Env, [{pragma, Ann, Pragma} | Rest]) ->
|
||||
check_pragma(Env, Ann, Pragma),
|
||||
check_modifiers_(Env, Rest);
|
||||
check_modifiers_(Env, [Decl | Rest]) ->
|
||||
type_error({bad_top_level_decl, Decl}),
|
||||
check_modifiers_(Env, Rest);
|
||||
check_modifiers_(_Env, []) -> ok.
|
||||
|
||||
-spec check_pragma(env(), aeso_syntax:ann(), aeso_syntax:pragma()) -> ok.
|
||||
check_pragma(_Env, Ann, {compiler, Op, Ver}) ->
|
||||
case aeso_compiler:numeric_version() of
|
||||
{error, Err} -> type_error({failed_to_get_compiler_version, Err});
|
||||
{ok, Version} ->
|
||||
Strip = fun(V) -> lists:reverse(lists:dropwhile(fun(X) -> X == 0 end, lists:reverse(V))) end,
|
||||
case apply(erlang, Op, [Strip(Version), Strip(Ver)]) of
|
||||
true -> ok;
|
||||
false -> type_error({compiler_version_mismatch, Ann, Version, Op, Ver})
|
||||
end
|
||||
end.
|
||||
|
||||
-spec check_modifiers1(contract | namespace, [aeso_syntax:decl()] | aeso_syntax:decl()) -> ok.
|
||||
check_modifiers1(What, Decls) when is_list(Decls) ->
|
||||
@@ -783,7 +821,8 @@ check_type(_Env, Type = {uvar, _, _}, Arity) ->
|
||||
ensure_base_type(Type, Arity),
|
||||
Type;
|
||||
check_type(_Env, {args_t, Ann, Ts}, _) ->
|
||||
type_error({new_tuple_syntax, Ann, Ts}).
|
||||
type_error({new_tuple_syntax, Ann, Ts}),
|
||||
{tuple_t, Ann, Ts}.
|
||||
|
||||
ensure_base_type(Type, Arity) ->
|
||||
[ type_error({wrong_type_arguments, Type, Arity, 0}) || Arity /= 0 ],
|
||||
@@ -1080,7 +1119,7 @@ infer_expr(_Env, Body={oracle_query_id, As, _}) ->
|
||||
infer_expr(_Env, Body={contract_pubkey, As, _}) ->
|
||||
Con = fresh_uvar(As),
|
||||
constrain([#is_contract_constraint{ contract_t = Con,
|
||||
context = Body }]),
|
||||
context = {contract_literal, Body} }]),
|
||||
{typed, As, Body, Con};
|
||||
infer_expr(_Env, Body={id, As, "_"}) ->
|
||||
{typed, As, Body, fresh_uvar(As)};
|
||||
@@ -1155,8 +1194,6 @@ infer_expr(Env, {typed, As, Body, Type}) ->
|
||||
{typed, _, NewBody, NewType} = check_expr(Env, Body, Type1),
|
||||
{typed, As, NewBody, NewType};
|
||||
infer_expr(Env, {app, Ann, Fun, Args0}) ->
|
||||
%% TODO: fix parser to give proper annotation for normal applications!
|
||||
FunAnn = aeso_syntax:get_ann(Fun),
|
||||
NamedArgs = [ Arg || Arg = {named_arg, _, _, _} <- Args0 ],
|
||||
Args = Args0 -- NamedArgs,
|
||||
case aeso_syntax:get_ann(format, Ann) of
|
||||
@@ -1165,15 +1202,15 @@ infer_expr(Env, {app, Ann, Fun, Args0}) ->
|
||||
prefix ->
|
||||
infer_op(Env, Ann, Fun, Args, fun infer_prefix/1);
|
||||
_ ->
|
||||
NamedArgsVar = fresh_uvar(FunAnn),
|
||||
NamedArgsVar = fresh_uvar(Ann),
|
||||
NamedArgs1 = [ infer_named_arg(Env, NamedArgsVar, Arg) || Arg <- NamedArgs ],
|
||||
%% TODO: named args constraints
|
||||
NewFun={typed, _, _, FunType} = infer_expr(Env, Fun),
|
||||
NewArgs = [infer_expr(Env, A) || A <- Args],
|
||||
ArgTypes = [T || {typed, _, _, T} <- NewArgs],
|
||||
ResultType = fresh_uvar(FunAnn),
|
||||
ResultType = fresh_uvar(Ann),
|
||||
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, ResultType}, {infer_app, Fun, Args, FunType, ArgTypes}),
|
||||
{typed, FunAnn, {app, Ann, NewFun, NamedArgs1 ++ NewArgs}, dereference(ResultType)}
|
||||
{typed, Ann, {app, Ann, NewFun, NamedArgs1 ++ NewArgs}, dereference(ResultType)}
|
||||
end;
|
||||
infer_expr(Env, {'if', Attrs, Cond, Then, Else}) ->
|
||||
NewCond = check_expr(Env, Cond, {id, Attrs, "bool"}),
|
||||
@@ -1333,7 +1370,7 @@ infer_case(Env, Attrs, Pattern, ExprType, Branch, SwitchType) ->
|
||||
end,
|
||||
NewEnv = bind_vars([{Var, fresh_uvar(Ann)} || Var = {id, Ann, _} <- Vars], Env#env{ in_pattern = true }),
|
||||
NewPattern = {typed, _, _, PatType} = infer_expr(NewEnv, Pattern),
|
||||
NewBranch = check_expr(NewEnv, Branch, SwitchType),
|
||||
NewBranch = check_expr(NewEnv#env{ in_pattern = false }, Branch, SwitchType),
|
||||
unify(Env, PatType, ExprType, {case_pat, Pattern, PatType, ExprType}),
|
||||
{'case', Attrs, NewPattern, NewBranch}.
|
||||
|
||||
@@ -1654,11 +1691,11 @@ check_record_create_constraints(Env, [C | Cs]) ->
|
||||
|
||||
check_is_contract_constraints(_Env, []) -> ok;
|
||||
check_is_contract_constraints(Env, [C | Cs]) ->
|
||||
#is_contract_constraint{ contract_t = Type, context = Lit } = C,
|
||||
#is_contract_constraint{ contract_t = Type, context = Cxt } = C,
|
||||
Type1 = unfold_types_in_type(Env, instantiate(Type)),
|
||||
case lookup_type(Env, record_type_name(Type1)) of
|
||||
{_, {_Ann, {[], {contract_t, _}}}} -> ok;
|
||||
_ -> type_error({not_a_contract_type, Type1, Lit})
|
||||
_ -> type_error({not_a_contract_type, Type1, Cxt})
|
||||
end,
|
||||
check_is_contract_constraints(Env, Cs).
|
||||
|
||||
@@ -1748,7 +1785,7 @@ solve_known_record_types(Env, Constraints) ->
|
||||
C
|
||||
end;
|
||||
_ ->
|
||||
type_error({not_a_record_type, RecType, When}),
|
||||
type_error({not_a_record_type, instantiate(RecType), When}),
|
||||
not_solved
|
||||
end
|
||||
end
|
||||
@@ -2079,6 +2116,9 @@ freshen_type_sig(Ann, TypeSig = {type_sig, _, Constr, _, _, _}) ->
|
||||
FunT.
|
||||
|
||||
apply_typesig_constraint(_Ann, none, _FunT) -> ok;
|
||||
apply_typesig_constraint(Ann, address_to_contract, {fun_t, _, [], [_], Type}) ->
|
||||
constrain([#is_contract_constraint{ contract_t = Type,
|
||||
context = {address_to_contract, Ann}}]);
|
||||
apply_typesig_constraint(Ann, bytes_concat, {fun_t, _, [], [A, B], C}) ->
|
||||
add_bytes_constraint({add_bytes, Ann, concat, A, B, C});
|
||||
apply_typesig_constraint(Ann, bytes_split, {fun_t, _, [], [C], {tuple_t, _, [A, B]}}) ->
|
||||
@@ -2177,12 +2217,28 @@ mk_error({not_a_record_type, Type, Why}) ->
|
||||
Msg = io_lib:format("~s\n", [pp_type("Not a record type: ", Type)]),
|
||||
{Pos, Ctxt} = pp_why_record(Why),
|
||||
mk_t_err(Pos, Msg, Ctxt);
|
||||
mk_error({not_a_contract_type, Type, Lit}) ->
|
||||
Msg = io_lib:format("The type ~s is not a contract type\n"
|
||||
"when checking that the contract literal at ~s\n~s\n"
|
||||
mk_error({not_a_contract_type, Type, Cxt}) ->
|
||||
Msg =
|
||||
case Type of
|
||||
{tvar, _, _} ->
|
||||
"Unresolved contract type\n";
|
||||
_ ->
|
||||
io_lib:format("The type ~s is not a contract type\n", [pp_type("", Type)])
|
||||
end,
|
||||
{Pos, Cxt1} =
|
||||
case Cxt of
|
||||
{contract_literal, Lit} ->
|
||||
{pos(Lit),
|
||||
io_lib:format("when checking that the contract literal\n~s\n"
|
||||
"has the type\n~s\n",
|
||||
[pp_type("", Type), pp_loc(Lit), pp_expr(" ", Lit), pp_type(" ", Type)]),
|
||||
mk_t_err(pos(Lit), Msg);
|
||||
[pp_expr(" ", Lit), pp_type(" ", Type)])};
|
||||
{address_to_contract, Ann} ->
|
||||
{pos(Ann),
|
||||
io_lib:format("when checking that the call to\n Address.to_contract\n"
|
||||
"has the type\n~s\n",
|
||||
[pp_type(" ", Type)])}
|
||||
end,
|
||||
mk_t_err(Pos, Msg, Cxt1);
|
||||
mk_error({non_linear_pattern, Pattern, Nonlinear}) ->
|
||||
Msg = io_lib:format("Repeated name~s ~s in pattern\n~s (at ~s)\n",
|
||||
[plural("", "s", Nonlinear), string:join(Nonlinear, ", "),
|
||||
@@ -2344,6 +2400,10 @@ mk_error({contract_has_no_entrypoints, Con}) ->
|
||||
"contract functions must be declared with the 'entrypoint' keyword instead of\n"
|
||||
"'function'.\n", [pp_expr("", Con), pp_loc(Con)]),
|
||||
mk_t_err(pos(Con), Msg);
|
||||
mk_error({definition_in_non_main_contract, Ann, {id, _, Id}}) ->
|
||||
Msg = "Only the main contract can contain defined functions or entrypoints.\n",
|
||||
Cxt = io_lib:format("Fix: replace the definition of '~s' by a type signature.\n", [Id]),
|
||||
mk_t_err(pos(Ann), Msg, Cxt);
|
||||
mk_error({unbound_type, Type}) ->
|
||||
Msg = io_lib:format("Unbound type ~s (at ~s).\n", [pp_type("", Type), pp_loc(Type)]),
|
||||
mk_t_err(pos(Type), Msg);
|
||||
@@ -2359,6 +2419,10 @@ mk_error({cannot_call_init_function, Ann}) ->
|
||||
Msg = "The 'init' function is called exclusively by the create contract transaction\n"
|
||||
"and cannot be called from the contract code.\n",
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({contract_treated_as_namespace, Ann, [Con, Fun] = QName}) ->
|
||||
Msg = io_lib:format("Invalid call to contract entrypoint '~s'.\n", [string:join(QName, ".")]),
|
||||
Cxt = io_lib:format("It must be called as 'c.~s' for some c : ~s.\n", [Fun, Con]),
|
||||
mk_t_err(pos(Ann), Msg, Cxt);
|
||||
mk_error({bad_top_level_decl, Decl}) ->
|
||||
What = case element(1, Decl) of
|
||||
letval -> "function or entrypoint";
|
||||
@@ -2383,6 +2447,15 @@ mk_error({unsolved_bytes_constraint, Ann, split, A, B, C}) ->
|
||||
[ 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}) ->
|
||||
Msg = io_lib:format("Failed to get compiler version. Error:\n ~p\n", [Err]),
|
||||
mk_t_err(pos(0, 0), Msg);
|
||||
mk_error({compiler_version_mismatch, Ann, Version, Op, Bound}) ->
|
||||
PrintV = fun(V) -> string:join([integer_to_list(N) || N <- V], ".") end,
|
||||
Msg = io_lib:format("Cannot compile with this version of the compiler,\n"
|
||||
"because it does not satisfy the constraint"
|
||||
" ~s ~s ~s\n", [PrintV(Version), Op, PrintV(Bound)]),
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error(Err) ->
|
||||
Msg = io_lib:format("Unknown error: ~p\n", [Err]),
|
||||
mk_t_err(pos(0, 0), Msg).
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
map_delete | map_member | map_size | string_length |
|
||||
string_concat | bits_set | bits_clear | bits_test | bits_sum |
|
||||
bits_intersection | bits_union | bits_difference |
|
||||
contract_to_address | crypto_verify_sig | crypto_verify_sig_secp256k1 |
|
||||
contract_to_address | address_to_contract | crypto_verify_sig | crypto_verify_sig_secp256k1 |
|
||||
crypto_sha3 | crypto_sha256 | crypto_blake2b |
|
||||
crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1.
|
||||
|
||||
@@ -200,7 +200,7 @@ builtins() ->
|
||||
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
|
||||
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]},
|
||||
{["Int"], [{"to_str", 1}]},
|
||||
{["Address"], [{"to_str", 1}, {"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]}
|
||||
{["Address"], [{"to_str", 1}, {"to_contract", 1}, {"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]}
|
||||
],
|
||||
maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}}
|
||||
|| {NS, Funs} <- Scopes,
|
||||
@@ -904,6 +904,7 @@ op_builtins() ->
|
||||
string_length, string_concat, string_sha3, string_sha256, string_blake2b,
|
||||
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
|
||||
bits_difference, int_to_str, address_to_str, crypto_verify_sig,
|
||||
address_to_contract,
|
||||
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b,
|
||||
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1
|
||||
].
|
||||
|
||||
@@ -494,6 +494,7 @@ is_builtin_fun({qid, _, ["Address", "to_str"]}, _Icode) ->
|
||||
is_builtin_fun({qid, _, ["Address", "is_oracle"]}, _Icode) -> true;
|
||||
is_builtin_fun({qid, _, ["Address", "is_contract"]}, _Icode) -> true;
|
||||
is_builtin_fun({qid, _, ["Address", "is_payable"]}, _Icode) -> true;
|
||||
is_builtin_fun({qid, _, ["Address", "to_contract"]}, _Icode) -> true;
|
||||
is_builtin_fun({qid, _, ["Bytes", "to_int"]}, _Icode) -> true;
|
||||
is_builtin_fun({qid, _, ["Bytes", "to_str"]}, _Icode) -> true;
|
||||
is_builtin_fun({qid, _, ["Bytes", "concat"]}, _Icode) -> true;
|
||||
@@ -713,6 +714,8 @@ builtin_code(_, {qid, _, ["Address", "is_contract"]}, [Addr], _, _, Icode) ->
|
||||
builtin_code(_, {qid, _, ["Address", "is_payable"]}, [Addr], _, _, Icode) ->
|
||||
prim_call(?PRIM_CALL_ADDR_IS_PAYABLE, #integer{value = 0},
|
||||
[ast_body(Addr, Icode)], [word], word);
|
||||
builtin_code(_, {qid, _, ["Address", "to_contract"]}, [Addr], _, _, Icode) ->
|
||||
ast_body(Addr, Icode);
|
||||
|
||||
builtin_code(_, {qid, _, ["Bytes", "to_int"]}, [Bytes], _, _, Icode) ->
|
||||
{typed, _, _, {bytes_t, _, N}} = Bytes,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
, create_calldata/3 %% deprecated
|
||||
, create_calldata/4
|
||||
, version/0
|
||||
, numeric_version/0
|
||||
, sophia_type_to_typerep/1
|
||||
, to_sophia_value/4 %% deprecated, need a backend
|
||||
, to_sophia_value/5
|
||||
@@ -65,6 +66,17 @@ version() ->
|
||||
{ok, list_to_binary(VsnString)}
|
||||
end.
|
||||
|
||||
-spec numeric_version() -> {ok, [non_neg_integer()]} | {error, term()}.
|
||||
numeric_version() ->
|
||||
case version() of
|
||||
{ok, Bin} ->
|
||||
[NoSuf | _] = binary:split(Bin, <<"-">>),
|
||||
Numbers = binary:split(NoSuf, <<".">>, [global]),
|
||||
{ok, [binary_to_integer(Num) || Num <- Numbers]};
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
-spec file(string()) -> {ok, map()} | {error, [aeso_errors:error()]}.
|
||||
file(Filename) ->
|
||||
file(Filename, []).
|
||||
|
||||
+31
-17
@@ -38,7 +38,7 @@
|
||||
|
||||
-define(i(X), {immediate, X}).
|
||||
-define(a, {stack, 0}).
|
||||
-define(s, {var, -1}). %% TODO: until we have state support in FATE
|
||||
-define(s, {store, 1}).
|
||||
-define(void, {var, 9999}).
|
||||
|
||||
-define(IsState(X), (is_tuple(X) andalso tuple_size(X) =:= 2 andalso element(1, X) =:= var andalso element(2, X) < 0)).
|
||||
@@ -103,6 +103,7 @@
|
||||
Op =:= 'ECVERIFY_SECP256K1' orelse
|
||||
Op =:= 'ECRECOVER_SECP256K1' orelse
|
||||
Op =:= 'CONTRACT_TO_ADDRESS' orelse
|
||||
Op =:= 'ADDRESS_TO_CONTRACT' orelse
|
||||
Op =:= 'AUTH_TX_HASH' orelse
|
||||
Op =:= 'BYTES_TO_INT' orelse
|
||||
Op =:= 'BYTES_TO_STR' orelse
|
||||
@@ -477,7 +478,7 @@ call_to_scode(Env, CallCode, Args) ->
|
||||
builtin_to_scode(_Env, get_state, []) ->
|
||||
[push(?s)];
|
||||
builtin_to_scode(Env, set_state, [_] = Args) ->
|
||||
call_to_scode(Env, [aeb_fate_ops:store(?s, ?a),
|
||||
call_to_scode(Env, [{'STORE', ?s, ?a},
|
||||
tuple(0)], Args);
|
||||
builtin_to_scode(Env, chain_event, Args) ->
|
||||
call_to_scode(Env, [erlang:apply(aeb_fate_ops, log, lists:duplicate(length(Args), ?a)),
|
||||
@@ -611,6 +612,7 @@ 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(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
|
||||
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
|
||||
op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a);
|
||||
op_to_scode(crypto_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a);
|
||||
op_to_scode(crypto_verify_sig_secp256k1) -> aeb_fate_ops:verify_sig_secp256k1(?a, ?a, ?a, ?a);
|
||||
op_to_scode(crypto_ecverify_secp256k1) -> aeb_fate_ops:ecverify_secp256k1(?a, ?a, ?a, ?a);
|
||||
@@ -624,7 +626,7 @@ op_to_scode(string_blake2b) -> aeb_fate_ops:blake2b(?a, ?a).
|
||||
|
||||
%% PUSH and STORE ?a are the same, so we use STORE to make optimizations
|
||||
%% easier, and specialize to PUSH (which is cheaper) at the end.
|
||||
push(A) -> aeb_fate_ops:store(?a, A).
|
||||
push(A) -> {'STORE', ?a, A}.
|
||||
|
||||
tuple(0) -> push(?i({tuple, {}}));
|
||||
tuple(N) -> aeb_fate_ops:tuple(?a, N).
|
||||
@@ -689,7 +691,7 @@ pp_ann(Ind, [{i, #{ live_in := In, live_out := Out }, I} | Code]) ->
|
||||
Fmt = fun([]) -> "()";
|
||||
(Xs) -> string:join([lists:concat(["var", N]) || {var, N} <- Xs], " ")
|
||||
end,
|
||||
Op = [Ind, pp_op(I)],
|
||||
Op = [Ind, pp_op(desugar_args(I))],
|
||||
Ann = [[" % ", Fmt(In), " -> ", Fmt(Out)] || In ++ Out /= []],
|
||||
[io_lib:format("~-40s~s\n", [Op, Ann]),
|
||||
pp_ann(Ind, Code)];
|
||||
@@ -701,7 +703,7 @@ pp_op(I) ->
|
||||
|
||||
pp_arg(?i(I)) -> io_lib:format("~w", [I]);
|
||||
pp_arg({arg, N}) -> io_lib:format("arg~p", [N]);
|
||||
pp_arg({var, N}) when N < 0 -> io_lib:format("store~p", [-N]);
|
||||
pp_arg(?s) -> "store1";
|
||||
pp_arg({var, N}) -> io_lib:format("var~p", [N]);
|
||||
pp_arg(?a) -> "a".
|
||||
|
||||
@@ -741,18 +743,20 @@ ann_reads([{switch, Arg, Type, Alts, Def} | Code], Reads, Acc) ->
|
||||
ann_reads([{i, Ann, I} | Code], Reads, Acc) ->
|
||||
#{ writes_in := WritesIn, writes_out := WritesOut } = Ann,
|
||||
#{ read := Rs, write := W, pure := Pure } = attributes(I),
|
||||
Reads1 =
|
||||
%% If we write it here it's not live in (unless we also read it)
|
||||
Reads1 = Reads -- [W],
|
||||
Reads2 =
|
||||
case {W, Pure andalso not ordsets:is_element(W, Reads)} of
|
||||
%% This is a little bit dangerous: if writing to a dead variable, we ignore
|
||||
%% the reads. Relies on dead writes to be removed by the
|
||||
%% optimisations below (r_write_to_dead_var).
|
||||
{{var, _}, true} -> Reads;
|
||||
_ -> ordsets:union(Reads, Rs)
|
||||
{{var, _}, true} -> Reads1;
|
||||
_ -> ordsets:union(Reads1, Rs)
|
||||
end,
|
||||
LiveIn = ordsets:intersection(Reads1, WritesIn),
|
||||
LiveIn = ordsets:intersection(Reads2, WritesIn),
|
||||
LiveOut = ordsets:intersection(Reads, WritesOut),
|
||||
Ann1 = #{ live_in => LiveIn, live_out => LiveOut },
|
||||
ann_reads(Code, Reads1, [{i, Ann1, I} | Acc]);
|
||||
ann_reads(Code, Reads2, [{i, Ann1, I} | Acc]);
|
||||
ann_reads([], Reads, Acc) -> {Acc, Reads}.
|
||||
|
||||
%% Instruction attributes: reads, writes and purity (pure means no side-effects
|
||||
@@ -849,6 +853,7 @@ attributes(I) ->
|
||||
{'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||
{'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]);
|
||||
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
|
||||
{'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]);
|
||||
{'AUTH_TX_HASH', A} -> Pure(A, []);
|
||||
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
|
||||
{'BYTES_TO_STR', A, B} -> Pure(A, [B]);
|
||||
@@ -922,6 +927,7 @@ independent({i, _, I}, {i, _, J}) ->
|
||||
if WI == pc; WJ == pc -> false; %% no jumps
|
||||
not (PureI or PureJ) -> false; %% at least one is pure
|
||||
StackI and StackJ -> false; %% cannot both use the stack
|
||||
WI == WJ -> false; %% cannot write to the same register
|
||||
true ->
|
||||
%% and cannot write to each other's inputs
|
||||
not lists:member(WI, RJ) andalso
|
||||
@@ -1282,7 +1288,7 @@ r_inline_store(_Acc, _, _, _) -> false.
|
||||
%% Shortcut write followed by final read
|
||||
r_one_shot_var({i, Ann1, I}, [{i, Ann2, J} | Code]) ->
|
||||
case op_view(I) of
|
||||
{Op, R, As} ->
|
||||
{Op, R = {var, _}, As} ->
|
||||
Copy = case J of
|
||||
{'STORE', S, R} -> {write_to, S};
|
||||
_ -> false
|
||||
@@ -1336,18 +1342,26 @@ unannotate({i, _Ann, I}) -> [I].
|
||||
|
||||
%% Desugar and specialize
|
||||
desugar({'ADD', ?a, ?i(1), ?a}) -> [aeb_fate_ops:inc()];
|
||||
desugar({'ADD', A, ?i(1), A}) -> [aeb_fate_ops:inc(A)];
|
||||
desugar({'ADD', A, ?i(1), A}) -> [aeb_fate_ops:inc(desugar_arg(A))];
|
||||
desugar({'ADD', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:inc()];
|
||||
desugar({'ADD', A, A, ?i(1)}) -> [aeb_fate_ops:inc(A)];
|
||||
desugar({'ADD', A, A, ?i(1)}) -> [aeb_fate_ops:inc(desugar_arg(A))];
|
||||
desugar({'SUB', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:dec()];
|
||||
desugar({'SUB', A, A, ?i(1)}) -> [aeb_fate_ops:dec(A)];
|
||||
desugar({'STORE', ?a, A}) -> [aeb_fate_ops:push(A)];
|
||||
desugar({'SUB', A, A, ?i(1)}) -> [aeb_fate_ops:dec(desugar_arg(A))];
|
||||
desugar({'STORE', ?a, A}) -> [aeb_fate_ops:push(desugar_arg(A))];
|
||||
desugar({switch, Arg, Type, Alts, Def}) ->
|
||||
[{switch, Arg, Type, [desugar(A) || A <- Alts], desugar(Def)}];
|
||||
[{switch, desugar_arg(Arg), Type, [desugar(A) || A <- Alts], desugar(Def)}];
|
||||
desugar(missing) -> missing;
|
||||
desugar(Code) when is_list(Code) ->
|
||||
lists:flatmap(fun desugar/1, Code);
|
||||
desugar(I) -> [I].
|
||||
desugar(I) -> [desugar_args(I)].
|
||||
|
||||
desugar_args(I) when is_tuple(I) ->
|
||||
[Op | Args] = tuple_to_list(I),
|
||||
list_to_tuple([Op | lists:map(fun desugar_arg/1, Args)]);
|
||||
desugar_args(I) -> I.
|
||||
|
||||
desugar_arg(?s) -> {var, -1};
|
||||
desugar_arg(A) -> A.
|
||||
|
||||
%% -- Phase III --------------------------------------------------------------
|
||||
%% Constructing basic blocks
|
||||
|
||||
+13
-2
@@ -87,6 +87,7 @@ decl() ->
|
||||
, ?RULE(token(payable), keyword(contract), con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract, _2, _3, _5}))
|
||||
, ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4})
|
||||
, ?RULE(keyword(include), str(), {include, get_ann(_1), _2})
|
||||
, pragma()
|
||||
|
||||
%% Type declarations TODO: format annotation for "type bla" vs "type bla()"
|
||||
, ?RULE(keyword(type), id(), {type_decl, _1, _2, []})
|
||||
@@ -104,6 +105,16 @@ decl() ->
|
||||
, ?RULE(keyword('let'), valdef(), set_pos(get_pos(_1), _2))
|
||||
])).
|
||||
|
||||
pragma() ->
|
||||
Op = choice([token(T) || T <- ['<', '=<', '==', '>=', '>']]),
|
||||
?RULE(tok('@'), id("compiler"), Op, version(), {pragma, get_ann(_1), {compiler, element(1, _3), _4}}).
|
||||
|
||||
version() ->
|
||||
?RULE(token(int), many({tok('.'), token(int)}), mk_version(_1, _2)).
|
||||
|
||||
mk_version({int, _, Maj}, Rest) ->
|
||||
[Maj | [N || {_, {int, _, N}} <- Rest]].
|
||||
|
||||
fun_or_entry() ->
|
||||
choice([?RULE(keyword(function), {function, _1}),
|
||||
?RULE(keyword(entrypoint), {entrypoint, _1})]).
|
||||
@@ -312,7 +323,7 @@ map_key(Key, {ok, {_, Val}}) -> {map_key, Key, Val}.
|
||||
|
||||
elim(E, []) -> E;
|
||||
elim(E, [{proj, Ann, P} | Es]) -> elim({proj, Ann, E, P}, Es);
|
||||
elim(E, [{app, Ann, Args} | Es]) -> elim({app, Ann, E, Args}, Es);
|
||||
elim(E, [{app, _Ann, Args} | Es]) -> elim({app, aeso_syntax:get_ann(E), E, Args}, Es);
|
||||
elim(E, [{rec_upd, Ann, Flds} | Es]) -> elim(record_update(Ann, E, Flds), Es);
|
||||
elim(E, [{map_get, Ann, Key} | Es]) -> elim({map_get, Ann, E, Key}, Es);
|
||||
elim(E, [{map_get, Ann, Key, Val} | Es]) -> elim({map_get, Ann, E, Key, Val}, Es).
|
||||
@@ -406,7 +417,7 @@ token(Tag) ->
|
||||
id(Id) ->
|
||||
?LET_P({id, A, X} = Y, id(),
|
||||
if X == Id -> Y;
|
||||
true -> fail({A, "expected 'bytes'"})
|
||||
true -> fail({A, "expected '" ++ Id ++ "'"})
|
||||
end).
|
||||
|
||||
id_or_addr() ->
|
||||
|
||||
@@ -149,6 +149,7 @@ decl({contract, _, C, Ds}) ->
|
||||
block(follow(text("contract"), hsep(name(C), text("="))), decls(Ds));
|
||||
decl({namespace, _, C, Ds}) ->
|
||||
block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds));
|
||||
decl({pragma, _, Pragma}) -> pragma(Pragma);
|
||||
decl({type_decl, _, T, Vars}) -> typedecl(alias_t, T, Vars);
|
||||
decl({type_def, _, T, Vars, Def}) ->
|
||||
Kind = element(1, Def),
|
||||
@@ -170,6 +171,10 @@ decl(D = {letfun, Attrs, _, _, _, _}) ->
|
||||
hsep(lists:map(Mod, Attrs) ++ [letdecl(Fun, D)]);
|
||||
decl(D = {letval, _, _, _, _}) -> letdecl("let", D).
|
||||
|
||||
-spec pragma(aeso_syntax:pragma()) -> doc().
|
||||
pragma({compiler, Op, Ver}) ->
|
||||
text("@compiler " ++ atom_to_list(Op) ++ " " ++ string:join([integer_to_list(N) || N <- Ver], ".")).
|
||||
|
||||
-spec expr(aeso_syntax:expr(), options()) -> doc().
|
||||
expr(E, Options) ->
|
||||
with_options(Options, fun() -> expr(E) end).
|
||||
|
||||
+20
-9
@@ -13,14 +13,15 @@
|
||||
override/2, push/2, pop/1]).
|
||||
|
||||
lexer() ->
|
||||
Number = fun(Digit) -> [Digit, "+(_", Digit, "+)*"] end,
|
||||
DIGIT = "[0-9]",
|
||||
HEXDIGIT = "[0-9a-fA-F]",
|
||||
LOWER = "[a-z_]",
|
||||
UPPER = "[A-Z]",
|
||||
CON = [UPPER, "[a-zA-Z0-9_]*"],
|
||||
INT = [DIGIT, "+"],
|
||||
HEX = ["0x", HEXDIGIT, "+"],
|
||||
BYTES = ["#", HEXDIGIT, "+"],
|
||||
INT = Number(DIGIT),
|
||||
HEX = ["0x", Number(HEXDIGIT)],
|
||||
BYTES = ["#", Number(HEXDIGIT)],
|
||||
WS = "[\\000-\\ ]+",
|
||||
ID = [LOWER, "[a-zA-Z0-9_']*"],
|
||||
TVAR = ["'", ID],
|
||||
@@ -53,7 +54,7 @@ lexer() ->
|
||||
, {CHAR, token(char, fun parse_char/1)}
|
||||
, {STRING, token(string, fun parse_string/1)}
|
||||
, {HEX, token(hex, fun parse_hex/1)}
|
||||
, {INT, token(int, fun list_to_integer/1)}
|
||||
, {INT, token(int, fun parse_int/1)}
|
||||
, {BYTES, token(bytes, fun parse_bytes/1)}
|
||||
|
||||
%% Identifiers (qualified first!)
|
||||
@@ -95,9 +96,11 @@ parse_char([$', C, $']) -> C.
|
||||
|
||||
unescape(Str) -> unescape(Str, []).
|
||||
|
||||
%% TODO: numeric escapes
|
||||
unescape([$"], Acc) ->
|
||||
list_to_binary(lists:reverse(Acc));
|
||||
unescape([$\\, $x, D1, D2 | Chars ], Acc) ->
|
||||
C = list_to_integer([D1, D2], 16),
|
||||
unescape(Chars, [C | Acc]);
|
||||
unescape([$\\, Code | Chars], Acc) ->
|
||||
Ok = fun(C) -> unescape(Chars, [C | Acc]) end,
|
||||
case Code of
|
||||
@@ -115,10 +118,18 @@ unescape([$\\, Code | Chars], Acc) ->
|
||||
unescape([C | Chars], Acc) ->
|
||||
unescape(Chars, [C | Acc]).
|
||||
|
||||
parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16).
|
||||
strip_underscores(S) ->
|
||||
lists:filter(fun(C) -> C /= $_ end, S).
|
||||
|
||||
parse_bytes("#" ++ Chars) ->
|
||||
N = list_to_integer(Chars, 16),
|
||||
Digits = (length(Chars) + 1) div 2,
|
||||
parse_hex("0x" ++ S) ->
|
||||
list_to_integer(strip_underscores(S), 16).
|
||||
|
||||
parse_int(S) ->
|
||||
list_to_integer(strip_underscores(S)).
|
||||
|
||||
parse_bytes("#" ++ S0) ->
|
||||
S = strip_underscores(S0),
|
||||
N = list_to_integer(S, 16),
|
||||
Digits = (length(S) + 1) div 2,
|
||||
<<N:Digits/unit:8>>.
|
||||
|
||||
|
||||
+6
-1
@@ -13,7 +13,7 @@
|
||||
-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([bin_op/0, un_op/0]).
|
||||
-export_type([decl/0, letbind/0, typedef/0]).
|
||||
-export_type([decl/0, letbind/0, typedef/0, pragma/0]).
|
||||
-export_type([arg/0, field_t/0, constructor_t/0, named_arg_t/0]).
|
||||
-export_type([type/0, constant/0, expr/0, arg_expr/0, field/1, stmt/0, alt/0, lvalue/0, elim/0, pat/0]).
|
||||
-export_type([ast/0]).
|
||||
@@ -36,11 +36,16 @@
|
||||
|
||||
-type decl() :: {contract, ann(), con(), [decl()]}
|
||||
| {namespace, ann(), con(), [decl()]}
|
||||
| {pragma, ann(), pragma()}
|
||||
| {type_decl, ann(), id(), [tvar()]}
|
||||
| {type_def, ann(), id(), [tvar()], typedef()}
|
||||
| {fun_decl, ann(), id(), type()}
|
||||
| letbind().
|
||||
|
||||
-type compiler_version() :: [non_neg_integer()].
|
||||
|
||||
-type pragma() :: {compiler, '==' | '<' | '>' | '=<' | '>=', compiler_version()}.
|
||||
|
||||
-type letbind()
|
||||
:: {letval, ann(), id(), type(), expr()}
|
||||
| {letfun, ann(), id(), [arg()], type(), expr()}.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{application, aesophia,
|
||||
[{description, "Contract Language for aeternity"},
|
||||
{vsn, "4.0.0-rc3"},
|
||||
{vsn, "4.0.0"},
|
||||
{registered, []},
|
||||
{applications,
|
||||
[kernel,
|
||||
|
||||
@@ -152,7 +152,8 @@ compilable_contracts() ->
|
||||
"manual_stdlib_include",
|
||||
"list_comp",
|
||||
"payable",
|
||||
"unapplied_builtins"
|
||||
"unapplied_builtins",
|
||||
"underscore_number_literals"
|
||||
].
|
||||
|
||||
not_yet_compilable(fate) -> [];
|
||||
@@ -175,6 +176,8 @@ not_yet_compilable(aevm) -> [].
|
||||
-define(PARSE_ERROR(Name, Errs), ?ERROR("Parse", Name, Errs)).
|
||||
|
||||
failing_contracts() ->
|
||||
{ok, V} = aeso_compiler:numeric_version(),
|
||||
Version = list_to_binary(string:join([integer_to_list(N) || N <- V], ".")),
|
||||
%% Parse errors
|
||||
[ ?PARSE_ERROR(field_parse_error,
|
||||
[<<?Pos(5, 26)
|
||||
@@ -301,7 +304,14 @@ failing_contracts() ->
|
||||
<<?Pos(54, 5)
|
||||
"Let binding at line 54, column 5 must be followed by an expression">>,
|
||||
<<?Pos(58, 5)
|
||||
"Let binding at line 58, column 5 must be followed by an expression">>])
|
||||
"Let binding at line 58, column 5 must be followed by an expression">>,
|
||||
<<?Pos(63, 5)
|
||||
"Cannot unify int\n"
|
||||
" and bool\n"
|
||||
"when checking the type of the expression at line 63, column 5\n"
|
||||
" id(n) : int\n"
|
||||
"against the expected type\n"
|
||||
" bool">>])
|
||||
, ?TYPE_ERROR(init_type_error,
|
||||
[<<?Pos(7, 3)
|
||||
"Cannot unify string\n"
|
||||
@@ -348,19 +358,19 @@ failing_contracts() ->
|
||||
, ?TYPE_ERROR(bad_address_literals,
|
||||
[<<?Pos(32, 5)
|
||||
"The type bytes(32) is not a contract type\n"
|
||||
"when checking that the contract literal at line 32, column 5\n"
|
||||
"when checking that the contract literal\n"
|
||||
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
|
||||
"has the type\n"
|
||||
" bytes(32)">>,
|
||||
<<?Pos(30, 5)
|
||||
"The type oracle(int, bool) is not a contract type\n"
|
||||
"when checking that the contract literal at line 30, column 5\n"
|
||||
"when checking that the contract literal\n"
|
||||
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
|
||||
"has the type\n"
|
||||
" oracle(int, bool)">>,
|
||||
<<?Pos(28, 5)
|
||||
"The type address is not a contract type\n"
|
||||
"when checking that the contract literal at line 28, column 5\n"
|
||||
"when checking that the contract literal\n"
|
||||
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
|
||||
"has the type\n"
|
||||
" address">>,
|
||||
@@ -432,7 +442,13 @@ failing_contracts() ->
|
||||
"when checking the type of the expression at line 7, column 5\n"
|
||||
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
|
||||
"against the expected type\n"
|
||||
" bytes(32)">>])
|
||||
" bytes(32)">>,
|
||||
<<?Pos(34, 5),
|
||||
"The type address is not a contract type\n"
|
||||
"when checking that the call to\n"
|
||||
" Address.to_contract\n"
|
||||
"has the type\n"
|
||||
" address">>])
|
||||
, ?TYPE_ERROR(stateful,
|
||||
[<<?Pos(13, 35)
|
||||
"Cannot reference stateful function Chain.spend (at line 13, column 35)\nin the definition of non-stateful function fail1.">>,
|
||||
@@ -556,6 +572,21 @@ failing_contracts() ->
|
||||
"and result types\n"
|
||||
" - bytes(20) (at line 18, column 25)\n"
|
||||
" - 'a (at line 19, column 5)">>])
|
||||
, ?TYPE_ERROR(wrong_compiler_version,
|
||||
[<<?Pos(1, 1)
|
||||
"Cannot compile with this version of the compiler,\n"
|
||||
"because it does not satisfy the constraint ", Version/binary, " < 1.0">>,
|
||||
<<?Pos(2, 1)
|
||||
"Cannot compile with this version of the compiler,\n"
|
||||
"because it does not satisfy the constraint ", Version/binary, " == 9.9.9">>])
|
||||
, ?TYPE_ERROR(multiple_contracts,
|
||||
[<<?Pos(2, 3)
|
||||
"Only the main contract can contain defined functions or entrypoints.\n"
|
||||
"Fix: replace the definition of 'foo' by a type signature.">>])
|
||||
, ?TYPE_ERROR(contract_as_namespace,
|
||||
[<<?Pos(5, 28)
|
||||
"Invalid call to contract entrypoint 'Foo.foo'.\n"
|
||||
"It must be called as 'c.foo' for some c : Foo.">>])
|
||||
].
|
||||
|
||||
-define(Path(File), "code_errors/" ??File).
|
||||
|
||||
@@ -11,4 +11,6 @@ contract AddressLiterals =
|
||||
oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY
|
||||
entrypoint contr() : Remote =
|
||||
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ
|
||||
entrypoint contr_addr() : Remote =
|
||||
Address.to_contract(addr())
|
||||
|
||||
|
||||
@@ -30,4 +30,6 @@ contract AddressLiterals =
|
||||
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ
|
||||
entrypoint contr3() : bytes(32) =
|
||||
ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ
|
||||
entrypoint contr4() : address =
|
||||
Address.to_contract(Contract.address)
|
||||
|
||||
|
||||
@@ -15,3 +15,5 @@ contract BasicAuth =
|
||||
entrypoint to_sign(h : hash, n : int) =
|
||||
Crypto.blake2b((h, n))
|
||||
|
||||
entrypoint weird_string() : string =
|
||||
"\x19Weird String\x42\nMore\n"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
contract Foo =
|
||||
entrypoint foo : () => int
|
||||
|
||||
contract Fail =
|
||||
entrypoint bad() : int = Foo.foo()
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
contract ContractOne =
|
||||
entrypoint foo() = "foo"
|
||||
|
||||
contract ContractTwo =
|
||||
entrypoint bar() = "bar"
|
||||
@@ -57,3 +57,8 @@ contract Test =
|
||||
let f() = 0
|
||||
let g() = f()
|
||||
|
||||
function id(x : 'a) : 'a = x
|
||||
|
||||
entrypoint wrong_return(n : int) : bool =
|
||||
id(n)
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
contract UnderscoreNumberLiterals =
|
||||
|
||||
entrypoint ints() : list(int) =
|
||||
[ 1_999_000_000,
|
||||
19_99_00_00_00,
|
||||
0xfff_FFF_010 ]
|
||||
|
||||
entrypoint bytes() : list(bytes(4)) =
|
||||
[ #abcd_ef_00,
|
||||
#01_02_03_04,
|
||||
#aaaa_FFFF ]
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
@compiler < 1.0
|
||||
@compiler == 9.9.9
|
||||
@compiler >= 0.1
|
||||
@compiler =< 100.0.1
|
||||
|
||||
contract Fail =
|
||||
entrypoint foo() = ()
|
||||
Reference in New Issue
Block a user