@@ -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