@@ -336,9 +336,11 @@ defmodule Module.Types.Pattern do
336336 end
337337 end
338338
339- # TODO: Implement domain key types
340- defp of_pattern_var ( [ { :key , _key } | rest ] , _type , context ) do
341- of_pattern_var ( rest , dynamic ( ) , context )
339+ defp of_pattern_var ( [ { :domain , domain } | rest ] , type , context ) do
340+ case map_get ( type , domain ) do
341+ { :ok , type } -> of_pattern_var ( rest , type , context )
342+ _ -> :error
343+ end
342344 end
343345
344346 defp of_pattern_var ( [ :head | rest ] , type , context ) do
@@ -489,18 +491,27 @@ defmodule Module.Types.Pattern do
489491 end
490492 end )
491493
492- if dynamic == [ ] do
493- { Enum . reduce ( static , & intersection / 2 ) , context }
494- else
495- # The dynamic parts have to be recomputed whenever they change
496- context = of_var ( var , version , [ % { root: { :intersection , dynamic } , expr: match } ] , context )
497-
498- # And everything else is also pushed as part of the argument intersection
499- if static == [ ] do
500- { { :intersection , dynamic } , context }
501- else
502- { { :intersection , [ Enum . reduce ( static , & intersection / 2 ) | dynamic ] } , context }
494+ return =
495+ cond do
496+ dynamic == [ ] -> Enum . reduce ( static , & intersection / 2 )
497+ static == [ ] -> { :intersection , dynamic }
498+ true -> { :intersection , [ Enum . reduce ( static , & intersection / 2 ) | dynamic ] }
503499 end
500+
501+ # If the path has domain keys or head, then it is imprecise,
502+ # and we need to keep filtering the match variable itself.
503+ imprecise? =
504+ Enum . any? ( path , fn
505+ { :domain , _ } -> true
506+ :head -> true
507+ _ -> false
508+ end )
509+
510+ if dynamic == [ ] and not imprecise? do
511+ { return , context }
512+ else
513+ context = of_var ( var , version , [ % { root: return , expr: match } ] , context )
514+ { return , context }
504515 end
505516 end
506517
@@ -643,20 +654,27 @@ defmodule Module.Types.Pattern do
643654 # TODO: Properly handle pin operator in keys
644655 defp of_open_map ( args , static , dynamic , path , stack , context ) do
645656 { static , dynamic , context } =
646- Enum . reduce ( args , { static , dynamic , context } , fn { key , value } , { static , dynamic , context } ->
647- { value_type , context } = of_pattern ( value , [ { :key , key } | path ] , stack , context )
648-
649- cond do
650- # Only atom keys become part of the type because the other keys are divisible
651- not is_atom ( key ) ->
652- { static , dynamic , context }
657+ Enum . reduce ( args , { static , dynamic , context } , fn
658+ { key , value } , { static , dynamic , context } when is_atom ( key ) ->
659+ { value_type , context } = of_pattern ( value , [ { :key , key } | path ] , stack , context )
653660
654- is_descr ( value_type ) ->
661+ if is_descr ( value_type ) do
655662 { [ { key , value_type } | static ] , dynamic , context }
656-
657- true ->
663+ else
658664 { static , [ { key , value_type } | dynamic ] , context }
659- end
665+ end
666+
667+ { key , value } , { static , dynamic , context } ->
668+ # Keys are always static and won't use the path
669+ { key_type , context } = of_pattern ( key , path , stack , context )
670+ true = is_descr ( key_type )
671+
672+ { _value_type , context } = of_pattern ( value , [ { :domain , key_type } | path ] , stack , context )
673+
674+ # A domain key cannot restrict the map in any way,
675+ # because we are matching only on one possible value
676+ # and we cannot assert anything about any of the others.
677+ { static , dynamic , context }
660678 end )
661679
662680 case dynamic do
0 commit comments