@@ -347,6 +347,63 @@ defmodule Module.Types.Of do
347347 # ensuring that domains for the same function have
348348 # no overlaps.
349349
350+ # Remote for callback info functions
351+
352+ kw = fn kw ->
353+ kw
354+ |> Enum . map ( fn { key , type } when is_atom ( key ) ->
355+ tuple ( [ atom ( [ key ] ) , type ] )
356+ end )
357+ |> Enum . reduce ( & union / 2 )
358+ |> list ( )
359+ end
360+
361+ fas = list ( tuple ( [ atom ( ) , integer ( ) ] ) )
362+
363+ shared_info = [
364+ attributes: list ( tuple ( [ atom ( ) , list ( term ( ) ) ] ) ) ,
365+ compile: kw . ( version: list ( integer ( ) ) , source: list ( integer ( ) ) , options: list ( term ( ) ) ) ,
366+ exports: fas ,
367+ md5: binary ( ) ,
368+ module: atom ( )
369+ ]
370+
371+ infos =
372+ % {
373+ behaviour_info: [
374+ callbacks: fas ,
375+ optional_callbacks: fas
376+ ] ,
377+ module_info: [ functions: fas , nifs: fas ] ++ shared_info ,
378+ __info__:
379+ [
380+ deprecated: list ( tuple ( [ tuple ( [ atom ( ) , integer ( ) ] ) , binary ( ) ] ) ) ,
381+ exports_md5: binary ( ) ,
382+ functions: fas ,
383+ macros: fas ,
384+ struct:
385+ list ( closed_map ( default: term ( ) , field: atom ( ) , required: boolean ( ) ) )
386+ |> union ( atom ( [ nil ] ) )
387+ ] ++ shared_info
388+ }
389+
390+ for { name , clauses } <- infos do
391+ domain = atom ( Keyword . keys ( clauses ) )
392+ clauses = Enum . map ( clauses , fn { key , return } -> { [ atom ( [ key ] ) ] , return } end )
393+
394+ defp remote ( unquote ( name ) , 1 ) do
395+ { :strong , [ unquote ( Macro . escape ( domain ) ) ] , unquote ( Macro . escape ( clauses ) ) }
396+ end
397+ end
398+
399+ defp remote ( :module_info , 0 ) do
400+ { :strong , nil , [ { [ ] , unquote ( Macro . escape ( kw . ( infos . module_info ) ) ) } ] }
401+ end
402+
403+ defp remote ( _ , _ ) , do: :none
404+
405+ # Remote for compiler functions
406+
350407 mfargs = [ atom ( ) , atom ( ) , list ( term ( ) ) ]
351408
352409 send_destination =
@@ -367,16 +424,6 @@ defmodule Module.Types.Of do
367424
368425 args_or_arity = union ( list ( term ( ) ) , integer ( ) )
369426 args_or_none = union ( list ( term ( ) ) , atom ( [ :none ] ) )
370-
371- kw = fn kw ->
372- kw
373- |> Enum . map ( fn { key , type } when is_atom ( key ) ->
374- tuple ( [ atom ( [ key ] ) , type ] )
375- end )
376- |> Enum . reduce ( & union / 2 )
377- |> list ( )
378- end
379-
380427 extra_info = kw . ( file: list ( integer ( ) ) , line: integer ( ) , error_info: open_map ( ) )
381428
382429 raise_stacktrace =
@@ -387,6 +434,16 @@ defmodule Module.Types.Of do
387434 |> union ( tuple ( [ fun ( ) , args_or_arity ] ) )
388435 )
389436
437+ and_signature =
438+ for left <- [ true , false ] , right <- [ true , false ] do
439+ { [ atom ( [ left ] ) , atom ( [ right ] ) ] , atom ( [ left and right ] ) }
440+ end
441+
442+ or_signature =
443+ for left <- [ true , false ] , right <- [ true , false ] do
444+ { [ atom ( [ left ] ) , atom ( [ right ] ) ] , atom ( [ left or right ] ) }
445+ end
446+
390447 for { mod , fun , clauses } <- [
391448 # :binary
392449 { :binary , :copy , [ { [ binary ( ) , integer ( ) ] , binary ( ) } ] } ,
@@ -407,6 +464,7 @@ defmodule Module.Types.Of do
407464 { :erlang , :> , [ { [ term ( ) , term ( ) ] , boolean ( ) } ] } ,
408465 { :erlang , :>= , [ { [ term ( ) , term ( ) ] , boolean ( ) } ] } ,
409466 { :erlang , :abs , [ { [ integer ( ) ] , integer ( ) } , { [ float ( ) ] , float ( ) } ] } ,
467+ { :erlang , :and , and_signature } ,
410468 { :erlang , :atom_to_binary , [ { [ atom ( ) ] , binary ( ) } ] } ,
411469 { :erlang , :atom_to_list , [ { [ atom ( ) ] , list ( integer ( ) ) } ] } ,
412470 { :erlang , :band , [ { [ integer ( ) , integer ( ) ] , integer ( ) } ] } ,
@@ -462,6 +520,7 @@ defmodule Module.Types.Of do
462520 { :erlang , :node , [ { [ ] , atom ( ) } ] } ,
463521 { :erlang , :node , [ { [ pid ( ) |> union ( reference ( ) ) |> union ( port ( ) ) ] , atom ( ) } ] } ,
464522 { :erlang , :not , [ { [ atom ( [ false ] ) ] , atom ( [ true ] ) } , { [ atom ( [ true ] ) ] , atom ( [ false ] ) } ] } ,
523+ { :erlang , :or , or_signature } ,
465524 { :erlang , :raise , [ { [ atom ( [ :error , :exit , :throw ] ) , term ( ) , raise_stacktrace ] , none ( ) } ] } ,
466525 { :erlang , :rem , [ { [ integer ( ) , integer ( ) ] , integer ( ) } ] } ,
467526 { :erlang , :round , [ { [ union ( integer ( ) , float ( ) ) ] , integer ( ) } ] } ,
@@ -478,20 +537,25 @@ defmodule Module.Types.Of do
478537 # TODO: Replace term()/dynamic() by parametric types
479538 { :erlang , :++ , [ { [ list ( term ( ) ) , term ( ) ] , dynamic ( list ( term ( ) , term ( ) ) ) } ] } ,
480539 { :erlang , :-- , [ { [ list ( term ( ) ) , list ( term ( ) ) ] , dynamic ( list ( term ( ) ) ) } ] } ,
540+ { :erlang , :andalso , [ { [ boolean ( ) , term ( ) ] , dynamic ( ) } ] } ,
481541 { :erlang , :delete_element , [ { [ integer ( ) , open_tuple ( [ ] ) ] , dynamic ( open_tuple ( [ ] ) ) } ] } ,
482542 { :erlang , :hd , [ { [ non_empty_list ( term ( ) , term ( ) ) ] , dynamic ( ) } ] } ,
483543 { :erlang , :element , [ { [ integer ( ) , open_tuple ( [ ] ) ] , dynamic ( ) } ] } ,
484544 { :erlang , :insert_element ,
485545 [ { [ integer ( ) , open_tuple ( [ ] ) , term ( ) ] , dynamic ( open_tuple ( [ ] ) ) } ] } ,
486546 { :erlang , :max , [ { [ term ( ) , term ( ) ] , dynamic ( ) } ] } ,
487547 { :erlang , :min , [ { [ term ( ) , term ( ) ] , dynamic ( ) } ] } ,
548+ { :erlang , :orelse , [ { [ boolean ( ) , term ( ) ] , dynamic ( ) } ] } ,
488549 { :erlang , :send , [ { [ send_destination , term ( ) ] , dynamic ( ) } ] } ,
489550 { :erlang , :setelement , [ { [ integer ( ) , open_tuple ( [ ] ) , term ( ) ] , dynamic ( open_tuple ( [ ] ) ) } ] } ,
490551 { :erlang , :tl , [ { [ non_empty_list ( term ( ) , term ( ) ) ] , dynamic ( ) } ] } ,
491552 { :erlang , :tuple_to_list , [ { [ open_tuple ( [ ] ) ] , dynamic ( list ( term ( ) ) ) } ] }
492553 ] do
493554 [ arity ] = Enum . map ( clauses , fn { args , _return } -> length ( args ) end ) |> Enum . uniq ( )
494- true = Code . ensure_loaded? ( mod ) and function_exported? ( mod , fun , arity )
555+
556+ true =
557+ Code . ensure_loaded? ( mod ) and
558+ ( function_exported? ( mod , fun , arity ) or fun in [ :orelse , :andalso ] )
495559
496560 domain_clauses =
497561 case clauses do
@@ -535,14 +599,29 @@ defmodule Module.Types.Of do
535599 { :none , context }
536600 else
537601 case remote ( module , fun , arity ) do
538- :none -> { :none , check_export ( module , fun , arity , meta , stack , context ) }
602+ :none -> export ( module , fun , arity , meta , stack , context )
539603 clauses -> { clauses , context }
540604 end
541605 end
542606 end
543607
544- # TODO: Fix ordering of tuple operations
608+ @ doc """
609+ Applies a function in unknown modules.
545610
611+ Used only by info functions.
612+ """
613+ def apply ( name , args_types , expr , stack , context ) do
614+ arity = length ( args_types )
615+
616+ case remote ( name , arity ) do
617+ :none -> { dynamic ( ) , context }
618+ info -> apply_remote ( info , args_types , expr , stack , context )
619+ end
620+ end
621+
622+ @ doc """
623+ Applies a function in a given module.
624+ """
546625 def apply ( :erlang , :element , [ _ , tuple ] , { _ , meta , [ index , _ ] } = expr , stack , context )
547626 when is_integer ( index ) do
548627 case tuple_fetch ( tuple , index - 1 ) do
@@ -677,15 +756,7 @@ defmodule Module.Types.Of do
677756
678757 false ->
679758 { info , context } = remote ( mod , name , arity , elem ( expr , 1 ) , stack , context )
680-
681- case apply_remote ( info , args_types , stack ) do
682- { :ok , type } ->
683- { type , context }
684-
685- { :error , domain , clauses } ->
686- error = { :badapply , expr , args_types , domain , clauses , context }
687- { error_type ( ) , error ( error , elem ( expr , 1 ) , stack , context ) }
688- end
759+ apply_remote ( info , args_types , expr , stack , context )
689760 end
690761 end
691762
@@ -697,6 +768,17 @@ defmodule Module.Types.Of do
697768 end
698769 end
699770
771+ defp apply_remote ( info , args_types , expr , stack , context ) do
772+ case apply_remote ( info , args_types , stack ) do
773+ { :ok , type } ->
774+ { type , context }
775+
776+ { :error , domain , clauses } ->
777+ error = { :badapply , expr , args_types , domain , clauses , context }
778+ { error_type ( ) , error ( error , elem ( expr , 1 ) , stack , context ) }
779+ end
780+ end
781+
700782 defp apply_remote ( :none , _args_types , _stack ) do
701783 { :ok , dynamic ( ) }
702784 end
@@ -740,25 +822,25 @@ defmodule Module.Types.Of do
740822
741823 defp zip_not_disjoint? ( [ ] , [ ] ) , do: true
742824
743- defp check_export ( module , fun , arity , meta , stack , context ) do
825+ defp export ( _module , :module_info , arity , _meta , _stack , context ) when arity in [ 0 , 1 ] do
826+ { remote ( :module_info , arity ) , context }
827+ end
828+
829+ defp export ( module , fun , arity , meta , stack , context ) do
744830 case ParallelChecker . fetch_export ( stack . cache , module , fun , arity ) do
745831 { :ok , mode , reason } ->
746- check_deprecated ( mode , module , fun , arity , reason , meta , stack , context )
747-
748- { :error , :module } ->
749- if warn_undefined? ( module , fun , arity , stack ) do
750- warn ( __MODULE__ , { :undefined_module , module , fun , arity } , meta , stack , context )
751- else
752- context
753- end
832+ { remote ( fun , arity ) ,
833+ check_deprecated ( mode , module , fun , arity , reason , meta , stack , context ) }
834+
835+ { :error , type } ->
836+ context =
837+ if warn_undefined? ( module , fun , arity , stack ) do
838+ warn ( __MODULE__ , { :undefined , type , module , fun , arity } , meta , stack , context )
839+ else
840+ context
841+ end
754842
755- { :error , :function } ->
756- if warn_undefined? ( module , fun , arity , stack ) do
757- payload = { :undefined_function , module , fun , arity }
758- warn ( __MODULE__ , payload , meta , stack , context )
759- else
760- context
761- end
843+ { :none , context }
762844 end
763845 end
764846
@@ -786,22 +868,6 @@ defmodule Module.Types.Of do
786868 end
787869 end
788870
789- # The protocol code dispatches to unknown modules, so we ignore them here.
790- #
791- # try do
792- # SomeProtocol.Atom.__impl__
793- # rescue
794- # ...
795- # end
796- #
797- # But for protocols we don't want to traverse the protocol code anyway.
798- # TODO: remove this clause once we no longer traverse the protocol code.
799- defp warn_undefined? ( _module , :__impl__ , 1 , _stack ) , do: false
800- defp warn_undefined? ( _module , :module_info , 0 , _stack ) , do: false
801- defp warn_undefined? ( _module , :module_info , 1 , _stack ) , do: false
802- defp warn_undefined? ( :erlang , :orelse , 2 , _stack ) , do: false
803- defp warn_undefined? ( :erlang , :andalso , 2 , _stack ) , do: false
804-
805871 defp warn_undefined? ( _ , _ , _ , % { no_warn_undefined: :all } ) do
806872 false
807873 end
@@ -1053,7 +1119,7 @@ defmodule Module.Types.Of do
10531119 }
10541120 end
10551121
1056- def format_diagnostic ( { :undefined_module , module , fun , arity } ) do
1122+ def format_diagnostic ( { :undefined , :module , module , fun , arity } ) do
10571123 top =
10581124 if fun == :__struct__ and arity == 0 do
10591125 "struct #{ inspect ( module ) } "
@@ -1074,15 +1140,15 @@ defmodule Module.Types.Of do
10741140 }
10751141 end
10761142
1077- def format_diagnostic ( { :undefined_function , module , :__struct__ , 0 } ) do
1143+ def format_diagnostic ( { :undefined , :function , module , :__struct__ , 0 } ) do
10781144 % {
10791145 message:
10801146 "struct #{ inspect ( module ) } is undefined (there is such module but it does not define a struct)" ,
10811147 group: true
10821148 }
10831149 end
10841150
1085- def format_diagnostic ( { :undefined_function , module , fun , arity } ) do
1151+ def format_diagnostic ( { :undefined , :function , module , fun , arity } ) do
10861152 % {
10871153 message:
10881154 IO . iodata_to_binary ( [
0 commit comments