@@ -9,17 +9,34 @@ defmodule Module.Types do
99
1010 @ doc false
1111 def infer ( module , file , defs , private , used , env ) do
12- finder = & List . keyfind ( defs , & 1 , 0 )
13- handler = & local_handler ( & 1 , & 2 , & 3 , finder )
12+ finder = & :lists . keyfind ( & 1 , 1 , defs )
13+
14+ handler = fn meta , fun_arity , stack , context ->
15+ case local_handler ( meta , fun_arity , stack , context , finder ) do
16+ false ->
17+ undefined_function! ( :undefined_function , meta , fun_arity , stack , env )
18+ false
19+
20+ { kind , _ , _ } = triplet ->
21+ if ( kind == :defmacro or kind == :defmacrop ) and not Keyword . has_key? ( meta , :super ) do
22+ undefined_function! ( :incorrect_dispatch , meta , fun_arity , stack , env )
23+ false
24+ else
25+ triplet
26+ end
27+ end
28+ end
29+
1430 stack = stack ( :infer , file , module , { :__info__ , 1 } , :all , env , handler )
1531 context = context ( % { } )
1632
1733 { types , % { local_sigs: local_sigs } } =
18- for { fun_arity , kind , _meta , _clauses } = def <- defs ,
34+ for { fun_arity , kind , meta , _clauses } = def <- defs ,
1935 kind == :def or kind == :defmacro ,
2036 reduce: { [ ] , context } do
2137 { types , context } ->
22- { _kind , inferred , context } = local_handler ( fun_arity , stack , context , fn _ -> def end )
38+ { _kind , inferred , context } =
39+ local_handler ( meta , fun_arity , stack , context , fn _ -> def end )
2340
2441 if kind == :def and fun_arity not in @ no_infer do
2542 { [ { fun_arity , inferred } | types ] , context }
@@ -37,6 +54,12 @@ defmodule Module.Types do
3754 { Map . new ( types ) , unreachable }
3855 end
3956
57+ defp undefined_function! ( reason , meta , { fun , arity } , stack , env ) do
58+ env = % { env | function: stack . function , file: stack . file }
59+ tuple = { reason , { fun , arity } , stack . module }
60+ :elixir_errors . module_error ( Helpers . with_span ( meta , fun ) , env , __MODULE__ , tuple )
61+ end
62+
4063 defp warn_unused_def ( { _fun_arity , _kind , false , _ } , _reachable , _used , _env ) do
4164 :ok
4265 end
@@ -80,14 +103,16 @@ defmodule Module.Types do
80103
81104 @ doc false
82105 def warnings ( module , file , defs , no_warn_undefined , cache ) do
83- finder = & List . keyfind ( defs , & 1 , 0 )
84- handler = & local_handler ( & 1 , & 2 , & 3 , finder )
106+ finder = & :lists . keyfind ( & 1 , 1 , defs )
107+ handler = & local_handler ( & 1 , & 2 , & 3 , & 4 , finder )
85108 stack = stack ( :dynamic , file , module , { :__info__ , 1 } , no_warn_undefined , cache , handler )
86109 context = context ( % { } )
87110
88111 context =
89- Enum . reduce ( defs , context , fn { fun_arity , _kind , _meta , _clauses } = def , context ->
90- { _kind , _inferred , context } = local_handler ( fun_arity , stack , context , fn _ -> def end )
112+ Enum . reduce ( defs , context , fn { fun_arity , _kind , meta , _clauses } = def , context ->
113+ { _kind , _inferred , context } =
114+ local_handler ( meta , fun_arity , stack , context , fn _ -> def end )
115+
91116 context
92117 end )
93118
@@ -112,7 +137,7 @@ defmodule Module.Types do
112137 end
113138 end
114139
115- defp local_handler ( fun_arity , stack , context , finder ) do
140+ defp local_handler ( _meta , fun_arity , stack , context , finder ) do
116141 case context . local_sigs do
117142 % { ^ fun_arity => { kind , inferred , _mapping } } ->
118143 { kind , inferred , context }
@@ -121,47 +146,58 @@ defmodule Module.Types do
121146 { kind , :none , context }
122147
123148 local_sigs ->
124- { { fun , arity } , kind , meta , clauses } =
125- finder . ( fun_arity ) || raise "could not find #{ inspect ( fun_arity ) } "
126-
127- expected = List . duplicate ( Descr . dynamic ( ) , arity )
128- stack = stack |> fresh_stack ( fun_arity ) |> with_file_meta ( meta )
129- context = put_in ( context . local_sigs , Map . put ( local_sigs , fun_arity , kind ) )
130-
131- { _ , _ , mapping , clauses_types , clauses_context } =
132- Enum . reduce ( clauses , { 0 , 0 , [ ] , [ ] , context } , fn
133- { meta , args , guards , body } , { index , total , mapping , inferred , context } ->
134- context = fresh_context ( context )
135-
136- try do
137- { args_types , context } =
138- Pattern . of_head ( args , guards , expected , :default , meta , stack , context )
139-
140- { return_type , context } =
141- Expr . of_expr ( body , stack , context )
142-
143- { type_index , inferred } =
144- add_inferred ( inferred , args_types , return_type , total - 1 , [ ] )
145-
146- if type_index == - 1 do
147- { index + 1 , total + 1 , [ { index , total } | mapping ] , inferred , context }
148- else
149- { index + 1 , total , [ { index , type_index } | mapping ] , inferred , context }
150- end
151- rescue
152- e ->
153- internal_error! ( e , __STACKTRACE__ , kind , meta , fun , args , guards , body , stack )
154- end
155- end )
156-
157- inferred = { :infer , Enum . reverse ( clauses_types ) }
158- triplet = { kind , inferred , mapping }
159- context = restore_context ( context , clauses_context )
160- context = update_in ( context . local_sigs , & Map . put ( & 1 , fun_arity , triplet ) )
161- { kind , inferred , context }
149+ case finder . ( fun_arity ) do
150+ { fun_arity , kind , meta , clauses } ->
151+ context = put_in ( context . local_sigs , Map . put ( local_sigs , fun_arity , kind ) )
152+
153+ { inferred , mapping , context } =
154+ local_handler ( fun_arity , kind , meta , clauses , stack , context )
155+
156+ context =
157+ update_in ( context . local_sigs , & Map . put ( & 1 , fun_arity , { kind , inferred , mapping } ) )
158+
159+ { kind , inferred , context }
160+
161+ false ->
162+ false
163+ end
162164 end
163165 end
164166
167+ defp local_handler ( { fun , arity } = fun_arity , kind , meta , clauses , stack , context ) do
168+ expected = List . duplicate ( Descr . dynamic ( ) , arity )
169+ stack = stack |> fresh_stack ( fun_arity ) |> with_file_meta ( meta )
170+
171+ { _ , _ , mapping , clauses_types , clauses_context } =
172+ Enum . reduce ( clauses , { 0 , 0 , [ ] , [ ] , context } , fn
173+ { meta , args , guards , body } , { index , total , mapping , inferred , context } ->
174+ context = fresh_context ( context )
175+
176+ try do
177+ { args_types , context } =
178+ Pattern . of_head ( args , guards , expected , :default , meta , stack , context )
179+
180+ { return_type , context } =
181+ Expr . of_expr ( body , stack , context )
182+
183+ { type_index , inferred } =
184+ add_inferred ( inferred , args_types , return_type , total - 1 , [ ] )
185+
186+ if type_index == - 1 do
187+ { index + 1 , total + 1 , [ { index , total } | mapping ] , inferred , context }
188+ else
189+ { index + 1 , total , [ { index , type_index } | mapping ] , inferred , context }
190+ end
191+ rescue
192+ e ->
193+ internal_error! ( e , __STACKTRACE__ , kind , meta , fun , args , guards , body , stack )
194+ end
195+ end )
196+
197+ inferred = { :infer , Enum . reverse ( clauses_types ) }
198+ { inferred , mapping , restore_context ( context , clauses_context ) }
199+ end
200+
165201 # We check for term equality of types as an optimization
166202 # to reduce the amount of check we do at runtime.
167203 defp add_inferred ( [ { args , existing_return } | tail ] , args , return , index , acc ) ,
@@ -306,4 +342,16 @@ defmodule Module.Types do
306342
307343 def format_error ( { :unused_def , { name , arity } , :defmacrop } ) ,
308344 do: "macro #{ name } /#{ arity } is unused"
345+
346+ def format_error ( { :undefined_function , { f , a } , _ } )
347+ when { f , a } in [ __info__: 1 , behaviour_info: 1 , module_info: 1 , module_info: 0 ] ,
348+ do:
349+ "undefined function #{ f } /#{ a } (this function is auto-generated by the compiler and must always be called as a remote, as in __MODULE__.#{ f } /#{ a } )"
350+
351+ def format_error ( { :undefined_function , { f , a } , module } ) ,
352+ do:
353+ "undefined function #{ f } /#{ a } (expected #{ inspect ( module ) } to define such a function or for it to be imported, but none are available)"
354+
355+ def format_error ( { :incorrect_dispatch , { f , a } , _module } ) ,
356+ do: "cannot invoke macro #{ f } /#{ a } before its definition"
309357end
0 commit comments