@@ -42,7 +42,7 @@ defmodule Module.Types.Pattern do
4242  end 
4343
4444  defp  of_pattern_args ( patterns ,  expected_types ,  stack ,  context )  do 
45-     context  =  % { context  |  pattern_info:  { % { } ,  % { } } } 
45+     context  =  % { context  |  pattern_info:  { % { } ,  % { } ,   0 } } 
4646    { trees ,  context }  =  of_pattern_args_index ( patterns ,  expected_types ,  0 ,  [ ] ,  stack ,  context ) 
4747
4848    { types ,  context }  = 
@@ -104,7 +104,7 @@ defmodule Module.Types.Pattern do
104104  the given expected and expr or an error in case of a typing conflict. 
105105  """ 
106106  def  of_match ( pattern ,  expected ,  expr ,  stack ,  context )  do 
107-     context  =  % { context  |  pattern_info:  { % { } ,  % { } } } 
107+     context  =  % { context  |  pattern_info:  { % { } ,  % { } ,   0 } } 
108108    { tree ,  context }  =  of_pattern ( pattern ,  [ { :arg ,  0 ,  expected ,  expr } ] ,  stack ,  context ) 
109109
110110    { [ type ] ,  context }  = 
@@ -118,15 +118,15 @@ defmodule Module.Types.Pattern do
118118  end 
119119
120120  defp  of_pattern_recur ( types ,  stack ,  context ,  callback )  do 
121-     % { pattern_info:  { pattern_vars ,  pattern_args } }  =  context 
121+     % { pattern_info:  { pattern_vars ,  pattern_info ,   _counter } }  =  context 
122122    context  =  % { context  |  pattern_info:  nil } 
123123    pattern_vars  =  Map . to_list ( pattern_vars ) 
124124    changed  =  :lists . seq ( 0 ,  length ( types )  -  1 ) 
125125
126126    try  do 
127127      case  callback . ( types ,  changed ,  context )  do 
128128        { :ok ,  types ,  context }  -> 
129-           of_pattern_recur ( types ,  pattern_vars ,  pattern_args ,  stack ,  context ,  callback ) 
129+           of_pattern_recur ( types ,  pattern_vars ,  pattern_info ,  stack ,  context ,  callback ) 
130130
131131        { :error ,  context }  -> 
132132          { types ,  error_vars ( pattern_vars ,  context ) } 
@@ -136,7 +136,7 @@ defmodule Module.Types.Pattern do
136136    end 
137137  end 
138138
139-   defp  of_pattern_recur ( types ,  vars ,  args ,  stack ,  context ,  callback )  do 
139+   defp  of_pattern_recur ( types ,  vars ,  info ,  stack ,  context ,  callback )  do 
140140    % { vars:  context_vars }  =  context 
141141
142142    { changed ,  context }  = 
@@ -148,7 +148,7 @@ defmodule Module.Types.Pattern do
148148            [ var ,  { :arg ,  index ,  expected ,  expr }  |  path ] ,  { var_changed? ,  context }  -> 
149149              actual  =  Enum . fetch! ( types ,  index ) 
150150
151-               case  of_pattern_var ( path ,  actual )  do 
151+               case  of_pattern_var ( path ,  actual ,   info ,   context )  do 
152152                { :ok ,  type }  -> 
153153                  case  Of . refine_var ( var ,  type ,  expr ,  stack ,  context )  do 
154154                    { :ok ,  type ,  context }  -> 
@@ -174,9 +174,11 @@ defmodule Module.Types.Pattern do
174174            case  paths  do 
175175              # A single change, check if there are other variables in this index. 
176176              [ [ _var ,  { :arg ,  index ,  _ ,  _ }  |  _ ] ]  -> 
177-                 case  args  do 
178-                   % { ^ index  =>  true }  ->  { [ index  |  changed ] ,  context } 
179-                   % { ^ index  =>  false }  ->  { changed ,  context } 
177+                 arg_cons  =  [ :arg  |  index ] 
178+ 
179+                 case  info  do 
180+                   % { ^ arg_cons  =>  true }  ->  { [ index  |  changed ] ,  context } 
181+                   % { ^ arg_cons  =>  false }  ->  { changed ,  context } 
180182                end 
181183
182184              # Several changes, we have to recompute all indexes. 
@@ -195,7 +197,7 @@ defmodule Module.Types.Pattern do
195197        case  callback . ( types ,  changed ,  context )  do 
196198          # A simple structural comparison for optimization 
197199          { :ok ,  ^ types ,  context }  ->  { types ,  context } 
198-           { :ok ,  types ,  context }  ->  of_pattern_recur ( types ,  vars ,  args ,  stack ,  context ,  callback ) 
200+           { :ok ,  types ,  context }  ->  of_pattern_recur ( types ,  vars ,  info ,  stack ,  context ,  callback ) 
199201          { :error ,  context }  ->  { types ,  error_vars ( vars ,  context ) } 
200202        end 
201203    end 
@@ -226,40 +228,44 @@ defmodule Module.Types.Pattern do
226228    end 
227229  end 
228230
229-   defp  of_pattern_var ( [ ] ,  type )  do 
231+   defp  of_pattern_var ( [ ] ,  type ,   _info ,   _context )  do 
230232    { :ok ,  type } 
231233  end 
232234
233-   defp  of_pattern_var ( [ { :elem ,  index }  |  rest ] ,  type )  when  is_integer ( index )  do 
235+   defp  of_pattern_var ( [ { :elem ,  index }  |  rest ] ,  type ,   info ,   context )  when  is_integer ( index )  do 
234236    case  tuple_fetch ( type ,  index )  do 
235-       { _optional? ,  type }  ->  of_pattern_var ( rest ,  type ) 
237+       { _optional? ,  type }  ->  of_pattern_var ( rest ,  type ,   info ,   context ) 
236238      _reason  ->  :error 
237239    end 
238240  end 
239241
240-   defp  of_pattern_var ( [ { :key ,  field }  |  rest ] ,  type )  when  is_atom ( field )  do 
242+   defp  of_pattern_var ( [ { :key ,  field }  |  rest ] ,  type ,   info ,   context )  when  is_atom ( field )  do 
241243    case  map_fetch ( type ,  field )  do 
242-       { _optional? ,  type }  ->  of_pattern_var ( rest ,  type ) 
244+       { _optional? ,  type }  ->  of_pattern_var ( rest ,  type ,   info ,   context ) 
243245      _reason  ->  :error 
244246    end 
245247  end 
246248
247249  # TODO: Implement domain key types 
248-   defp  of_pattern_var ( [ { :key ,  _key }  |  rest ] ,  _type )  do 
249-     of_pattern_var ( rest ,  dynamic ( ) ) 
250+   defp  of_pattern_var ( [ { :key ,  _key }  |  rest ] ,  _type ,   info ,   context )  do 
251+     of_pattern_var ( rest ,  dynamic ( ) ,   info ,   context ) 
250252  end 
251253
252-   defp  of_pattern_var ( [ :head   |  rest ] ,  type )  do 
254+   defp  of_pattern_var ( [ { :head ,   counter }   |  rest ] ,  type ,   info ,   context )  do 
253255    case  list_hd ( type )  do 
254-       { _ ,  head }  ->  of_pattern_var ( rest ,  head ) 
255-       _  ->  :error 
256+       { _ ,  head }  -> 
257+         tree  =  Map . fetch! ( info ,  [ :head  |  counter ] ) 
258+         type  =  intersection ( of_pattern_tree ( tree ,  context ) ,  head ) 
259+         of_pattern_var ( rest ,  type ,  info ,  context ) 
260+ 
261+       _  -> 
262+         :error 
256263    end 
257264  end 
258265
259-   # TODO: This should intersect with the list itself and its tail. 
260-   defp  of_pattern_var ( [ :tail  |  rest ] ,  type )  do 
266+   defp  of_pattern_var ( [ :tail  |  rest ] ,  type ,  info ,  context )  do 
261267    case  list_tl ( type )  do 
262-       { _ ,  tail }  ->  of_pattern_var ( rest ,  tail ) 
268+       { _ ,  tail }  ->  of_pattern_var ( rest ,  tail ,   info ,   context ) 
263269      _  ->  :error 
264270    end 
265271  end 
@@ -474,20 +480,21 @@ defmodule Module.Types.Pattern do
474480       when  is_atom ( name )  and  is_atom ( ctx )  do 
475481    version  =  Keyword . fetch! ( meta ,  :version ) 
476482    [ { :arg ,  arg ,  _type ,  _pattern }  |  _ ]  =  path  =  Enum . reverse ( reverse_path ) 
477-     { vars ,  args }  =  context . pattern_info 
483+     { vars ,  info ,   counter }  =  context . pattern_info 
478484
479485    paths  =  [ [ var  |  path ]  |  Map . get ( vars ,  version ,  [ ] ) ] 
480486    vars  =  Map . put ( vars ,  version ,  paths ) 
487+     arg_cons  =  [ :arg  |  arg ] 
481488
482489    # Our goal here is to compute if an argument has more than one variable. 
483-     args  = 
484-       case  args  do 
485-         % { ^ arg  =>  false }  ->  % { args  |  arg  =>  true } 
486-         % { ^ arg  =>  true }  ->  args 
487-         % { }  ->  Map . put ( args ,   arg ,  false ) 
490+     info  = 
491+       case  info  do 
492+         % { ^ arg_cons  =>  false }  ->  % { info  |  arg_cons  =>  true } 
493+         % { ^ arg_cons  =>  true }  ->  info 
494+         % { }  ->  Map . put ( info ,   arg_cons ,  false ) 
488495      end 
489496
490-     { { :var ,  version } ,  % { context  |  pattern_info:  { vars ,  args } } } 
497+     { { :var ,  version } ,  % { context  |  pattern_info:  { vars ,  info ,   counter } } } 
491498  end 
492499
493500  # TODO: Properly traverse domain keys 
@@ -542,26 +549,45 @@ defmodule Module.Types.Pattern do
542549  # [prefix1, prefix2, prefix3], [prefix1, prefix2 | suffix] 
543550  defp  of_list ( prefix ,  suffix ,  path ,  stack ,  context )  do 
544551    { suffix ,  context }  =  of_pattern ( suffix ,  [ :tail  |  path ] ,  stack ,  context ) 
545- 
546-     acc  = 
547-       Enum . reduce ( prefix ,  { [ ] ,  [ ] ,  context } ,  fn  arg ,  { static ,  dynamic ,  context }  -> 
548-         { type ,  context }  =  of_pattern ( arg ,  [ :head  |  path ] ,  stack ,  context ) 
549- 
550-         if  is_descr ( type )  do 
551-           { [ type  |  static ] ,  dynamic ,  context } 
552-         else 
553-           { static ,  [ type  |  dynamic ] ,  context } 
554-         end 
552+     { vars ,  info ,  counter }  =  context . pattern_info 
553+     context  =  % { context  |  pattern_info:  { vars ,  info ,  counter  +  length ( prefix ) } } 
554+ 
555+     { static ,  dynamic ,  info ,  context }  = 
556+       Enum . reduce ( prefix ,  { [ ] ,  [ ] ,  % { } ,  context } ,  fn 
557+         arg ,  { static ,  dynamic ,  info ,  context } 
558+         when  is_number ( arg )  or  is_atom ( arg )  or  is_binary ( arg )  or  arg  ==  [ ]  -> 
559+           { type ,  context }  =  of_pattern ( arg ,  [ ] ,  stack ,  context ) 
560+           { [ type  |  static ] ,  dynamic ,  info ,  context } 
561+ 
562+         arg ,  { static ,  dynamic ,  info ,  context }  -> 
563+           counter  =  map_size ( info )  +  counter 
564+           { type ,  context }  =  of_pattern ( arg ,  [ { :head ,  counter }  |  path ] ,  stack ,  context ) 
565+           info  =  Map . put ( info ,  [ :head  |  counter ] ,  type ) 
566+ 
567+           if  is_descr ( type )  do 
568+             { [ type  |  static ] ,  dynamic ,  info ,  context } 
569+           else 
570+             { static ,  [ type  |  dynamic ] ,  info ,  context } 
571+           end 
555572      end ) 
556573
557-     case  acc  do 
558-       { static ,  [ ] ,  context }  when  is_descr ( suffix )  -> 
574+     context  = 
575+       if  info  !=  % { }  do 
576+         update_in ( context . pattern_info ,  fn  { acc_vars ,  acc_info ,  acc_counter }  -> 
577+           { acc_vars ,  Map . merge ( acc_info ,  info ) ,  acc_counter } 
578+         end ) 
579+       else 
580+         context 
581+       end 
582+ 
583+     case  { static ,  dynamic }  do 
584+       { static ,  [ ] }  when  is_descr ( suffix )  -> 
559585        { non_empty_list ( Enum . reduce ( static ,  & union / 2 ) ,  suffix ) ,  context } 
560586
561-       { [ ] ,  dynamic ,   context }  -> 
587+       { [ ] ,  dynamic }  -> 
562588        { { :non_empty_list ,  dynamic ,  suffix } ,  context } 
563589
564-       { static ,  dynamic ,   context }  -> 
590+       { static ,  dynamic }  -> 
565591        { { :non_empty_list ,  [ Enum . reduce ( static ,  & union / 2 )  |  dynamic ] ,  suffix } ,  context } 
566592    end 
567593  end 
0 commit comments