@@ -174,7 +174,7 @@ defmodule Module.Types.Pattern do
174174 context
175175
176176 { :error , old_type , context } ->
177- throw ( { types , refine_error ( var , old_type , type , stack , context ) } )
177+ throw ( { types , badvar_error ( var , old_type , type , expr , stack , context ) } )
178178 end
179179
180180 :error ->
@@ -187,22 +187,21 @@ defmodule Module.Types.Pattern do
187187
188188 context =
189189 Enum . reduce ( changed , context , fn version , context ->
190- { _ , context } = of_pattern_var_dep ( vars_paths , version , tag , stack , context )
190+ { _ , context } = of_pattern_var_dep ( vars_paths , version , stack , context )
191191 context
192192 end )
193193
194- { types , of_pattern_var_deps ( changed , vars_paths , vars_deps , tag , stack , context ) }
194+ { types , of_pattern_var_deps ( changed , vars_paths , vars_deps , stack , context ) }
195195 catch
196- { :error , context } -> { types , error_vars ( pattern_info , context ) }
197196 { types , context } -> { types , error_vars ( pattern_info , context ) }
198197 end
199198 end
200199
201- defp of_pattern_var_deps ( [ ] , _vars_paths , _vars_deps , _tag , _stack , context ) do
200+ defp of_pattern_var_deps ( [ ] , _vars_paths , _vars_deps , _stack , context ) do
202201 context
203202 end
204203
205- defp of_pattern_var_deps ( previous_changed , vars_paths , vars_deps , tag , stack , context ) do
204+ defp of_pattern_var_deps ( previous_changed , vars_paths , vars_deps , stack , context ) do
206205 { changed , context } =
207206 previous_changed
208207 |> Enum . reduce ( % { } , fn version , acc ->
@@ -213,46 +212,53 @@ defmodule Module.Types.Pattern do
213212 end )
214213 |> Map . keys ( )
215214 |> Enum . reduce ( { [ ] , context } , fn version , { changed , context } ->
216- { var_changed? , context } = of_pattern_var_dep ( vars_paths , version , tag , stack , context )
215+ { var_changed? , context } = of_pattern_var_dep ( vars_paths , version , stack , context )
217216
218217 case var_changed? do
219218 false -> { changed , context }
220219 true -> { [ version | changed ] , context }
221220 end
222221 end )
223222
224- of_pattern_var_deps ( changed , vars_paths , vars_deps , tag , stack , context )
223+ of_pattern_var_deps ( changed , vars_paths , vars_deps , stack , context )
225224 end
226225
227- defp of_pattern_var_dep ( vars_paths , version , tag , stack , context ) do
226+ defp of_pattern_var_dep ( vars_paths , version , stack , context ) do
228227 paths = Map . get ( vars_paths , version , [ ] )
229228
230- Enum . reduce ( paths , { false , context } , fn
231- % { var: var , expr: expr , root: root , path: path } , { var_changed? , context } ->
232- index = 0
233- actual = of_pattern_tree ( root , context )
234-
235- case of_pattern_var ( path , actual , context ) do
236- { :ok , type } ->
237- # Optimization: if current type is already a subtype, there is nothing to refine.
238- with % { ^ version => % { type: current_type } } <- context . vars ,
239- true <- subtype? ( current_type , type ) do
240- { var_changed? , context }
241- else
242- _ ->
243- case Of . refine_head_var ( var , type , expr , stack , context ) do
244- { :ok , _type , context } ->
245- { true , context }
246-
247- { :error , old_type , context } ->
248- throw ( { :error , refine_error ( var , old_type , type , stack , context ) } )
249- end
250- end
229+ case context . vars do
230+ % { ^ version => % { type: current_type } = data } when not is_map_key ( data , :errored ) ->
231+ try do
232+ Enum . reduce ( paths , { false , context } , fn
233+ % { var: var , expr: expr , root: root , path: path } , { var_changed? , context } ->
234+ actual = of_pattern_tree ( root , context )
235+
236+ case of_pattern_var ( path , actual , context ) do
237+ { :ok , type } ->
238+ # Optimization: if current type is already a subtype, there is nothing to refine.
239+ if current_type != term ( ) and subtype? ( current_type , type ) do
240+ { var_changed? , context }
241+ else
242+ case Of . refine_head_var ( var , type , expr , stack , context ) do
243+ { :ok , _type , context } ->
244+ { true , context }
251245
252- :error ->
253- throw ( { :error , badpattern_error ( expr , index , tag , stack , context ) } )
246+ { :error , _ , context } ->
247+ throw ( badvar_error ( var , current_type , type , expr , stack , context ) )
248+ end
249+ end
250+
251+ :error ->
252+ throw ( badmatch_error ( expr , stack , Of . error_var ( var , context ) ) )
253+ end
254+ end )
255+ catch
256+ context -> { false , context }
254257 end
255- end )
258+
259+ _ ->
260+ { false , context }
261+ end
256262 end
257263
258264 defp error_vars ( { args_paths , vars_paths , _vars_deps } , context ) do
@@ -265,22 +271,34 @@ defmodule Module.Types.Pattern do
265271 context
266272 end
267273
268- # TODO: May pass the expression as well for more context
269- defp refine_error ( { _ , meta , _ } = var , old_type , type , stack , context ) do
270- error ( __MODULE__ , { :badvar , old_type , type , var , context } , meta , stack , context )
274+ defp badmatch_error ( expr , stack , context ) do
275+ error ( __MODULE__ , { :badmatch , expr , context } , error_meta ( expr , stack ) , stack , context )
271276 end
272277
273- defp badpattern_error ( expr , index , tag , stack , context ) do
274- meta =
275- if meta = get_meta ( expr ) do
276- meta ++ Keyword . take ( stack . meta , [ :generated , :line , :type_check ] )
278+ defp badvar_error ( { var_name , _ , var_context } = var , old_type , new_type , expr , stack , context ) do
279+ error =
280+ if var_name == :match and var_context == __MODULE__ do
281+ { :badmatch , expr , context }
277282 else
278- stack . meta
283+ { :badvar , old_type , new_type , var , context }
279284 end
280285
286+ error ( __MODULE__ , error , error_meta ( var , stack ) , stack , context )
287+ end
288+
289+ defp badpattern_error ( expr , index , tag , stack , context ) do
290+ meta = error_meta ( expr , stack )
281291 error ( __MODULE__ , { :badpattern , meta , expr , index , tag , context } , meta , stack , context )
282292 end
283293
294+ defp error_meta ( expr , stack ) do
295+ if meta = get_meta ( expr ) do
296+ meta ++ Keyword . take ( stack . meta , [ :generated , :line , :type_check ] )
297+ else
298+ stack . meta
299+ end
300+ end
301+
284302 defp of_pattern_intersect ( tree , expected , expr , index , tag , stack , context ) do
285303 actual = of_pattern_tree ( tree , context )
286304 type = intersection ( actual , expected )
@@ -387,7 +405,7 @@ defmodule Module.Types.Pattern do
387405 { type , context }
388406
389407 { :error , old_type , context } ->
390- { error_type ( ) , refine_error ( var , old_type , expected , stack , context ) }
408+ { error_type ( ) , badvar_error ( var , old_type , expected , expr , stack , context ) }
391409 end
392410 end
393411
@@ -587,6 +605,7 @@ defmodule Module.Types.Pattern do
587605 defp is_versioned_var ( _ ) , do: false
588606
589607 defp of_var ( var , version , reverse_path , context ) do
608+ context = Of . declare_var ( var , context )
590609 { args_paths , vars_paths , vars_deps } = context . pattern_info
591610 [ % { root: root , expr: expr } | path ] = Enum . reverse ( reverse_path )
592611 node = % { root: root , var: var , expr: expr , path: path }
@@ -811,7 +830,23 @@ defmodule Module.Types.Pattern do
811830 { pattern_info , % { context | pattern_info: nil } }
812831 end
813832
814- # TODO: Deal when new_type is none()
833+ def format_diagnostic ( { :badmatch , expr , context } ) do
834+ traces = collect_traces ( expr , context )
835+
836+ % {
837+ details: % { typing_traces: traces } ,
838+ message:
839+ IO . iodata_to_binary ( [
840+ """
841+ incompatible types in expression:
842+
843+ #{ expr_to_string ( expr ) |> indent ( 4 ) }
844+ """ ,
845+ format_traces ( traces )
846+ ] )
847+ }
848+ end
849+
815850 def format_diagnostic ( { :badvar , old_type , new_type , var , context } ) do
816851 traces = collect_traces ( var , context )
817852
0 commit comments