From e5e1f0212d10a7f8a309edf2e7d2d2711bab9f37 Mon Sep 17 00:00:00 2001 From: Gaith Hallak Date: Wed, 22 Jun 2022 17:08:01 +0400 Subject: [PATCH] Move the higher order entrypoint error to type checker --- src/aeso_ast_infer_types.erl | 36 +++++++++++++++++++ src/aeso_ast_to_fcode.erl | 12 +------ src/aeso_code_errors.erl | 22 ------------ test/aeso_compiler_tests.erl | 22 +++++++----- .../higher_order_entrypoint.aes | 0 .../higher_order_entrypoint_return.aes | 0 6 files changed, 50 insertions(+), 42 deletions(-) rename test/contracts/{code_errors => }/higher_order_entrypoint.aes (100%) rename test/contracts/{code_errors => }/higher_order_entrypoint_return.aes (100%) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 9cf197d..1ca4fc5 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -1032,6 +1032,8 @@ infer_contract(Env0, What, Defs0, Options) -> Env5 = Env4#env{ used_namespaces = OldUsedNamespaces }, %% Check that `init` doesn't read or write the state and that `init` is not missing check_state(Env4, Defs1), + %% Check that entrypoints have first-order arg types and return types + check_entrypoints(Defs1), destroy_and_report_type_errors(Env4), %% Add inferred types of definitions {Env5, TypeDefs ++ Decls ++ Defs1}. @@ -1623,6 +1625,27 @@ check_stateful_named_arg(#env{ stateful = false, current_function = Fun }, {id, end; check_stateful_named_arg(_, _, _) -> ok. +check_entrypoints(Defs) -> + [ ensure_first_order_entrypoint(LetFun) + || LetFun <- Defs, + aeso_syntax:get_ann(entrypoint, LetFun, false) ]. + +ensure_first_order_entrypoint({letfun, Ann, Id = {id, _, Name}, Args, Ret, _}) -> + [ ensure_first_order(ArgType, {higher_order_entrypoint, AnnArg, Id, {argument, ArgId, ArgType}}) + || {typed, AnnArg, ArgId, ArgType} <- Args ], + [ ensure_first_order(Ret, {higher_order_entrypoint, Ann, Id, {result, Ret}}) + || Name /= "init" ], %% init can return higher-order values, since they're written to the store + %% rather than being returned. + ok. + +ensure_first_order(Type, Err) -> + is_first_order(Type) orelse type_error(Err). + +is_first_order({fun_t, _, _, _, _}) -> false; +is_first_order(Ts) when is_list(Ts) -> lists:all(fun is_first_order/1, Ts); +is_first_order(Tup) when is_tuple(Tup) -> is_first_order(tuple_to_list(Tup)); +is_first_order(_) -> true. + check_state_init(Env) -> Top = Env#env.namespace, StateType = lookup_type(Env, {id, [{origin, system}], "state"}), @@ -3522,6 +3545,19 @@ 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({higher_order_entrypoint, Ann, {id, _, Name}, Thing}) -> + What = "higher-order (contains function types)", + ThingS = case Thing of + {argument, X, T} -> io_lib:format("argument\n~s`\n", [pp_typed(" `", X, T)]); + {result, T} -> io_lib:format("return type\n~s`\n", [pp_type(" `", T)]) + end, + Bad = case Thing of + {argument, _, _} -> io_lib:format("has a ~s type", [What]); + {result, _} -> io_lib:format("is ~s", [What]) + end, + Msg = io_lib:format("The ~sof entrypoint `~s` ~s", + [ThingS, Name, Bad]), + mk_t_err(pos(Ann), Msg); 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 53214b8..036d976 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -378,14 +378,12 @@ decls_to_fcode(Env, Decls) -> decl_to_fcode(Env, {fun_decl, _, _, _}) -> Env; decl_to_fcode(Env, {type_def, _Ann, Name, Args, Def}) -> typedef_to_fcode(Env, Name, Args, Def); -decl_to_fcode(Env = #{ functions := Funs }, {letfun, Ann, Id = {id, _, Name}, Args, Ret, [{guarded, _, [], Body}]}) -> +decl_to_fcode(Env = #{ functions := Funs }, {letfun, Ann, {id, _, Name}, Args, Ret, [{guarded, _, [], Body}]}) -> Attrs = get_attributes(Ann), FName = lookup_fun(Env, qname(Env, Name)), FArgs = args_to_fcode(Env, Args), FRet = type_to_fcode(Env, Ret), FBody = expr_to_fcode(Env#{ vars => [X || {X, _} <- FArgs] }, Body), - [ ensure_first_order_entrypoint(Ann, Id, Args, Ret, FArgs, FRet) - || aeso_syntax:get_ann(entrypoint, Ann, false) ], Def = #{ attrs => Attrs, args => FArgs, return => FRet, @@ -823,14 +821,6 @@ validate_aens_resolve_type(Ann, {app_t, _, _, [Type]}, {variant, [[], [FType]]}) _ -> fcode_error({invalid_aens_resolve_type, Ann, Type}) end. -ensure_first_order_entrypoint(Ann, Id = {id, _, Name}, Args, Ret, FArgs, FRet) -> - [ ensure_first_order(FT, {invalid_entrypoint, higher_order, Ann1, Id, {argument, X, T}}) - || {{typed, Ann1, X, T}, {_, FT}} <- lists:zip(Args, FArgs) ], - [ 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. - ensure_monomorphic(Type, Err) -> case is_monomorphic(Type) of true -> ok; diff --git a/src/aeso_code_errors.erl b/src/aeso_code_errors.erl index 1ab5738..8418838 100644 --- a/src/aeso_code_errors.erl +++ b/src/aeso_code_errors.erl @@ -10,22 +10,6 @@ -export([format/1, pos/1]). -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, - ThingS = case Thing of - {argument, X, T} -> io_lib:format("argument\n~s\n", [pp_typed(X, T)]); - {result, T} -> io_lib:format("return type\n~s\n", [pp_type(2, T)]) - end, - Bad = case Thing of - {argument, _, _} -> io_lib:format("has a ~s type", [What]); - {result, _} -> io_lib:format("is ~s", [What]) - end, - Msg = io_lib:format("The ~sof entrypoint '~s' ~s.", - [ThingS, Name, Bad]), - case Why of - higher_order -> mk_err(pos(Ann), Msg) - end; format({invalid_aens_resolve_type, Ann, T}) -> Msg = io_lib:format("Invalid return type of AENS.resolve:\n" "~s\n" @@ -52,12 +36,6 @@ pos(Ann) -> Col = aeso_syntax:get_ann(col, Ann, 0), aeso_errors:pos(File, Line, Col). -pp_typed(E, T) -> - prettypr:format(prettypr:nest(2, - lists:foldr(fun prettypr:beside/2, prettypr:empty(), - [aeso_pretty:expr(E), prettypr:text(" : "), - aeso_pretty:type(T)]))). - pp_expr(E) -> pp_expr(0, E). diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index cb4d047..e2299cd 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -1046,6 +1046,18 @@ failing_contracts() -> "Missing `init` function for the contract `AliasToAliasToType`.\n" "The `init` function can only be omitted if the state type is `unit`">> ]) + , ?TYPE_ERROR(higher_order_entrypoint, + [< int`\n" + "of entrypoint `apply` has a higher-order (contains function types) type">> + ]) + , ?TYPE_ERROR(higher_order_entrypoint_return, + [< int`\n" + "of entrypoint `add` is higher-order (contains function types)">> + ]) ]. -define(Path(File), "code_errors/" ??File). @@ -1054,15 +1066,7 @@ failing_contracts() -> -define(FATE_ERR(File, Line, Col, Err), {?Path(File), ?Msg(File, Line, Col, Err)}). failing_code_gen_contracts() -> - [ ?FATE_ERR(higher_order_entrypoint, 2, 20, - "The argument\n" - " f : (int) => int\n" - "of entrypoint 'apply' has a higher-order (contains function types) type.") - , ?FATE_ERR(higher_order_entrypoint_return, 2, 3, - "The return type\n" - " (int) => int\n" - "of entrypoint 'add' is higher-order (contains function types).") - , ?FATE_ERR(polymorphic_aens_resolve, 4, 5, + [ ?FATE_ERR(polymorphic_aens_resolve, 4, 5, "Invalid return type of AENS.resolve:\n" " 'a\n" "It must be a string or a pubkey type (address, oracle, etc).") diff --git a/test/contracts/code_errors/higher_order_entrypoint.aes b/test/contracts/higher_order_entrypoint.aes similarity index 100% rename from test/contracts/code_errors/higher_order_entrypoint.aes rename to test/contracts/higher_order_entrypoint.aes diff --git a/test/contracts/code_errors/higher_order_entrypoint_return.aes b/test/contracts/higher_order_entrypoint_return.aes similarity index 100% rename from test/contracts/code_errors/higher_order_entrypoint_return.aes rename to test/contracts/higher_order_entrypoint_return.aes