@@ -3,33 +3,117 @@ defmodule Module.Types do
33
44 alias Module.Types . { Descr , Expr , Pattern }
55
6+ # TODO: Inference of tail recursion
7+ # TODO: Checking of unused private functions/clauses
8+
69 # These functions are not inferred because they are added/managed by the compiler
710 @ no_infer [ __protocol__: 1 , behaviour_info: 1 ]
811
912 @ doc false
1013 def infer ( module , file , defs , env ) do
11- context = context ( )
12-
13- for { { fun , arity } , :def , _meta , clauses } <- defs ,
14- { fun , arity } not in @ no_infer ,
15- into: % { } do
16- stack = stack ( :infer , file , module , { fun , arity } , :all , env )
17- expected = List . duplicate ( Descr . dynamic ( ) , arity )
18-
19- pair_types =
20- Enum . reduce ( clauses , [ ] , fn { meta , args , guards , body } , inferred ->
21- try do
22- { args , context } =
23- Pattern . of_head ( args , guards , expected , :default , meta , stack , context )
24-
25- { return , _context } = Expr . of_expr ( body , stack , context )
26- add_inferred ( inferred , args , return , [ ] )
27- rescue
28- e -> internal_error! ( e , __STACKTRACE__ , :def , meta , module , fun , args , guards , body )
29- end
30- end )
31-
32- { { fun , arity } , { :infer , Enum . reverse ( pair_types ) } }
14+ finder = & List . keyfind ( defs , & 1 , 0 )
15+ handler = & infer_signature_for ( & 1 , & 2 , module , file , finder , env )
16+ context = context ( { handler , % { } } )
17+
18+ { types , _context } =
19+ for { fun_arity , kind , _meta , _clauses } = def <- defs ,
20+ kind == :def and fun_arity not in @ no_infer ,
21+ reduce: { [ ] , context } do
22+ { types , context } ->
23+ { _kind , inferred , context } =
24+ infer_signature_for ( fun_arity , context , module , file , fn _ -> def end , env )
25+
26+ { [ { fun_arity , inferred } | types ] , context }
27+ end
28+
29+ Map . new ( types )
30+ end
31+
32+ defp infer_signature_for ( fun_arity , context , module , file , finder , env ) do
33+ case context . local_handler do
34+ { _ , % { ^ fun_arity => { kind , inferred } } } ->
35+ { kind , inferred , context }
36+
37+ { _ , _ } ->
38+ { { fun , arity } , kind , _meta , clauses } = finder . ( fun_arity )
39+ expected = List . duplicate ( Descr . dynamic ( ) , arity )
40+
41+ stack = stack ( :infer , file , module , fun_arity , :all , env )
42+ context = update_local_state ( context , & Map . put ( & 1 , fun_arity , { kind , :none } ) )
43+
44+ { pair_types , context } =
45+ Enum . reduce ( clauses , { [ ] , context } , fn
46+ { meta , args , guards , body } , { inferred , context } ->
47+ context = context ( context . local_handler )
48+
49+ try do
50+ { args_types , context } =
51+ Pattern . of_head ( args , guards , expected , :default , meta , stack , context )
52+
53+ { return_type , context } = Expr . of_expr ( body , stack , context )
54+ { add_inferred ( inferred , args_types , return_type , [ ] ) , context }
55+ rescue
56+ e ->
57+ internal_error! ( e , __STACKTRACE__ , kind , meta , module , fun , args , guards , body )
58+ end
59+ end )
60+
61+ inferred = { :infer , Enum . reverse ( pair_types ) }
62+ { kind , inferred , update_local_state ( context , & Map . put ( & 1 , fun_arity , { kind , inferred } ) ) }
63+ end
64+ end
65+
66+ @ doc false
67+ def warnings ( module , file , defs , no_warn_undefined , cache ) do
68+ finder = & List . keyfind ( defs , & 1 , 0 )
69+ handler = & warnings_for ( & 1 , & 2 , module , file , finder , no_warn_undefined , cache )
70+ context = context ( { handler , % { } } )
71+
72+ context =
73+ Enum . reduce ( defs , context , fn { fun_arity , _kind , _meta , _clauses } = def , context ->
74+ finder = fn _ -> def end
75+
76+ { _kind , _inferred , context } =
77+ warnings_for ( fun_arity , context , module , file , finder , no_warn_undefined , cache )
78+
79+ context
80+ end )
81+
82+ context . warnings
83+ end
84+
85+ defp warnings_for ( fun_arity , context , module , file , finder , no_warn_undefined , cache ) do
86+ case context . local_handler do
87+ { _ , % { ^ fun_arity => { kind , inferred } } } ->
88+ { kind , inferred , context }
89+
90+ { _ , _ } ->
91+ { { fun , arity } , kind , meta , clauses } = finder . ( fun_arity )
92+ expected = List . duplicate ( Descr . dynamic ( ) , arity )
93+
94+ file = with_file_meta ( meta , file )
95+ stack = stack ( :dynamic , file , module , fun_arity , no_warn_undefined , cache )
96+ context = update_local_state ( context , & Map . put ( & 1 , fun_arity , { kind , :none } ) )
97+
98+ { pair_types , context } =
99+ Enum . reduce ( clauses , { [ ] , context } , fn
100+ { meta , args , guards , body } , { inferred , context } ->
101+ context = fresh_context ( context )
102+
103+ try do
104+ { args_types , context } =
105+ Pattern . of_head ( args , guards , expected , :default , meta , stack , context )
106+
107+ { return_type , context } = Expr . of_expr ( body , stack , context )
108+ { add_inferred ( inferred , args_types , return_type , [ ] ) , context }
109+ rescue
110+ e ->
111+ internal_error! ( e , __STACKTRACE__ , kind , meta , module , fun , args , guards , body )
112+ end
113+ end )
114+
115+ inferred = { :infer , Enum . reverse ( pair_types ) }
116+ { kind , inferred , update_local_state ( context , & Map . put ( & 1 , fun_arity , { kind , inferred } ) ) }
33117 end
34118 end
35119
@@ -44,30 +128,6 @@ defmodule Module.Types do
44128 defp add_inferred ( [ ] , args , return , acc ) ,
45129 do: [ { args , return } | Enum . reverse ( acc ) ]
46130
47- @ doc false
48- def warnings ( module , file , defs , no_warn_undefined , cache ) do
49- context = context ( )
50-
51- Enum . flat_map ( defs , fn { { fun , arity } , kind , meta , clauses } ->
52- file = with_file_meta ( meta , file )
53- stack = stack ( :dynamic , file , module , { fun , arity } , no_warn_undefined , cache )
54- expected = List . duplicate ( Descr . dynamic ( ) , arity )
55-
56- Enum . flat_map ( clauses , fn { meta , args , guards , body } ->
57- try do
58- { _types , context } =
59- Pattern . of_head ( args , guards , expected , :default , meta , stack , context )
60-
61- { _type , context } = Expr . of_expr ( body , stack , context )
62- context . warnings
63- rescue
64- e ->
65- internal_error! ( e , __STACKTRACE__ , kind , meta , module , fun , args , guards , body )
66- end
67- end )
68- end )
69- end
70-
71131 defp with_file_meta ( meta , file ) do
72132 case Keyword . fetch ( meta , :file ) do
73133 { :ok , { meta_file , _ } } -> meta_file
@@ -145,16 +205,26 @@ defmodule Module.Types do
145205 end
146206
147207 @ doc false
148- def context ( ) do
208+ def context ( local_handler , warnings \\ [ ] ) do
149209 % {
150210 # A list of all warnings found so far
151- warnings: [ ] ,
211+ warnings: warnings ,
152212 # All vars and their types
153213 vars: % { } ,
154214 # Variables and arguments from patterns
155215 pattern_info: nil ,
156216 # If type checking has found an error/failure
157- failed: false
217+ failed: false ,
218+ # Local handler
219+ local_handler: local_handler
158220 }
159221 end
222+
223+ defp fresh_context ( % { local_handler: local_handler , warnings: warnings } ) do
224+ context ( local_handler , warnings )
225+ end
226+
227+ defp update_local_state ( % { local_handler: { handler , state } } = context , fun ) do
228+ % { context | local_handler: { handler , fun . ( state ) } }
229+ end
160230end
0 commit comments