diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index f0fee08..12aeb71 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -76,7 +76,9 @@ -record(is_contract_constraint, { contract_t :: utype(), context :: {contract_literal, aeso_syntax:expr()} | - {address_to_contract, aeso_syntax:ann()} + {address_to_contract, aeso_syntax:ann()} | + {bytecode_hash, aeso_syntax:ann()} | + {var_args, aeso_syntax:ann(), aeso_syntax:expr()} }). -type field_constraint() :: #field_constraint{} | #record_create_constraint{} | #is_contract_constraint{}. @@ -99,7 +101,7 @@ -type qname() :: [string()]. -type typesig() :: {type_sig, aeso_syntax:ann(), type_constraints(), [aeso_syntax:named_arg_t()], [type()], type()}. --type type_constraints() :: none | bytes_concat | bytes_split | address_to_contract. +-type type_constraints() :: none | bytes_concat | bytes_split | address_to_contract | bytecode_hash. -type fun_info() :: {aeso_syntax:ann(), typesig() | type()}. -type type_info() :: {aeso_syntax:ann(), typedef()}. @@ -284,9 +286,14 @@ bind_contract({Contract, Ann, Id, Contents}, Env) [ {field_t, Sys, {id, Sys, ?CONSTRUCTOR_MOCK_NAME}, contract_call_type( case [ [ArgT || {typed, _, _, ArgT} <- Args] - || {letfun, _, {id, _, "init"}, Args, _, _} <- Contents] - ++ [ Args || {fun_decl, _, {id, _, "init"}, {fun_t, _, _, Args, _}} <- Contents] - ++ [ Args || {fun_decl, _, {id, _, "init"}, {type_sig, _, _, _, Args, _}} <- Contents] + || {letfun, AnnF, {id, _, "init"}, Args, _, _} <- Contents, + aeso_syntax:get_ann(entrypoint, AnnF, false)] + ++ [ Args + || {fun_decl, AnnF, {id, _, "init"}, {fun_t, _, _, Args, _}} <- Contents, + aeso_syntax:get_ann(entrypoint, AnnF, false)] + ++ [ Args + || {fun_decl, AnnF, {id, _, "init"}, {type_sig, _, _, _, Args, _}} <- Contents, + aeso_syntax:get_ann(entrypoint, AnnF, false)] of [] -> {fun_t, [stateful,payable|Sys], [], [], {id, Sys, "void"}}; [Args] -> {fun_t, [stateful,payable|Sys], [], Args, {id, Sys, "void"}} @@ -804,7 +811,7 @@ identify_main_contract(Contracts) -> (Contracts -- Children) ++ [{contract_main, Ann, Con, Body}]; _ -> type_error({ambiguous_main_contract}) end; - [_] -> Contracts; + [_] -> (Contracts -- Mains) ++ Mains; %% Move to the end _ -> type_error({multiple_main_contracts}) end. @@ -1615,15 +1622,20 @@ infer_var_args_fun(Env, {typed, Ann, Fun, FunType0}, NamedArgs, ArgTypes) -> {fun_t, _, NamedArgsT, var_args, RetT} = FunType0, GasCapMock = {named_arg_t, Ann, {id, Ann, "gas"}, {id, Ann, "int"}, {int, Ann, 0}}, ProtectedMock = {named_arg_t, Ann, {id, Ann, "protected"}, {id, Ann, "bool"}, {bool, Ann, false}}, - - check_contract_construction(Env, RetT, Fun, [GasCapMock, ProtectedMock|NamedArgsT], ArgTypes, RetT), + NamedArgsT1 = case NamedArgsT of + [Value|Rest] -> [GasCapMock, Value, ProtectedMock|Rest]; + % generally type error, but will be caught + _ -> [GasCapMock, ProtectedMock|NamedArgsT] + end, + check_contract_construction(Env, RetT, Fun, NamedArgsT1, ArgTypes, RetT), {fun_t, Ann, NamedArgsT, ArgTypes, RetT}; {qid, _, ["Chain", "clone"]} -> {fun_t, _, NamedArgsT, var_args, RetT} = FunType0, ContractT = case [ContractT || {named_arg, _, {id, _, "ref"}, {typed, _, _, ContractT}} <- NamedArgs] of [C] -> C; - _ -> type_error({clone_no_contract, Ann}) + _ -> type_error({clone_no_contract, Ann}), + fresh_uvar(Ann) end, NamedArgsTNoRef = lists:filter(fun({named_arg_t, _, {id, _, "ref"}, _, _}) -> false; (_) -> true end, NamedArgsT), @@ -1634,20 +1646,23 @@ infer_var_args_fun(Env, {typed, Ann, Fun, FunType0}, NamedArgs, ArgTypes) -> end, {typed, Ann, Fun, FunType}. +-spec check_contract_construction(env(), utype(), utype(), named_args_t(), [utype()], utype()) -> ok. check_contract_construction(Env, ContractT, Fun, NamedArgsT, ArgTypes, RetT) -> Ann = aeso_syntax:get_ann(Fun), InitT = fresh_uvar(Ann), unify(Env, InitT, {fun_t, Ann, NamedArgsT, ArgTypes, fresh_uvar(Ann)}, {checking_init_args, Ann, ContractT, ArgTypes}), unify(Env, RetT, ContractT, {return_contract, Fun, ContractT}), - constrain([ #field_constraint{ - record_t = unfold_types_in_type(Env, ContractT), - field = {id, Ann, ?CONSTRUCTOR_MOCK_NAME}, - field_t = InitT, - kind = project, - context = {var_args, Ann, Fun} } - , #is_contract_constraint{ contract_t = ContractT, - context = {var_args, Ann, Fun} } - ]). + constrain( + [ #field_constraint{ + record_t = unfold_types_in_type(Env, ContractT), + field = {id, Ann, ?CONSTRUCTOR_MOCK_NAME}, + field_t = InitT, + kind = project, + context = {var_args, Ann, Fun} } + , #is_contract_constraint{ contract_t = ContractT, + context = {var_args, Ann, Fun} } + ]), + ok. split_args(Args0) -> NamedArgs = [ Arg || Arg = {named_arg, _, _, _} <- Args0 ], @@ -2430,15 +2445,10 @@ unify1(Env, {if_t, _, {id, _, Id}, Then1, Else1}, {if_t, _, {id, _, Id}, Then2, unify1(_Env, {fun_t, _, _, _, _}, {fun_t, _, _, var_args, _}, When) -> type_error({unify_varargs, When}); unify1(_Env, {fun_t, _, _, var_args, _}, {fun_t, _, _, _, _}, When) -> - error({unify_varargs, When}); + type_error({unify_varargs, When}); unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When) when length(Args1) == length(Args2) -> - {Named1_, Named2_} = - if is_list(Named1) andalso is_list(Named2) -> - {lists:keysort(3, Named1), lists:keysort(3, Named2)}; - true -> {Named1, Named2} - end, - unify(Env, Named1_, Named2_, When) andalso + unify(Env, Named1, Named2, When) andalso unify(Env, Args1, Args2, When) andalso unify(Env, Result1, Result2, When); unify1(Env, {app_t, _, {Tag, _, F}, Args1}, {app_t, _, {Tag, _, F}, Args2}, When) when length(Args1) == length(Args2), Tag == id orelse Tag == qid -> @@ -2934,18 +2944,21 @@ mk_error({conflicting_updates_for_field, Upd, Key}) -> Msg = io_lib:format("Conflicting updates for field '~s'\n", [Key]), mk_t_err(pos(Upd), Msg); mk_error({ambiguous_main_contract}) -> - Msg = "Could not deduce the main contract. You can point it manually with `main` keyword.", + Msg = "Could not deduce the main contract. You can point it out manually with the `main` keyword.", mk_t_err(pos(0, 0), Msg); mk_error({main_contract_undefined}) -> - Msg = "No contract defined.", + Msg = "No contract defined.\n", mk_t_err(pos(0, 0), Msg); mk_error({multiple_main_contracts}) -> - Msg = "Up to one main contract can be defined.", + Msg = "Only one main contract can be defined.\n", mk_t_err(pos(0, 0), Msg); mk_error({unify_varargs, When}) -> - Msg = io_lib:format("Cannot unify variable argument list.\n"), + Msg = "Cannot unify variable argument list.\n", {Pos, Ctxt} = pp_when(When), mk_t_err(Pos, Msg, Ctxt); +mk_error({clone_no_contract, Ann}) -> + Msg = "Chain.clone requires `ref` named argument of contract type.\n", + mk_t_err(pos(Ann), Msg); mk_error(Err) -> Msg = io_lib:format("Unknown error: ~p\n", [Err]), mk_t_err(pos(0, 0), Msg). @@ -3096,15 +3109,15 @@ pp_when(unknown) -> {pos(0,0), ""}. pp_why_record({var_args, Ann, Fun}) -> {pos(Ann), io_lib:format("arising from resolution of variadic function ~s (at ~s)", - [pp_expr("", Fun), pp_loc(Fun)])}; + [pp_expr(Fun), pp_loc(Fun)])}; pp_why_record(Fld = {field, _Ann, LV, _E}) -> {pos(Fld), io_lib:format("arising from an assignment of the field ~s (at ~s)", - [pp_expr("", {lvalue, [], LV}), pp_loc(Fld)])}; + [pp_expr({lvalue, [], LV}), pp_loc(Fld)])}; pp_why_record(Fld = {field, _Ann, LV, _Alias, _E}) -> {pos(Fld), io_lib:format("arising from an assignment of the field ~s (at ~s)", - [pp_expr("", {lvalue, [], LV}), pp_loc(Fld)])}; + [pp_expr({lvalue, [], LV}), pp_loc(Fld)])}; pp_why_record({proj, _Ann, Rec, FldName}) -> {pos(Rec), io_lib:format("arising from the projection of the field ~s (at ~s)", diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index c51cce5..8199297 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -142,7 +142,7 @@ -type child_con_env() :: #{sophia_name() => fcode()}. -type builtins() :: #{ sophia_name() => {builtin(), non_neg_integer() | none | variable} }. --type context() :: {main_contract, string()} +-type context() :: {contract_def, string()} | {namespace, string()} | {abstract_contract, string()}. @@ -158,7 +158,8 @@ state_layout => state_layout(), context => context(), vars => [var_name()], - functions := #{ fun_name() => fun_def() } }. + functions := #{ fun_name() => fun_def() } + }. -define(HASH_BYTES, 32). @@ -166,7 +167,7 @@ %% Main entrypoint. Takes typed syntax produced by aeso_ast_infer_types:infer/1,2 %% and produces Fate intermediate code. --spec ast_to_fcode(aeso_syntax:ast(), [option()]) -> fcode(). +-spec ast_to_fcode(aeso_syntax:ast(), [option()]) -> {env(), fcode()}. ast_to_fcode(Code, Options) -> init_fresh_names(), {Env1, FCode1} = to_fcode(init_env(Options), Code), @@ -232,7 +233,8 @@ init_env(Options) -> ["Chain", "GAAttachTx"] => #con_tag{ tag = 21, arities = ChainTxArities } }, options => Options, - functions => #{} }. + functions => #{} + }. -spec builtins() -> builtins(). builtins() -> @@ -322,13 +324,13 @@ get_option(Opt, Env, Default) -> %% -- Compilation ------------------------------------------------------------ --spec to_fcode(env(), aeso_syntax:ast()) -> fcode(). +-spec to_fcode(env(), aeso_syntax:ast()) -> {env(), fcode()}. to_fcode(Env, [{Contract, Attrs, Con = {con, _, Name}, Decls}|Rest]) when ?IS_CONTRACT_HEAD(Contract) -> case Contract =:= contract_interface of false -> #{ builtins := Builtins } = Env, - ConEnv = Env#{ context => {main_contract, Name}, + ConEnv = Env#{ context => {contract_def, Name}, builtins => Builtins#{[Name, "state"] => {get_state, none}, [Name, "put"] => {set_state, 1}, [Name, "Chain", "event"] => {chain_event, 1}} }, @@ -357,8 +359,8 @@ to_fcode(Env, [{Contract, Attrs, Con = {con, _, Name}, Decls}|Rest]) Env1 = decls_to_fcode(Env#{ context => {abstract_contract, Con} }, Decls), to_fcode(Env1, Rest) end; -to_fcode(_Env, [NotMain = {NotMainHead, _ ,_ , _}]) when NotMainHead =/= main_contract -> - fcode_error({last_declaration_must_be_main_contract, NotMain}); +to_fcode(_Env, [NotMain = {NotMainHead, _ ,_ , _}]) when NotMainHead =/= contract_def -> + fcode_error({last_declaration_must_be_contract_def, NotMain}); to_fcode(Env, [{namespace, _, {con, _, Con}, Decls} | Code]) -> Env1 = decls_to_fcode(Env#{ context => {namespace, Con} }, Decls), to_fcode(Env1, Code). @@ -372,7 +374,7 @@ decls_to_fcode(Env, Decls) -> end, Env1, Decls). -spec decl_to_fcode(env(), aeso_syntax:decl()) -> env(). -decl_to_fcode(Env = #{context := {main_contract, _}}, {fun_decl, _, Id, _}) -> +decl_to_fcode(Env = #{context := {contract_def, _}}, {fun_decl, _, Id, _}) -> case is_no_code(Env) of false -> fcode_error({missing_definition, Id}); true -> Env @@ -435,7 +437,7 @@ typedef_to_fcode(Env, Id = {id, _, Name}, Xs, Def) -> Env3 = compute_state_layout(Env2, Name, FDef), bind_type(Env3, Q, FDef). -compute_state_layout(Env = #{ context := {main_contract, _} }, "state", Type) -> +compute_state_layout(Env = #{ context := {contract_def, _} }, "state", Type) -> NoLayout = get_option(no_flatten_state, Env), Layout = case Type([]) of @@ -461,7 +463,7 @@ compute_state_layout(R, [H | T]) -> compute_state_layout(R, _) -> {R + 1, {reg, R}}. -check_state_and_event_types(#{ context := {main_contract, _} }, Id, [_ | _]) -> +check_state_and_event_types(#{ context := {contract_def, _} }, Id, [_ | _]) -> case Id of {id, _, "state"} -> fcode_error({parameterized_state, Id}); {id, _, "event"} -> fcode_error({parameterized_event, Id}); @@ -1676,7 +1678,7 @@ add_fun_env(Env = #{ fun_env := FunEnv }, Decls) -> make_fun_name(#{ context := Context }, Ann, Name) -> Entrypoint = proplists:get_value(entrypoint, Ann, false), case Context of - {main_contract, Main} -> + {contract_def, Main} -> if Entrypoint -> {entrypoint, list_to_binary(Name)}; true -> {local_fun, [Main, Name]} end; @@ -1688,7 +1690,7 @@ make_fun_name(#{ context := Context }, Ann, Name) -> current_namespace(#{ context := Cxt }) -> case Cxt of {abstract_contract, Con} -> Con; - {main_contract, Con} -> Con; + {contract_def, Con} -> Con; {namespace, NS} -> NS end. @@ -2035,8 +2037,11 @@ internal_error(Error) -> %% -- Pretty printing -------------------------------------------------------- format_fcode(#{ functions := Funs }) -> - prettypr:format(pp_above( - [ pp_fun(Name, Def) || {Name, Def} <- maps:to_list(Funs) ])). + prettypr:format(format_funs(Funs)). + +format_funs(Funs) -> + pp_above( + [ pp_fun(Name, Def) || {Name, Def} <- maps:to_list(Funs) ]). format_fexpr(E) -> prettypr:format(pp_fexpr(E)). @@ -2153,7 +2158,9 @@ pp_fexpr({set_state, R, A}) -> pp_call(pp_text("set_state"), [{lit, {int, R}}, A]); pp_fexpr({get_state, R}) -> pp_call(pp_text("get_state"), [{lit, {int, R}}]); -pp_fexpr({switch, Split}) -> pp_split(Split). +pp_fexpr({switch, Split}) -> pp_split(Split); +pp_fexpr({contract_code, Contract}) -> + pp_beside(pp_text("contract "), pp_text(Contract)). pp_call(Fun, Args) -> pp_beside(Fun, pp_fexpr({tuple, Args})). diff --git a/src/aeso_code_errors.erl b/src/aeso_code_errors.erl index f12db23..cc547b7 100644 --- a/src/aeso_code_errors.erl +++ b/src/aeso_code_errors.erl @@ -11,7 +11,7 @@ -export([format/1, pos/1]). format({last_declaration_must_be_contract, Decl = {Kind, _, {con, _, C}, _}}) -> - Msg = io_lib:format("Expected a contract as the last declaration instead of the ~p '~s'\n", + Msg = io_lib:format("Expected a main contract as the last declaration instead of the ~p '~s'\n", [Kind, C]), mk_err(pos(Decl), Msg); format({missing_init_function, Con}) -> diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index 6a3c576..4a69687 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -180,7 +180,7 @@ string_to_code(ContractString, Options) -> , type_env => TypeEnv , ast => Ast }; fate -> - {Env, Fcode} = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, Options), + {Env, Fcode} = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, [{original_src, ContractString}|Options]), #{ fcode => Fcode , fcode_env => Env , unfolded_typed_ast => UnfoldedTypedAst diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index a296001..2186018 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -184,15 +184,16 @@ lit_to_fate(Env, L) -> {oracle_pubkey, K} -> aeb_fate_data:make_oracle(K); {oracle_query_id, H} -> aeb_fate_data:make_oracle_query(H); {contract_code, C} -> - FCode = maps:get(C, Env#env.child_contracts), - SCode = compile(Env#env.child_contracts, FCode, Env#env.options), - ByteCode = aeb_fate_code:serialize(SCode, []), + Options = Env#env.options, + FCode = maps:get(C, Env#env.child_contracts), + FateCode = compile(Env#env.child_contracts, FCode, Options), + ByteCode = aeb_fate_code:serialize(FateCode, []), {ok, Version} = aeso_compiler:version(), + OriginalSourceCode = proplists:get_value(original_src, Options, ""), Code = #{byte_code => ByteCode, compiler_version => Version, - contract_source => "child_contract_src_placeholder", + source_hash => crypto:hash(sha256, OriginalSourceCode ++ [0] ++ C), type_info => [], - fate_code => SCode, abi_version => aeb_fate_abi:abi_version(), payable => maps:get(payable, FCode) }, @@ -586,7 +587,6 @@ builtin_to_scode(Env, chain_clone, [Contract, TypeRep, Value, Prot | InitArgs] ); _ -> - io:format("\n\n************* GAS CAP: ~p\n\n", [GasCap]), call_to_scode(Env, aeb_fate_ops:clone_g(?a, ?a, ?a, ?a, ?a), [Contract, TypeRep, Value, GasCap, Prot | InitArgs] ) @@ -985,7 +985,7 @@ attributes(I) -> {'CREATE', A, B, C} -> Impure(?a, [A, B, C]); {'CLONE', A, B, C, D} -> Impure(?a, [A, B, C, D]); {'CLONE_G', A, B, C, D, E} -> Impure(?a, [A, B, C, D, E]); - {'BYTECODE_HASH', A, B} -> Pure(A, [B]); + {'BYTECODE_HASH', A, B} -> Impure(A, [B]); {'ABORT', A} -> Impure(pc, A); {'EXIT', A} -> Impure(pc, A); 'NOP' -> Pure(none, []) diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index b48e4ff..cab8edc 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -26,7 +26,7 @@ -type ann_format() :: '?:' | hex | infix | prefix | elif. -type ann() :: [ {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()} - | stateful | private] | payable | main | interface. + | stateful | private | payable | main | interface]. -type name() :: string(). -type id() :: {id, ann(), name()}. diff --git a/test/aeso_aci_tests.erl b/test/aeso_aci_tests.erl index 3254e69..46db9e8 100644 --- a/test/aeso_aci_tests.erl +++ b/test/aeso_aci_tests.erl @@ -103,17 +103,24 @@ aci_test_contract(Name) -> true -> [debug_mode]; false -> [] end ++ [{include, {file_system, [aeso_test_utils:contract_path()]}}], - {ok, JSON} = aeso_aci:contract_interface(json, String, Opts), - {ok, #{aci := JSON1}} = aeso_compiler:from_string(String, [{aci, json}, {backend, fate} | Opts]), - ?assertEqual(JSON, JSON1), + JSON = case aeso_aci:contract_interface(json, String, Opts) of + {ok, J} -> J; + {error, ErrorStringJ} when is_binary(ErrorStringJ) -> error(ErrorStringJ); + {error, ErrorJ} -> aeso_compiler_tests:print_and_throw(ErrorJ) + end, + case aeso_compiler:from_string(String, [{aci, json}, {backend, fate} | Opts]) of + {ok, #{aci := JSON1}} -> + ?assertEqual(JSON, JSON1), + io:format("JSON:\n~p\n", [JSON]), + {ok, ContractStub} = aeso_aci:render_aci_json(JSON), - io:format("JSON:\n~p\n", [JSON]), - {ok, ContractStub} = aeso_aci:render_aci_json(JSON), + io:format("STUB:\n~s\n", [ContractStub]), + check_stub(ContractStub, [{src_file, Name}]), - io:format("STUB:\n~s\n", [ContractStub]), - check_stub(ContractStub, [{src_file, Name}]), - - ok. + ok; + {error, ErrorString} when is_binary(ErrorString) -> error(ErrorString); + {error, Error} -> aeso_compiler_tests:print_and_throw(Error) + end. check_stub(Stub, Options) -> try aeso_parser:string(binary_to_list(Stub), Options) of diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 2691b03..83282d1 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -34,9 +34,7 @@ simple_compile_test_() -> #{fate_code := Code} when Backend == fate -> Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)), ?assertMatch({X, X}, {Code1, Code}); - ErrBin -> - io:format("\n~s", [ErrBin]), - error(ErrBin) + Error -> print_and_throw(Error) end end} || ContractName <- compilable_contracts(), Backend <- [aevm, fate], not lists:member(ContractName, not_compilable_on(Backend))] ++ @@ -878,14 +876,26 @@ validation_fails() -> "Byte code contract is not payable, but source code contract is.">>]}]. validate(Contract1, Contract2) -> - ByteCode = #{ fate_code := FCode } = compile(fate, Contract1), - FCode1 = aeb_fate_code:serialize(aeb_fate_code:strip_init_function(FCode)), - Source = aeso_test_utils:read_contract(Contract2), - aeso_compiler:validate_byte_code( - ByteCode#{ byte_code := FCode1 }, Source, - case lists:member(Contract2, debug_mode_contracts()) of - true -> [debug_mode]; - false -> [] - end ++ - [{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]). + case compile(fate, Contract1) of + ByteCode = #{ fate_code := FCode } -> + FCode1 = aeb_fate_code:serialize(aeb_fate_code:strip_init_function(FCode)), + Source = aeso_test_utils:read_contract(Contract2), + aeso_compiler:validate_byte_code( + ByteCode#{ byte_code := FCode1 }, Source, + case lists:member(Contract2, debug_mode_contracts()) of + true -> [debug_mode]; + false -> [] + end ++ + [{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]); + Error -> print_and_throw(Error) + end. +print_and_throw(Err) -> + case Err of + ErrBin when is_binary(ErrBin) -> + io:format("\n~s", [ErrBin]), + error(ErrBin); + Errors -> + io:format("Compilation error:\n~s", [string:join([aeso_errors:pp(E) || E <- Errors], "\n\n")]), + error(compilation_error) + end. diff --git a/test/contracts/clone.aes b/test/contracts/clone.aes index 0b3b04a..26c41d4 100644 --- a/test/contracts/clone.aes +++ b/test/contracts/clone.aes @@ -14,7 +14,7 @@ main contract C = let s1 = Chain.clone(ref=s) let Some(s2) = Chain.clone(ref=s, protected=true) let None = Chain.clone(ref=s, protected=true, gas=1) - let None = Chain.clone(ref=l, protected=true, 123) + let None = Chain.clone(ref=l, protected=true, 123) // since it should be HigherOrderState underneath let s3 = Chain.clone(ref=s1) require(s1.apply(2137) == 2137, "APPLY_S1_0") require(s2.apply(2137) == 2137, "APPLY_S2_0") diff --git a/test/contracts/test.aes b/test/contracts/test.aes index 2a0aab7..2b3783e 100644 --- a/test/contracts/test.aes +++ b/test/contracts/test.aes @@ -1,3 +1,6 @@ +// This is a custom test file if you need to run a compiler without +// changing aeso_compiler_tests.erl + include "List.aes" contract IntegerHolder = @@ -5,10 +8,5 @@ contract IntegerHolder = entrypoint init(x) = x entrypoint get() = state -main contract IntegerCollection = - record state = {template: IntegerHolder, payload: list(IntegerHolder)} - stateful entrypoint init() = {template = Chain.create(0), payload = []} - stateful entrypoint add(x) = - put(state{payload @ p = Chain.clone(ref=state.template, x) :: p}) - x - entrypoint sum() = List.sum(List.map((h) => h.get(), state.payload)) \ No newline at end of file +main contract Test = + stateful entrypoint f(c) = Chain.clone(ref=c, 123) \ No newline at end of file