Check that there are no maps in map keys already in type checker

This commit is contained in:
Ulf Norell 2019-09-03 12:04:22 +02:00
parent 510935d945
commit 0533ab27e1
4 changed files with 36 additions and 18 deletions

View File

@ -1708,7 +1708,7 @@ solve_known_record_types(Env, Constraints) ->
C C
end; end;
_ -> _ ->
type_error({not_a_record_type, RecId, When}), type_error({not_a_record_type, RecType, When}),
not_solved not_solved
end end
end end
@ -1823,6 +1823,10 @@ unfold_types(_Env, X, _Options) ->
unfold_types_in_type(Env, T) -> unfold_types_in_type(Env, T) ->
unfold_types_in_type(Env, T, []). unfold_types_in_type(Env, T, []).
unfold_types_in_type(Env, {app_t, Ann, Id = {id, _, "map"}, Args = [KeyType0, _]}, Options) ->
Args1 = [KeyType, _] = unfold_types_in_type(Env, Args, Options),
[ type_error({map_in_map_key, KeyType0}) || has_maps(KeyType) ],
{app_t, Ann, Id, Args1};
unfold_types_in_type(Env, {app_t, Ann, Id, Args}, Options) when ?is_type_id(Id) -> unfold_types_in_type(Env, {app_t, Ann, Id, Args}, Options) when ?is_type_id(Id) ->
UnfoldRecords = proplists:get_value(unfold_record_types, Options, false), UnfoldRecords = proplists:get_value(unfold_record_types, Options, false),
UnfoldVariants = proplists:get_value(unfold_variant_types, Options, false), UnfoldVariants = proplists:get_value(unfold_variant_types, Options, false),
@ -1870,6 +1874,13 @@ unfold_types_in_type(Env, [H|T], Options) ->
unfold_types_in_type(_Env, X, _Options) -> unfold_types_in_type(_Env, X, _Options) ->
X. X.
has_maps({app_t, _, {id, _, "map"}, _}) ->
true;
has_maps(L) when is_list(L) ->
lists:any(fun has_maps/1, L);
has_maps(T) when is_tuple(T) ->
has_maps(tuple_to_list(T));
has_maps(_) -> false.
subst_tvars(Env, Type) -> subst_tvars(Env, Type) ->
subst_tvars1([{V, T} || {{tvar, _, V}, T} <- Env], Type). subst_tvars1([{V, T} || {{tvar, _, V}, T} <- Env], Type).
@ -2282,6 +2293,10 @@ mk_error({new_tuple_syntax, Ann, Ts}) ->
Msg = io_lib:format("Invalid type\n~s (at ~s)\nThe syntax of tuple types changed in Sophia version 4.0. Did you mean\n~s\n", Msg = io_lib:format("Invalid type\n~s (at ~s)\nThe syntax of tuple types changed in Sophia version 4.0. Did you mean\n~s\n",
[pp_type(" ", {args_t, Ann, Ts}), pp_loc(Ann), pp_type(" ", {tuple_t, Ann, Ts})]), [pp_type(" ", {args_t, Ann, Ts}), pp_loc(Ann), pp_type(" ", {tuple_t, Ann, Ts})]),
mk_t_err(pos(Ann), Msg); mk_t_err(pos(Ann), Msg);
mk_error({map_in_map_key, KeyType}) ->
Msg = io_lib:format("Invalid key type\n~s\n", [pp_type(" ", KeyType)]),
Cxt = "Map keys cannot contain other maps.\n",
mk_t_err(pos(KeyType), Msg, Cxt);
mk_error(Err) -> mk_error(Err) ->
Msg = io_lib:format("Unknown error: ~p\n", [Err]), Msg = io_lib:format("Unknown error: ~p\n", [Err]),
mk_t_err(pos(0, 0), Msg). mk_t_err(pos(0, 0), Msg).

View File

@ -718,15 +718,11 @@ eta_expand(Id = {_, Ann0, _}, {fun_t, _, _, ArgsT, _}, Icode) ->
check_monomorphic_map({typed, Ann, _, MapType}, Icode) -> check_monomorphic_map({typed, Ann, _, MapType}, Icode) ->
check_monomorphic_map(Ann, MapType, Icode). check_monomorphic_map(Ann, MapType, Icode).
check_monomorphic_map(Ann, Type = ?map_t(KeyType, ValType), Icode) -> check_monomorphic_map(Ann, ?map_t(KeyType, ValType), _Icode) ->
case is_monomorphic(KeyType) of Err = fun(Why) -> gen_error({invalid_map_key_type, Why, Ann, KeyType}) end,
true -> [ Err(polymorphic) || not is_monomorphic(KeyType) ],
case has_maps(ast_type(KeyType, Icode)) of [ Err(function) || not is_first_order_type(KeyType) ],
false -> {KeyType, ValType}; {KeyType, ValType}.
true -> gen_error({cant_use_map_as_map_keys, Ann, Type})
end;
false -> gen_error({cant_compile_map_with_polymorphic_keys, Ann, Type})
end.
map_empty(KeyType, ValType, Icode) -> map_empty(KeyType, ValType, Icode) ->
prim_call(?PRIM_CALL_MAP_EMPTY, #integer{value = 0}, prim_call(?PRIM_CALL_MAP_EMPTY, #integer{value = 0},
@ -928,14 +924,6 @@ ast_fun_to_icode(Name, Attrs, Args, Body, TypeRep, #{functions := Funs} = Icode)
NewFuns = [{Name, Attrs, Args, Body, TypeRep}| Funs], NewFuns = [{Name, Attrs, Args, Body, TypeRep}| Funs],
aeso_icode:set_functions(NewFuns, Icode). aeso_icode:set_functions(NewFuns, Icode).
has_maps({map, _, _}) -> true;
has_maps(word) -> false;
has_maps(string) -> false;
has_maps(typerep) -> false;
has_maps({list, T}) -> has_maps(T);
has_maps({tuple, Ts}) -> lists:any(fun has_maps/1, Ts);
has_maps({variant, Cs}) -> lists:any(fun has_maps/1, lists:append(Cs)).
%% A function is private if not an 'entrypoint', or if it's not defined in the %% A function is private if not an 'entrypoint', or if it's not defined in the
%% main contract name space. (NOTE: changes when we introduce inheritance). %% main contract name space. (NOTE: changes when we introduce inheritance).
is_private(Ann, #{ contract_name := MainContract } = Icode) -> is_private(Ann, #{ contract_name := MainContract } = Icode) ->

View File

@ -468,6 +468,15 @@ failing_contracts() ->
[<<?Pos(2, 53) [<<?Pos(2, 53)
"Cannot unify int\n and string\nwhen checking the type of the pattern at line 2, column 53\n x : int\nagainst the expected type\n string">> "Cannot unify int\n and string\nwhen checking the type of the pattern at line 2, column 53\n x : int\nagainst the expected type\n string">>
]} ]}
, {"map_as_map_key",
[<<?Pos(5, 25)
"Invalid key type\n"
" map(int, int)\n"
"Map keys cannot contain other maps.">>,
<<?Pos(6, 25)
"Invalid key type\n"
" lm\n"
"Map keys cannot contain other maps.">>]}
]. ].
-define(Path(File), "code_errors/" ??File). -define(Path(File), "code_errors/" ??File).

View File

@ -0,0 +1,6 @@
contract MapAsMapKey =
type t('key) = map('key, int)
type lm = list(map(int, int))
entrypoint foo(m) : t(map(int, int)) = {[m] = 0}
entrypoint bar(m) : t(lm) = Map.delete(m, {})