@@ -30,17 +30,26 @@ defmodule Module.Types do
3030 @ modes [ :static , :dynamic , :infer , :traversal ]
3131
3232 # These functions are not inferred because they are added/managed by the compiler
33- @ no_infer [ __protocol__: 1 , behaviour_info: 1 ]
33+ @ no_infer [ behaviour_info: 1 ]
3434
3535 @ doc false
36- def infer ( module , file , defs , private , used_private , env , { _ , cache } ) do
37- infer_signatures? = :elixir_config . get ( :infer_signatures ) and cache != nil
36+ def infer ( module , file , attrs , defs , private , used_private , env , { _ , cache } ) do
37+ # We don't care about inferring signatures for protocols,
38+ # those will be replaced anyway. There is also nothing to
39+ # infer if there is no cache system, we only do traversals.
40+ infer_signatures? =
41+ :elixir_config . get ( :infer_signatures ) and cache != nil and not protocol? ( attrs )
42+
43+ impl = impl_for ( attrs )
3844
3945 finder =
4046 fn fun_arity ->
4147 case :lists . keyfind ( fun_arity , 1 , defs ) do
42- { _ , kind , _ , _ } = clause -> { infer_mode ( kind , infer_signatures? ) , clause }
43- false -> false
48+ { _ , kind , _ , _ } = clause ->
49+ { infer_mode ( kind , infer_signatures? ) , clause , default_domain ( fun_arity , impl ) }
50+
51+ false ->
52+ false
4453 end
4554 end
4655
@@ -67,7 +76,11 @@ defmodule Module.Types do
6776 kind in [ :def , :defmacro ] ,
6877 reduce: { [ ] , context ( ) } do
6978 { types , context } ->
70- finder = fn _ -> { infer_mode ( kind , infer_signatures? ) , def } end
79+ # Optimized version of finder, since we already the definition
80+ finder = fn _ ->
81+ { infer_mode ( kind , infer_signatures? ) , def , default_domain ( fun_arity , impl ) }
82+ end
83+
7184 { _kind , inferred , context } = local_handler ( meta , fun_arity , stack , context , finder )
7285
7386 if infer_signatures? and kind == :def and fun_arity not in @ no_infer do
@@ -111,6 +124,33 @@ defmodule Module.Types do
111124 if infer_signatures? and kind in [ :def , :defp ] , do: :infer , else: :traversal
112125 end
113126
127+ defp protocol? ( attrs ) do
128+ List . keymember? ( attrs , :__protocol__ , 0 )
129+ end
130+
131+ defp impl_for ( attrs ) do
132+ case List . keyfind ( attrs , :__impl__ , 0 ) do
133+ { :__impl__ , [ protocol: protocol , for: for ] } ->
134+ if Code . ensure_loaded? ( protocol ) and function_exported? ( protocol , :behaviour_info , 1 ) do
135+ { for , protocol . behaviour_info ( :callbacks ) }
136+ else
137+ nil
138+ end
139+
140+ _ ->
141+ nil
142+ end
143+ end
144+
145+ defp default_domain ( { _ , arity } = fun_arity , impl ) do
146+ with { for , callbacks } <- impl ,
147+ true <- fun_arity in callbacks do
148+ [ Module.Types.Of . impl ( for ) | List . duplicate ( Descr . dynamic ( ) , arity - 1 ) ]
149+ else
150+ _ -> List . duplicate ( Descr . dynamic ( ) , arity )
151+ end
152+ end
153+
114154 defp undefined_function! ( reason , meta , { fun , arity } , stack , env ) do
115155 env = % { env | function: stack . function , file: stack . file }
116156 tuple = { reason , { fun , arity } , stack . module }
@@ -159,10 +199,12 @@ defmodule Module.Types do
159199 end
160200
161201 @ doc false
162- def warnings ( module , file , defs , no_warn_undefined , cache ) do
202+ def warnings ( module , file , attrs , defs , no_warn_undefined , cache ) do
203+ impl = impl_for ( attrs )
204+
163205 finder = fn fun_arity ->
164206 case :lists . keyfind ( fun_arity , 1 , defs ) do
165- { _ , _ , _ , _ } = clause -> { :dynamic , clause }
207+ { _ , _ , _ , _ } = clause -> { :dynamic , clause , default_domain ( fun_arity , impl ) }
166208 false -> false
167209 end
168210 end
@@ -172,7 +214,8 @@ defmodule Module.Types do
172214
173215 context =
174216 Enum . reduce ( defs , context ( ) , fn { fun_arity , _kind , meta , _clauses } = def , context ->
175- finder = fn _ -> { :dynamic , def } end
217+ # Optimized version of finder, since we already the definition
218+ finder = fn _ -> { :dynamic , def , default_domain ( fun_arity , impl ) } end
176219 { _kind , _inferred , context } = local_handler ( meta , fun_arity , stack , context , finder )
177220 context
178221 end )
@@ -211,11 +254,11 @@ defmodule Module.Types do
211254
212255 local_sigs ->
213256 case finder . ( fun_arity ) do
214- { mode , { fun_arity , kind , meta , clauses } } ->
257+ { mode , { fun_arity , kind , meta , clauses } , expected } ->
215258 context = put_in ( context . local_sigs , Map . put ( local_sigs , fun_arity , kind ) )
216259
217260 { inferred , mapping , context } =
218- local_handler ( fun_arity , kind , meta , clauses , mode , stack , context )
261+ local_handler ( fun_arity , kind , meta , clauses , expected , mode , stack , context )
219262
220263 context =
221264 update_in ( context . local_sigs , & Map . put ( & 1 , fun_arity , { kind , inferred , mapping } ) )
@@ -228,8 +271,8 @@ defmodule Module.Types do
228271 end
229272 end
230273
231- defp local_handler ( { fun , arity } = fun_arity , kind , meta , clauses , mode , stack , context ) do
232- expected = List . duplicate ( Descr . dynamic ( ) , arity )
274+ defp local_handler ( fun_arity , kind , meta , clauses , expected , mode , stack , context ) do
275+ { fun , _arity } = fun_arity
233276 stack = stack |> fresh_stack ( mode , fun_arity ) |> with_file_meta ( meta )
234277
235278 { _ , _ , mapping , clauses_types , clauses_context } =
0 commit comments