Handle all user generated code errors in the type checker #885
@ -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).
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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).")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user