Skip to content

Commit 7be9618

Browse files
committed
All type checking guards
1 parent c20a874 commit 7be9618

File tree

2 files changed

+173
-144
lines changed

2 files changed

+173
-144
lines changed

lib/elixir/lib/module/types/apply.ex

Lines changed: 172 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,27 @@ defmodule Module.Types.Apply do
105105
{[float(), float()], float()}
106106
]
107107

108-
is_clauses = [{[term()], boolean()}]
108+
is_guards = [
109+
is_atom: atom(),
110+
is_binary: binary(),
111+
is_bitstring: binary(),
112+
is_boolean: boolean(),
113+
is_float: float(),
114+
is_function: fun(),
115+
is_integer: integer(),
116+
is_list: union(empty_list(), non_empty_list(term(), term())),
117+
is_map: open_map(),
118+
is_number: union(float(), integer()),
119+
is_pid: pid(),
120+
is_port: port(),
121+
is_reference: reference(),
122+
is_tuple: tuple()
123+
]
124+
125+
mod_fun_clauses_is_guards =
126+
for {guard, _type} <- is_guards do
127+
{:erlang, guard, [{[term()], boolean()}]}
128+
end
109129

110130
args_or_arity = union(list(term()), integer())
111131
args_or_none = union(list(term()), atom([:none]))
@@ -134,149 +154,139 @@ defmodule Module.Types.Apply do
134154
{[atom([left]), atom([right])], atom([left or right])}
135155
end
136156

137-
for {mod, fun, clauses} <- [
138-
# :binary
139-
{:binary, :copy, [{[binary(), integer()], binary()}]},
140-
141-
# :erlang
142-
{:erlang, :+, [{[integer()], integer()}, {[float()], float()}]},
143-
{:erlang, :+, basic_arith_2_args_clauses},
144-
{:erlang, :-, [{[integer()], integer()}, {[float()], float()}]},
145-
{:erlang, :-, basic_arith_2_args_clauses},
146-
{:erlang, :*, basic_arith_2_args_clauses},
147-
{:erlang, :/, [{[union(integer(), float()), union(integer(), float())], float()}]},
148-
{:erlang, :"/=", [{[term(), term()], boolean()}]},
149-
{:erlang, :"=/=", [{[term(), term()], boolean()}]},
150-
{:erlang, :<, [{[term(), term()], boolean()}]},
151-
{:erlang, :"=<", [{[term(), term()], boolean()}]},
152-
{:erlang, :==, [{[term(), term()], boolean()}]},
153-
{:erlang, :"=:=", [{[term(), term()], boolean()}]},
154-
{:erlang, :>, [{[term(), term()], boolean()}]},
155-
{:erlang, :>=, [{[term(), term()], boolean()}]},
156-
{:erlang, :abs, [{[integer()], integer()}, {[float()], float()}]},
157-
# TODO: Decide if it returns dynamic() or term()
158-
{:erlang, :apply, [{[fun(), list(term())], dynamic()}]},
159-
{:erlang, :apply, [{[atom(), atom(), list(term())], dynamic()}]},
160-
{:erlang, :and, and_signature},
161-
{:erlang, :atom_to_binary, [{[atom()], binary()}]},
162-
{:erlang, :atom_to_list, [{[atom()], list(integer())}]},
163-
{:erlang, :band, [{[integer(), integer()], integer()}]},
164-
{:erlang, :binary_part, [{[binary(), integer(), integer()], binary()}]},
165-
{:erlang, :binary_to_atom, [{[binary()], atom()}]},
166-
{:erlang, :binary_to_existing_atom, [{[binary()], atom()}]},
167-
{:erlang, :binary_to_integer, [{[binary()], integer()}]},
168-
{:erlang, :binary_to_integer, [{[binary(), integer()], integer()}]},
169-
{:erlang, :binary_to_float, [{[binary()], float()}]},
170-
{:erlang, :bit_size, [{[binary()], integer()}]},
171-
{:erlang, :bnot, [{[integer()], integer()}]},
172-
{:erlang, :bor, [{[integer(), integer()], integer()}]},
173-
{:erlang, :bsl, [{[integer(), integer()], integer()}]},
174-
{:erlang, :bsr, [{[integer(), integer()], integer()}]},
175-
{:erlang, :bxor, [{[integer(), integer()], integer()}]},
176-
{:erlang, :byte_size, [{[binary()], integer()}]},
177-
{:erlang, :ceil, [{[union(integer(), float())], integer()}]},
178-
{:erlang, :div, [{[integer(), integer()], integer()}]},
179-
{:erlang, :error, [{[term()], none()}]},
180-
{:erlang, :error, [{[term(), args_or_none], none()}]},
181-
{:erlang, :error, [{[term(), args_or_none, kw.(error_info: open_map())], none()}]},
182-
{:erlang, :floor, [{[union(integer(), float())], integer()}]},
183-
{:erlang, :function_exported, [{[atom(), atom(), integer()], boolean()}]},
184-
{:erlang, :integer_to_binary, [{[integer()], binary()}]},
185-
{:erlang, :integer_to_binary, [{[integer(), integer()], binary()}]},
186-
{:erlang, :integer_to_list, [{[integer()], non_empty_list(integer())}]},
187-
{:erlang, :integer_to_list, [{[integer(), integer()], non_empty_list(integer())}]},
188-
{:erlang, :is_atom, is_clauses},
189-
{:erlang, :is_binary, is_clauses},
190-
{:erlang, :is_bitstring, is_clauses},
191-
{:erlang, :is_boolean, is_clauses},
192-
{:erlang, :is_float, is_clauses},
193-
{:erlang, :is_function, is_clauses},
194-
{:erlang, :is_function, [{[term(), integer()], boolean()}]},
195-
{:erlang, :is_integer, is_clauses},
196-
{:erlang, :is_list, is_clauses},
197-
{:erlang, :is_map, is_clauses},
198-
{:erlang, :is_map_key, [{[term(), open_map()], boolean()}]},
199-
{:erlang, :is_number, is_clauses},
200-
{:erlang, :is_pid, is_clauses},
201-
{:erlang, :is_port, is_clauses},
202-
{:erlang, :is_reference, is_clauses},
203-
{:erlang, :is_tuple, is_clauses},
204-
{:erlang, :length, [{[list(term())], integer()}]},
205-
{:erlang, :list_to_atom, [{[list(integer())], atom()}]},
206-
{:erlang, :list_to_existing_atom, [{[list(integer())], atom()}]},
207-
{:erlang, :list_to_float, [{[non_empty_list(integer())], float()}]},
208-
{:erlang, :list_to_integer, [{[non_empty_list(integer())], integer()}]},
209-
{:erlang, :list_to_integer, [{[non_empty_list(integer()), integer()], integer()}]},
210-
{:erlang, :make_ref, [{[], reference()}]},
211-
{:erlang, :map_size, [{[open_map()], integer()}]},
212-
{:erlang, :node, [{[], atom()}]},
213-
{:erlang, :node, [{[pid() |> union(reference()) |> union(port())], atom()}]},
214-
{:erlang, :not, not_signature},
215-
{:erlang, :or, or_signature},
216-
{:erlang, :raise, [{[atom([:error, :exit, :throw]), term(), raise_stacktrace], none()}]},
217-
{:erlang, :rem, [{[integer(), integer()], integer()}]},
218-
{:erlang, :round, [{[union(integer(), float())], integer()}]},
219-
{:erlang, :self, [{[], pid()}]},
220-
{:erlang, :spawn, [{[fun(0)], pid()}]},
221-
{:erlang, :spawn, [{mfargs, pid()}]},
222-
{:erlang, :spawn_link, [{[fun(0)], pid()}]},
223-
{:erlang, :spawn_link, [{mfargs, pid()}]},
224-
{:erlang, :spawn_monitor, [{[fun(0)], tuple([reference(), pid()])}]},
225-
{:erlang, :spawn_monitor, [{mfargs, tuple([reference(), pid()])}]},
226-
{:erlang, :tuple_size, [{[open_tuple([])], integer()}]},
227-
{:erlang, :trunc, [{[union(integer(), float())], integer()}]},
228-
229-
# TODO: Replace term()/dynamic() by parametric types
230-
{:erlang, :++,
231-
[
232-
{[empty_list(), term()], dynamic(term())},
233-
{[non_empty_list(term()), term()], dynamic(non_empty_list(term(), term()))}
234-
]},
235-
{:erlang, :--, [{[list(term()), list(term())], dynamic(list(term()))}]},
236-
{:erlang, :andalso, [{[boolean(), term()], dynamic()}]},
237-
{:erlang, :delete_element, [{[integer(), open_tuple([])], dynamic(open_tuple([]))}]},
238-
{:erlang, :hd, [{[non_empty_list(term(), term())], dynamic()}]},
239-
{:erlang, :element, [{[integer(), open_tuple([])], dynamic()}]},
240-
{:erlang, :insert_element,
241-
[{[integer(), open_tuple([]), term()], dynamic(open_tuple([]))}]},
242-
{:erlang, :list_to_tuple, [{[list(term())], dynamic(open_tuple([]))}]},
243-
{:erlang, :max, [{[term(), term()], dynamic()}]},
244-
{:erlang, :min, [{[term(), term()], dynamic()}]},
245-
{:erlang, :orelse, [{[boolean(), term()], dynamic()}]},
246-
{:erlang, :send, [{[send_destination, term()], dynamic()}]},
247-
{:erlang, :setelement, [{[integer(), open_tuple([]), term()], dynamic(open_tuple([]))}]},
248-
{:erlang, :tl, [{[non_empty_list(term(), term())], dynamic()}]},
249-
{:erlang, :tuple_to_list, [{[open_tuple([])], dynamic(list(term()))}]},
250-
251-
## Map
252-
{Map, :from_struct, [{[open_map()], open_map(__struct__: not_set())}]},
253-
{Map, :get, [{[open_map(), term()], term()}]},
254-
{Map, :get, [{[open_map(), term(), term()], term()}]},
255-
{Map, :get_lazy, [{[open_map(), term(), fun(0)], term()}]},
256-
{Map, :pop, [{[open_map(), term()], tuple([term(), open_map()])}]},
257-
{Map, :pop, [{[open_map(), term(), term()], tuple([term(), open_map()])}]},
258-
{Map, :pop!, [{[open_map(), term()], tuple([term(), open_map()])}]},
259-
{Map, :pop_lazy, [{[open_map(), term(), fun(0)], tuple([term(), open_map()])}]},
260-
{Map, :put_new, [{[open_map(), term(), term()], open_map()}]},
261-
{Map, :put_new_lazy, [{[open_map(), term(), fun(0)], open_map()}]},
262-
{Map, :replace, [{[open_map(), term(), term()], open_map()}]},
263-
{Map, :replace_lazy, [{[open_map(), term(), fun(1)], open_map()}]},
264-
{Map, :update, [{[open_map(), term(), term(), fun(1)], open_map()}]},
265-
{Map, :update!, [{[open_map(), term(), fun(1)], open_map()}]},
266-
{:maps, :from_keys, [{[list(term()), term()], open_map()}]},
267-
{:maps, :find,
268-
[{[term(), open_map()], tuple([atom([:ok]), term()]) |> union(atom([:error]))}]},
269-
{:maps, :get, [{[term(), open_map()], term()}]},
270-
{:maps, :is_key, [{[term(), open_map()], boolean()}]},
271-
{:maps, :keys, [{[open_map()], list(term())}]},
272-
{:maps, :put, [{[term(), term(), open_map()], open_map()}]},
273-
{:maps, :remove, [{[term(), open_map()], open_map()}]},
274-
{:maps, :take,
275-
[{[term(), open_map()], tuple([term(), open_map()]) |> union(atom([:error]))}]},
276-
{:maps, :to_list, [{[open_map()], list(tuple([term(), term()]))}]},
277-
{:maps, :update, [{[term(), term(), open_map()], open_map()}]},
278-
{:maps, :values, [{[open_map()], list(term())}]}
279-
] do
157+
for {mod, fun, clauses} <-
158+
mod_fun_clauses_is_guards ++
159+
[
160+
# :binary
161+
{:binary, :copy, [{[binary(), integer()], binary()}]},
162+
163+
# :erlang
164+
{:erlang, :+, [{[integer()], integer()}, {[float()], float()}]},
165+
{:erlang, :+, basic_arith_2_args_clauses},
166+
{:erlang, :-, [{[integer()], integer()}, {[float()], float()}]},
167+
{:erlang, :-, basic_arith_2_args_clauses},
168+
{:erlang, :*, basic_arith_2_args_clauses},
169+
{:erlang, :/, [{[union(integer(), float()), union(integer(), float())], float()}]},
170+
{:erlang, :"/=", [{[term(), term()], boolean()}]},
171+
{:erlang, :"=/=", [{[term(), term()], boolean()}]},
172+
{:erlang, :<, [{[term(), term()], boolean()}]},
173+
{:erlang, :"=<", [{[term(), term()], boolean()}]},
174+
{:erlang, :==, [{[term(), term()], boolean()}]},
175+
{:erlang, :"=:=", [{[term(), term()], boolean()}]},
176+
{:erlang, :>, [{[term(), term()], boolean()}]},
177+
{:erlang, :>=, [{[term(), term()], boolean()}]},
178+
{:erlang, :abs, [{[integer()], integer()}, {[float()], float()}]},
179+
# TODO: Decide if it returns dynamic() or term()
180+
{:erlang, :apply, [{[fun(), list(term())], dynamic()}]},
181+
{:erlang, :apply, [{[atom(), atom(), list(term())], dynamic()}]},
182+
{:erlang, :and, and_signature},
183+
{:erlang, :atom_to_binary, [{[atom()], binary()}]},
184+
{:erlang, :atom_to_list, [{[atom()], list(integer())}]},
185+
{:erlang, :band, [{[integer(), integer()], integer()}]},
186+
{:erlang, :binary_part, [{[binary(), integer(), integer()], binary()}]},
187+
{:erlang, :binary_to_atom, [{[binary()], atom()}]},
188+
{:erlang, :binary_to_existing_atom, [{[binary()], atom()}]},
189+
{:erlang, :binary_to_integer, [{[binary()], integer()}]},
190+
{:erlang, :binary_to_integer, [{[binary(), integer()], integer()}]},
191+
{:erlang, :binary_to_float, [{[binary()], float()}]},
192+
{:erlang, :bit_size, [{[binary()], integer()}]},
193+
{:erlang, :bnot, [{[integer()], integer()}]},
194+
{:erlang, :bor, [{[integer(), integer()], integer()}]},
195+
{:erlang, :bsl, [{[integer(), integer()], integer()}]},
196+
{:erlang, :bsr, [{[integer(), integer()], integer()}]},
197+
{:erlang, :bxor, [{[integer(), integer()], integer()}]},
198+
{:erlang, :byte_size, [{[binary()], integer()}]},
199+
{:erlang, :ceil, [{[union(integer(), float())], integer()}]},
200+
{:erlang, :div, [{[integer(), integer()], integer()}]},
201+
{:erlang, :error, [{[term()], none()}]},
202+
{:erlang, :error, [{[term(), args_or_none], none()}]},
203+
{:erlang, :error, [{[term(), args_or_none, kw.(error_info: open_map())], none()}]},
204+
{:erlang, :floor, [{[union(integer(), float())], integer()}]},
205+
{:erlang, :function_exported, [{[atom(), atom(), integer()], boolean()}]},
206+
{:erlang, :integer_to_binary, [{[integer()], binary()}]},
207+
{:erlang, :integer_to_binary, [{[integer(), integer()], binary()}]},
208+
{:erlang, :integer_to_list, [{[integer()], non_empty_list(integer())}]},
209+
{:erlang, :integer_to_list, [{[integer(), integer()], non_empty_list(integer())}]},
210+
{:erlang, :is_function, [{[term(), integer()], boolean()}]},
211+
{:erlang, :is_map_key, [{[term(), open_map()], boolean()}]},
212+
{:erlang, :length, [{[list(term())], integer()}]},
213+
{:erlang, :list_to_atom, [{[list(integer())], atom()}]},
214+
{:erlang, :list_to_existing_atom, [{[list(integer())], atom()}]},
215+
{:erlang, :list_to_float, [{[non_empty_list(integer())], float()}]},
216+
{:erlang, :list_to_integer, [{[non_empty_list(integer())], integer()}]},
217+
{:erlang, :list_to_integer, [{[non_empty_list(integer()), integer()], integer()}]},
218+
{:erlang, :make_ref, [{[], reference()}]},
219+
{:erlang, :map_size, [{[open_map()], integer()}]},
220+
{:erlang, :node, [{[], atom()}]},
221+
{:erlang, :node, [{[pid() |> union(reference()) |> union(port())], atom()}]},
222+
{:erlang, :not, not_signature},
223+
{:erlang, :or, or_signature},
224+
{:erlang, :raise,
225+
[{[atom([:error, :exit, :throw]), term(), raise_stacktrace], none()}]},
226+
{:erlang, :rem, [{[integer(), integer()], integer()}]},
227+
{:erlang, :round, [{[union(integer(), float())], integer()}]},
228+
{:erlang, :self, [{[], pid()}]},
229+
{:erlang, :spawn, [{[fun(0)], pid()}]},
230+
{:erlang, :spawn, [{mfargs, pid()}]},
231+
{:erlang, :spawn_link, [{[fun(0)], pid()}]},
232+
{:erlang, :spawn_link, [{mfargs, pid()}]},
233+
{:erlang, :spawn_monitor, [{[fun(0)], tuple([pid(), reference()])}]},
234+
{:erlang, :spawn_monitor, [{mfargs, tuple([pid(), reference()])}]},
235+
{:erlang, :tuple_size, [{[open_tuple([])], integer()}]},
236+
{:erlang, :trunc, [{[union(integer(), float())], integer()}]},
237+
238+
# TODO: Replace term()/dynamic() by parametric types
239+
{:erlang, :++,
240+
[
241+
{[empty_list(), term()], dynamic(term())},
242+
{[non_empty_list(term()), term()], dynamic(non_empty_list(term(), term()))}
243+
]},
244+
{:erlang, :--, [{[list(term()), list(term())], dynamic(list(term()))}]},
245+
{:erlang, :andalso, [{[boolean(), term()], dynamic()}]},
246+
{:erlang, :delete_element, [{[integer(), open_tuple([])], dynamic(open_tuple([]))}]},
247+
{:erlang, :hd, [{[non_empty_list(term(), term())], dynamic()}]},
248+
{:erlang, :element, [{[integer(), open_tuple([])], dynamic()}]},
249+
{:erlang, :insert_element,
250+
[{[integer(), open_tuple([]), term()], dynamic(open_tuple([]))}]},
251+
{:erlang, :list_to_tuple, [{[list(term())], dynamic(open_tuple([]))}]},
252+
{:erlang, :max, [{[term(), term()], dynamic()}]},
253+
{:erlang, :min, [{[term(), term()], dynamic()}]},
254+
{:erlang, :orelse, [{[boolean(), term()], dynamic()}]},
255+
{:erlang, :send, [{[send_destination, term()], dynamic()}]},
256+
{:erlang, :setelement,
257+
[{[integer(), open_tuple([]), term()], dynamic(open_tuple([]))}]},
258+
{:erlang, :tl, [{[non_empty_list(term(), term())], dynamic()}]},
259+
{:erlang, :tuple_to_list, [{[open_tuple([])], dynamic(list(term()))}]},
260+
261+
## Map
262+
{Map, :from_struct, [{[open_map()], open_map(__struct__: not_set())}]},
263+
{Map, :get, [{[open_map(), term()], term()}]},
264+
{Map, :get, [{[open_map(), term(), term()], term()}]},
265+
{Map, :get_lazy, [{[open_map(), term(), fun(0)], term()}]},
266+
{Map, :pop, [{[open_map(), term()], tuple([term(), open_map()])}]},
267+
{Map, :pop, [{[open_map(), term(), term()], tuple([term(), open_map()])}]},
268+
{Map, :pop!, [{[open_map(), term()], tuple([term(), open_map()])}]},
269+
{Map, :pop_lazy, [{[open_map(), term(), fun(0)], tuple([term(), open_map()])}]},
270+
{Map, :put_new, [{[open_map(), term(), term()], open_map()}]},
271+
{Map, :put_new_lazy, [{[open_map(), term(), fun(0)], open_map()}]},
272+
{Map, :replace, [{[open_map(), term(), term()], open_map()}]},
273+
{Map, :replace_lazy, [{[open_map(), term(), fun(1)], open_map()}]},
274+
{Map, :update, [{[open_map(), term(), term(), fun(1)], open_map()}]},
275+
{Map, :update!, [{[open_map(), term(), fun(1)], open_map()}]},
276+
{:maps, :from_keys, [{[list(term()), term()], open_map()}]},
277+
{:maps, :find,
278+
[{[term(), open_map()], tuple([atom([:ok]), term()]) |> union(atom([:error]))}]},
279+
{:maps, :get, [{[term(), open_map()], term()}]},
280+
{:maps, :is_key, [{[term(), open_map()], boolean()}]},
281+
{:maps, :keys, [{[open_map()], list(term())}]},
282+
{:maps, :put, [{[term(), term(), open_map()], open_map()}]},
283+
{:maps, :remove, [{[term(), open_map()], open_map()}]},
284+
{:maps, :take,
285+
[{[term(), open_map()], tuple([term(), open_map()]) |> union(atom([:error]))}]},
286+
{:maps, :to_list, [{[open_map()], list(tuple([term(), term()]))}]},
287+
{:maps, :update, [{[term(), term(), open_map()], open_map()}]},
288+
{:maps, :values, [{[open_map()], list(term())}]}
289+
] do
280290
[arity] = Enum.map(clauses, fn {args, _return} -> length(args) end) |> Enum.uniq()
281291

282292
true =
@@ -325,6 +335,24 @@ defmodule Module.Types.Apply do
325335
{:none, Enum.map(args, fn _ -> term() end), context}
326336
end
327337

338+
@guard_info {:strong, nil, [{[term()], boolean()}]}
339+
340+
for {guard, type} <- is_guards do
341+
@true_type type
342+
@false_type negation(type)
343+
344+
def remote_domain(:erlang, unquote(guard), [_], expected, _meta, _stack, context) do
345+
arg =
346+
case booleaness(expected) do
347+
:always_true -> @true_type
348+
:always_false -> @false_type
349+
:undefined -> term()
350+
end
351+
352+
{@guard_info, [arg], context}
353+
end
354+
end
355+
328356
@is_function_info {:strong, nil, [{[term(), integer()], boolean()}]}
329357

330358
def remote_domain(:erlang, :is_function, [_, arity], expected, _meta, _stack, context)

lib/elixir/lib/module/types/pattern.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,7 @@ defmodule Module.Types.Pattern do
892892
end
893893
end
894894

895+
# TODO: Move orelse and andalso handling here
895896
defp of_remote(fun, meta, [left, right], call, {_root, expected}, stack, context)
896897
when fun in [:or, :orelse] do
897898
{info, [left_domain, right_domain], context} =

0 commit comments

Comments
 (0)