Move missing_init_function to the type checker
This commit is contained in:
parent
f0c1a96213
commit
9df883b155
@ -1030,8 +1030,8 @@ infer_contract(Env0, What, Defs0, Options) ->
|
||||
{Env4, Defs1} = check_sccs(Env3, FunMap, SCCs, []),
|
||||
%% Remove namespaces used in the current namespace
|
||||
Env5 = Env4#env{ used_namespaces = OldUsedNamespaces },
|
||||
%% Check that `init` doesn't read or write the state
|
||||
check_state_dependencies(Env4, Defs1),
|
||||
%% Check that `init` doesn't read or write the state and that `init` is not missing
|
||||
check_state(Env4, Defs1),
|
||||
destroy_and_report_type_errors(Env4),
|
||||
%% Add inferred types of definitions
|
||||
{Env5, TypeDefs ++ Decls ++ Defs1}.
|
||||
@ -1623,8 +1623,22 @@ check_stateful_named_arg(#env{ stateful = false, current_function = Fun }, {id,
|
||||
end;
|
||||
check_stateful_named_arg(_, _, _) -> ok.
|
||||
|
||||
%% Check that `init` doesn't read or write the state
|
||||
check_state_dependencies(Env, Defs) ->
|
||||
check_state_init(Env) ->
|
||||
Top = Env#env.namespace,
|
||||
StateType = lookup_type(Env, {id, [{origin, system}], "state"}),
|
||||
case unfold_types_in_type(Env, StateType) of
|
||||
false ->
|
||||
ok;
|
||||
{_, {_, {_, {alias_t, {tuple_t, _, []}}}}} ->
|
||||
ok;
|
||||
_ ->
|
||||
#scope{ ann = AnnCon } = get_scope(Env, Top),
|
||||
type_error({missing_init_function, {con, AnnCon, lists:last(Top)}})
|
||||
end.
|
||||
|
||||
%% Check that `init` doesn't read or write the state and that `init` is defined
|
||||
%% when the state type is not unit
|
||||
check_state(Env, Defs) ->
|
||||
Top = Env#env.namespace,
|
||||
GetState = Top ++ ["state"],
|
||||
SetState = Top ++ ["put"],
|
||||
@ -1633,7 +1647,7 @@ check_state_dependencies(Env, Defs) ->
|
||||
Funs = [ {Top ++ [Name], Fun} || Fun = {letfun, _, {id, _, Name}, _Args, _Type, _GuardedBodies} <- Defs ],
|
||||
Deps = maps:from_list([{Name, UsedNames(Def)} || {Name, Def} <- Funs]),
|
||||
case maps:get(Init, Deps, false) of
|
||||
false -> ok; %% No init, so nothing to check
|
||||
false -> get_option(no_code, false) orelse check_state_init(Env);
|
||||
_ ->
|
||||
[ type_error({init_depends_on_state, state, Chain})
|
||||
|| Chain <- get_call_chains(Deps, Init, GetState) ],
|
||||
@ -3504,6 +3518,10 @@ mk_error({parameterized_state, Ann}) ->
|
||||
mk_error({parameterized_event, Ann}) ->
|
||||
Msg = "The event type cannot be parameterized",
|
||||
mk_t_err(pos(Ann), Msg);
|
||||
mk_error({missing_init_function, Con}) ->
|
||||
Msg = io_lib:format("Missing `init` function for the contract `~s`.", [name(Con)]),
|
||||
Cxt = "The `init` function can only be omitted if the state type is `unit`",
|
||||
mk_t_err(pos(Con), Msg, Cxt);
|
||||
mk_error(Err) ->
|
||||
Msg = io_lib:format("Unknown error: ~p", [Err]),
|
||||
mk_t_err(pos(0, 0), Msg).
|
||||
|
@ -326,7 +326,7 @@ get_option(Opt, Env, Default) ->
|
||||
%% -- Compilation ------------------------------------------------------------
|
||||
|
||||
-spec to_fcode(env(), aeso_syntax:ast()) -> {env(), fcode()}.
|
||||
to_fcode(Env, [{Contract, Attrs, Con = {con, _, Name}, _Impls, Decls}|Rest])
|
||||
to_fcode(Env, [{Contract, Attrs, {con, _, Name}, _Impls, Decls}|Rest])
|
||||
when ?IS_CONTRACT_HEAD(Contract) ->
|
||||
case Contract =:= contract_interface of
|
||||
false ->
|
||||
@ -349,7 +349,7 @@ to_fcode(Env, [{Contract, Attrs, Con = {con, _, Name}, _Impls, Decls}|Rest])
|
||||
event_type => EventType,
|
||||
payable => Payable,
|
||||
functions => add_init_function(
|
||||
Env1, Con, StateType,
|
||||
Env1,
|
||||
add_event_function(Env1, EventType, Funs)) },
|
||||
case Contract of
|
||||
contract_main -> [] = Rest, {Env1, ConFcode};
|
||||
@ -1189,11 +1189,11 @@ builtin_to_fcode(_Layout, Builtin, Args) ->
|
||||
|
||||
%% -- Init function --
|
||||
|
||||
add_init_function(Env, Main, StateType, Funs0) ->
|
||||
add_init_function(Env, Funs0) ->
|
||||
case is_no_code(Env) of
|
||||
true -> Funs0;
|
||||
false ->
|
||||
Funs = add_default_init_function(Env, Main, StateType, Funs0),
|
||||
Funs = add_default_init_function(Env, Funs0),
|
||||
InitName = {entrypoint, <<"init">>},
|
||||
InitFun = #{ body := InitBody} = maps:get(InitName, Funs),
|
||||
Funs1 = Funs#{ InitName => InitFun#{ return => {tuple, []},
|
||||
@ -1201,16 +1201,14 @@ add_init_function(Env, Main, StateType, Funs0) ->
|
||||
Funs1
|
||||
end.
|
||||
|
||||
add_default_init_function(_Env, Main, StateType, Funs) ->
|
||||
add_default_init_function(_Env, Funs) ->
|
||||
InitName = {entrypoint, <<"init">>},
|
||||
case maps:get(InitName, Funs, none) of
|
||||
%% Only add default init function if state is unit.
|
||||
none when StateType == {tuple, []} ->
|
||||
none ->
|
||||
Funs#{ InitName => #{attrs => [],
|
||||
args => [],
|
||||
return => {tuple, []},
|
||||
body => {tuple, []}} };
|
||||
none -> fcode_error({missing_init_function, Main});
|
||||
_ -> Funs
|
||||
end.
|
||||
|
||||
|
@ -14,10 +14,6 @@ format({last_declaration_must_be_main_contract, Decl = {Kind, _, {con, _, C}, _}
|
||||
Msg = io_lib:format("Expected a main contract as the last declaration instead of the ~p '~s'",
|
||||
[Kind, C]),
|
||||
mk_err(pos(Decl), Msg);
|
||||
format({missing_init_function, Con}) ->
|
||||
Msg = io_lib:format("Missing init function for the contract '~s'.", [pp_expr(Con)]),
|
||||
Cxt = "The 'init' function can only be omitted if the state type is 'unit'.",
|
||||
mk_err(pos(Con), Msg, Cxt);
|
||||
format({invalid_entrypoint, Why, Ann, {id, _, Name}, Thing}) ->
|
||||
What = case Why of higher_order -> "higher-order (contains function types)";
|
||||
polymorphic -> "polymorphic (contains type variables)" end,
|
||||
|
@ -1035,6 +1035,11 @@ failing_contracts() ->
|
||||
[<<?Pos(3,12)
|
||||
"The event type cannot be parameterized">>
|
||||
])
|
||||
, ?TYPE_ERROR(missing_init_function,
|
||||
[<<?Pos(1,10)
|
||||
"Missing `init` function for the contract `MissingInitFunction`.\n"
|
||||
"The `init` function can only be omitted if the state type is `unit`">>
|
||||
])
|
||||
].
|
||||
|
||||
-define(Path(File), "code_errors/" ??File).
|
||||
@ -1051,9 +1056,6 @@ failing_code_gen_contracts() ->
|
||||
"The return type\n"
|
||||
" (int) => int\n"
|
||||
"of entrypoint 'add' is higher-order (contains function types).")
|
||||
, ?FATE_ERR(missing_init_function, 1, 10,
|
||||
"Missing init function for the contract 'MissingInitFunction'.\n"
|
||||
"The 'init' function can only be omitted if the state type is 'unit'.")
|
||||
, ?FATE_ERR(polymorphic_aens_resolve, 4, 5,
|
||||
"Invalid return type of AENS.resolve:\n"
|
||||
" 'a\n"
|
||||
|
Loading…
x
Reference in New Issue
Block a user