Fail gracefully on higher-order state in AEVM and accept it in FATE
This commit is contained in:
parent
325d69e96d
commit
602e99512f
@ -640,10 +640,12 @@ validate_aens_resolve_type(Ann, {app_t, _, _, [Type]}, {variant, [[], [FType]]})
|
|||||||
_ -> fcode_error({invalid_aens_resolve_type, Ann, Type})
|
_ -> fcode_error({invalid_aens_resolve_type, Ann, Type})
|
||||||
end.
|
end.
|
||||||
|
|
||||||
ensure_first_order_entrypoint(Ann, Name, Args, Ret, FArgs, FRet) ->
|
ensure_first_order_entrypoint(Ann, Id = {id, _, Name}, Args, Ret, FArgs, FRet) ->
|
||||||
[ ensure_first_order(FT, {invalid_entrypoint, higher_order, Ann1, Name, {argument, X, T}})
|
[ ensure_first_order(FT, {invalid_entrypoint, higher_order, Ann1, Id, {argument, X, T}})
|
||||||
|| {{arg, Ann1, X, T}, {_, FT}} <- lists:zip(Args, FArgs) ],
|
|| {{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.
|
ok.
|
||||||
|
|
||||||
ensure_monomorphic(Type, Err) ->
|
ensure_monomorphic(Type, Err) ->
|
||||||
|
@ -67,7 +67,7 @@ contract_to_icode([{namespace, _, Name, Defs} | Rest], Icode) ->
|
|||||||
NS = aeso_icode:get_namespace(Icode),
|
NS = aeso_icode:get_namespace(Icode),
|
||||||
Icode1 = contract_to_icode(Defs, aeso_icode:enter_namespace(Name, 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(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 }) ->
|
Icode = #{ types := Types, constructors := Constructors }) ->
|
||||||
TypeDef = make_type_def(Args, Def, Icode),
|
TypeDef = make_type_def(Args, Def, Icode),
|
||||||
NewConstructors =
|
NewConstructors =
|
||||||
@ -83,7 +83,11 @@ contract_to_icode([{type_def, _Attrib, Id = {id, _, Name}, Args, Def} | Rest],
|
|||||||
Icode1 = Icode#{ types := Types#{ TName => TypeDef },
|
Icode1 = Icode#{ types := Types#{ TName => TypeDef },
|
||||||
constructors := maps:merge(Constructors, NewConstructors) },
|
constructors := maps:merge(Constructors, NewConstructors) },
|
||||||
Icode2 = case Name of
|
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});
|
"state" -> gen_error({parameterized_state, Id});
|
||||||
"event" when Args == [] -> Icode1#{ event_type => Def };
|
"event" when Args == [] -> Icode1#{ event_type => Def };
|
||||||
"event" -> gen_error({parameterized_event, Id});
|
"event" -> gen_error({parameterized_event, Id});
|
||||||
|
@ -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)]),
|
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]),
|
Cxt = io_lib:format("The ~s type must not be ~s.\n", [What, WhyS]),
|
||||||
mk_err(pos(Ann), Msg, Cxt);
|
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) ->
|
format(Err) ->
|
||||||
mk_err(aeso_errors:pos(0, 0), io_lib:format("Unknown error: ~p\n", [Err])).
|
mk_err(aeso_errors:pos(0, 0), io_lib:format("Unknown error: ~p\n", [Err])).
|
||||||
|
@ -601,5 +601,9 @@ failing_code_gen_contracts() ->
|
|||||||
"Invalid oracle type\n"
|
"Invalid oracle type\n"
|
||||||
" oracle(string, (int) => int)\n"
|
" oracle(string, (int) => int)\n"
|
||||||
"The response type must not be higher-order (contain function types).")
|
"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.")
|
||||||
].
|
].
|
||||||
|
|
||||||
|
7
test/contracts/code_errors/higher_order_state.aes
Normal file
7
test/contracts/code_errors/higher_order_state.aes
Normal file
@ -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) })
|
Loading…
x
Reference in New Issue
Block a user