From 9df883b1554cafc4215e1ba9e041a526cc89bb7f Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Tue, 21 Jun 2022 19:31:21 +0400 Subject: [PATCH] Move missing_init_function to the type checker --- src/aeso_ast_infer_types.erl | 28 +++++++++++++++---- src/aeso_ast_to_fcode.erl | 14 ++++------ src/aeso_code_errors.erl | 4 --- test/aeso_compiler_tests.erl | 8 ++++-- .../missing_init_function.aes | 0 5 files changed, 34 insertions(+), 20 deletions(-) rename test/contracts/{code_errors => }/missing_init_function.aes (100%) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index b288961..9cf197d 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -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). diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index 0d769a7..8c1a962 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -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. diff --git a/src/aeso_code_errors.erl b/src/aeso_code_errors.erl index 66997a6..70f9c47 100644 --- a/src/aeso_code_errors.erl +++ b/src/aeso_code_errors.erl @@ -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, diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 1b26101..561254c 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -1035,6 +1035,11 @@ failing_contracts() -> [<> ]) + , ?TYPE_ERROR(missing_init_function, + [<> + ]) ]. -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" diff --git a/test/contracts/code_errors/missing_init_function.aes b/test/contracts/missing_init_function.aes similarity index 100% rename from test/contracts/code_errors/missing_init_function.aes rename to test/contracts/missing_init_function.aes