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