From 602e99512f6ccba99ed9f256d9e9eacd04b0f1a9 Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Tue, 3 Sep 2019 17:24:40 +0200 Subject: [PATCH] Fail gracefully on higher-order state in AEVM and accept it in FATE --- src/aeso_ast_to_fcode.erl | 8 +++++--- src/aeso_ast_to_icode.erl | 8 ++++++-- src/aeso_code_errors.erl | 4 ++++ test/aeso_compiler_tests.erl | 4 ++++ test/contracts/code_errors/higher_order_state.aes | 7 +++++++ 5 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 test/contracts/code_errors/higher_order_state.aes diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index f69ee86..e8ec0c9 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -640,10 +640,12 @@ validate_aens_resolve_type(Ann, {app_t, _, _, [Type]}, {variant, [[], [FType]]}) _ -> fcode_error({invalid_aens_resolve_type, Ann, Type}) end. -ensure_first_order_entrypoint(Ann, Name, Args, Ret, FArgs, FRet) -> - [ ensure_first_order(FT, {invalid_entrypoint, higher_order, Ann1, Name, {argument, X, T}}) +ensure_first_order_entrypoint(Ann, Id = {id, _, Name}, Args, Ret, FArgs, FRet) -> + [ ensure_first_order(FT, {invalid_entrypoint, higher_order, Ann1, Id, {argument, X, T}}) || {{arg, Ann1, X, T}, {_, FT}} <- lists:zip(Args, FArgs) ], - ensure_first_order(FRet, {invalid_entrypoint, higher_order, Ann, Name, {result, Ret}}), + [ ensure_first_order(FRet, {invalid_entrypoint, higher_order, Ann, Id, {result, Ret}}) + || Name /= "init" ], %% init can return higher-order values, since they're written to the store + %% rather than being returned. ok. ensure_monomorphic(Type, Err) -> diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index 2a9ef27..472c503 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -67,7 +67,7 @@ contract_to_icode([{namespace, _, Name, Defs} | Rest], Icode) -> NS = aeso_icode:get_namespace(Icode), Icode1 = contract_to_icode(Defs, aeso_icode:enter_namespace(Name, Icode)), contract_to_icode(Rest, aeso_icode:set_namespace(NS, Icode1)); -contract_to_icode([{type_def, _Attrib, Id = {id, _, Name}, Args, Def} | Rest], +contract_to_icode([Decl = {type_def, _Attrib, Id = {id, _, Name}, Args, Def} | Rest], Icode = #{ types := Types, constructors := Constructors }) -> TypeDef = make_type_def(Args, Def, Icode), NewConstructors = @@ -83,7 +83,11 @@ contract_to_icode([{type_def, _Attrib, Id = {id, _, Name}, Args, Def} | Rest], Icode1 = Icode#{ types := Types#{ TName => TypeDef }, constructors := maps:merge(Constructors, NewConstructors) }, Icode2 = case Name of - "state" when Args == [] -> Icode1#{ state_type => ast_typerep(Def, Icode) }; + "state" when Args == [] -> + case is_first_order_type(Def) of + true -> Icode1#{ state_type => ast_typerep(Def, Icode) }; + false -> gen_error({higher_order_state, Decl}) + end; "state" -> gen_error({parameterized_state, Id}); "event" when Args == [] -> Icode1#{ event_type => Def }; "event" -> gen_error({parameterized_event, Id}); diff --git a/src/aeso_code_errors.erl b/src/aeso_code_errors.erl index d51fff6..9a4d8b2 100644 --- a/src/aeso_code_errors.erl +++ b/src/aeso_code_errors.erl @@ -79,6 +79,10 @@ format({invalid_oracle_type, Why, What, Ann, Type}) -> Msg = io_lib:format("Invalid oracle type\n~s\n", [pp_type(2, Type)]), Cxt = io_lib:format("The ~s type must not be ~s.\n", [What, WhyS]), mk_err(pos(Ann), Msg, Cxt); +format({higher_order_state, {type_def, Ann, _, _, State}}) -> + Msg = io_lib:format("Invalid state type\n~s\n", [pp_type(2, State)]), + Cxt = "The state cannot contain functions in the AEVM. Use FATE if you need this.\n", + mk_err(pos(Ann), Msg, Cxt); format(Err) -> mk_err(aeso_errors:pos(0, 0), io_lib:format("Unknown error: ~p\n", [Err])). diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 9956ce6..ddad533 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -601,5 +601,9 @@ failing_code_gen_contracts() -> "Invalid oracle type\n" " oracle(string, (int) => int)\n" "The response type must not be higher-order (contain function types).") + , ?AEVM(higher_order_state, 3, 3, + "Invalid state type\n" + " {f : (int) => int}\n" + "The state cannot contain functions in the AEVM. Use FATE if you need this.") ]. diff --git a/test/contracts/code_errors/higher_order_state.aes b/test/contracts/code_errors/higher_order_state.aes new file mode 100644 index 0000000..81d15e7 --- /dev/null +++ b/test/contracts/code_errors/higher_order_state.aes @@ -0,0 +1,7 @@ +contract HigherOrderState = + + record state = {f : int => int} + + entrypoint init() = {f = (x) => x} + entrypoint apply(n) = state.f(n) + stateful entrypoint inc() = put(state{ f = (x) => state.f(x + 1) })