@@ -420,10 +420,8 @@ defmodule Module.Types.Pattern do
420420
421421 They behave like guards, so we need to take into account their scope.
422422 """
423- def of_size ( :match , arg , expr , stack , % { pattern_info: pattern_info } = context ) do
424- context = init_guard_info ( context )
425- { type , context } = of_guard ( arg , integer ( ) , expr , stack , context )
426- { type , % { context | pattern_info: pattern_info } }
423+ def of_size ( :match , arg , expr , stack , context ) do
424+ of_guard ( arg , integer ( ) , expr , stack , context )
427425 end
428426
429427 def of_size ( :guard , arg , expr , stack , context ) do
@@ -755,34 +753,35 @@ defmodule Module.Types.Pattern do
755753 context
756754 end
757755
758- defp of_guards ( guards , stack , context ) do
759- # TODO: This match? is temporary until we support multiple guards
760- single? = match? ( [ _ ] , guards )
761- context = init_guard_info ( context , single? )
762- return = if single? , do: @ atom_true , else: term ( )
756+ defp of_guards ( [ guard ] , stack , context ) do
757+ { type , context } = of_guard ( guard , stack , context )
758+ maybe_badguard ( type , guard , stack , context )
759+ end
763760
764- context =
765- Enum . reduce ( guards , context , fn guard , context ->
766- { type , context } = of_guard ( guard , return , guard , stack , context )
761+ defp of_guards ( guards , stack , context ) do
762+ cond_context = % { context | conditional_vars: % { } }
767763
768- if never_true? ( type ) do
769- error = { :badguard , type , guard , context }
770- error ( __MODULE__ , error , error_meta ( guard , stack ) , stack , context )
771- else
772- context
773- end
764+ { vars_conds , context } =
765+ Enum . map_reduce ( guards , context , fn guard , context ->
766+ { type , % { vars: vars , conditional_vars: cond_vars } } = of_guard ( guard , stack , cond_context )
767+ { { vars , cond_vars } , maybe_badguard ( type , guard , stack , context ) }
774768 end )
775769
776- { _ , context } = pop_guard_info ( context )
777- context
770+ when_expr = Enum . reduce ( guards , { : _, [ ] , [ ] } , & { :when , [ ] , [ & 2 , & 1 ] } )
771+ of_cond_vars ( vars_conds , when_expr , stack , context )
778772 end
779773
780- defp init_guard_info ( context , check_domain? \\ true ) do
781- % { context | pattern_info: { check_domain? } }
774+ defp maybe_badguard ( type , guard , stack , context ) do
775+ if never_true? ( type ) do
776+ error = { :badguard , type , guard , context }
777+ error ( __MODULE__ , error , error_meta ( guard , stack ) , stack , context )
778+ else
779+ context
780+ end
782781 end
783782
784- defp pop_guard_info ( % { pattern_info: pattern_info } = context ) do
785- { pattern_info , % { context | pattern_info: nil } }
783+ defp of_guard ( guard , stack , context ) do
784+ of_guard ( guard , @ atom_true , guard , stack , context )
786785 end
787786
788787 # :atom
@@ -871,10 +870,7 @@ defmodule Module.Types.Pattern do
871870
872871 # var
873872 def of_guard ( var , expected , expr , stack , context ) when is_var ( var ) do
874- case context . pattern_info do
875- { true } -> Of . refine_body_var ( var , expected , expr , stack , context )
876- { false } -> { Of . var ( var , context ) , context }
877- end
873+ Of . refine_body_var ( var , expected , expr , stack , context )
878874 end
879875
880876 defp of_remote ( fun , _meta , [ left , right ] , call , expected , stack , context )
@@ -888,29 +884,10 @@ defmodule Module.Types.Pattern do
888884 # For example, if the expected type is true for andalso, then it can
889885 # only be true if both clauses are executed, so we know the first
890886 # argument has to be true and the second has to be expected.
891- { left_domain , right_domain , surely_rhs? } =
892- if subtype? ( expected , both_domain ) do
893- { both_domain , expected , true }
894- else
895- { boolean ( ) , term ( ) , false }
896- end
897-
898- { left_type , context } = of_guard ( left , left_domain , call , stack , context )
899-
900- { right_type , context } =
901- if surely_rhs? do
902- of_guard ( right , right_domain , call , stack , context )
903- else
904- % { pattern_info: pattern_info } = context
905- context = % { context | pattern_info: { false } }
906- { type , context } = of_guard ( right , right_domain , call , stack , context )
907- { type , % { context | pattern_info: pattern_info } }
908- end
909-
910- if compatible? ( left_type , abort_domain ) do
911- { union ( abort_domain , right_type ) , context }
887+ if subtype? ( expected , both_domain ) do
888+ of_logical_both ( left , both_domain , right , expected , abort_domain , call , stack , context )
912889 else
913- { right_type , context }
890+ of_logical_cond ( left , right , expected , abort_domain , call , stack , context )
914891 end
915892 end
916893
@@ -924,6 +901,58 @@ defmodule Module.Types.Pattern do
924901 Apply . remote_apply ( info , :erlang , fun , args_types , call , stack , context )
925902 end
926903
904+ defp of_logical_both ( left , left_domain , right , right_domain , to_abort , call , stack , context ) do
905+ { left_type , context } = of_guard ( left , left_domain , call , stack , context )
906+ { right_type , context } = of_guard ( right , right_domain , call , stack , context )
907+
908+ if disjoint? ( left_type , to_abort ) do
909+ { right_type , context }
910+ else
911+ { union ( to_abort , right_type ) , context }
912+ end
913+ end
914+
915+ defp of_logical_cond ( left , right , expected , to_abort , call , stack , context ) do
916+ cond_context = % { context | conditional_vars: % { } }
917+
918+ # First we do pass to find the surely types, which are stored directly in the context
919+ { _left_type , context } = of_guard ( left , boolean ( ) , call , stack , context )
920+
921+ # Now we find the conditional ones
922+ { left_type , left_context } = of_guard ( left , expected , call , stack , cond_context )
923+ { right_type , right_context } = of_guard ( right , expected , call , stack , cond_context )
924+
925+ % { vars: left_vars , conditional_vars: left_cond } = left_context
926+ % { vars: right_vars , conditional_vars: right_cond } = right_context
927+ vars_conds = [ { left_vars , left_cond } , { right_vars , right_cond } ]
928+ context = of_cond_vars ( vars_conds , call , stack , context )
929+
930+ if disjoint? ( left_type , to_abort ) do
931+ { right_type , context }
932+ else
933+ { union ( to_abort , right_type ) , context }
934+ end
935+ end
936+
937+ defp of_cond_vars ( [ { vars , cond } | vars_conds ] , expr , stack , context ) do
938+ Enum . reduce ( Map . keys ( cond ) , context , fn version , context ->
939+ if Enum . all? ( vars_conds , fn { _vars , cond } -> is_map_key ( cond , version ) end ) do
940+ % { ^ version => % { type: type } } = vars
941+
942+ type =
943+ Enum . reduce ( vars_conds , type , fn { vars , _cond } , acc ->
944+ % { ^ version => % { type: type } } = vars
945+ union ( acc , type )
946+ end )
947+
948+ { _ , context } = Of . refine_body_var ( version , type , expr , stack , context )
949+ context
950+ else
951+ context
952+ end
953+ end )
954+ end
955+
927956 ## Helpers
928957
929958 def format_diagnostic ( { :badguard , type , expr , context } ) do
0 commit comments