diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index a55ef69..0e6b3e9 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -707,20 +707,34 @@ check_unexpected(Xs) -> check_modifiers(Env, Contracts) -> create_type_errors(), - [ case C of - {contract, _, Con, Decls} -> - check_modifiers1(contract, Decls), - case {lists:keymember(letfun, 1, Decls), - [ D || D <- Decls, aeso_syntax:get_ann(entrypoint, D, false) ]} of - {true, []} -> type_error({contract_has_no_entrypoints, Con}); - _ -> ok - end; - {namespace, _, _, Decls} -> check_modifiers1(namespace, Decls); - {pragma, Ann, Pragma} -> check_pragma(Env, Ann, Pragma); - Decl -> type_error({bad_top_level_decl, Decl}) - end || C <- Contracts ], + check_modifiers_(Env, Contracts), destroy_and_report_type_errors(Env). +check_modifiers_(Env, [{contract, _, Con, Decls} | Rest]) -> + IsMain = Rest == [], + check_modifiers1(contract, Decls), + case {lists:keymember(letfun, 1, Decls), + [ D || D <- Decls, aeso_syntax:get_ann(entrypoint, D, false) ]} of + {true, []} -> type_error({contract_has_no_entrypoints, Con}); + _ when not IsMain -> + case [ {Ann, Id} || {letfun, Ann, Id, _, _, _} <- Decls ] of + [{Ann, Id} | _] -> type_error({definition_in_non_main_contract, Ann, Id}); + [] -> ok + end; + _ -> ok + end, + check_modifiers_(Env, Rest); +check_modifiers_(Env, [{namespace, _, _, Decls} | Rest]) -> + check_modifiers1(namespace, Decls), + check_modifiers_(Env, Rest); +check_modifiers_(Env, [{pragma, Ann, Pragma} | Rest]) -> + check_pragma(Env, Ann, Pragma), + check_modifiers_(Env, Rest); +check_modifiers_(Env, [Decl | Rest]) -> + type_error({bad_top_level_decl, Decl}), + check_modifiers_(Env, Rest); +check_modifiers_(_Env, []) -> ok. + -spec check_pragma(env(), aeso_syntax:ann(), aeso_syntax:pragma()) -> ok. check_pragma(_Env, Ann, {compiler, Op, Ver}) -> case aeso_compiler:numeric_version() of @@ -2359,6 +2373,10 @@ mk_error({contract_has_no_entrypoints, Con}) -> "contract functions must be declared with the 'entrypoint' keyword instead of\n" "'function'.\n", [pp_expr("", Con), pp_loc(Con)]), mk_t_err(pos(Con), Msg); +mk_error({definition_in_non_main_contract, Ann, {id, _, Id}}) -> + Msg = "Only the main contract can contain defined functions or entrypoints.\n", + Cxt = io_lib:format("Fix: replace the definition of '~s' by a type signature.\n", [Id]), + mk_t_err(pos(Ann), Msg, Cxt); mk_error({unbound_type, Type}) -> Msg = io_lib:format("Unbound type ~s (at ~s).\n", [pp_type("", Type), pp_loc(Type)]), mk_t_err(pos(Type), Msg); diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 78d4a96..c2036d2 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -573,6 +573,10 @@ failing_contracts() -> <>]) + , ?TYPE_ERROR(multiple_contracts, + [<>]) ]. -define(Path(File), "code_errors/" ??File). diff --git a/test/contracts/multiple_contracts.aes b/test/contracts/multiple_contracts.aes new file mode 100644 index 0000000..41df9b4 --- /dev/null +++ b/test/contracts/multiple_contracts.aes @@ -0,0 +1,5 @@ +contract ContractOne = + entrypoint foo() = "foo" + +contract ContractTwo = + entrypoint bar() = "bar"