Skip to content

Commit b19a147

Browse files
committed
WIP
1 parent 0c21ed8 commit b19a147

File tree

2 files changed

+95
-49
lines changed

2 files changed

+95
-49
lines changed

lib/elixir/lib/module/types/descr.ex

Lines changed: 94 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,16 @@ defmodule Module.Types.Descr do
3434
@atom_top {:negation, :sets.new(version: 2)}
3535
@map_top [{:open, %{}, []}]
3636
@map_empty [{:closed, %{}, []}]
37+
@none %{}
3738

3839
# Type definitions
3940

4041
def dynamic(), do: %{dynamic: :term}
41-
def none(), do: %{}
42+
def none(), do: @none
4243
def term(), do: :term
44+
45+
defp unfold(:term), do: unfolded_term()
46+
defp unfold(other), do: other
4347
defp unfolded_term, do: %{bitmap: @bit_top, atom: @atom_top, map: @map_top}
4448

4549
def atom(as), do: %{atom: atom_new(as)}
@@ -81,9 +85,19 @@ defmodule Module.Types.Descr do
8185
def not_set(), do: @not_set
8286
defp term_or_optional(), do: @term_or_optional
8387

84-
def if_set(:term), do: @term_or_optional
88+
def if_set(:term), do: term_or_optional()
8589
def if_set(type), do: Map.update(type, :bitmap, @bit_optional, &(&1 ||| @bit_optional))
8690

91+
defguardp is_optional(map)
92+
when is_map(map) and
93+
((is_map_key(map, :bitmap) and (map.bitmap &&& @bit_optional) != 0) or
94+
(is_map_key(map, :dynamic) and is_map(map.dynamic) and
95+
is_map_key(map.dynamic, :bitmap) and
96+
(map.dynamic.bitmap &&& @bit_optional) != 0))
97+
98+
defguardp is_optional_static(map)
99+
when is_map(map) and is_map_key(map, :bitmap) and (map.bitmap &&& @bit_optional) != 0
100+
87101
## Set operations
88102

89103
def term_type?(:term), do: true
@@ -107,8 +121,12 @@ defmodule Module.Types.Descr do
107121
@doc """
108122
Computes the union of two descrs.
109123
"""
110-
# TODO!!!
111-
def union(%{} = left, %{} = right) do
124+
def union(:term, other) when not is_optional(other), do: :term
125+
def union(other, :term) when not is_optional(other), do: :term
126+
127+
def union(left, right) do
128+
left = unfold(left)
129+
right = unfold(right)
112130
is_gradual_left = gradual?(left)
113131
is_gradual_right = gradual?(right)
114132

@@ -135,8 +153,12 @@ defmodule Module.Types.Descr do
135153
@doc """
136154
Computes the intersection of two descrs.
137155
"""
138-
# TODO!!!
139-
def intersection(%{} = left, %{} = right) do
156+
def intersection(:term, other) when not is_optional(other), do: other
157+
def intersection(other, :term) when not is_optional(other), do: other
158+
159+
def intersection(left, right) do
160+
left = unfold(left)
161+
right = unfold(right)
140162
is_gradual_left = gradual?(left)
141163
is_gradual_right = gradual?(right)
142164

@@ -164,8 +186,12 @@ defmodule Module.Types.Descr do
164186
@doc """
165187
Computes the difference between two types.
166188
"""
167-
# TODO!!!
168-
def difference(left = %{}, right = %{}) do
189+
def difference(other, :term) when not is_optional(other), do: none()
190+
191+
def difference(left, right) do
192+
left = unfold(left)
193+
right = unfold(right)
194+
169195
if gradual?(left) or gradual?(right) do
170196
{left_dynamic, left_static} = Map.pop(left, :dynamic, left)
171197
{right_dynamic, right_static} = Map.pop(right, :dynamic, right)
@@ -180,7 +206,6 @@ defmodule Module.Types.Descr do
180206
end
181207

182208
# For static types, the difference is component-wise.
183-
# TODO!!!
184209
defp difference_static(left, right) do
185210
iterator_difference(:maps.next(:maps.iterator(right)), left)
186211
end
@@ -209,11 +234,17 @@ defmodule Module.Types.Descr do
209234
def empty?(:term), do: false
210235

211236
def empty?(%{} = descr) do
212-
descr = Map.get(descr, :dynamic, descr)
237+
case Map.get(descr, :dynamic, descr) do
238+
:term ->
239+
false
240+
241+
value when value == @none ->
242+
true
213243

214-
descr == none() or
215-
(not Map.has_key?(descr, :bitmap) and not Map.has_key?(descr, :atom) and
216-
(not Map.has_key?(descr, :map) or map_empty?(descr.map)))
244+
descr ->
245+
not Map.has_key?(descr, :bitmap) and not Map.has_key?(descr, :atom) and
246+
(not Map.has_key?(descr, :map) or map_empty?(descr.map))
247+
end
217248
end
218249

219250
@doc """
@@ -265,8 +296,11 @@ defmodule Module.Types.Descr do
265296
Because of the dynamic/static invariant in the `descr`, subtyping can be
266297
simplified in several cases according to which type is gradual or not.
267298
"""
268-
# TODO!!!
269-
def subtype?(%{} = left, %{} = right) do
299+
def subtype?(left, :term) when not is_optional(left), do: true
300+
301+
def subtype?(left, right) do
302+
left = unfold(left)
303+
right = unfold(right)
270304
is_grad_left = gradual?(left)
271305
is_grad_right = gradual?(right)
272306

@@ -312,15 +346,18 @@ defmodule Module.Types.Descr do
312346
include `dynamic()`, `integer()`, but also `dynamic() and (integer() or atom())`.
313347
Incompatible subtypes include `integer() or list()`, `dynamic() and atom()`.
314348
"""
315-
# TODO!!!
316-
def compatible?(input_type, expected_type) do
317-
{input_dynamic, input_static} = Map.pop(input_type, :dynamic, input_type)
318-
expected_dynamic = Map.get(expected_type, :dynamic, expected_type)
349+
def compatible?(left, :term) when not is_optional(left), do: true
350+
351+
def compatible?(left, right) do
352+
left = unfold(left)
353+
right = unfold(right)
354+
{left_dynamic, left_static} = Map.pop(left, :dynamic, left)
355+
right_dynamic = Map.get(right, :dynamic, right)
319356

320-
if empty?(input_static) do
321-
not empty?(intersection(input_dynamic, expected_dynamic))
357+
if empty?(left_static) do
358+
not empty?(intersection(left_dynamic, right_dynamic))
322359
else
323-
subtype_static(input_static, expected_dynamic)
360+
subtype_static(left_static, right_dynamic)
324361
end
325362
end
326363

@@ -568,20 +605,18 @@ defmodule Module.Types.Descr do
568605
# `:dynamic` field is not_set, or it contains a type equal to the static component
569606
# (that is, there are no extra dynamic values).
570607

571-
# TODO!!!
572-
defp dynamic_intersection(left, right) do
573-
inter = symmetrical_intersection(left, right, &intersection/3)
574-
if empty?(inter), do: 0, else: inter
575-
end
608+
defp dynamic_union(:term, other) when not is_optional_static(other), do: :term
609+
defp dynamic_union(other, :term) when not is_optional_static(other), do: :term
610+
defp dynamic_union(left, right), do: symmetrical_merge(unfold(left), unfold(right), &union/3)
576611

577-
# TODO!!!
578-
defp dynamic_difference(left, right) do
579-
diff = difference_static(left, right)
580-
if empty?(diff), do: 0, else: diff
581-
end
612+
defp dynamic_intersection(:term, other) when not is_optional_static(other), do: other
613+
defp dynamic_intersection(other, :term) when not is_optional_static(other), do: other
614+
615+
defp dynamic_intersection(left, right),
616+
do: symmetrical_intersection(unfold(left), unfold(right), &intersection/3)
582617

583-
# TODO!!!
584-
defp dynamic_union(left, right), do: symmetrical_merge(left, right, &union/3)
618+
defp dynamic_difference(left, :term) when not is_optional_static(left), do: %{}
619+
defp dynamic_difference(left, right), do: difference_static(unfold(left), unfold(right))
585620

586621
defp dynamic_to_quoted(descr) do
587622
cond do
@@ -631,6 +666,10 @@ defmodule Module.Types.Descr do
631666
end
632667
end
633668

669+
defp map_descr_pairs([{key, :term} | rest], acc, dynamic?) do
670+
map_descr_pairs(rest, [{key, :term} | acc], dynamic?)
671+
end
672+
634673
defp map_descr_pairs([{key, value} | rest], acc, dynamic?) do
635674
case :maps.take(:dynamic, value) do
636675
:error -> map_descr_pairs(rest, [{key, value} | acc], dynamic?)
@@ -642,9 +681,6 @@ defmodule Module.Types.Descr do
642681
{acc, dynamic?}
643682
end
644683

645-
defp optional?(%{bitmap: bitmap}) when (bitmap &&& @bit_optional) != 0, do: true
646-
defp optional?(_), do: false
647-
648684
defp map_tag_to_type(:open), do: term_or_optional()
649685
defp map_tag_to_type(:closed), do: not_set()
650686

@@ -665,7 +701,7 @@ defmodule Module.Types.Descr do
665701
case :maps.take(:dynamic, descr) do
666702
:error ->
667703
if is_map_key(descr, :map) and map_only?(descr) do
668-
{static_optional?, static_type} = map_fetch_static(descr, key) |> pop_optional()
704+
{static_optional?, static_type} = map_fetch_static(descr, key) |> pop_optional_static()
669705

670706
if static_optional? or empty?(static_type) do
671707
:badkey
@@ -681,8 +717,10 @@ defmodule Module.Types.Descr do
681717

682718
{dynamic, static} ->
683719
if is_map_key(dynamic, :map) and map_only?(static) do
684-
{dynamic_optional?, dynamic_type} = map_fetch_static(dynamic, key) |> pop_optional()
685-
{static_optional?, static_type} = map_fetch_static(static, key) |> pop_optional()
720+
{dynamic_optional?, dynamic_type} =
721+
map_fetch_static(dynamic, key) |> pop_optional_static()
722+
723+
{static_optional?, static_type} = map_fetch_static(static, key) |> pop_optional_static()
686724

687725
if static_optional? or empty?(dynamic_type) do
688726
:badkey
@@ -704,7 +742,7 @@ defmodule Module.Types.Descr do
704742
end
705743
end
706744

707-
defp pop_optional(type) do
745+
defp pop_optional_static(type) do
708746
case type do
709747
%{bitmap: @bit_optional} ->
710748
{true, Map.delete(type, :bitmap)}
@@ -733,6 +771,10 @@ defmodule Module.Types.Descr do
733771
:empty -> acc
734772
end
735773
end
774+
|> case do
775+
[] -> 0
776+
acc -> acc
777+
end
736778
end
737779

738780
# Intersects two map literals; throws if their intersection is empty.
@@ -802,6 +844,10 @@ defmodule Module.Types.Descr do
802844
end)
803845
end)
804846
end)
847+
|> case do
848+
[] -> 0
849+
acc -> acc
850+
end
805851
end
806852

807853
# Emptiness checking for maps.
@@ -826,7 +872,7 @@ defmodule Module.Types.Descr do
826872

827873
# The key is not shared between positive and negative maps,
828874
# and because the negative type is required, there is no value in common
829-
tag == :closed and not optional?(neg_type) ->
875+
tag == :closed and not is_optional_static(neg_type) ->
830876
false
831877

832878
# The key is not shared between positive and negative maps,
@@ -847,7 +893,7 @@ defmodule Module.Types.Descr do
847893
empty?(diff) or map_empty?(tag, Map.put(fields, key, diff), negs)
848894

849895
%{} ->
850-
if neg_tag == :closed and not optional?(type) do
896+
if neg_tag == :closed and not is_optional_static(type) do
851897
false
852898
else
853899
# an absent key in a open negative map can be ignored
@@ -909,11 +955,11 @@ defmodule Module.Types.Descr do
909955
defp map_empty_negation?(tag, fields, {neg_tag, neg_fields}) do
910956
(tag == :closed and
911957
Enum.any?(neg_fields, fn {neg_key, neg_type} ->
912-
not is_map_key(fields, neg_key) and not optional?(neg_type)
958+
not is_map_key(fields, neg_key) and not is_optional_static(neg_type)
913959
end)) or
914960
(neg_tag == :closed and
915961
Enum.any?(fields, fn {key, type} ->
916-
not is_map_key(neg_fields, key) and not optional?(type)
962+
not is_map_key(neg_fields, key) and not is_optional_static(type)
917963
end))
918964
end
919965

@@ -970,7 +1016,7 @@ defmodule Module.Types.Descr do
9701016
keyword? = Inspect.List.keyword?(sorted)
9711017

9721018
for {key, type} <- sorted,
973-
not (tag == :open and optional?(type) and term_type?(type)) do
1019+
not (tag == :open and is_optional_static(type) and term_type?(type)) do
9741020
key =
9751021
if keyword? do
9761022
{:__block__, [format: :keyword], [key]}
@@ -979,7 +1025,7 @@ defmodule Module.Types.Descr do
9791025
end
9801026

9811027
cond do
982-
not optional?(type) -> {key, to_quoted(type)}
1028+
not is_optional_static(type) -> {key, to_quoted(type)}
9831029
empty?(type) -> {key, {:not_set, [], []}}
9841030
true -> {key, {:if_set, [], [to_quoted(type)]}}
9851031
end
@@ -1122,7 +1168,7 @@ defmodule Module.Types.Descr do
11221168
%{^key => v2} ->
11231169
case fun.(key, v1, v2) do
11241170
0 -> acc
1125-
[] -> acc
1171+
value when value == @none -> acc
11261172
value -> [{key, value} | acc]
11271173
end
11281174

lib/elixir/test/elixir/module/types/descr_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ defmodule Module.Types.DescrTest do
263263

264264
assert atom_type?(atom([:foo, :bar]), :foo)
265265
refute atom_type?(atom([:foo, :bar]), :baz)
266-
assert atom_type?(negate(atom([:foo, :bar])), :baz)
266+
assert atom_type?(negation(atom([:foo, :bar])), :baz)
267267

268268
refute atom_type?(union(atom([:foo, :bar]), integer()), :baz)
269269
assert atom_type?(dynamic(union(atom([:foo, :bar]), integer())), :baz)

0 commit comments

Comments
 (0)