@@ -389,7 +389,7 @@ defmodule Module.Types.Apply do
389389 end
390390
391391 defp do_remote ( :erlang , name , [ left , right ] , expected , expr , stack , context , of_fun )
392- when name in [ :>= , :"=<" , :> , :< , :min , :max ] do
392+ when name in [ :>= , :"=<" , :> , :< ] do
393393 case sized_order ( name , left , right , expected ) do
394394 { arg , expected , precise? , return } ->
395395 { actual , context } = of_fun . ( arg , expected , expr , stack , context )
@@ -399,39 +399,41 @@ defmodule Module.Types.Apply do
399399 :none ->
400400 { left_type , context } = of_fun . ( left , term ( ) , expr , stack , context )
401401 { right_type , context } = of_fun . ( right , term ( ) , expr , stack , context )
402+ result = return ( boolean ( ) , [ left_type , right_type ] , stack )
402403
403- result =
404- if name in [ :min , :max ] do
405- union ( left_type , right_type )
406- else
407- return ( boolean ( ) , [ left_type , right_type ] , stack )
408- end
409-
410- if is_warning ( stack ) do
411- common = intersection ( left_type , right_type )
412-
413- cond do
414- # This check is incomplete. After all, we could have the number type nested
415- # inside a tuple or a list and the comparison would still be valid.
416- # However, nested comparison between distinct numbers is very uncommon,
417- # so we only check the direct value here.
418- empty? ( common ) and not ( number_type? ( left_type ) and number_type? ( right_type ) ) ->
419- error = { :mismatched_comparison , left_type , right_type }
420- remote_error ( error , :erlang , name , 2 , expr , stack , context )
421-
422- match? ( { false , _ } , map_fetch_key ( dynamic ( common ) , :__struct__ ) ) ->
423- error = { :struct_comparison , left_type , right_type }
424- remote_error ( error , :erlang , name , 2 , expr , stack , context )
425-
426- true ->
427- { result , context }
428- end
404+ if error = mismatched_ordered_comparison ( left_type , right_type , stack ) do
405+ remote_error ( error , :erlang , name , 2 , expr , stack , context )
429406 else
430407 { result , context }
431408 end
432409 end
433410 end
434411
412+ defp do_remote ( :erlang , name , [ left , right ] , expected , expr , stack , context , of_fun )
413+ when name in [ :min , :max ] do
414+ # While comparison between distinct types are allowed,
415+ # we check for disjointedness, so we effectively require
416+ # left and right to have at least one type in common.
417+ # Overall, it behaves as if we had this signature:
418+ #
419+ # integer(), integer() -> integer()
420+ # float(), float() -> float()
421+ # float(), integer() -> number()
422+ # integer(), float() -> number()
423+ # a and not number(), b and not number() -> a and b
424+ #
425+ # However, during inference, we type it as `a, b -> a and b` only.
426+ { left_type , context } = of_fun . ( left , expected , expr , stack , context )
427+ { right_type , context } = of_fun . ( right , expected , expr , stack , context )
428+ result = union ( left_type , right_type )
429+
430+ if error = mismatched_ordered_comparison ( left_type , right_type , stack ) do
431+ remote_error ( error , :erlang , name , 2 , expr , stack , context )
432+ else
433+ { result , context }
434+ end
435+ end
436+
435437 defp do_remote ( :erlang , :element , [ index , tuple ] , expected , expr , stack , context , of_fun )
436438 when is_integer ( index ) do
437439 tuple_type = open_tuple ( List . duplicate ( term ( ) , max ( index - 1 , 0 ) ) ++ [ expected ] )
@@ -684,28 +686,45 @@ defmodule Module.Types.Apply do
684686 end
685687 end
686688
687- defp sized_order ( name , left , right , expected ) do
688- if name in [ :>= , :"=<" , :> , :< ] do
689- case { left , right } do
690- { { { :. , _ , [ :erlang , fun ] } , _ , [ arg ] } , size } when is_data_size ( fun , size ) ->
691- case booleaness ( expected ) do
692- { true , _ } -> sized_order ( name , fun , size , arg , @ atom_true )
693- { false , _ } -> sized_order ( invert_order ( name ) , fun , size , arg , @ atom_false )
694- _ -> :none
695- end
689+ defp mismatched_ordered_comparison ( left_type , right_type , stack ) do
690+ if is_warning ( stack ) do
691+ common = intersection ( left_type , right_type )
696692
697- { size , { { :. , _ , [ :erlang , fun ] } , _ , [ arg ] } } when is_data_size ( fun , size ) ->
698- case booleaness ( expected ) do
699- { true , _ } -> sized_order ( invert_order ( name ) , fun , size , arg , @ atom_true )
700- { false , _ } -> sized_order ( name , fun , size , arg , @ atom_false )
701- _ -> :none
702- end
693+ cond do
694+ # This check is incomplete. After all, we could have the number type nested
695+ # inside a tuple or a list and the comparison would still be valid.
696+ # However, nested comparison between distinct numbers is very uncommon,
697+ # so we only check the direct value here.
698+ empty? ( common ) and not ( number_type? ( left_type ) and number_type? ( right_type ) ) ->
699+ { :mismatched_comparison , left_type , right_type }
703700
704- _ ->
705- :none
701+ match? ( { false , _ } , map_fetch_key ( dynamic ( common ) , :__struct__ ) ) ->
702+ { :struct_comparison , left_type , right_type }
703+
704+ true ->
705+ nil
706706 end
707- else
708- :none
707+ end
708+ end
709+
710+ defp sized_order ( name , left , right , expected ) do
711+ case { left , right } do
712+ { { { :. , _ , [ :erlang , fun ] } , _ , [ arg ] } , size } when is_data_size ( fun , size ) ->
713+ case booleaness ( expected ) do
714+ { true , _ } -> sized_order ( name , fun , size , arg , @ atom_true )
715+ { false , _ } -> sized_order ( invert_order ( name ) , fun , size , arg , @ atom_false )
716+ _ -> :none
717+ end
718+
719+ { size , { { :. , _ , [ :erlang , fun ] } , _ , [ arg ] } } when is_data_size ( fun , size ) ->
720+ case booleaness ( expected ) do
721+ { true , _ } -> sized_order ( invert_order ( name ) , fun , size , arg , @ atom_true )
722+ { false , _ } -> sized_order ( name , fun , size , arg , @ atom_false )
723+ _ -> :none
724+ end
725+
726+ _ ->
727+ :none
709728 end
710729 end
711730
0 commit comments