Handle all user generated code errors in the type checker #885

Merged
ghallak merged 15 commits from ghallak/270 into master 2022-07-26 00:48:47 +09:00
6 changed files with 45 additions and 28 deletions
Showing only changes of commit 55e5482e84 - Show all commits

View File

@ -87,7 +87,9 @@
-type byte_constraint() :: {is_bytes, utype()} -type byte_constraint() :: {is_bytes, utype()}
| {add_bytes, aeso_syntax:ann(), concat | split, utype(), utype(), utype()}. | {add_bytes, aeso_syntax:ann(), concat | split, utype(), utype(), utype()}.
-type constraint() :: named_argument_constraint() | field_constraint() | byte_constraint(). -type aens_resolve_constraint() :: {aens_resolve_type, utype()}.
-type constraint() :: named_argument_constraint() | field_constraint() | byte_constraint() | aens_resolve_constraint().
-record(field_info, -record(field_info,
{ ann :: aeso_syntax:ann() { ann :: aeso_syntax:ann()
@ -1818,6 +1820,8 @@ infer_expr(Env, {app, Ann, Fun, Args0} = App) ->
ResultType = fresh_uvar(Ann), ResultType = fresh_uvar(Ann),
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When), unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When),
when_warning(warn_negative_spend, fun() -> warn_potential_negative_spend(Ann, NewFun1, NewArgs) end), when_warning(warn_negative_spend, fun() -> warn_potential_negative_spend(Ann, NewFun1, NewArgs) end),
[ add_constraint({aens_resolve_type, GeneralResultType})
|| element(3, Fun) =:= ["AENS", "resolve"] ],
add_constraint( add_constraint(
#dependent_type_constraint{ named_args_t = NamedArgsVar, #dependent_type_constraint{ named_args_t = NamedArgsVar,
named_args = NamedArgs1, named_args = NamedArgs1,
@ -2338,11 +2342,15 @@ destroy_and_report_unsolved_constraints(Env) ->
(#named_argument_constraint{}) -> true; (#named_argument_constraint{}) -> true;
(_) -> false (_) -> false
end, OtherCs2), end, OtherCs2),
{BytesCs, []} = {BytesCs, OtherCs4} =
lists:partition(fun({is_bytes, _}) -> true; lists:partition(fun({is_bytes, _}) -> true;
({add_bytes, _, _, _, _, _}) -> true; ({add_bytes, _, _, _, _, _}) -> true;
(_) -> false (_) -> false
end, OtherCs3), end, OtherCs3),
{AensResolveCs, []} =
lists:partition(fun({aens_resolve_type, _}) -> true;
(_) -> false
end, OtherCs4),
Unsolved = [ S || S <- [ solve_constraint(Env, dereference_deep(C)) || C <- NamedArgCs ], Unsolved = [ S || S <- [ solve_constraint(Env, dereference_deep(C)) || C <- NamedArgCs ],
S == unsolved ], S == unsolved ],
@ -2360,6 +2368,7 @@ destroy_and_report_unsolved_constraints(Env) ->
check_record_create_constraints(Env, CreateCs), check_record_create_constraints(Env, CreateCs),
check_is_contract_constraints(Env, ContractCs), check_is_contract_constraints(Env, ContractCs),
check_bytes_constraints(Env, BytesCs), check_bytes_constraints(Env, BytesCs),
check_aens_resolve_constraints(Env, AensResolveCs),
destroy_constraints(). destroy_constraints().
@ -2489,6 +2498,21 @@ check_bytes_constraint(Env, {add_bytes, Ann, Fun, A0, B0, C0}) ->
_ -> type_error({unsolved_bytes_constraint, Ann, Fun, A, B, C}) _ -> type_error({unsolved_bytes_constraint, Ann, Fun, A, B, C})
end. end.
check_aens_resolve_constraints(_Env, []) ->
ok;
check_aens_resolve_constraints(Env, [{aens_resolve_type, Type} | Rest]) ->
Type1 = unfold_types_in_type(Env, instantiate(Type)),
{app_t, _, {id, _, "option"}, [Type2]} = Type1,
case Type2 of
{id, _, "string"} -> ok;
{id, _, "address"} -> ok;
{con, _, _} -> ok;
{app_t, _, {id, _, "oracle"}, [_, _]} -> ok;
{app_t, _, {id, _, "oracle_query"}, [_, _]} -> ok;
_ -> type_error({invalid_aens_resolve_type, aeso_syntax:get_ann(Type), Type2})
end,
check_aens_resolve_constraints(Env, Rest).
%% -- Field constraints -- %% -- Field constraints --
check_record_create_constraints(_, []) -> ok; check_record_create_constraints(_, []) -> ok;
@ -3558,6 +3582,12 @@ mk_error({higher_order_entrypoint, Ann, {id, _, Name}, Thing}) ->
Msg = io_lib:format("The ~sof entrypoint `~s` ~s", Msg = io_lib:format("The ~sof entrypoint `~s` ~s",
[ThingS, Name, Bad]), [ThingS, Name, Bad]),
mk_t_err(pos(Ann), Msg); mk_t_err(pos(Ann), Msg);
mk_error({invalid_aens_resolve_type, Ann, T}) ->
Msg = io_lib:format("Invalid return type of `AENS.resolve`:\n"
"~s`\n"
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)",
[pp_type(" `", T)]),
mk_t_err(pos(Ann), Msg);
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).

View File

@ -551,7 +551,6 @@ expr_to_fcode(Env, Type, {qid, Ann, X}) ->
{builtin_u, B = aens_resolve, Ar} -> {builtin_u, B = aens_resolve, Ar} ->
{fun_t, _, _, _, ResType} = Type, {fun_t, _, _, _, ResType} = Type,
AensType = type_to_fcode(Env, ResType), AensType = type_to_fcode(Env, ResType),
validate_aens_resolve_type(Ann, ResType, AensType),
TypeArgs = [{lit, {typerep, AensType}}], TypeArgs = [{lit, {typerep, AensType}}],
{builtin_u, B, Ar, TypeArgs}; {builtin_u, B, Ar, TypeArgs};
{builtin_u, B = bytes_split, Ar} -> {builtin_u, B = bytes_split, Ar} ->
@ -811,16 +810,6 @@ validate_oracle_type(Ann, Type, QType, RType) ->
ensure_first_order(RType, {invalid_oracle_type, higher_order, response, Ann, Type}), ensure_first_order(RType, {invalid_oracle_type, higher_order, response, Ann, Type}),
ok. ok.
validate_aens_resolve_type(Ann, {app_t, _, _, [Type]}, {variant, [[], [FType]]}) ->
case FType of
string -> ok;
address -> ok;
contract -> ok;
{oracle, _, _} -> ok;
oracle_query -> ok;
_ -> fcode_error({invalid_aens_resolve_type, Ann, Type})
end.
ensure_monomorphic(Type, Err) -> ensure_monomorphic(Type, Err) ->
case is_monomorphic(Type) of case is_monomorphic(Type) of
true -> ok; true -> ok;

View File

@ -10,12 +10,6 @@
-export([format/1, pos/1]). -export([format/1, pos/1]).
format({invalid_aens_resolve_type, Ann, T}) ->
Msg = io_lib:format("Invalid return type of AENS.resolve:\n"
"~s\n"
"It must be a string or a pubkey type (address, oracle, etc).",
[pp_type(2, T)]),
mk_err(pos(Ann), Msg);
format({invalid_oracle_type, Why, What, Ann, Type}) -> format({invalid_oracle_type, Why, What, Ann, Type}) ->
WhyS = case Why of higher_order -> "higher-order (contain function types)"; WhyS = case Why of higher_order -> "higher-order (contain function types)";
polymorphic -> "polymorphic (contain type variables)" end, polymorphic -> "polymorphic (contain type variables)" end,

View File

@ -1058,6 +1058,18 @@ failing_contracts() ->
" `(int) => int`\n" " `(int) => int`\n"
"of entrypoint `add` is higher-order (contains function types)">> "of entrypoint `add` is higher-order (contains function types)">>
]) ])
, ?TYPE_ERROR(polymorphic_aens_resolve,
[<<?Pos(4,5)
"Invalid return type of `AENS.resolve`:\n"
" `'a`\n"
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
])
, ?TYPE_ERROR(bad_aens_resolve,
[<<?Pos(6,5)
"Invalid return type of `AENS.resolve`:\n"
" `list(int)`\n"
"It must be a `string` or a pubkey type (`address`, `oracle`, etc)">>
])
]. ].
-define(Path(File), "code_errors/" ??File). -define(Path(File), "code_errors/" ??File).
@ -1066,15 +1078,7 @@ failing_contracts() ->
-define(FATE_ERR(File, Line, Col, Err), {?Path(File), ?Msg(File, Line, Col, Err)}). -define(FATE_ERR(File, Line, Col, Err), {?Path(File), ?Msg(File, Line, Col, Err)}).
failing_code_gen_contracts() -> failing_code_gen_contracts() ->
[ ?FATE_ERR(polymorphic_aens_resolve, 4, 5, [ ?FATE_ERR(polymorphic_query_type, 3, 5,
"Invalid return type of AENS.resolve:\n"
" 'a\n"
"It must be a string or a pubkey type (address, oracle, etc).")
, ?FATE_ERR(bad_aens_resolve, 6, 5,
"Invalid return type of AENS.resolve:\n"
" list(int)\n"
"It must be a string or a pubkey type (address, oracle, etc).")
, ?FATE_ERR(polymorphic_query_type, 3, 5,
"Invalid oracle type\n" "Invalid oracle type\n"
" oracle('a, 'b)\n" " oracle('a, 'b)\n"
"The query type must not be polymorphic (contain type variables).") "The query type must not be polymorphic (contain type variables).")