11defmodule Module.Types.Expr do
22 @ moduledoc false
33
4- alias Module.Types . { Remote , Pattern }
4+ alias Module.Types . { Of , Pattern }
55 import Module.Types . { Helpers , Infer }
66
77 def of_expr ( expr , % { context: stack_context } = stack , context ) when stack_context != :expr do
@@ -35,7 +35,7 @@ defmodule Module.Types.Expr do
3535
3636 # <<...>>>
3737 def of_expr ( { :<<>> , _meta , args } , stack , context ) do
38- result = of_binary ( args , stack , context , & of_expr / 3 )
38+ result = Of . binary ( args , stack , context , & of_expr / 3 )
3939
4040 case result do
4141 { :ok , context } -> { :ok , :binary , context }
@@ -142,41 +142,47 @@ defmodule Module.Types.Expr do
142142 def of_expr ( { :%{} , _ , [ { :| , _ , [ map , args ] } ] } = expr , stack , context ) do
143143 stack = push_expr_stack ( expr , stack )
144144
145- additional_pairs = [ { :optional , :dynamic , :dynamic } ]
146- map_update ( map , args , additional_pairs , stack , context )
145+ with { :ok , map_type , context } <- of_expr ( map , stack , context ) ,
146+ { :ok , { :map , arg_pairs } , context } <- Of . closed_map ( args , stack , context , & of_expr / 3 ) ,
147+ dynamic_value_pairs =
148+ Enum . map ( arg_pairs , fn { :required , key , _value } -> { :required , key , :dynamic } end ) ,
149+ args_type = { :map , dynamic_value_pairs ++ [ { :optional , :dynamic , :dynamic } ] } ,
150+ { :ok , type , context } <- unify ( args_type , map_type , stack , context ) do
151+ # Retrieve map type and overwrite with the new value types from the map update
152+ { :map , pairs } = resolve_var ( type , context )
153+
154+ updated_pairs =
155+ Enum . reduce ( arg_pairs , pairs , fn { :required , key , value } , pairs ->
156+ List . keyreplace ( pairs , key , 1 , { :required , key , value } )
157+ end )
158+
159+ { :ok , { :map , updated_pairs } , context }
160+ end
147161 end
148162
149163 # %Struct{map | ...}
150- def of_expr ( { :% , meta , [ module , { :%{} , _ , [ { :| , _ , [ map , args ] } ] } ] } = expr , stack , context ) do
151- context = Remote . check ( module , :__struct__ , 0 , meta , context )
164+ def of_expr ( { :% , meta , [ module , { :%{} , _ , [ { :| , _ , [ _ , _ ] } ] } = update ] } = expr , stack , context ) do
152165 stack = push_expr_stack ( expr , stack )
153166
154- additional_pairs = [ { :required , { :atom , :__struct__ } , { :atom , module } } ]
155- map_update ( map , args , additional_pairs , stack , context )
167+ with { :ok , struct , context } <- Of . struct ( module , meta , context ) ,
168+ { :ok , update , context } <- of_expr ( update , stack , context ) do
169+ unify ( update , struct , stack , context )
170+ end
156171 end
157172
158173 # %{...}
159174 def of_expr ( { :%{} , _meta , args } = expr , stack , context ) do
160175 stack = push_expr_stack ( expr , stack )
161-
162- case of_pairs ( args , stack , context ) do
163- { :ok , pairs , context } -> { :ok , { :map , pairs_to_unions ( pairs , context ) } , context }
164- { :error , reason } -> { :error , reason }
165- end
176+ Of . closed_map ( args , stack , context , & of_expr / 3 )
166177 end
167178
168179 # %Struct{...}
169180 def of_expr ( { :% , meta1 , [ module , { :%{} , _meta2 , args } ] } = expr , stack , context ) do
170- context = Remote . check ( module , :__struct__ , 0 , meta1 , context )
171181 stack = push_expr_stack ( expr , stack )
172182
173- case of_pairs ( args , stack , context ) do
174- { :ok , pairs , context } ->
175- pairs = [ { :required , { :atom , :__struct__ } , { :atom , module } } | pairs ]
176- { :ok , { :map , pairs } , context }
177-
178- { :error , reason } ->
179- { :error , reason }
183+ with { :ok , struct , context } <- Of . struct ( module , meta1 , context ) ,
184+ { :ok , map , context } <- Of . open_map ( args , stack , context , & of_expr / 3 ) do
185+ unify ( map , struct , stack , context )
180186 end
181187 end
182188
@@ -327,7 +333,7 @@ defmodule Module.Types.Expr do
327333
328334 # expr.fun(arg)
329335 def of_expr ( { { :. , meta1 , [ expr1 , fun ] } , _meta2 , args } = expr2 , stack , context ) do
330- context = Remote . check ( expr1 , fun , length ( args ) , meta1 , context )
336+ context = Of . remote ( expr1 , fun , length ( args ) , meta1 , context )
331337 stack = push_expr_stack ( expr2 , stack )
332338
333339 with { :ok , _expr_type , context } <- of_expr ( expr1 , stack , context ) ,
@@ -342,7 +348,7 @@ defmodule Module.Types.Expr do
342348 # &Foo.bar/1
343349 def of_expr ( { :& , meta , [ { :/ , _ , [ { { :. , _ , [ module , fun ] } , _ , [ ] } , arity ] } ] } , _stack , context )
344350 when is_atom ( module ) and is_atom ( fun ) do
345- context = Remote . check ( module , fun , arity , meta , context )
351+ context = Of . remote ( module , fun , arity , meta , context )
346352 { :ok , :dynamic , context }
347353 end
348354
@@ -363,51 +369,6 @@ defmodule Module.Types.Expr do
363369 end
364370 end
365371
366- defp of_pairs ( pairs , stack , context ) do
367- map_reduce_ok ( pairs , context , fn { key , value } , context ->
368- with { :ok , key_type , context } <- of_expr ( key , stack , context ) ,
369- { :ok , value_type , context } <- of_expr ( value , stack , context ) ,
370- do: { :ok , { :required , key_type , value_type } , context }
371- end )
372- end
373-
374- defp pairs_to_unions ( pairs , context ) do
375- # We are currently creating overlapping key types
376-
377- Enum . reduce ( pairs , [ ] , fn { kind_left , key , value_left } , pairs ->
378- case List . keyfind ( pairs , key , 1 ) do
379- { kind_right , ^ key , value_right } ->
380- kind = unify_kinds ( kind_left , kind_right )
381- value = to_union ( [ value_left , value_right ] , context )
382- List . keystore ( pairs , key , 1 , { kind , key , value } )
383-
384- nil ->
385- [ { kind_left , key , value_left } | pairs ]
386- end
387- end )
388- end
389-
390- defp map_update ( map , args , additional_pairs , stack , context ) do
391- with { :ok , map_type , context } <- of_expr ( map , stack , context ) ,
392- { :ok , arg_pairs , context } <- of_pairs ( args , stack , context ) ,
393- arg_pairs = pairs_to_unions ( arg_pairs , context ) ,
394- # Change value types to dynamic to reuse map unification for map updates
395- dynamic_value_pairs =
396- Enum . map ( arg_pairs , fn { :required , key , _value } -> { :required , key , :dynamic } end ) ,
397- args_type = { :map , additional_pairs ++ dynamic_value_pairs } ,
398- { :ok , type , context } <- unify ( args_type , map_type , stack , context ) do
399- # Retrieve map type and overwrite with the new value types from the map update
400- { :map , pairs } = resolve_var ( type , context )
401-
402- updated_pairs =
403- Enum . reduce ( arg_pairs , pairs , fn { :required , key , value } , pairs ->
404- List . keyreplace ( pairs , key , 1 , { :required , key , value } )
405- end )
406-
407- { :ok , { :map , updated_pairs } , context }
408- end
409- end
410-
411372 defp for_clause ( { :<- , _ , [ left , expr ] } , stack , context ) do
412373 { pattern , guards } = extract_head ( [ left ] )
413374
0 commit comments