@@ -24,20 +24,17 @@ defmodule Module.Types do
2424 #
2525 # * :infer - Same as :dynamic but skips remote calls.
2626 #
27- # * :traversal - Focused mostly on traversing AST, skips most type system
28- # operations. Used by macros and when skipping inference.
29- #
3027 # The mode may also control exhaustiveness checks in the future (to be decided).
3128 # We may also want for applications with subtyping in dynamic mode to always
3229 # intersect with dynamic, but this mode may be too lax (to be decided based on
3330 # feedback).
34- @ modes [ :static , :dynamic , :infer , :traversal ]
31+ @ modes [ :static , :dynamic , :infer ]
3532
3633 # These functions are not inferred because they are added/managed by the compiler
3734 @ no_infer [ behaviour_info: 1 ]
3835
3936 @ doc false
40- def infer ( module , file , attrs , defs , private , used_private , env , { _ , cache } ) do
37+ def infer ( module , file , attrs , defs , used_private , env , { _ , cache } ) do
4138 # We don't care about inferring signatures for protocols,
4239 # those will be replaced anyway. There is also nothing to
4340 # infer if there is no cache system, we only do traversals.
@@ -75,11 +72,12 @@ defmodule Module.Types do
7572
7673 stack = stack ( :infer , file , module , { :__info__ , 1 } , env , cache , handler )
7774
78- { types , % { local_sigs: reachable_sigs } = context } =
79- for { fun_arity , kind , meta , _clauses } = def <- defs ,
80- kind in [ :def , :defmacro ] ,
81- reduce: { [ ] , context ( ) } do
82- { types , context } ->
75+ # In case there are loops, the other we traverse matters,
76+ # so we sort the definitions for determinism
77+ { types , private , % { local_sigs: reachable_sigs } = context } =
78+ for { fun_arity , kind , meta , _clauses } = def <- Enum . sort ( defs ) ,
79+ reduce: { [ ] , [ ] , context ( ) } do
80+ { types , private , context } when kind in [ :def , :defmacro ] ->
8381 # Optimized version of finder, since we already have the definition
8482 finder = fn _ ->
8583 default_domain ( infer_mode ( kind , infer_signatures? ) , def , fun_arity , impl )
@@ -88,10 +86,13 @@ defmodule Module.Types do
8886 { _kind , inferred , context } = local_handler ( meta , fun_arity , stack , context , finder )
8987
9088 if infer_signatures? and kind == :def and fun_arity not in @ no_infer do
91- { [ { fun_arity , inferred } | types ] , context }
89+ { [ { fun_arity , inferred } | types ] , private , context }
9290 else
93- { types , context }
91+ { types , private , context }
9492 end
93+
94+ { types , private , context } ->
95+ { types , [ def | private ] , context }
9596 end
9697
9798 # Now traverse all used privates to find any other private that have been used by them.
@@ -105,8 +106,8 @@ defmodule Module.Types do
105106
106107 { unreachable , _context } =
107108 Enum . reduce ( private , { [ ] , context } , fn
108- { fun_arity , kind , _meta , _defaults } = info , { unreachable , context } ->
109- warn_unused_def ( info , used_sigs , env )
109+ { fun_arity , kind , meta , _clauses } , { unreachable , context } ->
110+ warn_unused_def ( fun_arity , kind , meta , used_sigs , env )
110111
111112 # Find anything undefined within unused functions
112113 { _kind , _inferred , context } = local_handler ( [ ] , fun_arity , stack , context , finder )
@@ -125,7 +126,7 @@ defmodule Module.Types do
125126 end
126127
127128 defp infer_mode ( kind , infer_signatures? ) do
128- if infer_signatures? and kind in [ :def , :defp ] , do: :infer , else: :traversal
129+ if infer_signatures? and kind in [ :def , :defp ] , do: :infer , else: :traverse
129130 end
130131
131132 defp protocol? ( attrs ) do
@@ -154,7 +155,7 @@ defmodule Module.Types do
154155 | List . duplicate ( Descr . dynamic ( ) , arity - 1 )
155156 ]
156157
157- { fun_arity , kind , meta , clauses } = def
158+ { _fun_arity , kind , meta , clauses } = def
158159
159160 clauses =
160161 for { meta , args , guards , body } <- clauses do
@@ -173,29 +174,30 @@ defmodule Module.Types do
173174 :elixir_errors . module_error ( Helpers . with_span ( meta , fun ) , env , __MODULE__ , tuple )
174175 end
175176
176- defp warn_unused_def ( { _fun_arity , _kind , false , _ } , _used , _env ) do
177- :ok
178- end
177+ defp warn_unused_def ( fun_arity , kind , meta , used , env ) do
178+ default = Keyword . get ( meta , :defaults , 0 )
179179
180- defp warn_unused_def ( { fun_arity , kind , meta , 0 } , used , env ) do
181- case is_map_key ( used , fun_arity ) do
182- true -> :ok
183- false -> :elixir_errors . file_warn ( meta , env , __MODULE__ , { :unused_def , fun_arity , kind } )
184- end
180+ cond do
181+ Keyword . get ( meta , :context ) != nil ->
182+ :ok
185183
186- :ok
187- end
184+ default == 0 ->
185+ case is_map_key ( used , fun_arity ) do
186+ true -> :ok
187+ false -> :elixir_errors . file_warn ( meta , env , __MODULE__ , { :unused_def , fun_arity , kind } )
188+ end
188189
189- defp warn_unused_def ( { tuple , kind , meta , default } , used , env ) when default > 0 do
190- { name , arity } = tuple
191- min = arity - default
192- max = arity
190+ default > 0 ->
191+ { name , arity } = fun_arity
192+ min = arity - default
193+ max = arity
193194
194- case min_reachable_default ( max , min , :none , name , used ) do
195- :none -> :elixir_errors . file_warn ( meta , env , __MODULE__ , { :unused_def , tuple , kind } )
196- ^ min -> :ok
197- ^ max -> :elixir_errors . file_warn ( meta , env , __MODULE__ , { :unused_args , tuple } )
198- diff -> :elixir_errors . file_warn ( meta , env , __MODULE__ , { :unused_args , tuple , diff } )
195+ case min_reachable_default ( max , min , :none , name , used ) do
196+ :none -> :elixir_errors . file_warn ( meta , env , __MODULE__ , { :unused_def , fun_arity , kind } )
197+ ^ min -> :ok
198+ ^ max -> :elixir_errors . file_warn ( meta , env , __MODULE__ , { :unused_args , fun_arity } )
199+ diff -> :elixir_errors . file_warn ( meta , env , __MODULE__ , { :unused_args , fun_arity , diff } )
200+ end
199201 end
200202
201203 :ok
@@ -291,7 +293,7 @@ defmodule Module.Types do
291293 context = put_in ( context . local_sigs , Map . put ( local_sigs , fun_arity , kind ) )
292294
293295 { inferred , mapping , context } =
294- local_handler ( fun_arity , kind , meta , clauses , expected , mode , stack , context )
296+ local_handler ( mode , fun_arity , kind , meta , clauses , expected , stack , context )
295297
296298 context =
297299 update_in ( context . local_sigs , & Map . put ( & 1 , fun_arity , { kind , inferred , mapping } ) )
@@ -304,7 +306,17 @@ defmodule Module.Types do
304306 end
305307 end
306308
307- defp local_handler ( fun_arity , kind , meta , clauses , expected , mode , stack , context ) do
309+ defp local_handler ( :traverse , { _ , arity } , _kind , _meta , clauses , _expected , stack , context ) do
310+ context =
311+ Enum . reduce ( clauses , context , fn { _meta , _args , _guards , body } , context ->
312+ Module.Types.Traverse . of_expr ( body , stack , context )
313+ end )
314+
315+ inferred = { :infer , nil , [ { List . duplicate ( Descr . term ( ) , arity ) , Descr . dynamic ( ) } ] }
316+ { inferred , [ { 0 , 0 } ] , context }
317+ end
318+
319+ defp local_handler ( mode , fun_arity , kind , meta , clauses , expected , stack , context ) do
308320 { fun , _arity } = fun_arity
309321 stack = stack |> fresh_stack ( mode , fun_arity ) |> with_file_meta ( meta )
310322
@@ -320,12 +332,7 @@ defmodule Module.Types do
320332 { return_type , context } =
321333 Expr . of_expr ( body , Descr . term ( ) , body , stack , context )
322334
323- args_types =
324- if stack . mode == :traversal do
325- expected
326- else
327- Pattern . of_domain ( trees , context )
328- end
335+ args_types = Pattern . of_domain ( trees , context )
329336
330337 { type_index , inferred } =
331338 add_inferred ( inferred , args_types , return_type , total - 1 , [ ] )
@@ -442,7 +449,9 @@ defmodule Module.Types do
442449 warnings: [ ] ,
443450 # All vars and their types
444451 vars: % { } ,
445- # Variables and arguments from patterns
452+ # Variables that are specific to the current environment/conditional
453+ conditional_vars: nil ,
454+ # Track metadata specific to matches and guards
446455 pattern_info: nil ,
447456 # If type checking has found an error/failure
448457 failed: false ,
0 commit comments