Skip to content

Commit 0c21ed8

Browse files
committed
Optimize term
1 parent ff26a8f commit 0c21ed8

File tree

2 files changed

+62
-25
lines changed

2 files changed

+62
-25
lines changed

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

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,12 @@ defmodule Module.Types.Descr do
3535
@map_top [{:open, %{}, []}]
3636
@map_empty [{:closed, %{}, []}]
3737

38-
# Guard helpers
39-
40-
@term %{bitmap: @bit_top, atom: @atom_top, map: @map_top}
41-
@none %{}
42-
@dynamic %{dynamic: @term}
43-
4438
# Type definitions
4539

46-
def dynamic(), do: @dynamic
47-
def term(), do: @term
48-
def none(), do: @none
40+
def dynamic(), do: %{dynamic: :term}
41+
def none(), do: %{}
42+
def term(), do: :term
43+
defp unfolded_term, do: %{bitmap: @bit_top, atom: @atom_top, map: @map_top}
4944

5045
def atom(as), do: %{atom: atom_new(as)}
5146
def atom(), do: %{atom: @atom_top}
@@ -84,14 +79,17 @@ defmodule Module.Types.Descr do
8479
@term_or_optional %{bitmap: @bit_top ||| @bit_optional, atom: @atom_top, map: @map_top}
8580

8681
def not_set(), do: @not_set
87-
def if_set(type), do: Map.update(type, :bitmap, @bit_optional, &(&1 ||| @bit_optional))
8882
defp term_or_optional(), do: @term_or_optional
8983

84+
def if_set(:term), do: @term_or_optional
85+
def if_set(type), do: Map.update(type, :bitmap, @bit_optional, &(&1 ||| @bit_optional))
86+
9087
## Set operations
9188

92-
def term_type?(@term), do: true
93-
def term_type?(descr), do: subtype_static(@term, Map.delete(descr, :dynamic))
89+
def term_type?(:term), do: true
90+
def term_type?(descr), do: subtype_static(unfolded_term(), Map.delete(descr, :dynamic))
9491

92+
def gradual?(:term), do: false
9593
def gradual?(descr), do: is_map_key(descr, :dynamic)
9694

9795
@doc """
@@ -102,13 +100,14 @@ defmodule Module.Types.Descr do
102100
def dynamic(descr) do
103101
case descr do
104102
%{dynamic: dynamic} -> %{dynamic: dynamic}
105-
%{} -> %{dynamic: descr}
103+
_ -> %{dynamic: descr}
106104
end
107105
end
108106

109107
@doc """
110108
Computes the union of two descrs.
111109
"""
110+
# TODO!!!
112111
def union(%{} = left, %{} = right) do
113112
is_gradual_left = gradual?(left)
114113
is_gradual_right = gradual?(right)
@@ -136,6 +135,7 @@ defmodule Module.Types.Descr do
136135
@doc """
137136
Computes the intersection of two descrs.
138137
"""
138+
# TODO!!!
139139
def intersection(%{} = left, %{} = right) do
140140
is_gradual_left = gradual?(left)
141141
is_gradual_right = gradual?(right)
@@ -164,21 +164,23 @@ defmodule Module.Types.Descr do
164164
@doc """
165165
Computes the difference between two types.
166166
"""
167+
# TODO!!!
167168
def difference(left = %{}, right = %{}) do
168169
if gradual?(left) or gradual?(right) do
169170
{left_dynamic, left_static} = Map.pop(left, :dynamic, left)
170171
{right_dynamic, right_static} = Map.pop(right, :dynamic, right)
171172
dynamic_part = difference_static(left_dynamic, right_static)
172173

173174
if empty?(dynamic_part),
174-
do: @none,
175+
do: none(),
175176
else: Map.put(difference_static(left_static, right_dynamic), :dynamic, dynamic_part)
176177
else
177178
difference_static(left, right)
178179
end
179180
end
180181

181182
# For static types, the difference is component-wise.
183+
# TODO!!!
182184
defp difference_static(left, right) do
183185
iterator_difference(:maps.next(:maps.iterator(right)), left)
184186
end
@@ -193,7 +195,8 @@ defmodule Module.Types.Descr do
193195
@doc """
194196
Compute the negation of a type.
195197
"""
196-
def negation(%{} = descr), do: difference(term(), descr)
198+
def negation(:term), do: none()
199+
def negation(%{} = descr), do: difference(unfolded_term(), descr)
197200

198201
@doc """
199202
Check if a type is empty.
@@ -203,18 +206,20 @@ defmodule Module.Types.Descr do
203206
(bitmap, atom) are checked first for speed since, if they are present,
204207
the type is non-empty as we normalize then during construction.
205208
"""
209+
def empty?(:term), do: false
210+
206211
def empty?(%{} = descr) do
207212
descr = Map.get(descr, :dynamic, descr)
208213

209-
descr == @none or
214+
descr == none() or
210215
(not Map.has_key?(descr, :bitmap) and not Map.has_key?(descr, :atom) and
211216
(not Map.has_key?(descr, :map) or map_empty?(descr.map)))
212217
end
213218

214219
@doc """
215220
Converts a descr to its quoted representation.
216221
"""
217-
def to_quoted(%{} = descr) do
222+
def to_quoted(descr) do
218223
if term_type?(descr) do
219224
{:term, [], []}
220225
else
@@ -260,6 +265,7 @@ defmodule Module.Types.Descr do
260265
Because of the dynamic/static invariant in the `descr`, subtyping can be
261266
simplified in several cases according to which type is gradual or not.
262267
"""
268+
# TODO!!!
263269
def subtype?(%{} = left, %{} = right) do
264270
is_grad_left = gradual?(left)
265271
is_grad_right = gradual?(right)
@@ -278,6 +284,7 @@ defmodule Module.Types.Descr do
278284
end
279285
end
280286

287+
defp subtype_static(same, same), do: true
281288
defp subtype_static(left, right), do: empty?(difference_static(left, right))
282289

283290
@doc """
@@ -305,6 +312,7 @@ defmodule Module.Types.Descr do
305312
include `dynamic()`, `integer()`, but also `dynamic() and (integer() or atom())`.
306313
Incompatible subtypes include `integer() or list()`, `dynamic() and atom()`.
307314
"""
315+
# TODO!!!
308316
def compatible?(input_type, expected_type) do
309317
{input_dynamic, input_static} = Map.pop(input_type, :dynamic, input_type)
310318
expected_dynamic = Map.get(expected_type, :dynamic, expected_type)
@@ -323,35 +331,35 @@ defmodule Module.Types.Descr do
323331
"""
324332
def fun_type?(%{dynamic: %{bitmap: bitmap}}) when (bitmap &&& @bit_fun) != 0, do: true
325333
def fun_type?(%{bitmap: bitmap}) when (bitmap &&& @bit_fun) != 0, do: true
326-
def fun_type?(%{}), do: false
334+
def fun_type?(_), do: false
327335

328336
@doc """
329337
Optimized version of `not empty?(intersection(binary(), type))`.
330338
"""
331339
def binary_type?(%{dynamic: %{bitmap: bitmap}}) when (bitmap &&& @bit_binary) != 0, do: true
332340
def binary_type?(%{bitmap: bitmap}) when (bitmap &&& @bit_binary) != 0, do: true
333-
def binary_type?(%{}), do: false
341+
def binary_type?(_), do: false
334342

335343
@doc """
336344
Optimized version of `not empty?(intersection(integer(), type))`.
337345
"""
338346
def integer_type?(%{dynamic: %{bitmap: bitmap}}) when (bitmap &&& @bit_integer) != 0, do: true
339347
def integer_type?(%{bitmap: bitmap}) when (bitmap &&& @bit_integer) != 0, do: true
340-
def integer_type?(%{}), do: false
348+
def integer_type?(_), do: false
341349

342350
@doc """
343351
Optimized version of `not empty?(intersection(float(), type))`.
344352
"""
345353
def float_type?(%{dynamic: %{bitmap: bitmap}}) when (bitmap &&& @bit_float) != 0, do: true
346354
def float_type?(%{bitmap: bitmap}) when (bitmap &&& @bit_float) != 0, do: true
347-
def float_type?(%{}), do: false
355+
def float_type?(_), do: false
348356

349357
@doc """
350358
Optimized version of `not empty?(intersection(integer() or float(), type))`.
351359
"""
352360
def number_type?(%{dynamic: %{bitmap: bitmap}}) when (bitmap &&& @bit_number) != 0, do: true
353361
def number_type?(%{bitmap: bitmap}) when (bitmap &&& @bit_number) != 0, do: true
354-
def number_type?(%{}), do: false
362+
def number_type?(_), do: false
355363

356364
defp bitmap_union(v1, v2), do: v1 ||| v2
357365
defp bitmap_intersection(v1, v2), do: v1 &&& v2
@@ -400,16 +408,19 @@ defmodule Module.Types.Descr do
400408
"""
401409
def atom_type?(%{dynamic: %{atom: _}}), do: true
402410
def atom_type?(%{atom: _}), do: true
403-
def atom_type?(%{}), do: false
411+
def atom_type?(_), do: false
404412

405413
@doc """
406414
Optimized version of `not empty?(intersection(atom([atom]), type))`.
407415
"""
416+
def atom_type?(:term, _atom), do: false
417+
408418
def atom_type?(%{} = descr, atom) do
409419
{static_or_dynamic, static} = Map.pop(descr, :dynamic, descr)
410420

411421
atom_only?(static) and
412422
case static_or_dynamic do
423+
:term -> true
413424
%{atom: {:union, set}} -> :sets.is_element(atom, set)
414425
%{atom: {:negation, set}} -> not :sets.is_element(atom, set)
415426
%{} -> false
@@ -423,11 +434,14 @@ defmodule Module.Types.Descr do
423434
`:error` otherwise. Notice `known_set` may be empty in infinite
424435
cases, due to negations.
425436
"""
437+
def atom_fetch(:term), do: :error
438+
426439
def atom_fetch(%{} = descr) do
427440
{static_or_dynamic, static} = Map.pop(descr, :dynamic, descr)
428441

429442
if atom_only?(static) do
430443
case static_or_dynamic do
444+
:term -> {:infinite, []}
431445
%{atom: {:union, set}} -> {:finite, :sets.to_list(set)}
432446
%{atom: {:negation, _}} -> {:infinite, []}
433447
%{} -> :error
@@ -554,19 +568,22 @@ defmodule Module.Types.Descr do
554568
# `:dynamic` field is not_set, or it contains a type equal to the static component
555569
# (that is, there are no extra dynamic values).
556570

571+
# TODO!!!
557572
defp dynamic_intersection(left, right) do
558573
inter = symmetrical_intersection(left, right, &intersection/3)
559574
if empty?(inter), do: 0, else: inter
560575
end
561576

577+
# TODO!!!
562578
defp dynamic_difference(left, right) do
563579
diff = difference_static(left, right)
564580
if empty?(diff), do: 0, else: diff
565581
end
566582

583+
# TODO!!!
567584
defp dynamic_union(left, right), do: symmetrical_merge(left, right, &union/3)
568585

569-
defp dynamic_to_quoted(%{} = descr) do
586+
defp dynamic_to_quoted(descr) do
570587
cond do
571588
term_type?(descr) -> [{:dynamic, [], []}]
572589
single = indivisible_bitmap(descr) -> [single]
@@ -642,6 +659,8 @@ defmodule Module.Types.Descr do
642659
In static mode, we likely want to raise if `map.field`
643660
(or pattern matching?) is called on an optional key.
644661
"""
662+
def map_fetch(:term, _key), do: :badmap
663+
645664
def map_fetch(%{} = descr, key) do
646665
case :maps.take(:dynamic, descr) do
647666
:error ->
@@ -657,7 +676,7 @@ defmodule Module.Types.Descr do
657676
:badmap
658677
end
659678

660-
{%{map: {:open, fields, []}}, static} when fields == %{} and static == @none ->
679+
{:term, _static} ->
661680
{true, dynamic()}
662681

663682
{dynamic, static} ->

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ defmodule Module.Types.DescrTest do
1313
test "term" do
1414
assert union(term(), float()) == term()
1515
assert union(term(), binary()) == term()
16+
assert union(term(), if_set(binary())) == if_set(term())
1617
end
1718

1819
test "none" do
@@ -214,6 +215,8 @@ defmodule Module.Types.DescrTest do
214215
# optional
215216
refute subtype?(closed_map(a: if_set(integer())), closed_map(a: integer()))
216217
assert subtype?(closed_map(a: integer()), closed_map(a: if_set(integer())))
218+
refute subtype?(closed_map(a: if_set(term())), closed_map(a: term()))
219+
assert subtype?(closed_map(a: term()), closed_map(a: if_set(term())))
217220
end
218221
end
219222

@@ -253,12 +256,27 @@ defmodule Module.Types.DescrTest do
253256
end
254257
end
255258

259+
describe "queries" do
260+
test "atom_type?" do
261+
refute atom_type?(term(), :foo)
262+
assert atom_type?(dynamic(), :foo)
263+
264+
assert atom_type?(atom([:foo, :bar]), :foo)
265+
refute atom_type?(atom([:foo, :bar]), :baz)
266+
assert atom_type?(negate(atom([:foo, :bar])), :baz)
267+
268+
refute atom_type?(union(atom([:foo, :bar]), integer()), :baz)
269+
assert atom_type?(dynamic(union(atom([:foo, :bar]), integer())), :baz)
270+
end
271+
end
272+
256273
describe "projections" do
257274
test "atom_fetch" do
258275
assert atom_fetch(term()) == :error
259276
assert atom_fetch(union(term(), dynamic(atom([:foo, :bar])))) == :error
260277

261278
assert atom_fetch(atom()) == {:infinite, []}
279+
assert atom_fetch(dynamic()) == {:infinite, []}
262280

263281
assert atom_fetch(atom([:foo, :bar])) ==
264282
{:finite, [:foo, :bar] |> :sets.from_list(version: 2) |> :sets.to_list()}

0 commit comments

Comments
 (0)