@@ -15,6 +15,9 @@ defmodule Module.Types.Apply do
1515 # reduce the computation cost of inferred code.
1616 @ max_clauses 16
1717
18+ @ atom_true atom ( [ true ] )
19+ @ atom_false atom ( [ false ] )
20+
1821 ## Signatures
1922
2023 # Define strong arrows found in the standard library.
@@ -359,25 +362,15 @@ defmodule Module.Types.Apply do
359362
360363 # The functions implemented with custom do_remote functions work on values,
361364 # rather on types, hence the custom behaviour.
362- defp do_remote ( :erlang , name , [ left , right ] = args , _expected , expr , stack , context , of_fun )
365+ defp do_remote ( :erlang , name , [ left , right ] , expected , expr , stack , context , of_fun )
363366 when name in [ :== , :"/=" , :"=:=" , :"=/=" ] do
364- { left_type , context } = of_fun . ( left , term ( ) , expr , stack , context )
365- { right_type , context } = of_fun . ( right , term ( ) , expr , stack , context )
366- result = return ( boolean ( ) , [ left_type , right_type ] , stack )
367-
368- cond do
369- not is_warning ( stack ) or Macro . quoted_literal? ( args ) ->
370- { result , context }
371-
372- name in [ :== , :"/=" ] and number_type? ( left_type ) and number_type? ( right_type ) ->
373- { result , context }
367+ left_literal? = Macro . quoted_literal? ( left )
368+ right_literal? = Macro . quoted_literal? ( right )
374369
375- disjoint? ( left_type , right_type ) ->
376- error = { :mismatched_comparison , left_type , right_type }
377- remote_error ( error , :erlang , name , 2 , expr , stack , context )
378-
379- true ->
380- { result , context }
370+ case { left_literal? , right_literal? } do
371+ { true , false } -> custom_compare ( name , right , left , expected , expr , stack , context , of_fun )
372+ { false , true } -> custom_compare ( name , left , right , expected , expr , stack , context , of_fun )
373+ { literal? , _ } -> compare ( name , left , right , literal? , expr , stack , context , of_fun )
381374 end
382375 end
383376
@@ -479,6 +472,74 @@ defmodule Module.Types.Apply do
479472 remote_domain ( mod , fun , args , expected , elem ( expr , 1 ) , stack , context )
480473 end
481474
475+ @ empty_list empty_list ( )
476+ @ non_empty_list non_empty_list ( term ( ) )
477+ @ empty_map empty_map ( )
478+ @ non_empty_map difference ( open_map ( ) , empty_map ( ) )
479+
480+ defp custom_compare (
481+ name ,
482+ { { :. , _ , [ :erlang , fun ] } , _ , [ arg ] } = left ,
483+ literal ,
484+ expected ,
485+ expr ,
486+ stack ,
487+ context ,
488+ of_fun
489+ )
490+ when fun in [ :length , :map_size , :tuple_size ] and is_integer ( literal ) and literal >= 0 do
491+ case booleaness ( expected ) do
492+ :undefined ->
493+ compare ( name , left , literal , false , expr , stack , context , of_fun )
494+
495+ boolean ->
496+ { polarity , return } =
497+ case boolean do
498+ :always_true -> { name in [ :== , :"=:=" ] , @ atom_true }
499+ :always_false -> { name in [ :"/=" , :"=/=" ] , @ atom_false }
500+ end
501+
502+ expected =
503+ case fun do
504+ :length when :erlang . xor ( polarity , literal > 0 ) -> @ empty_list
505+ :length -> @ non_empty_list
506+ :map_size when :erlang . xor ( polarity , literal > 0 ) -> @ empty_map
507+ :map_size -> @ non_empty_map
508+ :tuple_size when polarity -> tuple ( List . duplicate ( term ( ) , literal ) )
509+ :tuple_size -> difference ( open_tuple ( [ ] ) , tuple ( List . duplicate ( term ( ) , literal ) ) )
510+ end
511+
512+ { actual , context } = of_fun . ( arg , expected , expr , stack , context )
513+ result = if compatible? ( actual , expected ) , do: return , else: boolean ( )
514+ { result , context }
515+ end
516+ end
517+
518+ defp custom_compare ( name , left , right , _expected , expr , stack , context , of_fun ) do
519+ compare ( name , left , right , false , expr , stack , context , of_fun )
520+ end
521+
522+ defp compare ( name , left , right , literal? , expr , stack , context , of_fun ) do
523+ { left_type , context } = of_fun . ( left , term ( ) , expr , stack , context )
524+ { right_type , context } = of_fun . ( right , term ( ) , expr , stack , context )
525+ result = return ( boolean ( ) , [ left_type , right_type ] , stack )
526+
527+ cond do
528+ literal? or not is_warning ( stack ) ->
529+ { result , context }
530+
531+ name in [ :== , :"/=" ] and number_type? ( left_type ) and number_type? ( right_type ) ->
532+ { result , context }
533+
534+ disjoint? ( left_type , right_type ) ->
535+ error = { :mismatched_comparison , left_type , right_type }
536+ remote_error ( error , :erlang , name , 2 , expr , stack , context )
537+
538+ true ->
539+ { result , context }
540+ end
541+ end
542+
482543 @ doc """
483544 Returns the domain of an unknown module.
484545
0 commit comments