Skip to content

Commit cfcc26b

Browse files
authored
Merge pull request #201 from NelsonVides/binary_comprehension
Binary comprehension
2 parents 7433863 + e44a85b commit cfcc26b

File tree

3 files changed

+103
-22
lines changed

3 files changed

+103
-22
lines changed

src/typechecker.erl

Lines changed: 80 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,52 @@ expect_tuple_union([], AccTy, AccCs, _NoAny, _N, _Env) ->
13941394
{AccTy, AccCs}.
13951395

13961396

1397+
-spec expect_binary_type(type(), env()) -> R when
1398+
R :: any
1399+
| {elem_ty, type(), constraints()}
1400+
| {elem_tys, [type()], constraints()}
1401+
| {type_error, type()}.
1402+
expect_binary_type(?type(any), _) ->
1403+
any;
1404+
expect_binary_type(ElemTy = {type, _, binary, _}, _) ->
1405+
{elem_ty, ElemTy, constraints:empty()};
1406+
expect_binary_type(Union = {type, _, union, UnionTys}, Env) ->
1407+
{Tys, Cs} = expect_binary_union(UnionTys, [], constraints:empty(), Env),
1408+
case Tys of
1409+
[] ->
1410+
{type_error, Union};
1411+
[Ty] ->
1412+
{elem_ty, Ty, Cs};
1413+
_ ->
1414+
{elem_tys, Tys, Cs}
1415+
end;
1416+
expect_binary_type({var, _, Var}, _) ->
1417+
TyVar = gradualizer_tyvar:new(Var, ?MODULE, ?LINE),
1418+
{elem_ty,
1419+
{var, erl_anno:new(0), TyVar},
1420+
constraints:add_var(TyVar,
1421+
constraints:upper(Var, {type, erl_anno:new(0), binary,
1422+
[{integer, erl_anno:new(0), 0},
1423+
{integer, erl_anno:new(0), 1}]}))};
1424+
expect_binary_type(Ty, _) ->
1425+
{type_error, Ty}.
1426+
1427+
-spec expect_binary_union([type()], [type()], constraints(), env()) -> {[type()], constraints()}.
1428+
expect_binary_union([Ty|Tys], AccTy, AccCs, Env) ->
1429+
case expect_binary_type(normalize(Ty, Env), Env) of
1430+
{type_error, _} ->
1431+
expect_binary_union(Tys, AccTy, AccCs, Env);
1432+
any ->
1433+
expect_binary_union(Tys, [type(any) | AccTy], AccCs, Env);
1434+
{elem_ty, NTy, Cs} ->
1435+
expect_binary_union(Tys, [NTy | AccTy], constraints:combine(Cs, AccCs), Env);
1436+
{elem_tys, NTys, Cs} ->
1437+
expect_binary_union(Tys, NTys ++ AccTy, constraints:combine(Cs, AccCs), Env)
1438+
end;
1439+
expect_binary_union([], AccTy, AccCs, _Env) ->
1440+
{AccTy, AccCs}.
1441+
1442+
13971443
-spec allow_empty_list(type()) -> type().
13981444
allow_empty_list({type, P, nonempty_list, []}) ->
13991445
{type, P, list, []};
@@ -3334,28 +3380,40 @@ type_check_comprehension_in(Env, ResTy, OrigExpr, lc, Expr, _P, []) ->
33343380
throw(type_error(OrigExpr, type(list), ResTy))
33353381
end;
33363382
type_check_comprehension_in(Env, ResTy, OrigExpr, bc, Expr, _P, []) ->
3337-
ExprTy = case ResTy of
3338-
{type, _, binary, [{integer, _, 0}, {integer, _, _N}]} ->
3339-
%% The result is a multiple of N bits.
3340-
%% Expr must be a multiple of N bits too.
3341-
ResTy;
3342-
{type, _, binary, [{integer, _, M}, {integer, _, _N}]}
3343-
when M > 0 ->
3344-
%% The result is a binary with a minimum size of M. This
3345-
%% requires that the generators are non-empty. We don't
3346-
%% check that. At least, we can check that Gen is a
3347-
%% bitstring.
3348-
{type, erl_anno:new(0), binary,
3349-
[{integer, erl_anno:new(0), 0},
3350-
{integer, erl_anno:new(0), 1}]};
3351-
_ ->
3352-
Ty = {type, erl_anno:new(0), binary,
3353-
[{integer, erl_anno:new(0), 0},
3354-
{integer, erl_anno:new(0), 1}]},
3355-
throw(type_error(OrigExpr, Ty, ResTy))
3356-
end,
3357-
{_VB, Cs} = type_check_expr_in(Env, ExprTy, Expr),
3358-
{Env, Cs};
3383+
case expect_binary_type(ResTy, Env) of
3384+
any ->
3385+
{_Ty, _VB, Cs} = type_check_expr(Env, Expr),
3386+
{Env, Cs};
3387+
{elem_ty, ElemTy, Cs1} ->
3388+
ExprTy = case ElemTy of
3389+
{type, _, binary, [{integer, _, 0}, {integer, _, _N}]} ->
3390+
%% The result is a multiple of N bits.
3391+
%% Expr must be a multiple of N bits too.
3392+
ElemTy;
3393+
{type, _, binary, [{integer, _, M}, {integer, _, _N}]}
3394+
when M > 0 ->
3395+
%% The result is a binary with a minimum size of M. This
3396+
%% requires that the generators are non-empty. We don't
3397+
%% check that. At least, we can check that Gen is a
3398+
%% bitstring.
3399+
%% TODO: Actually typecheck this case
3400+
{type, erl_anno:new(0), binary,
3401+
[{integer, erl_anno:new(0), 0},
3402+
{integer, erl_anno:new(0), 1}]};
3403+
_ ->
3404+
Ty = {type, erl_anno:new(0), binary,
3405+
[{integer, erl_anno:new(0), 0},
3406+
{integer, erl_anno:new(0), 1}]},
3407+
throw({type_error, OrigExpr, Ty, ElemTy})
3408+
end,
3409+
{_VB, Cs2} = type_check_expr_in(Env, ExprTy, Expr),
3410+
{Env, constraints:combine(Cs1, Cs2)};
3411+
{elem_tys, ElemTys, Cs1} ->
3412+
{VB, Cs2} = type_check_union_in(Env, ElemTys, Expr),
3413+
{VB, constraints:combine(Cs1, Cs2)};
3414+
{type_error, Ty} ->
3415+
throw({type_error, OrigExpr, Ty, ResTy})
3416+
end;
33593417
type_check_comprehension_in(Env, ResTy, OrigExpr, Compr, Expr, P,
33603418
[{generate, _, Pat, Gen} | Quals]) ->
33613419
{Ty, _VB1, Cs1} = type_check_expr(Env, Gen),
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-module(binary_comprehension).
2+
-compile([debug_info]).
3+
-export([
4+
bitstring_match/0
5+
]).
6+
7+
-spec bitstring_match() -> <<_:7, _:_*3>> | string().
8+
bitstring_match() ->
9+
<< <<1:N>> || N <- lists:seq(7, 12)>>.

test/should_pass/binary_in_union.erl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-module(binary_in_union).
2+
-export([
3+
iodata_binary/0,
4+
nested_bitstrings/0
5+
]).
6+
7+
-spec iodata_binary() -> iodata().
8+
iodata_binary() ->
9+
<<<<"A">> || _ <- lists:seq(1, 10)>>.
10+
11+
-type nested() :: string() | bitstring() | binary().
12+
-spec nested_bitstrings() -> nested() | boolean() | bitstring().
13+
nested_bitstrings() ->
14+
<< <<1:N>> || N <- lists:seq(1, 12)>>.

0 commit comments

Comments
 (0)