From cfd036b19927d255e6d6e980fb8ee4cc125c470a Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 9 Jul 2019 09:30:01 +0200 Subject: [PATCH 01/35] Test deadcode elimination for FATE backend --- test/aeso_compiler_tests.erl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index f3e3b67..9a9f743 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -53,15 +53,17 @@ simple_compile_test_() -> #{byte_code := Code2} = compile(aevm, "include"), ?assertMatch(true, Code1 == Code2) end} ] ++ - [ {"Testing deadcode elimination", + [ {"Testing deadcode elimination for " ++ atom_to_list(Backend), fun() -> - #{ byte_code := NoDeadCode } = compile(aevm, "nodeadcode"), - #{ byte_code := DeadCode } = compile(aevm, "deadcode"), + #{ byte_code := NoDeadCode } = compile(Backend, "nodeadcode"), + #{ byte_code := DeadCode } = compile(Backend, "deadcode"), SizeNoDeadCode = byte_size(NoDeadCode), SizeDeadCode = byte_size(DeadCode), - ?assertMatch({_, _, true}, {SizeDeadCode, SizeNoDeadCode, SizeDeadCode + 40 < SizeNoDeadCode}), + Delta = if Backend == aevm -> 40; + Backend == fate -> 20 end, + ?assertMatch({_, _, true}, {SizeDeadCode, SizeNoDeadCode, SizeDeadCode + Delta < SizeNoDeadCode}), ok - end} ]. + end} || Backend <- [aevm, fate] ]. check_errors(Expect, ErrorString) -> %% This removes the final single \n as well. From cbc890995476a4d10623dc30147711663571dc7b Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Wed, 21 Aug 2019 11:41:19 +0200 Subject: [PATCH 02/35] Add default init function in fcode pass instead of in assembler --- src/aeso_ast_to_fcode.erl | 39 +++++++++++++++++++++++++++----------- src/aeso_fcode_to_fate.erl | 19 +------------------ 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index a916d4f..512e2ab 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -226,6 +226,9 @@ init_type_env() -> ["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}) }. +is_no_code(Env) -> + proplists:get_value(no_code, maps:get(options, Env, []), false). + %% -- Compilation ------------------------------------------------------------ -spec to_fcode(env(), aeso_syntax:ast()) -> fcode(). @@ -244,7 +247,7 @@ to_fcode(Env, [{contract, Attrs, {con, _, Main}, Decls}]) -> state_type => StateType, event_type => EventType, payable => Payable, - functions => add_init_function(Env1, + functions => add_init_function(Env1, StateType, add_event_function(Env1, EventType, Funs)) }; to_fcode(Env, [{contract, _, {con, _, Con}, Decls} | Code]) -> Env1 = decls_to_fcode(Env#{ context => {abstract_contract, Con} }, Decls), @@ -268,7 +271,7 @@ decls_to_fcode(Env, Decls) -> -spec decl_to_fcode(env(), aeso_syntax:decl()) -> env(). decl_to_fcode(Env, {type_decl, _, _, _}) -> Env; decl_to_fcode(Env = #{context := {main_contract, _}}, {fun_decl, Ann, {id, _, Name}, _}) -> - case proplists:get_value(no_code, maps:get(options, Env, []), false) of + case is_no_code(Env) of false -> fcode_error({missing_definition, Name, lists:keydelete(entrypoint, 1, Ann)}); true -> Env end; @@ -852,16 +855,30 @@ builtin_to_fcode(Builtin, Args) -> %% -- Init function -- -add_init_function(_Env, Funs) -> +add_init_function(Env, StateType, Funs0) -> + case is_no_code(Env) of + true -> Funs0; + false -> + Funs = add_default_init_function(Env, StateType, Funs0), + InitName = {entrypoint, <<"init">>}, + InitFun = #{ args := InitArgs } = maps:get(InitName, Funs, none), + Vars = [ {var, X} || {X, _} <- InitArgs ], + Funs#{ init => InitFun#{ return => {tuple, []}, + body => {builtin, set_state, [{def, InitName, Vars}]} } } + end. + +add_default_init_function(_Env, StateType, Funs) -> InitName = {entrypoint, <<"init">>}, - InitFun = #{ args := InitArgs } = - case maps:get(InitName, Funs, none) of - none -> #{ attrs => [], args => [], return => {tuple, []}, body => {tuple, []} }; - Info -> Info - end, - Vars = [ {var, X} || {X, _} <- InitArgs ], - Funs#{ init => InitFun#{ return => {tuple, []}, - body => {builtin, set_state, [{def, InitName, Vars}]} } }. + case maps:get(InitName, Funs, none) of + %% Only add default init function if state is unit. + none when StateType == {tuple, []} -> + Funs#{ InitName => #{attrs => [], + args => [], + return => {tuple, []}, + body => {tuple, []}} }; + none -> fcode_error(missing_init_function); + _ -> Funs + end. %% -- Event function -- diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index ef75237..2c44090 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -130,12 +130,10 @@ debug(Tag, Options, Fmt, Args) -> %% @doc Main entry point. compile(FCode, Options) -> #{ contract_name := ContractName, - state_type := StateType, functions := Functions } = FCode, SFuns = functions_to_scode(ContractName, Functions, Options), SFuns1 = optimize_scode(SFuns, Options), - SFuns2 = add_default_init_function(SFuns1, StateType), - FateCode = to_basic_blocks(SFuns2), + FateCode = to_basic_blocks(SFuns1), debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]), FateCode. @@ -196,21 +194,6 @@ type_to_scode({tvar, X}) -> J -> {tvar, J} end. -add_default_init_function(SFuns, StateType) when StateType /= {tuple, []} -> - %% Only add default if the type is unit. - SFuns; -add_default_init_function(SFuns, {tuple, []}) -> - %% Only add default if the init function is not present - InitName = make_function_name({entrypoint, <<"init">>}), - case maps:find(InitName, SFuns) of - {ok, _} -> - SFuns; - error -> - Sig = {[], {tuple, []}}, - Body = [tuple(0)], - SFuns#{ InitName => {[], Sig, Body} } - end. - %% -- Phase I ---------------------------------------------------------------- %% Icode to structured assembly From e94b5379eda4ab13a17e486e7800d8053d2a391f Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Wed, 21 Aug 2019 11:41:47 +0200 Subject: [PATCH 03/35] Deadcode elimination pass --- src/aeso_ast_to_fcode.erl | 60 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 512e2ab..0b83908 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -1007,13 +1007,16 @@ lambda_lift_exprs(As) -> [lambda_lift_expr(A) || A <- As]. -spec optimize_fcode(fcode()) -> fcode(). optimize_fcode(Code = #{ functions := Funs }) -> - Code#{ functions := maps:map(fun(Name, Def) -> optimize_fun(Code, Name, Def) end, Funs) }. + Code1 = Code#{ functions := maps:map(fun(Name, Def) -> optimize_fun(Code, Name, Def) end, Funs) }, + eliminate_dead_code(Code1). -spec optimize_fun(fcode(), fun_name(), fun_def()) -> fun_def(). optimize_fun(Fcode, Fun, Def = #{ body := Body }) -> %% io:format("Optimizing ~p =\n~s\n", [_Fun, prettypr:format(pp_fexpr(_Body))]), Def#{ body := inliner(Fcode, Fun, Body) }. +%% --- Inlining --- + -spec inliner(fcode(), fun_name(), fexpr()) -> fexpr(). inliner(Fcode, Fun, {def, Fun1, Args} = E) when Fun1 /= Fun -> case should_inline(Fcode, Fun1) of @@ -1026,6 +1029,33 @@ should_inline(_Fcode, _Fun1) -> false == list_to_atom("true"). %% Dialyzer inline(_Fcode, Fun, Args) -> {def, Fun, Args}. %% TODO +%% --- Deadcode elimination --- + +-spec eliminate_dead_code(fcode()) -> fcode(). +eliminate_dead_code(Code = #{ functions := Funs }) -> + UsedFuns = used_functions(Funs), + Code#{ functions := maps:filter(fun(Name, _) -> maps:is_key(Name, UsedFuns) end, + Funs) }. + +-spec used_functions(#{ fun_name() => fun_def() }) -> #{ fun_name() => true }. +used_functions(Funs) -> + Exported = [ Fun || {Fun, #{ attrs := Attrs }} <- maps:to_list(Funs), + not lists:member(private, Attrs) ], + used_functions(#{}, Exported, Funs). + +used_functions(Used, [], _) -> Used; +used_functions(Used, [Name | Rest], Defs) -> + case maps:is_key(Name, Used) of + true -> used_functions(Used, Rest, Defs); + false -> + New = + case maps:get(Name, Defs, undef) of + undef -> []; %% We might be compiling a stub + #{ body := Body } -> used_defs(Body) + end, + used_functions(Used#{ Name => true }, New ++ Rest, Defs) + end. + %% -- Helper functions ------------------------------------------------------- %% -- Types -- @@ -1195,6 +1225,34 @@ free_vars(Expr) -> {'case', P, A} -> free_vars(A) -- lists:sort(fsplit_pat_vars(P)) end. +used_defs(Xs) when is_list(Xs) -> + lists:umerge([ used_defs(X) || X <- Xs ]); +used_defs(Expr) -> + case Expr of + {var, _} -> []; + {lit, _} -> []; + nil -> []; + {def, F, As} -> lists:umerge([F], used_defs(As)); + {def_u, F, _} -> [F]; + {remote, _, _, Ct, _, As} -> used_defs([Ct | As]); + {remote_u, _, _, Ct, _} -> used_defs(Ct); + {builtin, _, As} -> used_defs(As); + {builtin_u, _, _} -> []; + {con, _, _, As} -> used_defs(As); + {tuple, As} -> used_defs(As); + {proj, A, _} -> used_defs(A); + {set_proj, A, _, B} -> used_defs([A, B]); + {op, _, As} -> used_defs(As); + {'let', _, A, B} -> used_defs([A, B]); + {funcall, A, Bs} -> used_defs([A | Bs]); + {lam, _, B} -> used_defs(B); + {closure, F, A} -> lists:umerge([F], used_defs(A)); + {switch, A} -> used_defs(A); + {split, _, _, As} -> used_defs(As); + {nosplit, A} -> used_defs(A); + {'case', _, A} -> used_defs(A) + end. + get_named_args(NamedArgsT, Args) -> IsNamed = fun({named_arg, _, _, _}) -> true; (_) -> false end, From bde76c8580c56097a24af20b07954a9f5f044d7e Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Thu, 22 Aug 2019 13:30:00 +0200 Subject: [PATCH 04/35] Preparing 4.0.0-rc1 --- CHANGELOG.md | 34 +++++++++++++++++++++++++++++----- rebar.config | 2 +- src/aesophia.app.src | 2 +- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e8044c..02e3a35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,15 +6,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added -- New builtin function `Crypto.ecrecover_secp256k1: (hash, bytes(65)) => bytes(32)` - for recovering Ethereum address from message hash and signature. +### Changed +### Removed +## [4.0.0-rc1] - 2019-08-22 +### Added +- FATE backend - the compiler is able to produce VM code for both `AEVM` and `FATE`. Many + of the APIs now take `{backend, aevm | fate}` to decide wich backend to produce artifacts + for. +- New builtin functions `Crypto.ecrecover_secp256k1: (hash, bytes(65)) => option(bytes(20))` + and `Crypto.ecverify_secp256k1 : (hash, bytes(20), bytes(65)) => bool` for recovering + and verifying an Ethereum address for a message hash and a signature. +- Sophia supports list comprehensions known from languages like Python, Haskell or Erlang. + Example syntax: +``` +[x + y | x <- [1,2,3,4,5], let k = x*x, if (k > 5), y <- [k, k+1, k+2]] +// yields [12,13,14,20,21,22,30,31,32] +``` +- A new contract, and endpoint, modifier `payable` is introduced. Contracts, and enpoints, + that shall be able to receive funds should be marked as payable. `Address.is_payable(a)` + can be used to check if an (contract) address is payable or not. ### Changed - New syntax for tuple types. Now 0-tuple type is encoded as `unit` instead of `()` and - regular tuples are encoded by interspersing inner types with `*`, for instance `int * string`. + regular tuples are encoded by interspersing inner types with `*`, for instance `int * string`. Parens are not necessary. Note it only affects the types, values remain as their were before, so `(1, "a") : int * string` - +- The `AENS.transfer` and `AENS.revoke` functions have been updated to take a name `string` + instead of a name `hash`. +- Fixed a bug where the `AEVM` backend complained about a missing `init` function when + trying to generate calldata from an ACI-generated interface. +- Compiler now returns the ABI-version in the compiler result map. +- Renamed `Crypto.ecverify` and `Crypto.ecverify_secp256k1` into `Crypto.verify_sig` and + `Crypto.verify_sig_secp256k1` respectively. ### Removed ## [3.2.0] - 2019-06-28 @@ -111,7 +134,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 pass a (stubbed) contract string. -[Unreleased]: https://github.com/aeternity/aesophia/compare/v3.2.0...HEAD +[Unreleased]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc1...HEAD +[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 [3.1.0]: https://github.com/aeternity/aesophia/compare/v3.0.0...v3.1.0 [3.0.0]: https://github.com/aeternity/aesophia/compare/v2.1.0...v3.0.0 diff --git a/rebar.config b/rebar.config index 106dc43..7d60a8e 100644 --- a/rebar.config +++ b/rebar.config @@ -15,7 +15,7 @@ {base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]} ]}. -{relx, [{release, {aesophia, "3.2.0"}, +{relx, [{release, {aesophia, "4.0.0-rc1"}, [aesophia, aebytecode, getopt]}, {dev_mode, true}, diff --git a/src/aesophia.app.src b/src/aesophia.app.src index f5606a7..7ac1266 100644 --- a/src/aesophia.app.src +++ b/src/aesophia.app.src @@ -1,6 +1,6 @@ {application, aesophia, [{description, "Contract Language for aeternity"}, - {vsn, "3.2.0"}, + {vsn, "4.0.0-rc1"}, {registered, []}, {applications, [kernel, From a942561907c0d29d382bbd066d67e1c718451baa Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Thu, 22 Aug 2019 14:49:48 +0200 Subject: [PATCH 05/35] Improved optimizations of FATE code --- src/aeso_fcode_to_fate.erl | 77 +++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 2c44090..0c1d7f1 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -936,11 +936,20 @@ simpl_s({switch, Arg, Type, Alts, Def}, Options) -> {switch, Arg, Type, [simplify(A, Options) || A <- Alts], simplify(Def, Options)}; simpl_s(I, _) -> I. -simpl_top(I, Code, Options) -> - apply_rules(rules(), I, Code, Options). +%% Safe-guard against loops in the rewriting. Shouldn't happen so throw an +%% error if we run out. +-define(SIMPL_FUEL, 5000). -apply_rules(Rules, I, Code, Options) -> - Cons = fun(X, Xs) -> simpl_top(X, Xs, Options) end, +simpl_top(I, Code, Options) -> + simpl_top(?SIMPL_FUEL, I, Code, Options). + +simpl_top(0, I, Code, _Options) -> + error({out_of_fuel, I, Code}); +simpl_top(Fuel, I, Code, Options) -> + apply_rules(Fuel, rules(), I, Code, Options). + +apply_rules(Fuel, Rules, I, Code, Options) -> + Cons = fun(X, Xs) -> simpl_top(Fuel - 1, X, Xs, Options) end, case apply_rules_once(Rules, I, Code) of false -> [I | Code]; {RName, New, Rest} -> @@ -968,6 +977,7 @@ merge_rules() -> rules() -> merge_rules() ++ [?RULE(r_swap_push), + ?RULE(r_swap_pop), ?RULE(r_swap_write), ?RULE(r_constant_propagation), ?RULE(r_prune_impossible_branches), @@ -1005,11 +1015,12 @@ inline_push(Ann1, Arg, Stack, [{i, Ann2, I} = AI | Code], Acc) -> {As0, As1} = split_stack_arg(Stack, As), Acc1 = [{i, merge_ann(Ann1, Ann2), from_op_view(Op, R, As0 ++ [Arg] ++ As1)} | Acc], {lists:reverse(Acc1), Code}; - false -> + false when Arg /= R -> {AI1, {i, Ann1b, _}} = swap_instrs({i, Ann1, {'STORE', ?a, Arg}}, AI), - inline_push(Ann1b, Arg, Stack + Produces - Consumes, Code, [AI1 | Acc]) + inline_push(Ann1b, Arg, Stack + Produces - Consumes, Code, [AI1 | Acc]); + false -> false end; - false -> false + _ -> false end; inline_push(_, _, _, _, _) -> false. @@ -1021,16 +1032,42 @@ split_stack_arg(N, [A | As], Acc) -> true -> N end, split_stack_arg(N1, As, [A | Acc]). -%% Move PUSH A past non-stack instructions. -r_swap_push(Push = {i, _, {'STORE', ?a, _}}, [I | Code]) -> - case independent(Push, I) of - true -> - {I1, Push1} = swap_instrs(Push, I), - {[I1, Push1], Code}; - false -> false +%% Move PUSHes past non-stack instructions. +r_swap_push(Push = {i, _, PushI}, [I | Code]) -> + case op_view(PushI) of + {_, ?a, _} -> + case independent(Push, I) of + true -> + {I1, Push1} = swap_instrs(Push, I), + {[I1, Push1], Code}; + false -> false + end; + _ -> false end; r_swap_push(_, _) -> false. +%% Move non-stack instruction past POPs. +r_swap_pop(IA = {i, _, I}, [JA = {i, _, J} | Code]) -> + case independent(IA, JA) of + true -> + case {op_view(I), op_view(J)} of + {false, _} -> false; + {_, false} -> false; + {{_, IR, IAs}, {_, RJ, JAs}} -> + NonStackI = not lists:member(?a, [IR | IAs]), + %% RJ /= ?a to not conflict with r_swap_push + PopJ = RJ /= ?a andalso lists:member(?a, JAs), + case NonStackI andalso PopJ of + false -> false; + true -> + {JA1, IA1} = swap_instrs(IA, JA), + {[JA1, IA1], Code} + end + end; + false -> false + end; +r_swap_pop(_, _) -> false. + %% Match up writes to variables with instructions further down. r_swap_write(I = {i, _, _}, [J | Code]) -> case {var_writes(I), independent(I, J)} of @@ -1166,6 +1203,8 @@ r_float_switch_body(I = {i, _, _}, [switch_body | Code]) -> r_float_switch_body(_, _) -> false. %% Inline stores +r_inline_store({i, _, {'STORE', R, R}}, Code) -> + {[], Code}; r_inline_store(I = {i, _, {'STORE', R = {var, _}, A}}, Code) -> %% Not when A is var unless updating the annotations properly. Inline = case A of @@ -1258,9 +1297,13 @@ unannotate(Code) when is_list(Code) -> unannotate({i, _Ann, I}) -> [I]. %% Desugar and specialize -desugar({'ADD', ?a, ?i(1), ?a}) -> [aeb_fate_ops:inc()]; -desugar({'SUB', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:dec()]; -desugar({'STORE', ?a, A}) -> [aeb_fate_ops:push(A)]; +desugar({'ADD', ?a, ?i(1), ?a}) -> [aeb_fate_ops:inc()]; +desugar({'ADD', A, ?i(1), A}) -> [aeb_fate_ops:inc(A)]; +desugar({'ADD', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:inc()]; +desugar({'ADD', A, A, ?i(1)}) -> [aeb_fate_ops:inc(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({switch, Arg, Type, Alts, Def}) -> [{switch, Arg, Type, [desugar(A) || A <- Alts], desugar(Def)}]; desugar(missing) -> missing; From 20064b72fa5db0aecbfec8d8b05aea4f946ac726 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Thu, 22 Aug 2019 14:50:15 +0200 Subject: [PATCH 06/35] Compile tail-calls to current function to jumps --- src/aeso_fcode_to_fate.erl | 39 +++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 0c1d7f1..4b79fa0 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -114,7 +114,7 @@ Op =:= 'CREATOR' orelse false)). --record(env, { contract, vars = [], locals = [], tailpos = true }). +-record(env, { contract, vars = [], locals = [], current_function, tailpos = true }). %% -- Debugging -------------------------------------------------------------- @@ -154,10 +154,10 @@ functions_to_scode(ContractName, Functions, Options) -> attrs := Attrs, return := Type}} <- maps:to_list(Functions)]). -function_to_scode(ContractName, Functions, _Name, Attrs0, Args, Body, ResType, _Options) -> +function_to_scode(ContractName, Functions, Name, Attrs0, Args, Body, ResType, _Options) -> {ArgTypes, ResType1} = typesig_to_scode(Args, ResType), Attrs = Attrs0 -- [stateful], %% Only track private and payable from here. - SCode = to_scode(init_env(ContractName, Functions, Args), Body), + SCode = to_scode(init_env(ContractName, Functions, Name, Args), Body), {Attrs, {ArgTypes, ResType1}, SCode}. -define(tvars, '$tvars'). @@ -199,10 +199,11 @@ type_to_scode({tvar, X}) -> %% -- Environment functions -- -init_env(ContractName, FunNames, Args) -> +init_env(ContractName, FunNames, Name, Args) -> #env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ], contract = ContractName, locals = FunNames, + current_function = Name, tailpos = true }. next_var(#env{ vars = Vars }) -> @@ -305,6 +306,24 @@ to_scode(Env, {'let', X, Expr, Body}) -> aeb_fate_ops:store({var, I}, {stack, 0}), to_scode(Env1, Body) ]; +to_scode(Env = #env{ current_function = Fun, tailpos = true }, {def, Fun, Args}) -> + %% Tail-call to current function, f(e0..en). Compile to + %% [ let xi = ei ] + %% [ STORE argi xi ] + %% jump 0 + {Vars, Code, _Env} = + lists:foldl(fun(Arg, {Is, Acc, Env1}) -> + {I, Env2} = bind_local("_", Env1), + ArgCode = to_scode(notail(Env2), Arg), + Acc1 = [Acc, ArgCode, + aeb_fate_ops:store({var, I}, ?a)], + {[I | Is], Acc1, Env2} + end, {[], [], Env}, Args), + [ Code, + [ aeb_fate_ops:store({arg, I}, {var, J}) + || {I, J} <- lists:zip(lists:seq(0, length(Vars) - 1), + lists:reverse(Vars)) ], + loop ]; to_scode(Env, {def, Fun, Args}) -> FName = make_function_id(Fun), Lbl = aeb_fate_data:make_string(FName), @@ -662,12 +681,15 @@ 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, aeb_fate_pp:format_op(I, #{})], + Op = [Ind, pp_op(I)], Ann = [[" % ", Fmt(In), " -> ", Fmt(Out)] || In ++ Out /= []], [io_lib:format("~-40s~s\n", [Op, Ann]), pp_ann(Ind, Code)]; pp_ann(_, []) -> []. +pp_op(loop) -> "LOOP"; +pp_op(I) -> + aeb_fate_pp:format_op(I, #{}). pp_arg(?i(I)) -> io_lib:format("~w", [I]); pp_arg({arg, N}) -> io_lib:format("arg~p", [N]); @@ -734,6 +756,7 @@ attributes(I) -> Pure = fun(W, R) -> Attr(W, R, true) end, Impure = fun(W, R) -> Attr(W, R, false) end, case I of + loop -> Impure(pc, []); 'RETURN' -> Impure(pc, []); {'RETURNR', A} -> Impure(pc, A); {'CALL', _} -> Impure(?a, []); @@ -1503,7 +1526,8 @@ split_calls(Ref, [], Acc, Blocks) -> split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL'; element(1, I) == 'CALL_R'; element(1, I) == 'CALL_GR'; - element(1, I) == 'jumpif' -> + element(1, I) == 'jumpif'; + I == loop -> split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]); split_calls(Ref, [{'ABORT', _} = I | _Code], Acc, Blocks) -> lists:reverse([{Ref, lists:reverse([I | Acc])} | Blocks]); @@ -1516,7 +1540,8 @@ split_calls(Ref, [I | Code], Acc, Blocks) -> set_labels(Labels, {Ref, Code}) when is_reference(Ref) -> {maps:get(Ref, Labels), [ set_labels(Labels, I) || I <- Code ]}; -set_labels(Labels, {jump, Ref}) -> aeb_fate_ops:jump(maps:get(Ref, Labels)); +set_labels(_Labels, loop) -> aeb_fate_ops:jump(0); +set_labels(Labels, {jump, Ref}) -> aeb_fate_ops:jump(maps:get(Ref, Labels)); set_labels(Labels, {jumpif, Arg, Ref}) -> aeb_fate_ops:jumpif(Arg, maps:get(Ref, Labels)); set_labels(Labels, {switch, Arg, Refs}) -> case [ maps:get(Ref, Labels) || Ref <- Refs ] of From 2620aa64b4a060af5f5682a2c84394c17c78144b Mon Sep 17 00:00:00 2001 From: Hans Svensson Date: Thu, 22 Aug 2019 15:21:41 +0200 Subject: [PATCH 07/35] Add some no_implicit_stdlib for now --- src/aeso_compiler.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index fff9870..dadfbc8 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -317,7 +317,7 @@ to_sophia_value(_, _, revert, Data, Options) -> {ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}} end; to_sophia_value(ContractString, FunName, ok, Data, Options0) -> - Options = [no_code | Options0], + Options = [no_implicit_stdlib, no_code | Options0], try Code = string_to_code(ContractString, Options), #{ typed_ast := TypedAst, type_env := TypeEnv} = Code, @@ -378,7 +378,7 @@ create_calldata(Code, Fun, Args) -> {ok, binary()} | {error, term()}. create_calldata(Code, Fun, Args, Options0) -> - Options = [no_code | Options0], + Options = [no_implicit_stdlib, no_code | Options0], case proplists:get_value(backend, Options, aevm) of aevm -> case check_call(Code, Fun, Args, Options) of @@ -401,7 +401,7 @@ decode_calldata(ContractString, FunName, Calldata) -> decode_calldata(ContractString, FunName, Calldata, [{backend, aevm}]). decode_calldata(ContractString, FunName, Calldata, Options0) -> - Options = [no_code | Options0], + Options = [no_implicit_stdlib, no_code | Options0], try Code = string_to_code(ContractString, Options), #{ typed_ast := TypedAst, type_env := TypeEnv} = Code, From 07cf1627032933e566ea6029877c07e5c7f5be1f Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Fri, 23 Aug 2019 10:07:43 +0200 Subject: [PATCH 08/35] Fix performance problem in FATE optimiser caused by debug printing --- src/aeso_fcode_to_fate.erl | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 4b79fa0..eadd9dd 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -118,9 +118,12 @@ %% -- Debugging -------------------------------------------------------------- -debug(Tag, Options, Fmt, Args) -> +is_debug(Tag, Options) -> Tags = proplists:get_value(debug, Options, []), - case Tags == all orelse lists:member(Tag, Tags) of + Tags == all orelse lists:member(Tag, Tags). + +debug(Tag, Options, Fmt, Args) -> + case is_debug(Tag, Options) of true -> io:format(Fmt, Args); false -> ok end. @@ -976,7 +979,12 @@ apply_rules(Fuel, Rules, I, Code, Options) -> case apply_rules_once(Rules, I, Code) of false -> [I | Code]; {RName, New, Rest} -> - debug(opt_rules, Options, " Applied ~p:\n~s ==>\n~s\n", [RName, pp_ann(" ", [I | Code]), pp_ann(" ", New ++ Rest)]), + case is_debug(opt_rules, Options) of + true -> + {OldCode, NewCode} = drop_common_suffix([I | Code], New ++ Rest), + debug(opt_rules, Options, " Applied ~p:\n~s ==>\n~s\n", [RName, pp_ann(" ", OldCode), pp_ann(" ", NewCode)]); + false -> ok + end, lists:foldr(Cons, Rest, New) end. @@ -1556,3 +1564,11 @@ set_labels(_, I) -> I. with_ixs(Xs) -> lists:zip(lists:seq(0, length(Xs) - 1), Xs). +drop_common_suffix(Xs, Ys) -> + drop_common_suffix_r(lists:reverse(Xs), lists:reverse(Ys)). + +drop_common_suffix_r([X | Xs], [X | Ys]) -> + drop_common_suffix_r(Xs, Ys); +drop_common_suffix_r(Xs, Ys) -> + {lists:reverse(Xs), lists:reverse(Ys)}. + From 02af75aa348dde8711bb56682d146a8d386e3a45 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 27 Aug 2019 10:52:38 +0200 Subject: [PATCH 09/35] Move stdlib code to priv dir and don't do any implicit includes --- priv/stdlib/Func.aes | 46 ++++ priv/stdlib/List.aes | 209 +++++++++++++++++ priv/stdlib/ListInternal.aes | 9 + priv/stdlib/Option.aes | 76 +++++++ priv/stdlib/Pair.aes | 20 ++ priv/stdlib/Triple.aes | 37 +++ src/aeso_compiler.erl | 20 +- src/aeso_parser.erl | 17 +- src/aeso_stdlib.erl | 423 +---------------------------------- 9 files changed, 410 insertions(+), 447 deletions(-) create mode 100644 priv/stdlib/Func.aes create mode 100644 priv/stdlib/List.aes create mode 100644 priv/stdlib/ListInternal.aes create mode 100644 priv/stdlib/Option.aes create mode 100644 priv/stdlib/Pair.aes create mode 100644 priv/stdlib/Triple.aes diff --git a/priv/stdlib/Func.aes b/priv/stdlib/Func.aes new file mode 100644 index 0000000..7d633a2 --- /dev/null +++ b/priv/stdlib/Func.aes @@ -0,0 +1,46 @@ +namespace Func = + + function id(x : 'a) : 'a = x + + function const(x : 'a) : 'b => 'a = (y) => x + + function flip(f : ('a, 'b) => 'c) : ('b, 'a) => 'c = (b, a) => f(a, b) + + function comp(f : 'b => 'c, g : 'a => 'b) : 'a => 'c = (x) => f(g(x)) + + function pipe(f : 'a => 'b, g : 'b => 'c) : 'a => 'c = (x) => g(f(x)) + + function rapply(x : 'a, f : 'a => 'b) : 'b = f(x) + + /* The Z combinator - replacement for local and anonymous recursion. + */ + function recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res = + (x) => f(recur(f), x) + + function iter(n : int, f : 'a => 'a) : 'a => 'a = iter_(n, f, (x) => x) + private function iter_(n : int, f : 'a => 'a, acc : 'a => 'a) : 'a => 'a = + if(n == 0) acc + elif(n == 1) comp(f, acc) + else iter_(n / 2, comp(f, f), if(n mod 2 == 0) acc else comp(f, acc)) + + function curry2(f : ('a, 'b) => 'c) : 'a => ('b => 'c) = + (x) => (y) => f(x, y) + function curry3(f : ('a, 'b, 'c) => 'd) : 'a => ('b => ('c => 'd)) = + (x) => (y) => (z) => f(x, y, z) + + function uncurry2(f : 'a => ('b => 'c)) : ('a, 'b) => 'c = + (x, y) => f(x)(y) + function uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd = + (x, y, z) => f(x)(y)(z) + + function tuplify2(f : ('a, 'b) => 'c) : (('a * 'b)) => 'c = + (t) => switch(t) + (x, y) => f(x, y) + function tuplify3(f : ('a, 'b, 'c) => 'd) : 'a * 'b * 'c => 'd = + (t) => switch(t) + (x, y, z) => f(x, y, z) + + function untuplify2(f : 'a * 'b => 'c) : ('a, 'b) => 'c = + (x, y) => f((x, y)) + function untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd = + (x, y, z) => f((x, y, z)) diff --git a/priv/stdlib/List.aes b/priv/stdlib/List.aes new file mode 100644 index 0000000..3c7a643 --- /dev/null +++ b/priv/stdlib/List.aes @@ -0,0 +1,209 @@ +include "ListInternal.aes" + +namespace List = + + function is_empty(l : list('a)) : bool = switch(l) + [] => true + _ => false + + function first(l : list('a)) : option('a) = switch(l) + [] => None + h::_ => Some(h) + + function tail(l : list('a)) : option(list('a)) = switch(l) + [] => None + _::t => Some(t) + + function last(l : list('a)) : option('a) = switch(l) + [] => None + [x] => Some(x) + _::t => last(t) + + function find(p : 'a => bool, l : list('a)) : option('a) = switch(l) + [] => None + h::t => if(p(h)) Some(h) else find(p, t) + + function find_all(p : 'a => bool, l : list('a)) : list('a) = find_all_(p, l, []) + private function find_all_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) + [] => reverse(acc) + h::t => find_all_(p, t, if(p(h)) h::acc else acc) + + function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, []) + private function find_indices_( p : 'a => bool + , l : list('a) + , n : int + , acc : list(int) + ) : list(int) = switch(l) + [] => reverse(acc) + h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc) + + function nth(n : int, l : list('a)) : option('a) = switch(l) + [] => None + h::t => if(n == 0) Some(h) else nth(n-1, t) + + /* Unsafe version of `nth` */ + function get(n : int, l : list('a)) : 'a = switch(l) + [] => abort("Out of index get") + h::t => if(n == 0) h else get(n-1, t) + + + function length(l : list('a)) : int = length_(l, 0) + private function length_(l : list('a), acc : int) : int = switch(l) + [] => acc + _::t => length_(t, acc + 1) + + + /* Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow */ + function replace_at(n : int, e : 'a, l : list('a)) : list('a) = + if(n<0) abort("insert_at underflow") else replace_at_(n, e, l, []) + private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = + switch(l) + [] => abort("replace_at overflow") + h::t => if (n == 0) reverse(e::acc) ++ t + else replace_at_(n-1, e, t, h::acc) + + /* Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow */ + function insert_at(n : int, e : 'a, l : list('a)) : list('a) = + if(n<0) abort("insert_at underflow") else insert_at_(n, e, l, []) + private function insert_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = + if (n == 0) reverse(e::acc) ++ l + else switch(l) + [] => abort("insert_at overflow") + h::t => insert_at_(n-1, e, t, h::acc) + + function insert_by(f : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = + switch(l) + [] => [x] + (e :: l') => + if(f(x, e)) + e :: insert_by(f, x, l') + else + x :: l + + function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l) + [] => nil + h::t => cons(h, foldr(cons, nil, t)) + + function foldl(rcons : ('b, 'a) => 'b, acc : 'b, l : list('a)) : 'b = switch(l) + [] => acc + h::t => foldl(rcons, rcons(acc, h), t) + + function foreach(f : 'a => unit, l : list('a)) : unit = + switch(l) + [] => () + e :: l' => + f(e) + foreach(f, l') + + function reverse(l : list('a)) : list('a) = foldl((lst, el) => el :: lst, [], l) + + function map(f : 'a => 'b, l : list('a)) : list('b) = map_(f, l, []) + private function map_(f : 'a => 'b, l : list('a), acc : list('b)) : list('b) = switch(l) + [] => reverse(acc) + h::t => map_(f, t, f(h)::acc) + + function flat_map(f : 'a => list('b), l : list('a)) : list('b) = flat_map_(f, l, []) + private function flat_map_(f : 'a => list('b), l : list('a), acc : list('b)) : list('b) = switch(l) + [] => reverse(acc) + h::t => flat_map_(f, t, reverse(f(h)) ++ acc) + + function filter(p : 'a => bool, l : list('a)) : list('a) = filter_(p, l, []) + private function filter_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) + [] => reverse(acc) + h::t => filter_(p, t, if(p(h)) h::acc else acc) + + /* Take `n` first elements */ + function take(n : int, l : list('a)) : list('a) = + if(n < 0) abort("Take negative number of elements") else take_(n, l, []) + private function take_(n : int, l : list('a), acc : list('a)) : list('a) = + if(n == 0) reverse(acc) + else switch(l) + [] => reverse(acc) + h::t => take_(n-1, t, h::acc) + + /* Drop `n` first elements */ + function drop(n : int, l : list('a)) : list('a) = + if(n < 0) abort("Drop negative number of elements") + elif (n == 0) l + else switch(l) + [] => [] + h::t => drop(n-1, t) + + /* Get the longest prefix of a list in which every element matches predicate `p` */ + function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, l, []) + private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) + [] => reverse(acc) + h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc) + + /* Drop elements from `l` until `p` holds */ + function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l) + [] => [] + h::t => if(p(h)) drop_while(p, t) else l + + /* Splits list into two lists of elements that respectively match and don't match predicate `p` */ + function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], []) + private function partition_( p : 'a => bool + , l : list('a) + , acc_t : list('a) + , acc_f : list('a) + ) : (list('a) * list('a)) = switch(l) + [] => (reverse(acc_t), reverse(acc_f)) + h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f) + + + function concats(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll) + + function all(p : 'a => bool, l : list('a)) : bool = switch(l) + [] => true + h::t => if(p(h)) all(p, t) else false + + function any(p : 'a => bool, l : list('a)) : bool = switch(l) + [] => false + h::t => if(p(h)) true else any(p, t) + + function sum(l : list(int)) : int = foldl ((a, b) => a + b, 0, l) + + function product(l : list(int)) : int = foldl((a, b) => a * b, 1, l) + + + /* Zips two list by applying bimapping function on respective elements. Drops longer tail. */ + function zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) = zip_with_(f, l1, l2, []) + private function zip_with_( f : ('a, 'b) => 'c + , l1 : list('a) + , l2 : list('b) + , acc : list('c) + ) : list('c) = switch ((l1, l2)) + (h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc) + _ => reverse(acc) + + /* Zips two lists into list of pairs. Drops longer tail. */ + function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2) + + function unzip(l : list('a * 'b)) : list('a) * list('b) = unzip_(l, [], []) + private function unzip_( l : list('a * 'b) + , acc_l : list('a) + , acc_r : list('b) + ) : (list('a) * list('b)) = switch(l) + [] => (reverse(acc_l), reverse(acc_r)) + (left, right)::t => unzip_(t, left::acc_l, right::acc_r) + + + // TODO: Improve? + function sort(lesser_cmp : ('a, 'a) => bool, l : list('a)) : list('a) = switch(l) + [] => [] + h::t => switch (partition((x) => lesser_cmp(x, h), t)) + (lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger) + + + function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, []) + private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l) + [] => reverse(acc) + [e] => reverse(e::acc) + h::t => intersperse_(delim, t, delim::h::acc) + + + function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0, []) + private function enumerate_(l : list('a), n : int, acc : list(int * 'a)) : list(int * 'a) = switch(l) + [] => reverse(acc) + h::t => enumerate_(t, n + 1, (n, h)::acc) + diff --git a/priv/stdlib/ListInternal.aes b/priv/stdlib/ListInternal.aes new file mode 100644 index 0000000..b30a9ce --- /dev/null +++ b/priv/stdlib/ListInternal.aes @@ -0,0 +1,9 @@ +namespace ListInternal = + + private function reverse_(xs : list('a), ys : list('a)) : list('a) = + switch(xs) + [] => ys + x :: xs => reverse_(xs, x :: ys) + + function reverse(xs : list('a)) = reverse_(xs, []) + diff --git a/priv/stdlib/Option.aes b/priv/stdlib/Option.aes new file mode 100644 index 0000000..843e62d --- /dev/null +++ b/priv/stdlib/Option.aes @@ -0,0 +1,76 @@ +include "List.aes" + +namespace Option = + + function is_none(o : option('a)) : bool = switch(o) + None => true + Some(_) => false + + function is_some(o : option('a)) : bool = switch(o) + None => false + Some(_) => true + + + function match(n : 'b, s : 'a => 'b, o : option('a)) : 'b = switch(o) + None => n + Some(x) => s(x) + + function default(def : 'a, o : option('a)) : 'a = match(def, (x) => x, o) + + function force(o : option('a)) : 'a = default(abort("Forced None value"), o) + + function on_elem(f : 'a => unit, o : option('a)) : unit = match((), f, o) + + function map(f : 'a => 'b, o : option('a)) : option('b) = switch(o) + None => None + Some(x) => Some(f(x)) + + function map2(f : ('a, 'b) => 'c + , o1 : option('a) + , o2 : option('b) + ) : option('c) = switch((o1, o2)) + (Some(x1), Some(x2)) => Some(f(x1, x2)) + _ => None + + function map3( f : ('a, 'b, 'c) => 'd + , o1 : option('a) + , o2 : option('b) + , o3 : option('c) + ) : option('d) = switch((o1, o2, o3)) + (Some(x1), Some(x2), Some(x3)) => Some(f(x1, x2, x3)) + _ => None + + function app_over(f : option ('a => 'b), o : option('a)) : option('b) = switch((f, o)) + (Some(ff), Some(xx)) => Some(ff(xx)) + _ => None + + function flat_map(f : 'a => option('b), o : option('a)) : option('b) = switch(o) + None => None + Some(x) => f(x) + + + function to_list(o : option('a)) : list('a) = switch(o) + None => [] + Some(x) => [x] + + function filter_options(l : list(option('a))) : list('a) = filter_options_(l, []) + private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l) + [] => List.reverse(acc) + None::t => filter_options_(t, acc) + Some(x)::t => filter_options_(t, x::acc) + + function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, []) + private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l) + [] => Some(List.reverse(acc)) + None::t => None + Some(x)::t => seq_options_(t, x::acc) + + + function choose(o1 : option('a), o2 : option('a)) : option('a) = + if(is_some(o1)) o1 else o2 + + function choose_first(l : list(option('a))) : option('a) = switch(l) + [] => None + None::t => choose_first(t) + Some(x)::_ => Some(x) + diff --git a/priv/stdlib/Pair.aes b/priv/stdlib/Pair.aes new file mode 100644 index 0000000..22312e3 --- /dev/null +++ b/priv/stdlib/Pair.aes @@ -0,0 +1,20 @@ +namespace Pair = + + function fst(t : ('a * 'b)) : 'a = switch(t) + (x, _) => x + + function snd(t : ('a * 'b)) : 'b = switch(t) + (_, y) => y + + function map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) = switch(t) + (x, y) => (f(x), y) + + function map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) = switch(t) + (x, y) => (x, f(y)) + + function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) = switch(t) + (x, y) => (f(x), g(y)) + + function swap(t : ('a * 'b)) : ('b * 'a) = switch(t) + (x, y) => (y, x) + diff --git a/priv/stdlib/Triple.aes b/priv/stdlib/Triple.aes new file mode 100644 index 0000000..84f3ddd --- /dev/null +++ b/priv/stdlib/Triple.aes @@ -0,0 +1,37 @@ +namespace Triple = + + function fst(t : ('a * 'b * 'c)) : 'a = switch(t) + (x, _, _) => x + + function snd(t : ('a * 'b * 'c)) : 'b = switch(t) + (_, y, _) => y + + function thd(t : ('a * 'b * 'c)) : 'c = switch(t) + (_, _, z) => z + + + function map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) = switch(t) + (x, y, z) => (f(x), y, z) + + function map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) = switch(t) + (x, y, z) => (x, f(y), z) + + function map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) = switch(t) + (x, y, z) => (x, y, f(z)) + + function trimap( f : 'a => 'x + , g : 'b => 'y + , h : 'c => 'z + , t : ('a * 'b * 'c) + ) : ('x * 'y * 'z) = switch(t) + (x, y, z) => (f(x), g(y), h(z)) + + function swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) = switch(t) + (x, y, z) => (z, y, x) + + function rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) = switch(t) + (x, y, z) => (z, x, y) + + function rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) = switch(t) + (x, y, z) => (y, z, x) + diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index dadfbc8..0a0fe39 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -142,16 +142,7 @@ from_string1(fate, ContractString, Options) -> -spec string_to_code(string(), options()) -> map(). string_to_code(ContractString, Options) -> - Ast = case lists:member(no_implicit_stdlib, Options) of - true -> parse(ContractString, Options); - false -> - IncludedSTD = sets:from_list( - [aeso_parser:hash_include(F, C) - || {F, C} <- aeso_stdlib:stdlib_list()]), - InitAst = parse(ContractString, IncludedSTD, Options), - STD = parse_stdlib(), - STD ++ InitAst - end, + Ast = parse(ContractString, Options), pp_sophia_code(Ast, Options), pp_ast(Ast, Options), {TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env]), @@ -579,15 +570,6 @@ pp(Code, Options, Option, PPFun) -> %% ------------------------------------------------------------------- --spec parse_stdlib() -> none() | aeso_syntax:ast(). -parse_stdlib() -> - lists:foldr( - fun ({Lib, LibCode}, Acc) -> - parse(LibCode, [{src_file, binary_to_list(Lib)}]) ++ Acc - end, - [], - aeso_stdlib:stdlib_list()). - sophia_type_to_typerep(String) -> {ok, Ast} = aeso_parser:type(String), try aeso_ast_to_icode:ast_typerep(Ast) of diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index c53712e..191369c 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -549,12 +549,13 @@ bad_expr_err(Reason, E) -> prettypr:nest(2, aeso_pretty:expr(E))])). %% -- Helper functions ------------------------------------------------------- + expand_includes(AST, Included, Opts) -> expand_includes(AST, Included, [], Opts). expand_includes([], _Included, Acc, _Opts) -> {ok, lists:reverse(Acc)}; -expand_includes([{include, Ann, {string, SAnn, File}} | AST], Included, Acc, Opts) -> +expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) -> case get_include_code(File, Ann, Opts) of {ok, Code} -> Hashed = hash_include(File, Code), @@ -564,10 +565,7 @@ expand_includes([{include, Ann, {string, SAnn, File}} | AST], Included, Acc, Opt Included1 = sets:add_element(Hashed, Included), case string(Code, Included1, Opts1) of {ok, AST1} -> - Dependencies = [ {include, Ann, {string, SAnn, Dep}} - || Dep <- aeso_stdlib:dependencies(File) - ], - expand_includes(Dependencies ++ AST1 ++ AST, Included1, Acc, Opts); + expand_includes(AST1 ++ AST, Included1, Acc, Opts); Err = {error, _} -> Err end; @@ -593,12 +591,15 @@ read_file(File, Opts) -> end end. +stdlib_options() -> + [{include, {file_system, [aeso_stdlib:stdlib_include_path()]}}]. + get_include_code(File, Ann, Opts) -> - case {read_file(File, Opts), maps:find(File, aeso_stdlib:stdlib())} of + case {read_file(File, Opts), read_file(File, stdlib_options())} of {{ok, _}, {ok,_ }} -> return_error(ann_pos(Ann), "Illegal redefinition of standard library " ++ File); - {_, {ok, Lib}} -> - {ok, Lib}; + {_, {ok, Bin}} -> + {ok, binary_to_list(Bin)}; {{ok, Bin}, _} -> {ok, binary_to_list(Bin)}; {_, _} -> diff --git a/src/aeso_stdlib.erl b/src/aeso_stdlib.erl index 463697b..8ac47ae 100644 --- a/src/aeso_stdlib.erl +++ b/src/aeso_stdlib.erl @@ -10,425 +10,8 @@ -module(aeso_stdlib). --export([stdlib/0, stdlib_list/0, dependencies/1]). +-export([stdlib_include_path/0]). -stdlib() -> - maps:from_list(stdlib_list()). +stdlib_include_path() -> + filename:join([code:priv_dir(aesophia), "stdlib"]). -stdlib_list() -> - [ {<<"List.aes">>, std_list()} - , {<<"Func.aes">>, std_func()} - , {<<"Option.aes">>, std_option()} - , {<<"Pair.aes">>, std_pair()} - , {<<"Triple.aes">>, std_triple()} - ]. - -dependencies(Q) -> - case Q of - <<"Option.aes">> -> - [<<"List.aes">>]; - _ -> [] - end. - -std_func() -> -" -namespace Func = - - function id(x : 'a) : 'a = x - - function const(x : 'a) : 'b => 'a = (y) => x - - function flip(f : ('a, 'b) => 'c) : ('b, 'a) => 'c = (b, a) => f(a, b) - - function comp(f : 'b => 'c, g : 'a => 'b) : 'a => 'c = (x) => f(g(x)) - - function pipe(f : 'a => 'b, g : 'b => 'c) : 'a => 'c = (x) => g(f(x)) - - function rapply(x : 'a, f : 'a => 'b) : 'b = f(x) - - /* The Z combinator - replacement for local and anonymous recursion. - */ - function recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res = - (x) => f(recur(f), x) - - function iter(n : int, f : 'a => 'a) : 'a => 'a = iter_(n, f, (x) => x) - private function iter_(n : int, f : 'a => 'a, acc : 'a => 'a) : 'a => 'a = - if(n == 0) acc - elif(n == 1) comp(f, acc) - else iter_(n / 2, comp(f, f), if(n mod 2 == 0) acc else comp(f, acc)) - - function curry2(f : ('a, 'b) => 'c) : 'a => ('b => 'c) = - (x) => (y) => f(x, y) - function curry3(f : ('a, 'b, 'c) => 'd) : 'a => ('b => ('c => 'd)) = - (x) => (y) => (z) => f(x, y, z) - - function uncurry2(f : 'a => ('b => 'c)) : ('a, 'b) => 'c = - (x, y) => f(x)(y) - function uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd = - (x, y, z) => f(x)(y)(z) - - function tuplify2(f : ('a, 'b) => 'c) : (('a * 'b)) => 'c = - (t) => switch(t) - (x, y) => f(x, y) - function tuplify3(f : ('a, 'b, 'c) => 'd) : 'a * 'b * 'c => 'd = - (t) => switch(t) - (x, y, z) => f(x, y, z) - - function untuplify2(f : 'a * 'b => 'c) : ('a, 'b) => 'c = - (x, y) => f((x, y)) - function untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd = - (x, y, z) => f((x, y, z)) -". - -std_list() ->" -namespace List = - - function is_empty(l : list('a)) : bool = switch(l) - [] => true - _ => false - - function first(l : list('a)) : option('a) = switch(l) - [] => None - h::_ => Some(h) - - function tail(l : list('a)) : option(list('a)) = switch(l) - [] => None - _::t => Some(t) - - function last(l : list('a)) : option('a) = switch(l) - [] => None - [x] => Some(x) - _::t => last(t) - - function find(p : 'a => bool, l : list('a)) : option('a) = switch(l) - [] => None - h::t => if(p(h)) Some(h) else find(p, t) - - function find_all(p : 'a => bool, l : list('a)) : list('a) = find_all_(p, l, []) - private function find_all_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) - h::t => find_all_(p, t, if(p(h)) h::acc else acc) - - function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, []) - private function find_indices_( p : 'a => bool - , l : list('a) - , n : int - , acc : list(int) - ) : list(int) = switch(l) - [] => reverse(acc) - h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc) - - function nth(n : int, l : list('a)) : option('a) = switch(l) - [] => None - h::t => if(n == 0) Some(h) else nth(n-1, t) - - /* Unsafe version of `nth` */ - function get(n : int, l : list('a)) : 'a = switch(l) - [] => abort(\"Out of index get\") - h::t => if(n == 0) h else get(n-1, t) - - - function length(l : list('a)) : int = length_(l, 0) - private function length_(l : list('a), acc : int) : int = switch(l) - [] => acc - _::t => length_(t, acc + 1) - - - /* Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow */ - function replace_at(n : int, e : 'a, l : list('a)) : list('a) = - if(n<0) abort(\"insert_at underflow\") else replace_at_(n, e, l, []) - private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = - switch(l) - [] => abort(\"replace_at overflow\") - h::t => if (n == 0) reverse(e::acc) ++ t - else replace_at_(n-1, e, t, h::acc) - - /* Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow */ - function insert_at(n : int, e : 'a, l : list('a)) : list('a) = - if(n<0) abort(\"insert_at underflow\") else insert_at_(n, e, l, []) - private function insert_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) = - if (n == 0) reverse(e::acc) ++ l - else switch(l) - [] => abort(\"insert_at overflow\") - h::t => insert_at_(n-1, e, t, h::acc) - - function insert_by(f : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = - switch(l) - [] => [x] - (e :: l') => - if(f(x, e)) - e :: insert_by(f, x, l') - else - x :: l - - function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l) - [] => nil - h::t => cons(h, foldr(cons, nil, t)) - - function foldl(rcons : ('b, 'a) => 'b, acc : 'b, l : list('a)) : 'b = switch(l) - [] => acc - h::t => foldl(rcons, rcons(acc, h), t) - - function foreach(f : 'a => unit, l : list('a)) : unit = - switch(l) - [] => () - e :: l' => - f(e) - foreach(f, l') - - - function reverse(l : list('a)) : list('a) = foldl((lst, el) => el :: lst, [], l) - - - function map(f : 'a => 'b, l : list('a)) : list('b) = map_(f, l, []) - private function map_(f : 'a => 'b, l : list('a), acc : list('b)) : list('b) = switch(l) - [] => reverse(acc) - h::t => map_(f, t, f(h)::acc) - - function flat_map(f : 'a => list('b), l : list('a)) : list('b) = flat_map_(f, l, []) - private function flat_map_(f : 'a => list('b), l : list('a), acc : list('b)) : list('b) = switch(l) - [] => reverse(acc) - h::t => flat_map_(f, t, reverse(f(h)) ++ acc) - - function filter(p : 'a => bool, l : list('a)) : list('a) = filter_(p, l, []) - private function filter_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) - h::t => filter_(p, t, if(p(h)) h::acc else acc) - - /* Take `n` first elements */ - function take(n : int, l : list('a)) : list('a) = - if(n < 0) abort(\"Take negative number of elements\") else take_(n, l, []) - private function take_(n : int, l : list('a), acc : list('a)) : list('a) = - if(n == 0) reverse(acc) - else switch(l) - [] => reverse(acc) - h::t => take_(n-1, t, h::acc) - - /* Drop `n` first elements */ - function drop(n : int, l : list('a)) : list('a) = - if(n < 0) abort(\"Drop negative number of elements\") - elif (n == 0) l - else switch(l) - [] => [] - h::t => drop(n-1, t) - - /* Get the longest prefix of a list in which every element matches predicate `p` */ - function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, l, []) - private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) - h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc) - - /* Drop elements from `l` until `p` holds */ - function drop_while(p : 'a => bool, l : list('a)) : list('a) = switch(l) - [] => [] - h::t => if(p(h)) drop_while(p, t) else l - - /* Splits list into two lists of elements that respectively match and don't match predicate `p` */ - function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], []) - private function partition_( p : 'a => bool - , l : list('a) - , acc_t : list('a) - , acc_f : list('a) - ) : (list('a) * list('a)) = switch(l) - [] => (reverse(acc_t), reverse(acc_f)) - h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f) - - - function concats(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll) - - function all(p : 'a => bool, l : list('a)) : bool = switch(l) - [] => true - h::t => if(p(h)) all(p, t) else false - - function any(p : 'a => bool, l : list('a)) : bool = switch(l) - [] => false - h::t => if(p(h)) true else any(p, t) - - function sum(l : list(int)) : int = foldl ((a, b) => a + b, 0, l) - - function product(l : list(int)) : int = foldl((a, b) => a * b, 1, l) - - - /* Zips two list by applying bimapping function on respective elements. Drops longer tail. */ - function zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) = zip_with_(f, l1, l2, []) - private function zip_with_( f : ('a, 'b) => 'c - , l1 : list('a) - , l2 : list('b) - , acc : list('c) - ) : list('c) = switch ((l1, l2)) - (h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc) - _ => reverse(acc) - - /* Zips two lists into list of pairs. Drops longer tail. */ - function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2) - - function unzip(l : list('a * 'b)) : list('a) * list('b) = unzip_(l, [], []) - private function unzip_( l : list('a * 'b) - , acc_l : list('a) - , acc_r : list('b) - ) : (list('a) * list('b)) = switch(l) - [] => (reverse(acc_l), reverse(acc_r)) - (left, right)::t => unzip_(t, left::acc_l, right::acc_r) - - - // TODO: Improve? - function sort(lesser_cmp : ('a, 'a) => bool, l : list('a)) : list('a) = switch(l) - [] => [] - h::t => switch (partition((x) => lesser_cmp(x, h), t)) - (lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger) - - - function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, []) - private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) - [e] => reverse(e::acc) - h::t => intersperse_(delim, t, delim::h::acc) - - - function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0, []) - private function enumerate_(l : list('a), n : int, acc : list(int * 'a)) : list(int * 'a) = switch(l) - [] => reverse(acc) - h::t => enumerate_(t, n + 1, (n, h)::acc) - -". - -std_option() -> " -namespace Option = - - function is_none(o : option('a)) : bool = switch(o) - None => true - Some(_) => false - - function is_some(o : option('a)) : bool = switch(o) - None => false - Some(_) => true - - - function match(n : 'b, s : 'a => 'b, o : option('a)) : 'b = switch(o) - None => n - Some(x) => s(x) - - function default(def : 'a, o : option('a)) : 'a = match(def, (x) => x, o) - - function force(o : option('a)) : 'a = default(abort(\"Forced None value\"), o) - - function on_elem(f : 'a => unit, o : option('a)) : unit = match((), f, o) - - - function map(f : 'a => 'b, o : option('a)) : option('b) = switch(o) - None => None - Some(x) => Some(f(x)) - - function map2(f : ('a, 'b) => 'c - , o1 : option('a) - , o2 : option('b) - ) : option('c) = switch((o1, o2)) - (Some(x1), Some(x2)) => Some(f(x1, x2)) - _ => None - - function map3( f : ('a, 'b, 'c) => 'd - , o1 : option('a) - , o2 : option('b) - , o3 : option('c) - ) : option('d) = switch((o1, o2, o3)) - (Some(x1), Some(x2), Some(x3)) => Some(f(x1, x2, x3)) - _ => None - - function app_over(f : option ('a => 'b), o : option('a)) : option('b) = switch((f, o)) - (Some(ff), Some(xx)) => Some(ff(xx)) - _ => None - - function flat_map(f : 'a => option('b), o : option('a)) : option('b) = switch(o) - None => None - Some(x) => f(x) - - - function to_list(o : option('a)) : list('a) = switch(o) - None => [] - Some(x) => [x] - - function filter_options(l : list(option('a))) : list('a) = filter_options_(l, []) - private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l) - [] => List.reverse(acc) - None::t => filter_options_(t, acc) - Some(x)::t => filter_options_(t, x::acc) - - function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, []) - private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l) - [] => Some(List.reverse(acc)) - None::t => None - Some(x)::t => seq_options_(t, x::acc) - - - function choose(o1 : option('a), o2 : option('a)) : option('a) = - if(is_some(o1)) o1 else o2 - - function choose_first(l : list(option('a))) : option('a) = switch(l) - [] => None - None::t => choose_first(t) - Some(x)::_ => Some(x) -". - -std_pair() -> " -namespace Pair = - - function fst(t : ('a * 'b)) : 'a = switch(t) - (x, _) => x - - function snd(t : ('a * 'b)) : 'b = switch(t) - (_, y) => y - - - function map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b) = switch(t) - (x, y) => (f(x), y) - - function map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c) = switch(t) - (x, y) => (x, f(y)) - - function bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd) = switch(t) - (x, y) => (f(x), g(y)) - - - function swap(t : ('a * 'b)) : ('b * 'a) = switch(t) - (x, y) => (y, x) -". - -std_triple() -> " -namespace Triple = - - function fst(t : ('a * 'b * 'c)) : 'a = switch(t) - (x, _, _) => x - - function snd(t : ('a * 'b * 'c)) : 'b = switch(t) - (_, y, _) => y - - function thd(t : ('a * 'b * 'c)) : 'c = switch(t) - (_, _, z) => z - - - function map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c) = switch(t) - (x, y, z) => (f(x), y, z) - - function map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c) = switch(t) - (x, y, z) => (x, f(y), z) - - function map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm) = switch(t) - (x, y, z) => (x, y, f(z)) - - function trimap( f : 'a => 'x - , g : 'b => 'y - , h : 'c => 'z - , t : ('a * 'b * 'c) - ) : ('x * 'y * 'z) = switch(t) - (x, y, z) => (f(x), g(y), h(z)) - - - function swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a) = switch(t) - (x, y, z) => (z, y, x) - - function rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b) = switch(t) - (x, y, z) => (z, x, y) - - function rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a) = switch(t) - (x, y, z) => (y, z, x) -". From e7d3a5b9f2303e1e41102ee91696fa59f686aaff Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 27 Aug 2019 11:33:43 +0200 Subject: [PATCH 10/35] Put flat_map in ListInternal.aes --- priv/stdlib/List.aes | 6 ++---- priv/stdlib/ListInternal.aes | 10 +++++----- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/priv/stdlib/List.aes b/priv/stdlib/List.aes index 3c7a643..cdd2464 100644 --- a/priv/stdlib/List.aes +++ b/priv/stdlib/List.aes @@ -102,10 +102,8 @@ namespace List = [] => reverse(acc) h::t => map_(f, t, f(h)::acc) - function flat_map(f : 'a => list('b), l : list('a)) : list('b) = flat_map_(f, l, []) - private function flat_map_(f : 'a => list('b), l : list('a), acc : list('b)) : list('b) = switch(l) - [] => reverse(acc) - h::t => flat_map_(f, t, reverse(f(h)) ++ acc) + function flat_map(f : 'a => list('b), l : list('a)) : list('b) = + ListInternal.flat_map(f, l) function filter(p : 'a => bool, l : list('a)) : list('a) = filter_(p, l, []) private function filter_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) diff --git a/priv/stdlib/ListInternal.aes b/priv/stdlib/ListInternal.aes index b30a9ce..efd61ac 100644 --- a/priv/stdlib/ListInternal.aes +++ b/priv/stdlib/ListInternal.aes @@ -1,9 +1,9 @@ namespace ListInternal = - private function reverse_(xs : list('a), ys : list('a)) : list('a) = + // -- Flatmap ---------------------------------------------------------------- + + function flat_map(f : 'a => list('b), xs : list('a)) : list('b) = switch(xs) - [] => ys - x :: xs => reverse_(xs, x :: ys) - - function reverse(xs : list('a)) = reverse_(xs, []) + [] => [] + x :: xs => f(x) ++ flat_map(f, xs) From d23208c191b4c08d82c68ddc4d8304b9e97b3c28 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 27 Aug 2019 13:55:45 +0200 Subject: [PATCH 11/35] Fix bugs in dependency analysis --- src/aeso_syntax_utils.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/aeso_syntax_utils.erl b/src/aeso_syntax_utils.erl index f2d2fac..5aa43e9 100644 --- a/src/aeso_syntax_utils.erl +++ b/src/aeso_syntax_utils.erl @@ -49,7 +49,7 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) -> {type_def, _, I, _, D} -> Plus(BindType(I), Decl(D)); {fun_decl, _, _, T} -> Type(T); {letval, _, F, T, E} -> Sum([BindExpr(F), Type(T), Expr(E)]); - {letfun, _, F, Xs, T, E} -> Sum([BindExpr(F), Type(T), Scoped(BindExpr(Xs), Expr(E))]); + {letfun, _, F, Xs, T, E} -> Sum([BindExpr(F), Type(T), Expr(Xs ++ [E])]); %% typedef() {alias_t, T} -> Type(T); {record_t, Fs} -> Type(Fs); @@ -74,7 +74,7 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) -> {list_comp, _, Y, []} -> Expr(Y); {list_comp, A, Y, [{comprehension_bind, I, E}|R]} -> Plus(Expr(E), Scoped(BindExpr(I), Expr({list_comp, A, Y, R}))); - {list_comp, A, Y, [{comprehension_if, E}|R]} -> + {list_comp, A, Y, [{comprehension_if, _, E}|R]} -> Plus(Expr(E), Expr({list_comp, A, Y, R})); {list_comp, A, Y, [D = {letval, _, F, _, _} | R]} -> Plus(Decl(D), Scoped(BindExpr(F), Expr({list_comp, A, Y, R}))); @@ -92,7 +92,7 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) -> {field, _, LV, E} -> Expr([LV, E]); {field, _, LV, _, E} -> Expr([LV, E]); %% arg() - {arg, _, X, T} -> Plus(Expr(X), Type(T)); + {arg, _, Y, T} -> Plus(BindExpr(Y), Type(T)); %% alt() {'case', _, P, E} -> Scoped(BindExpr(P), Expr(E)); %% elim() @@ -124,12 +124,12 @@ used_types([Top] = _CurrentNS, T) -> entity_alg() -> IsBound = fun({K, _}) -> lists:member(K, [bound_term, bound_type]) end, Unbind = fun(bound_term) -> term; (bound_type) -> type end, - Remove = fun(Keys, Map) -> lists:foldl(fun maps:remove/2, Map, Keys) end, + Remove = fun(Keys, Map) -> maps:without(Keys, Map) end, Scoped = fun(Xs, Ys) -> - Bound = [E || E <- maps:keys(Ys), IsBound(E)], - Others = Remove(Bound, Ys), + Bound = [E || E <- maps:keys(Xs), IsBound(E)], Bound1 = [ {Unbind(Tag), X} || {Tag, X} <- Bound ], - maps:merge(Remove(Bound1, Xs), Others) + Others = Remove(Bound1, Ys), + maps:merge(Remove(Bound, Xs), Others) end, #alg{ zero = #{} , plus = fun maps:merge/2 From 79a928e53087a243fdc400404db68a69b9fda251 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 27 Aug 2019 13:56:02 +0200 Subject: [PATCH 12/35] Fix bad type specs --- src/aeso_syntax.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index 403dfaa..af5abba 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -105,9 +105,9 @@ | id() | qid() | con() | qcon() | constant(). --type comprehension_exp() :: [{ comprehension_bind, ann(), id(), expr()} - | {comprehension_if, expr()} - | letbind()]. +-type comprehension_exp() :: [ {comprehension_bind, id(), expr()} + | {comprehension_if, ann(), expr()} + | letbind() ]. -type arg_expr() :: expr() | {named_arg, ann(), id(), expr()}. From 5f733e01ddb28d298bbf33bc56093aecb5a82b59 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 27 Aug 2019 13:56:39 +0200 Subject: [PATCH 13/35] Implement [a..b] --- CHANGELOG.md | 2 ++ priv/stdlib/ListInternal.aes | 7 +++++++ src/aeso_ast_infer_types.erl | 3 +++ src/aeso_ast_to_fcode.erl | 6 +++++- src/aeso_ast_to_icode.erl | 6 +++++- 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02e3a35..e693f8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Added the `[a..b]` language construct, returning the list of numbers between + `a` and `b` (inclusive). Returns the empty list if `a` > `b`. ### Changed ### Removed diff --git a/priv/stdlib/ListInternal.aes b/priv/stdlib/ListInternal.aes index efd61ac..7d6773a 100644 --- a/priv/stdlib/ListInternal.aes +++ b/priv/stdlib/ListInternal.aes @@ -7,3 +7,10 @@ namespace ListInternal = [] => [] x :: xs => f(x) ++ flat_map(f, xs) + // -- From..to --------------------------------------------------------------- + + function from_to(a : int, b : int) : list(int) = from_to_(a, b, []) + + private function from_to_(a, b, acc) = + if (a > b) acc else from_to_(a, b - 1, b :: acc) + diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 6993768..482bd16 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -1352,6 +1352,9 @@ infer_infix({RelOp, As}) T = fresh_uvar(As), %% allow any type here, check in ast_to_icode that we have comparison for it Bool = {id, As, "bool"}, {fun_t, As, [], [T, T], Bool}; +infer_infix({'..', As}) -> + Int = {id, As, "int"}, + {fun_t, As, [], [Int, Int], {app_t, As, {id, As, "list"}, [Int]}}; infer_infix({'::', As}) -> ElemType = fresh_uvar(As), ListType = {app_t, As, {id, As, "list"}, [ElemType]}, diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 0b83908..16fe99e 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -453,12 +453,16 @@ expr_to_fcode(Env, _Type, {list, _, Es}) -> lists:foldr(fun(E, L) -> {op, '::', [expr_to_fcode(Env, E), L]} end, nil, Es); +expr_to_fcode(Env, _Type, {app, _, {'..', _}, [A, B]}) -> + {def_u, FromTo, _} = resolve_fun(Env, ["ListInternal", "from_to"]), + {def, FromTo, [expr_to_fcode(Env, A), expr_to_fcode(Env, B)]}; + expr_to_fcode(Env, _Type, {list_comp, _, Yield, []}) -> {op, '::', [expr_to_fcode(Env, Yield), nil]}; expr_to_fcode(Env, _Type, {list_comp, As, Yield, [{comprehension_bind, {typed, {id, _, Arg}, _}, BindExpr}|Rest]}) -> Env1 = bind_var(Env, Arg), Bind = {lam, [Arg], expr_to_fcode(Env1, {list_comp, As, Yield, Rest})}, - {def_u, FlatMap, _} = resolve_fun(Env, ["List", "flat_map"]), + {def_u, FlatMap, _} = resolve_fun(Env, ["ListInternal", "flat_map"]), {def, FlatMap, [Bind, expr_to_fcode(Env, BindExpr)]}; expr_to_fcode(Env, Type, {list_comp, As, Yield, [{comprehension_if, _, Cond}|Rest]}) -> make_if(expr_to_fcode(Env, Cond), diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index 7b0c839..ca1433a 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -523,6 +523,10 @@ ast_body({app, _, {typed, _, {con, _, Name}, _}, Args}, Icode) -> ast_body({app, _, {typed, _, {qcon, _, Name}, _}, Args}, Icode) -> Tag = aeso_icode:get_constructor_tag(Name, Icode), #tuple{cpts = [#integer{value = Tag} | [ ast_body(Arg, Icode) || Arg <- Args ]]}; +ast_body({app, _, {'..', _}, [A, B]}, Icode) -> + #funcall + { function = #var_ref{ name = ["ListInternal", "from_to"] } + , args = [ast_body(A, Icode), ast_body(B, Icode)] }; ast_body({app,As,Fun,Args}, Icode) -> case aeso_syntax:get_ann(format, As) of infix -> @@ -541,7 +545,7 @@ ast_body({list_comp, _, Yield, []}, Icode) -> #list{elems = [ast_body(Yield, Icode)]}; ast_body({list_comp, As, Yield, [{comprehension_bind, {typed, Arg, ArgType}, BindExpr}|Rest]}, Icode) -> #funcall - { function = #var_ref{ name = ["List", "flat_map"] } + { function = #var_ref{ name = ["ListInternal", "flat_map"] } , args = [ #lambda{ args=[#arg{name = ast_id(Arg), type = ast_type(ArgType, Icode)}] , body = ast_body({list_comp, As, Yield, Rest}, Icode) From 7705138ab2156a98f26d1cb163782ad19ce0a192 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 27 Aug 2019 13:59:36 +0200 Subject: [PATCH 14/35] auto-import ListInternal when using list comprehensions or [a..b] --- src/aeso_parser.erl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index 191369c..f0f2078 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -551,7 +551,10 @@ bad_expr_err(Reason, E) -> %% -- Helper functions ------------------------------------------------------- expand_includes(AST, Included, Opts) -> - expand_includes(AST, Included, [], Opts). + Ann = [{origin, system}], + AST1 = [ {include, Ann, {string, Ann, File}} + || File <- lists:usort(auto_imports(AST)) ] ++ AST, + expand_includes(AST1, Included, [], Opts). expand_includes([], _Included, Acc, _Opts) -> {ok, lists:reverse(Acc)}; @@ -611,3 +614,11 @@ hash_include(File, Code) when is_binary(File) -> hash_include(binary_to_list(File), Code); hash_include(File, Code) when is_list(File) -> {filename:basename(File), crypto:hash(sha256, Code)}. + +auto_imports({comprehension_bind, _, _}) -> [<<"ListInternal.aes">>]; +auto_imports({'..', _}) -> [<<"ListInternal.aes">>]; +auto_imports(L) when is_list(L) -> + lists:flatmap(fun auto_imports/1, L); +auto_imports(T) when is_tuple(T) -> + auto_imports(tuple_to_list(T)); +auto_imports(_) -> []. From 0efbcf302cb43005668325b86a7e4a2b30177eee Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 27 Aug 2019 14:00:02 +0200 Subject: [PATCH 15/35] Fix roundtrip test to ignore ListInternal --- test/aeso_parser_tests.erl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/aeso_parser_tests.erl b/test/aeso_parser_tests.erl index 9b54d94..4c75854 100644 --- a/test/aeso_parser_tests.erl +++ b/test/aeso_parser_tests.erl @@ -86,12 +86,16 @@ parse_expr(Text) -> round_trip(Text) -> Contract = parse_string(Text, [no_implicit_stdlib]), - Text1 = prettypr:format(aeso_pretty:decls(Contract)), + Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))), Contract1 = parse_string(Text1, [no_implicit_stdlib]), NoSrcLoc = remove_line_numbers(Contract), NoSrcLoc1 = remove_line_numbers(Contract1), ?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)). +strip_stdlib([{namespace, _, {con, _, "ListInternal"}, _} | Decls]) -> + strip_stdlib(Decls); +strip_stdlib(Decls) -> Decls. + remove_line_numbers({line, _L}) -> {line, 0}; remove_line_numbers({col, _C}) -> {col, 0}; remove_line_numbers([H|T]) -> From 3f1c23ace3b0b70a75f493f43eea0edceb8ad90f Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 27 Aug 2019 14:00:23 +0200 Subject: [PATCH 16/35] Use .. in list comprehension test --- test/contracts/list_comp.aes | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/contracts/list_comp.aes b/test/contracts/list_comp.aes index 266f2e8..db98bb5 100644 --- a/test/contracts/list_comp.aes +++ b/test/contracts/list_comp.aes @@ -10,11 +10,11 @@ contract ListComp = entrypoint l2_true() = [5,6,6,7,7,8] entrypoint l3() = [x ++ y | x <- [[":)"] | x <- [1,2]] - , y <- [[":("]]] + , y <- [[":("]]] entrypoint l3_true() = [[":)", ":("], [":)", ":("]] entrypoint l4() = [(a, b, c) | let is_pit(a, b, c) = a*a + b*b == c*c - , let base = [1,2,3,4,5,6,7,8,9,10] + , let base = [1..10] , a <- base , b <- base, if (b >= a) , c <- base, if (c >= b) From 850221aaf375faa3a09ccb1b12b026e49bd46dcb Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 27 Aug 2019 14:05:22 +0200 Subject: [PATCH 17/35] Remove no_implicit_stdlib option --- src/aeso_compiler.erl | 7 +++---- test/aeso_abi_tests.erl | 14 +++++++------- test/aeso_aci_tests.erl | 5 ++--- test/aeso_calldata_tests.erl | 12 ++++-------- test/aeso_compiler_tests.erl | 12 +----------- test/aeso_parser_tests.erl | 8 ++++---- test/contracts/manual_stdlib_include.aes | 3 +-- test/contracts/stdlib_include.aes | 3 +++ 8 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index 0a0fe39..eeba90c 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -36,7 +36,6 @@ | pp_assembler | pp_bytecode | no_code - | no_implicit_stdlib | {backend, aevm | fate} | {include, {file_system, [string()]} | {explicit_files, #{string() => binary()}}} @@ -308,7 +307,7 @@ to_sophia_value(_, _, revert, Data, Options) -> {ok, {app, [], {id, [], "abort"}, [{string, [], Err}]}} end; to_sophia_value(ContractString, FunName, ok, Data, Options0) -> - Options = [no_implicit_stdlib, no_code | Options0], + Options = [no_code | Options0], try Code = string_to_code(ContractString, Options), #{ typed_ast := TypedAst, type_env := TypeEnv} = Code, @@ -369,7 +368,7 @@ create_calldata(Code, Fun, Args) -> {ok, binary()} | {error, term()}. create_calldata(Code, Fun, Args, Options0) -> - Options = [no_implicit_stdlib, no_code | Options0], + Options = [no_code | Options0], case proplists:get_value(backend, Options, aevm) of aevm -> case check_call(Code, Fun, Args, Options) of @@ -392,7 +391,7 @@ decode_calldata(ContractString, FunName, Calldata) -> decode_calldata(ContractString, FunName, Calldata, [{backend, aevm}]). decode_calldata(ContractString, FunName, Calldata, Options0) -> - Options = [no_implicit_stdlib, no_code | Options0], + Options = [no_code | Options0], try Code = string_to_code(ContractString, Options), #{ typed_ast := TypedAst, type_env := TypeEnv} = Code, diff --git a/test/aeso_abi_tests.erl b/test/aeso_abi_tests.erl index 9d0a022..c6ce469 100644 --- a/test/aeso_abi_tests.erl +++ b/test/aeso_abi_tests.erl @@ -80,11 +80,11 @@ encode_decode_sophia_string(SophiaType, String) -> , " record r = {x : an_alias(int), y : variant}\n" , " datatype variant = Red | Blue(map(string, int))\n" , " entrypoint foo : arg_type => arg_type\n" ], - case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], [no_implicit_stdlib]) of + case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], []) of {ok, _, {[Type], _}, [Arg]} -> io:format("Type ~p~n", [Type]), Data = encode(Arg), - case aeso_compiler:to_sophia_value(Code, "foo", ok, Data, [no_implicit_stdlib]) of + case aeso_compiler:to_sophia_value(Code, "foo", ok, Data, []) of {ok, Sophia} -> lists:flatten(io_lib:format("~s", [prettypr:format(aeso_pretty:expr(Sophia))])); {error, Err} -> @@ -152,7 +152,7 @@ oracle_test() -> " Oracle.get_question(o, q)\n", {ok, _, {[word, word], {list, string}}, [16#123, 16#456]} = aeso_compiler:check_call(Contract, "question", ["ok_111111111111111111111111111111ZrdqRz9", - "oq_1111111111111111111111111111113AFEFpt5"], [no_implicit_stdlib]), + "oq_1111111111111111111111111111113AFEFpt5"], []), ok. @@ -162,7 +162,7 @@ permissive_literals_fail_test() -> " stateful entrypoint haxx(o : oracle(list(string), option(int))) =\n" " Chain.spend(o, 1000000)\n", {error, <<"Type errors\nCannot unify", _/binary>>} = - aeso_compiler:check_call(Contract, "haxx", ["#123"], [no_implicit_stdlib]), + aeso_compiler:check_call(Contract, "haxx", ["#123"], []), ok. encode_decode_calldata(FunName, Types, Args) -> @@ -173,8 +173,8 @@ encode_decode_calldata(FunName, Types, Args, RetType) -> encode_decode_calldata_(Code, FunName, Args, RetType). encode_decode_calldata_(Code, FunName, Args, RetVMType) -> - {ok, Calldata} = aeso_compiler:create_calldata(Code, FunName, Args, [no_implicit_stdlib]), - {ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}, no_implicit_stdlib]), + {ok, Calldata} = aeso_compiler:create_calldata(Code, FunName, Args, []), + {ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}]), ?assertEqual(RetType, RetVMType), CalldataType = {tuple, [word, {tuple, ArgTypes}]}, {ok, {_Hash, ArgTuple}} = aeb_heap:from_binary(CalldataType, Calldata), @@ -182,7 +182,7 @@ encode_decode_calldata_(Code, FunName, Args, RetVMType) -> "init" -> ok; _ -> - {ok, _ArgTypes, ValueASTs} = aeso_compiler:decode_calldata(Code, FunName, Calldata, [no_implicit_stdlib]), + {ok, _ArgTypes, ValueASTs} = aeso_compiler:decode_calldata(Code, FunName, Calldata, []), Values = [ prettypr:format(aeso_pretty:expr(V)) || V <- ValueASTs ], ?assertMatch({X, X}, {Args, Values}) end, diff --git a/test/aeso_aci_tests.erl b/test/aeso_aci_tests.erl index 18cb499..eee30ae 100644 --- a/test/aeso_aci_tests.erl +++ b/test/aeso_aci_tests.erl @@ -9,7 +9,7 @@ simple_aci_test_() -> test_contract(N) -> {Contract,MapACI,DecACI} = test_cases(N), - {ok,JSON} = aeso_aci:contract_interface(json, Contract, [no_implicit_stdlib]), + {ok,JSON} = aeso_aci:contract_interface(json, Contract), ?assertEqual([MapACI], JSON), ?assertEqual({ok, DecACI}, aeso_aci:render_aci_json(JSON)). @@ -90,8 +90,7 @@ aci_test_() -> fun() -> aci_test_contract(ContractName) end} || ContractName <- all_contracts()]. -all_contracts() -> [C || C <- aeso_compiler_tests:compilable_contracts() - , not aeso_compiler_tests:wants_stdlib(C)]. +all_contracts() -> aeso_compiler_tests:compilable_contracts(). aci_test_contract(Name) -> String = aeso_test_utils:read_contract(Name), diff --git a/test/aeso_calldata_tests.erl b/test/aeso_calldata_tests.erl index aa732a7..2f419cc 100644 --- a/test/aeso_calldata_tests.erl +++ b/test/aeso_calldata_tests.erl @@ -21,14 +21,12 @@ calldata_test_() -> ContractString = aeso_test_utils:read_contract(ContractName), AevmExprs = case not lists:member(ContractName, not_yet_compilable(aevm)) of - true -> ast_exprs(ContractString, Fun, Args, [{backend, aevm}] - ++ [no_implicit_stdlib || not aeso_compiler_tests:wants_stdlib(ContractName)]); + true -> ast_exprs(ContractString, Fun, Args, [{backend, aevm}]); false -> undefined end, FateExprs = case not lists:member(ContractName, not_yet_compilable(fate)) of - true -> ast_exprs(ContractString, Fun, Args, [{backend, fate}] - ++ [no_implicit_stdlib || not aeso_compiler_tests:wants_stdlib(ContractName)]); + true -> ast_exprs(ContractString, Fun, Args, [{backend, fate}]); false -> undefined end, case FateExprs == undefined orelse AevmExprs == undefined of @@ -47,14 +45,12 @@ calldata_aci_test_() -> io:format("ACI:\n~s\n", [ContractACIBin]), AevmExprs = case not lists:member(ContractName, not_yet_compilable(aevm)) of - true -> ast_exprs(ContractACI, Fun, Args, [{backend, aevm}] - ++ [no_implicit_stdlib || not aeso_compiler_tests:wants_stdlib(ContractName)]); + true -> ast_exprs(ContractACI, Fun, Args, [{backend, aevm}]); false -> undefined end, FateExprs = case not lists:member(ContractName, not_yet_compilable(fate)) of - true -> ast_exprs(ContractACI, Fun, Args, [{backend, fate}] - ++ [no_implicit_stdlib || not aeso_compiler_tests:wants_stdlib(ContractName)]); + true -> ast_exprs(ContractACI, Fun, Args, [{backend, fate}]); false -> undefined end, case FateExprs == undefined orelse AevmExprs == undefined of diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 9a9f743..6ba37e1 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -76,8 +76,7 @@ check_errors(Expect, ErrorString) -> compile(Backend, Name) -> compile(Backend, Name, - [{include, {file_system, [aeso_test_utils:contract_path()]}}] - ++ [no_implicit_stdlib || not wants_stdlib(Name)]). + [{include, {file_system, [aeso_test_utils:contract_path()]}}]). compile(Backend, Name, Options) -> String = aeso_test_utils:read_contract(Name), @@ -375,12 +374,3 @@ failing_contracts() -> ]} ]. -wants_stdlib(Name) -> - lists:member - (Name, - [ "stdlib_include", - "list_comp", - "list_comp_not_a_list", - "list_comp_if_not_bool", - "list_comp_bad_shadow" - ]). diff --git a/test/aeso_parser_tests.erl b/test/aeso_parser_tests.erl index 4c75854..ea6c517 100644 --- a/test/aeso_parser_tests.erl +++ b/test/aeso_parser_tests.erl @@ -15,7 +15,7 @@ simple_contracts_test_() -> ?assertMatch( [{contract, _, {con, _, "Identity"}, [{letfun, _, {id, _, "id"}, [{arg, _, {id, _, "x"}, {id, _, "_"}}], {id, _, "_"}, - {id, _, "x"}}]}], parse_string(Text, [no_implicit_stdlib])), + {id, _, "x"}}]}], parse_string(Text)), ok end}, {"Operator precedence test.", @@ -81,13 +81,13 @@ parse_string(Text, Opts) -> parse_expr(Text) -> [{letval, _, _, _, Expr}] = - parse_string("let _ = " ++ Text, [no_implicit_stdlib]), + parse_string("let _ = " ++ Text), Expr. round_trip(Text) -> - Contract = parse_string(Text, [no_implicit_stdlib]), + Contract = parse_string(Text), Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))), - Contract1 = parse_string(Text1, [no_implicit_stdlib]), + Contract1 = parse_string(Text1), NoSrcLoc = remove_line_numbers(Contract), NoSrcLoc1 = remove_line_numbers(Contract1), ?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)). diff --git a/test/contracts/manual_stdlib_include.aes b/test/contracts/manual_stdlib_include.aes index 5937c37..76a896e 100644 --- a/test/contracts/manual_stdlib_include.aes +++ b/test/contracts/manual_stdlib_include.aes @@ -1,5 +1,4 @@ -// This contract should be compiled with no_implicit_stdlib option. -// It should include Lists.aes implicitly however, because Option.aes depends on it. +// This should include Lists.aes implicitly, since Option.aes does. include "Option.aes" contract Test = diff --git a/test/contracts/stdlib_include.aes b/test/contracts/stdlib_include.aes index 6149db2..d890ae8 100644 --- a/test/contracts/stdlib_include.aes +++ b/test/contracts/stdlib_include.aes @@ -1,3 +1,6 @@ +include "List.aes" +include "Func.aes" + contract StdInc = entrypoint test() = List.map((x) => Func.id(x), [1,2,3,4]) From 3ff93c5c890c438bc5623355bb353e5a44ef2a97 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 27 Aug 2019 14:29:24 +0200 Subject: [PATCH 18/35] Fix bug in include chasing ... making it possible for the same file to be included multiple times --- src/aeso_parser.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index f0f2078..b3d184f 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -566,7 +566,7 @@ expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Op false -> Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}), Included1 = sets:add_element(Hashed, Included), - case string(Code, Included1, Opts1) of + case parse_and_scan(file(), Code, Opts1) of {ok, AST1} -> expand_includes(AST1 ++ AST, Included1, Acc, Opts); Err = {error, _} -> From 3b2daf8cd652dc28b850b9b6b61ad6daa0f269c9 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 27 Aug 2019 15:08:56 +0200 Subject: [PATCH 19/35] Better errors when using old tuple type syntax --- src/aeso_ast_infer_types.erl | 7 ++++++- src/aeso_parser.erl | 18 +++++++++--------- src/aeso_pretty.erl | 2 ++ 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 482bd16..5a07a9f 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -767,7 +767,9 @@ check_type(Env, Type = {fun_t, Ann, NamedArgs, Args, Ret}, Arity) -> {fun_t, Ann, NamedArgs1, Args1, Ret1}; check_type(_Env, Type = {uvar, _, _}, Arity) -> ensure_base_type(Type, Arity), - Type. + Type; +check_type(_Env, {args_t, Ann, Ts}, _) -> + type_error({new_tuple_syntax, Ann, Ts}). ensure_base_type(Type, Arity) -> [ type_error({wrong_type_arguments, Type, Arity, 0}) || Arity /= 0 ], @@ -2222,6 +2224,9 @@ pp_error({contract_has_no_entrypoints, Con}) -> "'function'.\n", [pp_expr("", Con), pp_loc(Con)]); pp_error({unbound_type, Type}) -> io_lib:format("Unbound type ~s (at ~s).\n", [pp_type("", Type), pp_loc(Type)]); +pp_error({new_tuple_syntax, Ann, Ts}) -> + io_lib:format("Invalid type\n~s (at ~s)\nThe syntax of tuple types changed in Sophia version 4.0. Did you mean\n~s\n", + [pp_type(" ", {args_t, Ann, Ts}), pp_loc(Ann), pp_type(" ", {tuple_t, Ann, Ts})]); pp_error(Err) -> io_lib:format("Unknown error: ~p\n", [Err]). diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index b3d184f..38e6d64 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -150,7 +150,7 @@ type() -> ?LAZY_P(type100()). type100() -> type200(). type200() -> - ?RULE(many({fun_domain(), keyword('=>')}), type300(), fun_t(_1, _2)). + ?RULE(many({type300(), keyword('=>')}), type300(), fun_t(_1, _2)). type300() -> ?RULE(sep1(type400(), tok('*')), tuple_t(get_ann(lists:nth(1, _1)), _1)). @@ -169,16 +169,15 @@ type400() -> typeAtom() -> ?LAZY_P(choice( [ parens(type()) + , args_t() , id(), token(con), token(qcon), token(qid), tvar() ])). -fun_domain() -> ?LAZY_P(choice( - [ ?RULE(tok('('), tok(')'), []) - %% Note avoidance of ambiguity: `(int)` can be treated as: - %% - literally `int` - %% - list of arguments with just one element – int. This approach is dropped. - , ?RULE(tok('('), type(), tok(','), sep1(type(), tok(',')), tok(')'), [_2|_4]) - , ?RULE(type300(), [_1]) +args_t() -> + ?LAZY_P(choice( + [ ?RULE(tok('('), tok(')'), {args_t, get_ann(_1), []}) + %% Singleton case handled separately + , ?RULE(tok('('), type(), tok(','), sep1(type(), tok(',')), tok(')'), {args_t, get_ann(_1), [_2|_4]}) ])). %% -- Statements ------------------------------------------------------------- @@ -501,7 +500,8 @@ tuple_t(_Ann, [Type]) -> Type; %% Not a tuple tuple_t(Ann, Types) -> {tuple_t, Ann, Types}. fun_t(Domains, Type) -> - lists:foldr(fun({Dom, Ann}, T) -> {fun_t, Ann, [], Dom, T} end, + lists:foldr(fun({{args_t, _, Dom}, Ann}, T) -> {fun_t, Ann, [], Dom, T}; + ({Dom, Ann}, T) -> {fun_t, Ann, [], [Dom], T} end, Type, Domains). tuple_e(_Ann, [Expr]) -> Expr; %% Not a tuple diff --git a/src/aeso_pretty.erl b/src/aeso_pretty.erl index e368354..dc36374 100644 --- a/src/aeso_pretty.erl +++ b/src/aeso_pretty.erl @@ -243,6 +243,8 @@ type({app_t, _, Type, Args}) -> beside(type(Type), args_type(Args)); type({tuple_t, _, Args}) -> tuple_type(Args); +type({args_t, _, Args}) -> + args_type(Args); type({bytes_t, _, any}) -> text("bytes(_)"); type({bytes_t, _, Len}) -> text(lists:concat(["bytes(", Len, ")"])); From c51531f620ded471eb03e270489f9657bb9dfc72 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 27 Aug 2019 18:04:32 +0200 Subject: [PATCH 20/35] please dialyzer --- src/aeso_syntax.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index af5abba..2e9f69a 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -59,6 +59,7 @@ -type type() :: {fun_t, ann(), [named_arg_t()], [type()], type()} | {app_t, ann(), type(), [type()]} | {tuple_t, ann(), [type()]} + | {args_t, ann(), [type()]} %% old tuple syntax, old for error messages | {bytes_t, ann(), integer() | any} | id() | qid() | con() | qcon() %% contracts From 80075a9d365cfc96d2bad1aa08077076a9c5177a Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Wed, 28 Aug 2019 09:45:25 +0200 Subject: [PATCH 21/35] Add missing case for builtin unit type --- src/aeso_icode.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/aeso_icode.erl b/src/aeso_icode.erl index 22d2d44..df8721b 100644 --- a/src/aeso_icode.erl +++ b/src/aeso_icode.erl @@ -78,6 +78,7 @@ builtin_types() -> , "string" => fun([]) -> string end , "address" => Word , "hash" => Word + , "unit" => fun([]) -> {tuple, []} end , "signature" => fun([]) -> {tuple, [word, word]} end , "oracle" => fun([_, _]) -> word end , "oracle_query" => fun([_, _]) -> word end From cce243e513e3547139237b5d450c28f2b3a51d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Rowicki?= <35342116+radrow@users.noreply.github.com> Date: Wed, 28 Aug 2019 14:17:30 +0200 Subject: [PATCH 22/35] Remove find_all from stdlib It was just a duplicated `filter` --- priv/stdlib/List.aes | 5 ----- 1 file changed, 5 deletions(-) diff --git a/priv/stdlib/List.aes b/priv/stdlib/List.aes index cdd2464..ddef362 100644 --- a/priv/stdlib/List.aes +++ b/priv/stdlib/List.aes @@ -23,11 +23,6 @@ namespace List = [] => None h::t => if(p(h)) Some(h) else find(p, t) - function find_all(p : 'a => bool, l : list('a)) : list('a) = find_all_(p, l, []) - private function find_all_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => reverse(acc) - h::t => find_all_(p, t, if(p(h)) h::acc else acc) - function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, []) private function find_indices_( p : 'a => bool , l : list('a) From 1d962f200153fa4b40003a875f52163f3293f01d Mon Sep 17 00:00:00 2001 From: radrow Date: Thu, 29 Aug 2019 13:41:04 +0200 Subject: [PATCH 23/35] Updated some functions, renamed, added from_to --- priv/stdlib/List.aes | 48 +++++++++++++++++++++++++++--------------- priv/stdlib/Option.aes | 2 +- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/priv/stdlib/List.aes b/priv/stdlib/List.aes index cdd2464..637faeb 100644 --- a/priv/stdlib/List.aes +++ b/priv/stdlib/List.aes @@ -37,14 +37,16 @@ namespace List = [] => reverse(acc) h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc) - function nth(n : int, l : list('a)) : option('a) = switch(l) - [] => None - h::t => if(n == 0) Some(h) else nth(n-1, t) + function nth(n : int, l : list('a)) : option('a) = + if(n < 0) None else switch(l) + [] => None + h::t => if(n == 0) Some(h) else nth(n-1, t) /* Unsafe version of `nth` */ - function get(n : int, l : list('a)) : 'a = switch(l) - [] => abort("Out of index get") - h::t => if(n == 0) h else get(n-1, t) + function get(n : int, l : list('a)) : 'a = + if(n < 0) abort("Negative index get") else switch(l) + [] => abort("Out of index get") + h::t => if(n == 0) h else get(n-1, t) function length(l : list('a)) : int = length_(l, 0) @@ -53,6 +55,15 @@ namespace List = _::t => length_(t, acc + 1) + function from_to(a : int, b : int) : list(int) = from_to_(a, b, []) + private function from_to_(a, b, acc) = + if (a > b) acc else from_to_(a, b - 1, b :: acc) + + function from_to_step(a : int, b : int, s : int) : list(int) = from_to_step_(a, b, s, []) + private function from_to_step_(a, b, s, acc) = + if (a > b) reverse(acc) else from_to_step_(a + s, b, s, a :: acc) + + /* Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow */ function replace_at(n : int, e : 'a, l : list('a)) : list('a) = if(n<0) abort("insert_at underflow") else replace_at_(n, e, l, []) @@ -71,14 +82,17 @@ namespace List = [] => abort("insert_at overflow") h::t => insert_at_(n-1, e, t, h::acc) - function insert_by(f : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = + function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) = + insert_by_(cmp, x, l, []) + private function insert_by_(cmp : (('a, 'a) => bool), x : 'a, l : list('a), acc : list('a)) : list('a) = switch(l) - [] => [x] - (e :: l') => - if(f(x, e)) - e :: insert_by(f, x, l') + [] => reverse(x::acc) + h::t => + if(cmp(x, e)) // x < e + reverse(acc) ++ (x::l) else - x :: l + insert_by(cmp, x, t, e::acc) + function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l) [] => nil @@ -88,12 +102,12 @@ namespace List = [] => acc h::t => foldl(rcons, rcons(acc, h), t) - function foreach(f : 'a => unit, l : list('a)) : unit = + function foreach(l : list('a), f : 'a => unit) : unit = switch(l) - [] => () - e :: l' => + [] => () + e::l' => f(e) - foreach(f, l') + foreach(l', f) function reverse(l : list('a)) : list('a) = foldl((lst, el) => el :: lst, [], l) @@ -149,7 +163,7 @@ namespace List = h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f) - function concats(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll) + function flatten(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll) function all(p : 'a => bool, l : list('a)) : bool = switch(l) [] => true diff --git a/priv/stdlib/Option.aes b/priv/stdlib/Option.aes index 843e62d..6ebf98c 100644 --- a/priv/stdlib/Option.aes +++ b/priv/stdlib/Option.aes @@ -19,7 +19,7 @@ namespace Option = function force(o : option('a)) : 'a = default(abort("Forced None value"), o) - function on_elem(f : 'a => unit, o : option('a)) : unit = match((), f, o) + function on_elem(o : option('a), f : 'a => unit) : unit = match((), f, o) function map(f : 'a => 'b, o : option('a)) : option('b) = switch(o) None => None From 076d635dbeb01a92939ddb46ad3de862058802bb Mon Sep 17 00:00:00 2001 From: radrow Date: Thu, 29 Aug 2019 15:32:10 +0200 Subject: [PATCH 24/35] Fix errors --- priv/stdlib/List.aes | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/priv/stdlib/List.aes b/priv/stdlib/List.aes index 637faeb..8b070b5 100644 --- a/priv/stdlib/List.aes +++ b/priv/stdlib/List.aes @@ -38,13 +38,15 @@ namespace List = h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc) function nth(n : int, l : list('a)) : option('a) = - if(n < 0) None else switch(l) + if(n < 0) None + else switch(l) [] => None h::t => if(n == 0) Some(h) else nth(n-1, t) /* Unsafe version of `nth` */ function get(n : int, l : list('a)) : 'a = - if(n < 0) abort("Negative index get") else switch(l) + if(n < 0) abort("Negative index get") + else switch(l) [] => abort("Out of index get") h::t => if(n == 0) h else get(n-1, t) @@ -88,10 +90,10 @@ namespace List = switch(l) [] => reverse(x::acc) h::t => - if(cmp(x, e)) // x < e + if(cmp(x, h)) // x < h reverse(acc) ++ (x::l) else - insert_by(cmp, x, t, e::acc) + insert_by_(cmp, x, t, h::acc) function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l) From 1ce95b32aca3febd857d002d881a51155f766e52 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Fri, 30 Aug 2019 10:21:36 +0200 Subject: [PATCH 25/35] Add checks for polymorphic/higher order oracles and higher order entrypoints (FATE) --- src/aeso_ast_to_fcode.erl | 49 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 16fe99e..3f0c960 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -282,10 +282,13 @@ decl_to_fcode(Env = #{ functions := Funs }, {letfun, Ann, {id, _, Name}, Args, R Attrs = get_attributes(Ann), FName = lookup_fun(Env, qname(Env, Name)), FArgs = args_to_fcode(Env, Args), + FRet = type_to_fcode(Env, Ret), FBody = expr_to_fcode(Env#{ vars => [X || {X, _} <- FArgs] }, Body), + [ ensure_first_order_entrypoint(Ann, FArgs, FRet) + || aeso_syntax:get_ann(entrypoint, Ann, false) ], Def = #{ attrs => Attrs, args => FArgs, - return => type_to_fcode(Env, Ret), + return => FRet, body => FBody }, NewFuns = Funs#{ FName => Def }, Env#{ functions := NewFuns }. @@ -510,7 +513,7 @@ expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) -> end; %% Function calls -expr_to_fcode(Env, Type, {app, _Ann, Fun = {typed, _, _, {fun_t, _, NamedArgsT, _, _}}, Args}) -> +expr_to_fcode(Env, Type, {app, _, Fun = {typed, _, _, {fun_t, _, NamedArgsT, _, _}}, Args}) -> Args1 = get_named_args(NamedArgsT, Args), FArgs = [expr_to_fcode(Env, Arg) || Arg <- Args1], case expr_to_fcode(Env, Fun) of @@ -524,11 +527,13 @@ expr_to_fcode(Env, Type, {app, _Ann, Fun = {typed, _, _, {fun_t, _, NamedArgsT, %% Get the type of the oracle from the args or the expression itself OType = get_oracle_type(B, Type, Args1), {oracle, QType, RType} = type_to_fcode(Env, OType), + validate_oracle_type(aeso_syntax:get_ann(Fun), QType, RType), TypeArgs = [{lit, {typerep, QType}}, {lit, {typerep, RType}}], builtin_to_fcode(B, FArgs ++ TypeArgs); {builtin_u, B, _} when B =:= aens_resolve -> %% Get the type we are assuming the name resolves to AensType = type_to_fcode(Env, Type), + validate_aens_resolve_type(aeso_syntax:get_ann(Fun), AensType), TypeArgs = [{lit, {typerep, AensType}}], builtin_to_fcode(B, FArgs ++ TypeArgs); {builtin_u, B, _Ar} -> builtin_to_fcode(B, FArgs); @@ -604,6 +609,46 @@ get_oracle_type(oracle_check, _Type, [{typed, _, _Expr, OType}]) -> get_oracle_type(oracle_check_query, _Type, [{typed, _, _Expr, OType} | _]) -> OType; get_oracle_type(oracle_respond, _Type, [_, {typed, _,_Expr, OType} | _]) -> OType. +validate_oracle_type(Ann, QType, RType) -> + ensure_monomorphic(QType, {polymorphic_query_type, Ann, QType}), + ensure_monomorphic(RType, {polymorphic_response_type, Ann, RType}), + ensure_first_order(QType, {higher_order_query_type, Ann, QType}), + ensure_first_order(RType, {higher_order_response_type, Ann, RType}), + ok. + +validate_aens_resolve_type(Ann, {variant, [[], [Type]]}) -> + ensure_monomorphic(Type, {polymorphic_aens_resolve, Ann, Type}), + ensure_first_order(Type, {higher_order_aens_resolve, Ann, Type}), + ok. + +ensure_first_order_entrypoint(Ann, Args, Ret) -> + [ ensure_first_order(T, {higher_order_entrypoint_argument, Ann, X, T}) + || {X, T} <- Args ], + ensure_first_order(Ret, {higher_order_entrypoint_return, Ann, Ret}), + ok. + +ensure_monomorphic(Type, Err) -> + case is_monomorphic(Type) of + true -> ok; + false -> fcode_error(Err) + end. + +ensure_first_order(Type, Err) -> + case is_first_order(Type) of + true -> ok; + false -> fcode_error(Err) + end. + +is_monomorphic({tvar, _}) -> false; +is_monomorphic(Ts) when is_list(Ts) -> lists:all(fun is_monomorphic/1, Ts); +is_monomorphic(Tup) when is_tuple(Tup) -> is_monomorphic(tuple_to_list(Tup)); +is_monomorphic(_) -> true. + +is_first_order({function, _, _}) -> false; +is_first_order(Ts) when is_list(Ts) -> lists:all(fun is_first_order/1, Ts); +is_first_order(Tup) when is_tuple(Tup) -> is_first_order(tuple_to_list(Tup)); +is_first_order(_) -> true. + %% -- Pattern matching -- -spec alts_to_fcode(env(), ftype(), var_name(), [aeso_syntax:alt()]) -> fsplit(). From 6fd39d4cb19651ddee556307f62631782a9e5d53 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Fri, 30 Aug 2019 11:13:13 +0200 Subject: [PATCH 26/35] Add checks for polymorphic/higher order oracles and higher order entrypoints (AEVM) --- src/aeso_ast_to_icode.erl | 72 ++++++++++++++++++++------------ test/contracts/complex_types.aes | 2 +- test/contracts/test.aes | 5 ++- test/contracts/tuple_match.aes | 2 +- 4 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index ca1433a..c245e29 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -93,6 +93,8 @@ contract_to_icode([{letfun, Attrib, Name, Args, _What, Body={typed,_,_,T}}|Rest] FunAttrs = [ stateful || proplists:get_value(stateful, Attrib, false) ] ++ [ payable || proplists:get_value(payable, Attrib, false) ] ++ [ private || is_private(Attrib, Icode) ], + [ check_entrypoint_type(Attrib, Name, Args, T) + || aeso_syntax:get_ann(entrypoint, Attrib, false) ], %% TODO: Handle types FunName = ast_id(Name), %% TODO: push funname to env @@ -105,7 +107,7 @@ contract_to_icode([{letfun, Attrib, Name, Args, _What, Body={typed,_,_,T}}|Rest] #{ state_type := StateType } = Icode, {#tuple{ cpts = [type_value(StateType), ast_body(Body, Icode)] }, {tuple, [typerep, ast_typerep(T, Icode)]}}; - _ -> {ast_body(Body, Icode), ast_typerep(T, Icode)} + _ -> {ast_body(Body, Icode), ast_typerep1(T, Icode)} end, QName = aeso_icode:qualify(Name, Icode), NewIcode = ast_fun_to_icode(ast_id(QName), FunAttrs, FunArgs, FunBody, TypeRep, Icode), @@ -121,7 +123,7 @@ ast_id({id, _, Id}) -> Id; ast_id({qid, _, Id}) -> Id. ast_args([{arg, _, Name, Type}|Rest], Acc, Icode) -> - ast_args(Rest, [{ast_id(Name), ast_type(Type, Icode)}| Acc], Icode); + ast_args(Rest, [{ast_id(Name), ast_typerep1(Type, Icode)}| Acc], Icode); ast_args([], Acc, _Icode) -> lists:reverse(Acc). ast_type(T, Icode) -> @@ -582,7 +584,7 @@ ast_body({block,As,[E|Rest]}, Icode) -> #switch{expr=ast_body(E, Icode), cases=[{#var_ref{name="_"},ast_body({block,As,Rest}, Icode)}]}; ast_body({lam,_,Args,Body}, Icode) -> - #lambda{args=[#arg{name = ast_id(P), type = ast_type(T, Icode)} || {arg,_,P,T} <- Args], + #lambda{args=[#arg{name = ast_id(P), type = ast_typerep1(T, Icode)} || {arg,_,P,T} <- Args], body=ast_body(Body, Icode)}; ast_body({typed,_,{record,Attrs,Fields},{record_t,DefFields}}, Icode) -> %% Compile as a tuple with the fields in the order they appear in the definition. @@ -717,6 +719,22 @@ map_upd(Key, Default, ValFun, Map = {typed, Ann, _, MapType}, Icode) -> Args = [ast_body(Map, Icode), ast_body(Key, Icode), ast_body(Default, Icode), ast_body(ValFun, Icode)], builtin_call(FunName, Args). +check_entrypoint_type(Ann, Name, Args, Ret) -> + Check = fun(T, Err) -> + case is_simple_type(T) of + false -> gen_error(Err); + true -> ok + end end, + [ Check(T, {entrypoint_argument_must_have_simple_type, Ann1, Name, X, T}) + || {arg, Ann1, X, T} <- Args ], + Check(Ret, {entrypoint_must_have_simple_return_type, Ann, Name, Ret}). + +is_simple_type({tvar, _, _}) -> false; +is_simple_type({fun_t, _, _, _, _}) -> false; +is_simple_type(Ts) when is_list(Ts) -> lists:all(fun is_simple_type/1, Ts); +is_simple_type(T) when is_tuple(T) -> is_simple_type(tuple_to_list(T)); +is_simple_type(_) -> true. + is_monomorphic({tvar, _, _}) -> false; is_monomorphic([H|T]) -> is_monomorphic(H) andalso is_monomorphic(T); @@ -757,42 +775,49 @@ make_type_def(Args, Def, Icode = #{ type_vars := TypeEnv }) -> TVars = [ X || {tvar, _, X} <- Args ], fun(Types) -> TypeEnv1 = maps:from_list(lists:zip(TVars, Types)), - ast_typerep(Def, Icode#{ type_vars := maps:merge(TypeEnv, TypeEnv1) }) + ast_typerep1(Def, Icode#{ type_vars := maps:merge(TypeEnv, TypeEnv1) }) end. -spec ast_typerep(aeso_syntax:type()) -> aeb_aevm_data:type(). -ast_typerep(Type) -> ast_typerep(Type, aeso_icode:new([])). +ast_typerep(Type) -> + ast_typerep(Type, aeso_icode:new([])). -ast_typerep({id, _, Name}, Icode) -> +ast_typerep(Type, Icode) -> + case is_simple_type(Type) of + false -> gen_error({not_a_simple_type, Type}); + true -> ast_typerep1(Type, Icode) + end. + +ast_typerep1({id, _, Name}, Icode) -> lookup_type_id(Name, [], Icode); -ast_typerep({qid, _, Name}, Icode) -> +ast_typerep1({qid, _, Name}, Icode) -> lookup_type_id(Name, [], Icode); -ast_typerep({con, _, _}, _) -> +ast_typerep1({con, _, _}, _) -> word; %% Contract type -ast_typerep({bytes_t, _, Len}, _) -> +ast_typerep1({bytes_t, _, Len}, _) -> bytes_t(Len); -ast_typerep({app_t, _, {I, _, Name}, Args}, Icode) when I =:= id; I =:= qid -> - ArgReps = [ ast_typerep(Arg, Icode) || Arg <- Args ], +ast_typerep1({app_t, _, {I, _, Name}, Args}, Icode) when I =:= id; I =:= qid -> + ArgReps = [ ast_typerep1(Arg, Icode) || Arg <- Args ], lookup_type_id(Name, ArgReps, Icode); -ast_typerep({tvar,_,A}, #{ type_vars := TypeVars }) -> +ast_typerep1({tvar,_,A}, #{ type_vars := TypeVars }) -> case maps:get(A, TypeVars, undefined) of undefined -> word; %% We serialize type variables just as addresses in the originating VM. Type -> Type end; -ast_typerep({tuple_t,_,Cpts}, Icode) -> - {tuple, [ast_typerep(C, Icode) || C<-Cpts]}; -ast_typerep({record_t,Fields}, Icode) -> +ast_typerep1({tuple_t,_,Cpts}, Icode) -> + {tuple, [ast_typerep1(C, Icode) || C<-Cpts]}; +ast_typerep1({record_t,Fields}, Icode) -> {tuple, [ begin {field_t, _, _, T} = Field, - ast_typerep(T, Icode) + ast_typerep1(T, Icode) end || Field <- Fields]}; -ast_typerep({fun_t,_,_,_,_}, _Icode) -> +ast_typerep1({fun_t,_,_,_,_}, _Icode) -> function; -ast_typerep({alias_t, T}, Icode) -> ast_typerep(T, Icode); -ast_typerep({variant_t, Cons}, Icode) -> +ast_typerep1({alias_t, T}, Icode) -> ast_typerep1(T, Icode); +ast_typerep1({variant_t, Cons}, Icode) -> {variant, [ begin {constr_t, _, _, Args} = Con, - [ ast_typerep(Arg, Icode) || Arg <- Args ] + [ ast_typerep1(Arg, Icode) || Arg <- Args ] end || Con <- Cons ]}. ttl_t(Icode) -> @@ -841,13 +866,6 @@ type_value({map, K, V}) -> #tuple{ cpts = [#integer{ value = ?TYPEREP_MAP_TAG }, type_value(K), type_value(V)] }. -%% As abort is a built-in in the future it will be illegal to for -%% users to define abort. For the time being strip away all user -%% defined abort functions. - -ast_fun_to_icode("abort", _Atts, _Args, _Body, _TypeRep, Icode) -> - %% Strip away all user defined abort functions. - Icode; ast_fun_to_icode(Name, Attrs, Args, Body, TypeRep, #{functions := Funs} = Icode) -> NewFuns = [{Name, Attrs, Args, Body, TypeRep}| Funs], aeso_icode:set_functions(NewFuns, Icode). diff --git a/test/contracts/complex_types.aes b/test/contracts/complex_types.aes index 613371f..57e19d7 100644 --- a/test/contracts/complex_types.aes +++ b/test/contracts/complex_types.aes @@ -50,7 +50,7 @@ contract ComplexTypes = entrypoint remote_pair(n : int, s : string) : int * string = state.worker.pair(gas = 10000, n, s) - entrypoint map(f, xs) = + function map(f, xs) = switch(xs) [] => [] x :: xs => f(x) :: map(f, xs) diff --git a/test/contracts/test.aes b/test/contracts/test.aes index 04de90f..bd1a130 100644 --- a/test/contracts/test.aes +++ b/test/contracts/test.aes @@ -91,10 +91,11 @@ contract Identity = // } // let id(x) = x // let main(xs) = map(double,xs) - entrypoint z(f,x) = x + function z(f,x) = x function s(n) = (f,x)=>f(n(f,x)) function add(m,n) = (f,x)=>m(f,n(f,x)) - entrypoint main(_) = + + entrypoint main() = let three=s(s(s(z))) add(three,three) (((i)=>i+1),0) diff --git a/test/contracts/tuple_match.aes b/test/contracts/tuple_match.aes index 0bcab8c..b6196e3 100644 --- a/test/contracts/tuple_match.aes +++ b/test/contracts/tuple_match.aes @@ -1,6 +1,6 @@ contract TuplesMatch = - entrypoint tuplify3() = (t) => switch(t) + function tuplify3() = (t) => switch(t) (x, y, z) => 3 entrypoint fst(p : int * string) = From f27ba528d873059cc740d2c5403af23c89ca0d6a Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Fri, 30 Aug 2019 11:21:26 +0200 Subject: [PATCH 27/35] aebytecode commit --- rebar.config | 2 +- rebar.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index 7d60a8e..043dc58 100644 --- a/rebar.config +++ b/rebar.config @@ -2,7 +2,7 @@ {erl_opts, [debug_info]}. -{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"10cc127"}}} +{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"e7f2be7"}}} , {getopt, "1.0.1"} , {eblake2, "1.0.0"} , {jsx, {git, "https://github.com/talentdeficit/jsx.git", diff --git a/rebar.lock b/rebar.lock index 84f3ab4..924dffc 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,7 +1,7 @@ {"1.1.0", [{<<"aebytecode">>, {git,"https://github.com/aeternity/aebytecode.git", - {ref,"10cc1278831ad7e90138533466ceef4bcafd74a9"}}, + {ref,"e7f2be7ce878b1e22bad287f3342f32579e98599"}}, 0}, {<<"aeserialization">>, {git,"https://github.com/aeternity/aeserialization.git", From 256aadd5754708f8d13979d9f12f28ce0aa2fcb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Rowicki?= <35342116+radrow@users.noreply.github.com> Date: Fri, 30 Aug 2019 13:44:26 +0200 Subject: [PATCH 28/35] [......] Co-Authored-By: Ulf Norell --- priv/stdlib/List.aes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/stdlib/List.aes b/priv/stdlib/List.aes index 8b070b5..2113bc4 100644 --- a/priv/stdlib/List.aes +++ b/priv/stdlib/List.aes @@ -57,7 +57,7 @@ namespace List = _::t => length_(t, acc + 1) - function from_to(a : int, b : int) : list(int) = from_to_(a, b, []) + function from_to(a : int, b : int) : list(int) = [a..b] private function from_to_(a, b, acc) = if (a > b) acc else from_to_(a, b - 1, b :: acc) From 71a556ce81726908aad08e0d2428486a5b9b61af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Rowicki?= <35342116+radrow@users.noreply.github.com> Date: Fri, 30 Aug 2019 13:46:02 +0200 Subject: [PATCH 29/35] nth update --- priv/stdlib/List.aes | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/priv/stdlib/List.aes b/priv/stdlib/List.aes index 2113bc4..57d2250 100644 --- a/priv/stdlib/List.aes +++ b/priv/stdlib/List.aes @@ -38,16 +38,14 @@ namespace List = h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc) function nth(n : int, l : list('a)) : option('a) = - if(n < 0) None - else switch(l) + switch(l) [] => None h::t => if(n == 0) Some(h) else nth(n-1, t) /* Unsafe version of `nth` */ function get(n : int, l : list('a)) : 'a = - if(n < 0) abort("Negative index get") - else switch(l) - [] => abort("Out of index get") + switch(l) + [] => abort(if(n < 0) "Negative index get" else "Out of index get") h::t => if(n == 0) h else get(n-1, t) From 6408969cd31dac7631e383c4a799034d1d23566a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Rowicki?= <35342116+radrow@users.noreply.github.com> Date: Fri, 30 Aug 2019 14:06:46 +0200 Subject: [PATCH 30/35] Remove from_to_ --- priv/stdlib/List.aes | 2 -- 1 file changed, 2 deletions(-) diff --git a/priv/stdlib/List.aes b/priv/stdlib/List.aes index 57d2250..97cf126 100644 --- a/priv/stdlib/List.aes +++ b/priv/stdlib/List.aes @@ -56,8 +56,6 @@ namespace List = function from_to(a : int, b : int) : list(int) = [a..b] - private function from_to_(a, b, acc) = - if (a > b) acc else from_to_(a, b - 1, b :: acc) function from_to_step(a : int, b : int, s : int) : list(int) = from_to_step_(a, b, s, []) private function from_to_step_(a, b, s, acc) = From 062309e578fc852bc4e37c26fc53e1dd19ae5407 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Fri, 30 Aug 2019 14:22:31 +0200 Subject: [PATCH 31/35] Type variables mentioned in local functions should not be flexible (cc #112) --- src/aeso_ast_infer_types.erl | 2 +- test/contracts/local_poly_fail.aes | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 test/contracts/local_poly_fail.aes diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 5a07a9f..5e58a45 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -1327,7 +1327,7 @@ infer_block(Env, _, [E], BlockType) -> [check_expr(Env, E, BlockType)]; infer_block(Env, Attrs, [Def={letfun, Ann, _, _, _, _}|Rest], BlockType) -> {{Name, TypeSig}, LetFun} = infer_letfun(Env, Def), - FunT = freshen_type(Ann, typesig_to_fun_t(TypeSig)), + FunT = typesig_to_fun_t(TypeSig), NewE = bind_var({id, Ann, Name}, FunT, Env), [LetFun|infer_block(NewE, Attrs, Rest, BlockType)]; infer_block(Env, _, [{letval, Attrs, Pattern, Type, E}|Rest], BlockType) -> diff --git a/test/contracts/local_poly_fail.aes b/test/contracts/local_poly_fail.aes new file mode 100644 index 0000000..189ab23 --- /dev/null +++ b/test/contracts/local_poly_fail.aes @@ -0,0 +1,7 @@ + +contract Fail = + + entrypoint tttt() : bool * int = + let f(x : 'a) : 'a = x + (f(true), f(1)) + From 83d06977f972463cba6114187b395eda998b2df8 Mon Sep 17 00:00:00 2001 From: sennui Date: Mon, 12 Aug 2019 18:31:42 +0200 Subject: [PATCH 32/35] add extra argument to claim for bidding --- src/aeso_ast_infer_types.erl | 2 +- src/aeso_ast_to_fcode.erl | 3 +-- src/aeso_ast_to_icode.erl | 6 +++--- src/aeso_fcode_to_fate.erl | 7 +++---- test/contracts/aens.aes | 9 +++++---- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 5e58a45..ea9c1ae 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -443,7 +443,7 @@ global_env() -> { funs = MkDefs( [{"resolve", Fun([String, String], option_t(Ann, A))}, {"preclaim", SignFun([Address, Hash], Unit)}, - {"claim", SignFun([Address, String, Int], Unit)}, + {"claim", SignFun([Address, String, Int, Int], Unit)}, {"transfer", SignFun([Address, Address, String], Unit)}, {"revoke", SignFun([Address, String], Unit)}]) }, diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 3f0c960..29473e7 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -185,7 +185,7 @@ builtins() -> {["Oracle"], [{"register", 4}, {"query_fee", 1}, {"query", 5}, {"get_question", 2}, {"respond", 4}, {"extend", 3}, {"get_answer", 2}, {"check", 1}, {"check_query", 2}]}, - {["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 4}, {"transfer", 4}, + {["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4}, {"revoke", 3}]}, {["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2}, {"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]}, @@ -1600,4 +1600,3 @@ pp_pat(Pat) -> pp_fexpr(Pat). is_infix(Op) -> C = hd(atom_to_list(Op)), C < $a orelse C > $z. - diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index c245e29..586952f 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -270,10 +270,10 @@ ast_body(?qid_app(["AENS", "preclaim"], Args, _, _), Icode) -> [word, word, sign_t()], {tuple, []}); ast_body(?qid_app(["AENS", "claim"], Args, _, _), Icode) -> - {Sign, [Addr, Name, Salt]} = get_signature_arg(Args), + {Sign, [Addr, Name, Salt, NameFee]} = get_signature_arg(Args), prim_call(?PRIM_CALL_AENS_CLAIM, #integer{value = 0}, - [ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Salt, Icode), ast_body(Sign, Icode)], - [word, string, word, sign_t()], {tuple, []}); + [ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Salt, Icode), ast_body(Sign, Icode), ast_body(NameFee, Icode)], + [word, string, word, sign_t(), word], {tuple, []}); ast_body(?qid_app(["AENS", "transfer"], Args, _, _), Icode) -> {Sign, [FromAddr, ToAddr, Name]} = get_signature_arg(Args), diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index eadd9dd..304ffdb 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -557,8 +557,8 @@ builtin_to_scode(Env, aens_resolve, [_Name, _Key, _Type] = Args) -> builtin_to_scode(Env, aens_preclaim, [_Sign, _Account, _Hash] = Args) -> call_to_scode(Env, [aeb_fate_ops:aens_preclaim(?a, ?a, ?a), tuple(0)], Args); -builtin_to_scode(Env, aens_claim, [_Sign, _Account, _NameString, _Salt] = Args) -> - call_to_scode(Env, [aeb_fate_ops:aens_claim(?a, ?a, ?a, ?a), +builtin_to_scode(Env, aens_claim, [_Sign, _Account, _NameString, _Salt, _NameFee] = Args) -> + call_to_scode(Env, [aeb_fate_ops:aens_claim(?a, ?a, ?a, ?a, ?a), tuple(0)], Args); builtin_to_scode(Env, aens_transfer, [_Sign, _From, _To, _Name] = Args) -> call_to_scode(Env, [aeb_fate_ops:aens_transfer(?a, ?a, ?a, ?a), @@ -883,7 +883,7 @@ attributes(I) -> {'ORACLE_QUERY_FEE', A, B} -> Impure(A, [B]); {'AENS_RESOLVE', A, B, C, D} -> Impure(A, [B, C, D]); {'AENS_PRECLAIM', A, B, C} -> Impure(none, [A, B, C]); - {'AENS_CLAIM', A, B, C, D} -> Impure(none, [A, B, C, D]); + {'AENS_CLAIM', A, B, C, D, E} -> Impure(none, [A, B, C, D, E]); 'AENS_UPDATE' -> Impure(none, []);%% TODO {'AENS_TRANSFER', A, B, C, D} -> Impure(none, [A, B, C, D]); {'AENS_REVOKE', A, B, C} -> Impure(none, [A, B, C]); @@ -1571,4 +1571,3 @@ drop_common_suffix_r([X | Xs], [X | Ys]) -> drop_common_suffix_r(Xs, Ys); drop_common_suffix_r(Xs, Ys) -> {lists:reverse(Xs), lists:reverse(Ys)}. - diff --git a/test/contracts/aens.aes b/test/contracts/aens.aes index 9d5e063..ee1ba27 100644 --- a/test/contracts/aens.aes +++ b/test/contracts/aens.aes @@ -22,14 +22,16 @@ contract AENSTest = stateful entrypoint claim(addr : address, name : string, - salt : int) : unit = - AENS.claim(addr, name, salt) + salt : int, + name_fee : int) : unit = + AENS.claim(addr, name, salt, name_fee) stateful entrypoint signedClaim(addr : address, name : string, salt : int, + name_fee : int, sign : signature) : unit = - AENS.claim(addr, name, salt, signature = sign) + AENS.claim(addr, name, salt, name_fee, signature = sign) // TODO: update() -- how to handle pointers? @@ -52,4 +54,3 @@ contract AENSTest = name : string, sign : signature) : unit = AENS.revoke(owner, name, signature = sign) - From cf91a27fb2ca25b68188ce04c443483f6a222325 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Wed, 28 Aug 2019 10:59:47 +0200 Subject: [PATCH 33/35] Keep sign last --- src/aeso_ast_to_icode.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index 586952f..d1d5a36 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -272,7 +272,7 @@ ast_body(?qid_app(["AENS", "preclaim"], Args, _, _), Icode) -> ast_body(?qid_app(["AENS", "claim"], Args, _, _), Icode) -> {Sign, [Addr, Name, Salt, NameFee]} = get_signature_arg(Args), prim_call(?PRIM_CALL_AENS_CLAIM, #integer{value = 0}, - [ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Salt, Icode), ast_body(Sign, Icode), ast_body(NameFee, Icode)], + [ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Salt, Icode), ast_body(NameFee, Icode), ast_body(Sign, Icode)], [word, string, word, sign_t(), word], {tuple, []}); ast_body(?qid_app(["AENS", "transfer"], Args, _, _), Icode) -> From 1ee5a57924e3a17678f4973f112d16a4c5f4c1f9 Mon Sep 17 00:00:00 2001 From: sennui Date: Wed, 14 Aug 2019 15:22:30 +0200 Subject: [PATCH 34/35] change aebytecode version, aeserialization and add enacl --- rebar.config | 2 +- rebar.lock | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index 043dc58..ac0d491 100644 --- a/rebar.config +++ b/rebar.config @@ -2,7 +2,7 @@ {erl_opts, [debug_info]}. -{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"e7f2be7"}}} +{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"72b2a58"}}} , {getopt, "1.0.1"} , {eblake2, "1.0.0"} , {jsx, {git, "https://github.com/talentdeficit/jsx.git", diff --git a/rebar.lock b/rebar.lock index 924dffc..4e9247b 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,17 +1,21 @@ {"1.1.0", [{<<"aebytecode">>, {git,"https://github.com/aeternity/aebytecode.git", - {ref,"e7f2be7ce878b1e22bad287f3342f32579e98599"}}, + {ref,"72b2a581d5a6d488a208331da88de1a488ac2da1"}}, 0}, {<<"aeserialization">>, {git,"https://github.com/aeternity/aeserialization.git", - {ref,"816bf994ffb5cee218c3f22dc5fea296c9e0882e"}}, + {ref,"47aaa8f5434b365c50a35bfd1490340b19241991"}}, 1}, {<<"base58">>, {git,"https://github.com/aeternity/erl-base58.git", {ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}}, 2}, {<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0}, + {<<"enacl">>, + {git,"https://github.com/aeternity/enacl.git", + {ref,"26180f42c0b3a450905d2efd8bc7fd5fd9cece75"}}, + 2}, {<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}, {<<"jsx">>, {git,"https://github.com/talentdeficit/jsx.git", From e9f717a17bddb83739e959660408671e6b4a34cd Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Mon, 2 Sep 2019 10:21:35 +0200 Subject: [PATCH 35/35] Update src/aeso_ast_to_icode.erl Co-Authored-By: Ulf Norell --- src/aeso_ast_to_icode.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index d1d5a36..38bba87 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -273,7 +273,7 @@ ast_body(?qid_app(["AENS", "claim"], Args, _, _), Icode) -> {Sign, [Addr, Name, Salt, NameFee]} = get_signature_arg(Args), prim_call(?PRIM_CALL_AENS_CLAIM, #integer{value = 0}, [ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Salt, Icode), ast_body(NameFee, Icode), ast_body(Sign, Icode)], - [word, string, word, sign_t(), word], {tuple, []}); + [word, string, word, word, sign_t()], {tuple, []}); ast_body(?qid_app(["AENS", "transfer"], Args, _, _), Icode) -> {Sign, [FromAddr, ToAddr, Name]} = get_signature_arg(Args),