Skip to content

Commit bbbf3c9

Browse files
committed
Convert optional to a separate descr field
The previous implementation was expensive to do upfront checks.
1 parent 503c28d commit bbbf3c9

File tree

2 files changed

+44
-57
lines changed

2 files changed

+44
-57
lines changed

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

Lines changed: 42 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,26 @@ defmodule Module.Types.Descr do
2020
@bit_pid 1 <<< 4
2121
@bit_port 1 <<< 5
2222
@bit_reference 1 <<< 6
23-
2423
@bit_fun 1 <<< 7
2524
@bit_top (1 <<< 8) - 1
26-
2725
@bit_number @bit_integer ||| @bit_float
28-
@bit_optional 1 <<< 8
2926

3027
@atom_top {:negation, :sets.new(version: 2)}
31-
@tuple_top [{:open, [], []}]
3228
@map_top [{:open, %{}, []}]
29+
@non_empty_list_top [{:term, :term, []}]
30+
@tuple_top [{:open, [], []}]
3331
@map_empty [{:closed, %{}, []}]
32+
3433
@none %{}
3534
@empty_list %{bitmap: @bit_empty_list}
36-
# A definition of non empty list that does not use negation. Includes empty list.
3735
@not_non_empty_list %{bitmap: @bit_top, atom: @atom_top, tuple: @tuple_top, map: @map_top}
38-
@non_empty_list_top [{:term, @not_non_empty_list, []}]
36+
@term %{
37+
bitmap: @bit_top,
38+
atom: @atom_top,
39+
tuple: @tuple_top,
40+
map: @map_top,
41+
list: @non_empty_list_top
42+
}
3943

4044
# Type definitions
4145

@@ -47,15 +51,7 @@ defmodule Module.Types.Descr do
4751

4852
defp unfold(:term), do: unfolded_term()
4953
defp unfold(other), do: other
50-
51-
defp unfolded_term,
52-
do: %{
53-
bitmap: @bit_top,
54-
atom: @atom_top,
55-
tuple: @tuple_top,
56-
map: @map_top,
57-
list: @non_empty_list_top
58-
}
54+
defp unfolded_term, do: @term
5955

6056
def atom(as), do: %{atom: atom_new(as)}
6157
def atom(), do: %{atom: @atom_top}
@@ -92,30 +88,23 @@ defmodule Module.Types.Descr do
9288
#
9389
# `not_set()` has no meaning outside of map types.
9490

95-
@not_set %{bitmap: @bit_optional}
96-
@term_or_optional %{
97-
bitmap: @bit_top ||| @bit_optional,
98-
atom: @atom_top,
99-
tuple: @tuple_top,
100-
map: @map_top,
101-
list: @non_empty_list_top
102-
}
91+
@not_set %{optional: 1}
92+
@term_or_optional Map.put(@term, :optional, 1)
10393

10494
def not_set(), do: @not_set
10595
defp term_or_optional(), do: @term_or_optional
10696

10797
def if_set(:term), do: term_or_optional()
108-
def if_set(type), do: Map.update(type, :bitmap, @bit_optional, &(&1 ||| @bit_optional))
98+
def if_set(type), do: Map.put(type, :optional, 1)
10999

110100
defguardp is_optional(map)
111101
when is_map(map) and
112-
((is_map_key(map, :bitmap) and (map.bitmap &&& @bit_optional) != 0) or
102+
(is_map_key(map, :optional) or
113103
(is_map_key(map, :dynamic) and is_map(map.dynamic) and
114-
is_map_key(map.dynamic, :bitmap) and
115-
(map.dynamic.bitmap &&& @bit_optional) != 0))
104+
is_map_key(map.dynamic, :optional)))
116105

117106
defguardp is_optional_static(map)
118-
when is_map(map) and is_map_key(map, :bitmap) and (map.bitmap &&& @bit_optional) != 0
107+
when is_map(map) and is_map_key(map, :optional)
119108

120109
defp descr_key?(:term, _key), do: true
121110
defp descr_key?(descr, key), do: is_map_key(descr, key)
@@ -125,8 +114,6 @@ defmodule Module.Types.Descr do
125114
def term_type?(:term), do: true
126115
def term_type?(descr), do: subtype_static?(unfolded_term(), Map.delete(descr, :dynamic))
127116

128-
def dynamic_term_type?(descr), do: descr == %{dynamic: :term}
129-
130117
def gradual?(:term), do: false
131118
def gradual?(descr), do: is_map_key(descr, :dynamic)
132119

@@ -171,12 +158,13 @@ defmodule Module.Types.Descr do
171158
end
172159

173160
@compile {:inline, union: 3}
174-
defp union(:bitmap, v1, v2), do: bitmap_union(v1, v2)
175161
defp union(:atom, v1, v2), do: atom_union(v1, v2)
162+
defp union(:bitmap, v1, v2), do: v1 ||| v2
176163
defp union(:dynamic, v1, v2), do: dynamic_union(v1, v2)
164+
defp union(:list, v1, v2), do: list_union(v1, v2)
177165
defp union(:map, v1, v2), do: map_union(v1, v2)
166+
defp union(:optional, 1, 1), do: 1
178167
defp union(:tuple, v1, v2), do: tuple_union(v1, v2)
179-
defp union(:list, v1, v2), do: list_union(v1, v2)
180168

181169
@doc """
182170
Computes the intersection of two descrs.
@@ -208,12 +196,13 @@ defmodule Module.Types.Descr do
208196

209197
# Returning 0 from the callback is taken as none() for that subtype.
210198
@compile {:inline, intersection: 3}
211-
defp intersection(:bitmap, v1, v2), do: bitmap_intersection(v1, v2)
212199
defp intersection(:atom, v1, v2), do: atom_intersection(v1, v2)
200+
defp intersection(:bitmap, v1, v2), do: v1 &&& v2
213201
defp intersection(:dynamic, v1, v2), do: dynamic_intersection(v1, v2)
202+
defp intersection(:list, v1, v2), do: list_intersection(v1, v2)
214203
defp intersection(:map, v1, v2), do: map_intersection(v1, v2)
204+
defp intersection(:optional, 1, 1), do: 1
215205
defp intersection(:tuple, v1, v2), do: tuple_intersection(v1, v2)
216-
defp intersection(:list, v1, v2), do: list_intersection(v1, v2)
217206

218207
@doc """
219208
Computes the difference between two types.
@@ -265,12 +254,13 @@ defmodule Module.Types.Descr do
265254

266255
# Returning 0 from the callback is taken as none() for that subtype.
267256
@compile {:inline, difference: 3}
268-
defp difference(:bitmap, v1, v2), do: bitmap_difference(v1, v2)
269257
defp difference(:atom, v1, v2), do: atom_difference(v1, v2)
258+
defp difference(:bitmap, v1, v2), do: v1 - (v1 &&& v2)
270259
defp difference(:dynamic, v1, v2), do: dynamic_difference(v1, v2)
260+
defp difference(:list, v1, v2), do: list_difference(v1, v2)
271261
defp difference(:map, v1, v2), do: map_difference(v1, v2)
262+
defp difference(:optional, 1, 1), do: 0
272263
defp difference(:tuple, v1, v2), do: tuple_difference(v1, v2)
273-
defp difference(:list, v1, v2), do: list_difference(v1, v2)
274264

275265
@doc """
276266
Compute the negation of a type.
@@ -297,10 +287,12 @@ defmodule Module.Types.Descr do
297287
true
298288

299289
descr ->
300-
not Map.has_key?(descr, :bitmap) and not Map.has_key?(descr, :atom) and
301-
(not Map.has_key?(descr, :tuple) or tuple_empty?(descr.tuple)) and
290+
not Map.has_key?(descr, :atom) and
291+
not Map.has_key?(descr, :bitmap) and
292+
not Map.has_key?(descr, :optional) and
302293
(not Map.has_key?(descr, :map) or map_empty?(descr.map)) and
303-
(not Map.has_key?(descr, :list) or list_empty?(descr.list))
294+
(not Map.has_key?(descr, :list) or list_empty?(descr.list)) and
295+
(not Map.has_key?(descr, :tuple) or tuple_empty?(descr.tuple))
304296
end
305297
end
306298

@@ -319,12 +311,12 @@ defmodule Module.Types.Descr do
319311
end
320312

321313
@compile {:inline, to_quoted: 2}
322-
defp to_quoted(:bitmap, val), do: bitmap_to_quoted(val)
323314
defp to_quoted(:atom, val), do: atom_to_quoted(val)
315+
defp to_quoted(:bitmap, val), do: bitmap_to_quoted(val)
324316
defp to_quoted(:dynamic, descr), do: dynamic_to_quoted(descr)
325317
defp to_quoted(:map, dnf), do: map_to_quoted(dnf)
326-
defp to_quoted(:tuple, dnf), do: tuple_to_quoted(dnf)
327318
defp to_quoted(:list, dnf), do: list_to_quoted(dnf)
319+
defp to_quoted(:tuple, dnf), do: tuple_to_quoted(dnf)
328320

329321
@doc """
330322
Converts a descr to its quoted string representation.
@@ -454,8 +446,6 @@ defmodule Module.Types.Descr do
454446
end
455447
end
456448

457-
## Bitmaps
458-
459449
@doc """
460450
Optimized version of `not empty?(intersection(empty_list(), type))`.
461451
"""
@@ -513,9 +503,7 @@ defmodule Module.Types.Descr do
513503
def list_type?(%{list: _}), do: true
514504
def list_type?(_), do: false
515505

516-
defp bitmap_union(v1, v2), do: v1 ||| v2
517-
defp bitmap_intersection(v1, v2), do: v1 &&& v2
518-
defp bitmap_difference(v1, v2), do: v1 - (v1 &&& v2)
506+
## Bitmaps
519507

520508
defp bitmap_to_quoted(val) do
521509
pairs =
@@ -1218,16 +1206,12 @@ defmodule Module.Types.Descr do
12181206
end
12191207
end
12201208

1221-
defp pop_optional_static(type) do
1222-
case type do
1223-
%{bitmap: @bit_optional} ->
1224-
{true, Map.delete(type, :bitmap)}
1225-
1226-
%{bitmap: bitmap} when (bitmap &&& @bit_optional) != 0 ->
1227-
{true, %{type | bitmap: bitmap - @bit_optional}}
1209+
defp pop_optional_static(:term), do: {false, :term}
12281210

1229-
_ ->
1230-
{false, type}
1211+
defp pop_optional_static(type) do
1212+
case :maps.take(:optional, type) do
1213+
:error -> {false, type}
1214+
{1, type} -> {true, type}
12311215
end
12321216
end
12331217

@@ -1587,8 +1571,10 @@ defmodule Module.Types.Descr do
15871571
literal_to_quoted(key)
15881572
end
15891573

1574+
{optional?, type} = pop_optional_static(type)
1575+
15901576
cond do
1591-
not is_optional_static(type) -> {key, to_quoted(type)}
1577+
not optional? -> {key, to_quoted(type)}
15921578
empty?(type) -> {key, {:not_set, [], []}}
15931579
true -> {key, {:if_set, [], [to_quoted(type)]}}
15941580
end

lib/elixir/lib/module/types/helpers.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ defmodule Module.Types.Helpers do
111111
defp collect_var_traces(traces) do
112112
traces
113113
|> Enum.reject(fn {_expr, _file, type, _formatter} ->
114-
Module.Types.Descr.dynamic_term_type?(type)
114+
# As an otimization do not care about dynamic terms
115+
type == %{dynamic: :term}
115116
end)
116117
|> case do
117118
[] -> traces

0 commit comments

Comments
 (0)