Skip to content

Commit

Permalink
wip: Extract expect_map_type/2
Browse files Browse the repository at this point in the history
  • Loading branch information
erszcz committed Feb 24, 2022
1 parent 239b475 commit 679ce43
Showing 1 changed file with 45 additions and 37 deletions.
82 changes: 45 additions & 37 deletions src/typechecker.erl
Original file line number Diff line number Diff line change
Expand Up @@ -4249,43 +4249,30 @@ add_type_pat({record, P, Record, Fields}, Ty, Env, VEnv) ->
{type_error, _Type} ->
throw({type_error, record_pattern, P, Record, Ty})
end;
%% TODO implement expect_list_type
%add_type_pat({map, _, _} = MapPat, MapTy, _Env, VEnv) ->
% case expect_map_type(normalize(ListTy, Env), Env) of
% end;
%% this is just a workaround:
add_type_pat({map, _, _} = MapPat, ?type(any), _Env, VEnv) ->
{type(none), type(any), add_any_types_pat(MapPat, VEnv), constraints:empty()};
add_type_pat({map, _, _} = MapPat, {var, _, Var} = TyVar, _Env, VEnv) ->
%% FIXME this is a quite rudimentary implementation
%% - variables from the map pattern become any()
%% - the constraint could contain the map keys
Cs = constraints:add_var(
Var, constraints:upper(Var, type(map, any))),
{type(none), TyVar, add_any_types_pat(MapPat, VEnv), Cs};
add_type_pat({map, _, _} = MapPat, ?top(), Env, VEnv) ->
%% TODO instead of implemented an expect_map_type properly
%% just handle the one missing case here
%% expect_map_type(top()) would return #{top() => top()}
AllMapsTy = type(map, [type(map_field_assoc, [top() ,top()])]),
add_type_pat(MapPat, AllMapsTy, Env, VEnv);
add_type_pat({map, _P, PatAssocs}, {type, _, map, MapTyAssocs} = MapTy, Env, VEnv) ->
%% Check each Key := Value and binds vars in Value.
{NewVEnv, Css} =
lists:foldl(fun ({map_field_exact, _, Key, ValuePat}, {VEnvIn, CsAcc}) ->
case add_type_pat_map_key(Key, MapTyAssocs, Env, VEnvIn) of
{ok, ValueTy, Cs1} ->
{_ValPatTy, _ValUBound, VEnvOut, Cs2} =
add_type_pat(ValuePat, normalize(ValueTy, Env),
Env, VEnvIn),
{VEnvOut, [Cs1, Cs2 | CsAcc]};
error ->
throw({type_error, badkey, Key, MapTy})
end
end,
{VEnv, []},
PatAssocs),
{rewrite_map_assocs_to_exacts(MapTy), MapTy, NewVEnv, constraints:combine(Css)};
add_type_pat({map, P, AssocPats} = MapPat, MapTy, Env, VEnv) ->
case expect_map_type(normalize(MapTy, Env), Env) of
any ->
{type(none), type(any), add_any_types_pat(MapPat, VEnv), constraints:empty()};
{assoc_tys, AssocTys, Cs0} ->
%% Check each Key := Value and bind vars in Value.
{NewVEnv, Css} =
lists:foldl(fun ({map_field_exact, _, Key, ValuePat}, {VEnvIn, CsAcc}) ->
case add_type_pat_map_key(Key, AssocTys, Env, VEnvIn) of
{ok, ValueTy, Cs1} ->
{_ValPatTy, _ValUBound, VEnvOut, Cs2} =
add_type_pat(ValuePat, normalize(ValueTy, Env),
Env, VEnvIn),
{VEnvOut, [Cs1, Cs2 | CsAcc]};
error ->
throw({type_error, badkey, Key, MapTy})
end
end,
{VEnv, [Cs0]},
AssocPats),
{rewrite_map_assocs_to_exacts(MapTy), MapTy, NewVEnv, constraints:combine(Css)};
{type_error, _Type} ->
throw({type_error, pattern, P, MapPat, MapTy})
end;
add_type_pat({match, _, {var, _, _Var} = PatVar, Pat}, Ty, Env, VEnv) ->
add_type_pat_var(Pat, PatVar, Ty, Env, VEnv);
add_type_pat({match, _, Pat, {var, _, _Var} = PatVar}, Ty, Env, VEnv) ->
Expand Down Expand Up @@ -4315,11 +4302,32 @@ add_type_pat(OpPat = {op, _Anno, _Op, _Pat}, Ty, Env, VEnv) ->
add_type_pat(Pat, Ty, _Env, _VEnv) ->
throw({type_error, pattern, element(2, Pat), Pat, Ty}).

-spec expect_map_type(type(), env()) -> R when
R :: any
| {assoc_tys, [type()] | any, constraints:constraints()}
| {type_error, type()}.
expect_map_type(?type(any), _Env) ->
any;
expect_map_type(?top(), _Env) ->
{assoc_tys, [type(map_field_assoc, [top(), top()])], constraints:empty()};
expect_map_type({var, _, Var}, _Env) ->
%% FIXME this is a quite rudimentary implementation
%% - variables from the map pattern become any()
%% - the constraint could contain the map keys
Cs = constraints:add_var(Var, constraints:upper(Var, type(map, any))),
{assoc_tys, any, Cs};
expect_map_type(?type(map, AssocTys), _Env) ->
{assoc_tys, AssocTys, constraints:empty()}.

%% Rewrite map_field_assoc to map_field_exact to return in pattern types.
%%
%% Similarly to map field type inference on map creation - if a pattern matches,
%% then the map field is exact (:=), not assoc (=>).
%% There isn't even syntax for optional fields in map patterns.
rewrite_map_assocs_to_exacts(?top()) ->
top();
rewrite_map_assocs_to_exacts({var, _, _Var}) ->
type(none);
rewrite_map_assocs_to_exacts(?type(map, Assocs)) ->
type(map, lists:map(fun ({type, Ann, _, KVTy}) ->
{type, Ann, map_field_exact, KVTy}
Expand Down

0 comments on commit 679ce43

Please sign in to comment.