Skip to content

Commit 5cbea01

Browse files
committed
Remove more hardcoded dynamic conversions
1 parent 27164e3 commit 5cbea01

File tree

10 files changed

+466
-286
lines changed

10 files changed

+466
-286
lines changed

lib/elixir/lib/module/types.ex

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ defmodule Module.Types do
1010
Enum.flat_map(defs, fn {{fun, arity} = function, kind, meta, clauses} ->
1111
stack = stack(with_file_meta(meta, file), module, function, no_warn_undefined, cache)
1212

13-
Enum.flat_map(clauses, fn {_meta, args, guards, body} ->
13+
Enum.flat_map(clauses, fn {meta, args, guards, body} ->
1414
try do
15-
warnings_from_clause(args, guards, body, stack, context)
15+
warnings_from_clause(meta, args, guards, body, stack, context)
1616
rescue
1717
e ->
1818
def_expr = {kind, meta, [guards_to_expr(guards, {fun, [], args}), [do: body]]}
@@ -51,9 +51,9 @@ defmodule Module.Types do
5151
guards_to_expr(guards, {:when, [], [left, guard]})
5252
end
5353

54-
defp warnings_from_clause(args, guards, body, stack, context) do
55-
with {:ok, _types, context} <- Pattern.of_head(args, guards, stack, context),
56-
{:ok, _type, context} <- Expr.of_expr(body, stack, context) do
54+
defp warnings_from_clause(meta, args, guards, body, stack, context) do
55+
with {:ok, _types, context} <- Pattern.of_head(args, guards, meta, stack, context),
56+
{:ok, _type, context} <- Expr.of_expr(body, %{stack | meta: []}, context) do
5757
context.warnings
5858
else
5959
{:error, context} -> context.warnings
@@ -63,6 +63,8 @@ defmodule Module.Types do
6363
@doc false
6464
def stack(file, module, function, no_warn_undefined, cache) do
6565
%{
66+
# The fallback meta used for literals in patterns and guards
67+
meta: [],
6668
# File of module
6769
file: file,
6870
# Module of definitions
@@ -72,7 +74,9 @@ defmodule Module.Types do
7274
# List of calls to not warn on as undefined
7375
no_warn_undefined: no_warn_undefined,
7476
# A list of cached modules received from the parallel compiler
75-
cache: cache
77+
cache: cache,
78+
# If variable refinements is enabled or not
79+
refine: true
7680
}
7781
end
7882

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

Lines changed: 110 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ defmodule Module.Types.Descr do
1111
# * DNF - disjunctive normal form which is a pair of unions and negations.
1212
# In the case of maps, we augment each pair with the open/closed tag.
1313

14-
# TODO: When we convert from AST to descr, we need to normalize
15-
# the dynamic type.
14+
# TODO: When we convert from AST to descr, we need to normalize the dynamic type.
1615
import Bitwise
1716

1817
@bit_binary 1 <<< 0
@@ -28,6 +27,7 @@ defmodule Module.Types.Descr do
2827
@bit_fun 1 <<< 9
2928
@bit_top (1 <<< 10) - 1
3029

30+
@bit_list @bit_empty_list ||| @bit_non_empty_list
3131
@bit_number @bit_integer ||| @bit_float
3232
@bit_optional 1 <<< 10
3333

@@ -50,16 +50,16 @@ defmodule Module.Types.Descr do
5050
def atom(as), do: %{atom: atom_new(as)}
5151
def atom(), do: %{atom: @atom_top}
5252
def binary(), do: %{bitmap: @bit_binary}
53+
def closed_map(pairs), do: %{map: map_new(:closed, Map.new(pairs))}
5354
def empty_list(), do: %{bitmap: @bit_empty_list}
55+
def empty_map(), do: %{map: @map_empty}
5456
def integer(), do: %{bitmap: @bit_integer}
5557
def float(), do: %{bitmap: @bit_float}
5658
def fun(), do: %{bitmap: @bit_fun}
57-
# TODO: We need to propagate any dynamic up
58-
def open_map(pairs), do: %{map: map_new(:open, Map.new(pairs))}
59-
def closed_map(pairs), do: %{map: map_new(:closed, Map.new(pairs))}
60-
def open_map(), do: %{map: @map_top}
61-
def empty_map(), do: %{map: @map_empty}
59+
def list(), do: %{bitmap: @bit_list}
6260
def non_empty_list(), do: %{bitmap: @bit_non_empty_list}
61+
def open_map(), do: %{map: @map_top}
62+
def open_map(pairs), do: %{map: map_new(:open, Map.new(pairs))}
6363
def pid(), do: %{bitmap: @bit_pid}
6464
def port(), do: %{bitmap: @bit_port}
6565
def reference(), do: %{bitmap: @bit_reference}
@@ -81,33 +81,17 @@ defmodule Module.Types.Descr do
8181
# `not_set()` has no meaning outside of map types.
8282

8383
@not_set %{bitmap: @bit_optional}
84-
@term_or_not_set %{bitmap: @bit_top ||| @bit_optional, atom: @atom_top, map: @map_top}
84+
@term_or_optional %{bitmap: @bit_top ||| @bit_optional, atom: @atom_top, map: @map_top}
8585

8686
def not_set(), do: @not_set
8787
def if_set(type), do: Map.update(type, :bitmap, @bit_optional, &(&1 ||| @bit_optional))
88-
defp term_or_not_set(), do: @term_or_not_set
88+
defp term_or_optional(), do: @term_or_optional
8989

90-
## Query operations
90+
## Set operations
9191

9292
def term_type?(descr), do: subtype_static(@term, Map.delete(descr, :dynamic))
9393
def gradual?(descr), do: is_map_key(descr, :dynamic)
9494

95-
@doc """
96-
Optimized check if the type represents any number.
97-
"""
98-
def number_type?(%{dynamic: %{bitmap: bitmap}}) when (bitmap &&& @bit_number) != 0, do: true
99-
def number_type?(%{bitmap: bitmap}) when (bitmap &&& @bit_number) != 0, do: true
100-
def number_type?(%{}), do: false
101-
102-
@doc """
103-
Optimized check if the type represents any atom.
104-
"""
105-
def atom_type?(%{dynamic: %{atom: _}}), do: true
106-
def atom_type?(%{atom: _}), do: true
107-
def atom_type?(%{}), do: false
108-
109-
## Set operations
110-
11195
@doc """
11296
Make a whole type dynamic.
11397
@@ -228,6 +212,10 @@ defmodule Module.Types.Descr do
228212
@doc """
229213
Converts a descr to its quoted representation.
230214
"""
215+
def to_quoted(@term) do
216+
{:term, [], []}
217+
end
218+
231219
def to_quoted(%{} = descr) do
232220
case Enum.flat_map(descr, fn {key, value} -> to_quoted(key, value) end) do
233221
[] -> {:none, [], []}
@@ -299,11 +287,6 @@ defmodule Module.Types.Descr do
299287
left == right or (subtype?(left, right) and subtype?(right, left))
300288
end
301289

302-
@doc """
303-
Check if two types have a non-empty intersection.
304-
"""
305-
def intersect?(left, right), do: not empty?(intersection(left, right))
306-
307290
@doc """
308291
Checks if a type is a compatible subtype of another.
309292
@@ -325,14 +308,42 @@ defmodule Module.Types.Descr do
325308
expected_dynamic = Map.get(expected_type, :dynamic, expected_type)
326309

327310
if empty?(input_static) do
328-
intersect?(input_dynamic, expected_dynamic)
311+
not empty?(intersection(input_dynamic, expected_dynamic))
329312
else
330313
subtype_static(input_static, expected_dynamic)
331314
end
332315
end
333316

334317
## Bitmaps
335318

319+
@doc """
320+
Optimized version of `not empty?(intersection(binary(), type))`.
321+
"""
322+
def binary_type?(%{dynamic: %{bitmap: bitmap}}) when (bitmap &&& @bit_binary) != 0, do: true
323+
def binary_type?(%{bitmap: bitmap}) when (bitmap &&& @bit_binary) != 0, do: true
324+
def binary_type?(%{}), do: false
325+
326+
@doc """
327+
Optimized version of `not empty?(intersection(integer(), type))`.
328+
"""
329+
def integer_type?(%{dynamic: %{bitmap: bitmap}}) when (bitmap &&& @bit_integer) != 0, do: true
330+
def integer_type?(%{bitmap: bitmap}) when (bitmap &&& @bit_integer) != 0, do: true
331+
def integer_type?(%{}), do: false
332+
333+
@doc """
334+
Optimized version of `not empty?(intersection(float(), type))`.
335+
"""
336+
def float_type?(%{dynamic: %{bitmap: bitmap}}) when (bitmap &&& @bit_float) != 0, do: true
337+
def float_type?(%{bitmap: bitmap}) when (bitmap &&& @bit_float) != 0, do: true
338+
def float_type?(%{}), do: false
339+
340+
@doc """
341+
Optimized version of `not empty?(intersection(integer() or float(), type))`.
342+
"""
343+
def number_type?(%{dynamic: %{bitmap: bitmap}}) when (bitmap &&& @bit_number) != 0, do: true
344+
def number_type?(%{bitmap: bitmap}) when (bitmap &&& @bit_number) != 0, do: true
345+
def number_type?(%{}), do: false
346+
336347
defp bitmap_union(v1, v2), do: v1 ||| v2
337348
defp bitmap_intersection(v1, v2), do: v1 &&& v2
338349
defp bitmap_difference(v1, v2), do: v1 - (v1 &&& v2)
@@ -375,6 +386,31 @@ defmodule Module.Types.Descr do
375386
# an empty list of atoms. It is simplified to `0` in set operations, and the key
376387
# is removed from the map.
377388

389+
@doc """
390+
Optimized version of `not empty?(intersection(atom(), type))`.
391+
"""
392+
def atom_type?(%{dynamic: %{atom: _}}), do: true
393+
def atom_type?(%{atom: _}), do: true
394+
def atom_type?(%{}), do: false
395+
396+
@doc """
397+
Optimized version of `not empty?(intersection(atom([atom]), type))`.
398+
"""
399+
def atom_type?(%{} = descr, atom) do
400+
case :maps.take(:dynamic, descr) do
401+
:error ->
402+
atom_only?(descr) and atom_type_static?(descr, atom)
403+
404+
{dynamic, static} ->
405+
atom_only?(static) and
406+
(atom_type_static?(dynamic, atom) or atom_type_static?(static, atom))
407+
end
408+
end
409+
410+
defp atom_type_static?(%{atom: {:union, set}}, atom), do: :sets.is_element(atom, set)
411+
defp atom_type_static?(%{atom: {:negation, set}}, atom), do: not :sets.is_element(atom, set)
412+
defp atom_type_static?(%{}, _atom), do: false
413+
378414
@doc """
379415
Returns a set of all known atoms.
380416
@@ -592,28 +628,16 @@ defmodule Module.Types.Descr do
592628
defp optional?(%{bitmap: bitmap}) when (bitmap &&& @bit_optional) != 0, do: true
593629
defp optional?(_), do: false
594630

595-
defp pop_not_set(type) do
596-
case type do
597-
%{bitmap: @bit_optional} ->
598-
{true, Map.delete(type, :bitmap)}
599-
600-
%{bitmap: bitmap} when (bitmap &&& @bit_optional) != 0 ->
601-
{true, %{type | bitmap: bitmap - @bit_optional}}
602-
603-
_ ->
604-
{false, type}
605-
end
606-
end
607-
608-
defp map_tag_to_type(:open), do: term_or_not_set()
631+
defp map_tag_to_type(:open), do: term_or_optional()
609632
defp map_tag_to_type(:closed), do: not_set()
610633

634+
# TODO: We need to propagate any dynamic up
611635
defp map_new(tag, fields), do: [{tag, fields, []}]
612636
defp map_descr(tag, fields), do: %{map: map_new(tag, fields)}
613637

614638
@doc """
615-
Gets the type of the value returned by accessing `key` on `map`
616-
with the assumption that the descr is exclusively a map.
639+
Fetches the type of the value returned by accessing `key` on `map`
640+
with the assumption that the descr is exclusively a map (or dynamic).
617641
618642
It returns a two element tuple or `:error`. The first element says
619643
if the type is optional or not, the second element is the type.
@@ -625,18 +649,24 @@ defmodule Module.Types.Descr do
625649
:error ->
626650
if is_map_key(descr, :map) and map_only?(descr) do
627651
map_fetch_static(descr, key)
652+
|> none_if_not_set()
653+
|> badkey_if_empty_or_tag(false)
628654
else
629-
:error
655+
:badmap
630656
end
631657

658+
{%{map: {:open, fields, []}}, static} when fields == %{} and static == @none ->
659+
{true, dynamic()}
660+
632661
{dynamic, static} ->
633662
if (is_map_key(dynamic, :map) or is_map_key(static, :map)) and map_only?(static) do
634-
{dynamic_optional?, dynamic_type} = map_fetch_static(dynamic, key)
635-
{static_optional?, static_type} = map_fetch_static(static, key)
663+
{optional?, dynamic_type} = map_fetch_static(dynamic, key) |> pop_not_set()
664+
static_type = map_fetch_static(static, key) |> none_if_not_set()
636665

637-
{dynamic_optional? or static_optional?, union(dynamic(dynamic_type), static_type)}
666+
union(dynamic(dynamic_type), static_type)
667+
|> badkey_if_empty_or_tag(optional?)
638668
else
639-
:error
669+
:badmap
640670
end
641671
end
642672
end
@@ -645,13 +675,32 @@ defmodule Module.Types.Descr do
645675

646676
defp map_fetch_static(descr, key) when is_atom(key) do
647677
case descr do
648-
%{map: map} ->
649-
map_split_on_key(map, key)
650-
|> Enum.reduce(none(), &union/2)
651-
|> pop_not_set()
678+
%{map: map} -> Enum.reduce(map_split_on_key(map, key), none(), &union/2)
679+
%{} -> none()
680+
end
681+
end
682+
683+
defp badkey_if_empty_or_tag(type, tag) do
684+
if empty?(type), do: :badkey, else: {tag, type}
685+
end
652686

653-
%{} ->
654-
{false, none()}
687+
defp pop_not_set(type) do
688+
case type do
689+
%{bitmap: @bit_optional} ->
690+
{true, Map.delete(type, :bitmap)}
691+
692+
%{bitmap: bitmap} when (bitmap &&& @bit_optional) != 0 ->
693+
{true, %{type | bitmap: bitmap - @bit_optional}}
694+
695+
_ ->
696+
{false, type}
697+
end
698+
end
699+
700+
defp none_if_not_set(type) do
701+
case type do
702+
%{bitmap: bitmap} when (bitmap &&& @bit_optional) != 0 -> @none
703+
_ -> type
655704
end
656705
end
657706

@@ -780,7 +829,7 @@ defmodule Module.Types.Descr do
780829

781830
# There may be value in common
782831
tag == :open ->
783-
diff = difference(term_or_not_set(), neg_type)
832+
diff = difference(term_or_optional(), neg_type)
784833
empty?(diff) or map_empty?(tag, Map.put(fields, neg_key, diff), negs)
785834
end
786835
end
@@ -816,7 +865,7 @@ defmodule Module.Types.Descr do
816865
# %{...} the open map in a positive intersection can be ignored
817866
{fst, snd} =
818867
if tag == :open and fields == %{} do
819-
{term_or_not_set(), term_or_not_set()}
868+
{term_or_optional(), term_or_optional()}
820869
else
821870
map_pop_key(tag, fields, key)
822871
end

0 commit comments

Comments
 (0)