@@ -8,6 +8,22 @@ defmodule Module.Types.Pattern do
88 alias Module.Types . { Apply , Of }
99 import Module.Types . { Helpers , Descr }
1010
11+ @ doc """
12+ Defines if two list of arguments are subtypes of each other.
13+ """
14+ def args_subtype? ( [ ] , [ ] ) ,
15+ do: true
16+
17+ def args_subtype? ( [ type ] , previous ) ,
18+ do: subtype? ( type , Enum . reduce ( previous , none ( ) , & union ( & 2 , hd ( & 1 ) ) ) )
19+
20+ def args_subtype? ( args , previous ) do
21+ subtype? (
22+ args_to_domain ( args ) ,
23+ Enum . reduce ( previous , none ( ) , & union ( & 2 , args_to_domain ( & 1 ) ) )
24+ )
25+ end
26+
1127 @ doc """
1228 Refine the dependencies of variables represented by version.
1329 """
@@ -127,7 +143,7 @@ defmodule Module.Types.Pattern do
127143 4. Then we propagate all dependencies to refine variables
128144
129145 """
130- def of_head ( patterns , guards , expected , previous \\ nil , tag , meta , stack , context ) do
146+ def of_head ( patterns , guards , expected , previous \\ [ ] , tag , meta , stack , context ) do
131147 % { vars: vars } = context
132148 stack = % { stack | meta: meta }
133149
@@ -216,7 +232,7 @@ defmodule Module.Types.Pattern do
216232
217233 with { :ok , types } <- of_pattern_intersect ( args , 0 , [ ] , pattern_info , tag , stack , context ) ,
218234 { [ type ] , changed , context } <-
219- of_pattern_refine ( types , nil , pattern_info , tag , stack , context ) do
235+ of_pattern_refine ( types , [ ] , pattern_info , tag , stack , context ) do
220236 { type , of_changed ( changed , stack , context ) }
221237 else
222238 { :error , context } -> { expected , context }
@@ -237,7 +253,7 @@ defmodule Module.Types.Pattern do
237253
238254 with { :ok , types } <- of_pattern_intersect ( args , 0 , [ ] , pattern_info , tag , stack , context ) ,
239255 { _types , changed , context } <-
240- of_pattern_refine ( types , nil , pattern_info , tag , stack , context ) do
256+ of_pattern_refine ( types , [ ] , pattern_info , tag , stack , context ) do
241257 { _precise? , context } = of_guards ( guards , changed , vars , stack , context )
242258 context
243259 else
@@ -265,13 +281,17 @@ defmodule Module.Types.Pattern do
265281
266282 defp of_pattern_refine ( types , previous , pattern_info , tag , stack , context ) do
267283 types =
268- case previous do
269- nil ->
284+ case types do
285+ _ when previous == [ ] ->
270286 types
271287
272- [ previous ] ->
273- [ type ] = types
274- [ difference ( type , previous ) ]
288+ [ type ] ->
289+ [ Enum . reduce ( previous , type , & difference ( & 2 , hd ( & 1 ) ) ) ]
290+
291+ [ _ | _ ] ->
292+ previous
293+ |> Enum . reduce ( args_to_domain ( types ) , & difference ( & 2 , args_to_domain ( & 1 ) ) )
294+ |> domain_to_flat_args ( types )
275295 end
276296
277297 try do
@@ -1346,15 +1366,17 @@ defmodule Module.Types.Pattern do
13461366 # $ type tag = head_pattern() or match_pattern()
13471367 #
13481368 # $ typep head_pattern =
1349- # :for_reduce or :with_else or :receive or :try_catch or :fn or :default or
1350- # {:try_else, type} or {{:case, meta, expr}, [domain], [previous_type]}
1369+ # :for_reduce or :with_else or :fn or :default or
1370+ # {{:case | :try_else, meta, expr, type}, [arg], [previous]} or
1371+ # {:receive | :try_catch, [arg], [previous]}
13511372 #
13521373 # $ typep match_pattern =
13531374 # :with or :for or {:match, type}
13541375 #
13551376 # The match pattern ones have the whole expression instead
13561377 # of a single pattern.
13571378 def format_diagnostic ( { :badpattern , meta , pattern_or_expr , index , tag , context } ) do
1379+ # TODO: stop passing pattern_or_expr as argument
13581380 { to_trace , message } = badpattern ( tag , pattern_or_expr , index )
13591381 traces = collect_traces ( to_trace , context )
13601382
@@ -1370,24 +1392,9 @@ defmodule Module.Types.Pattern do
13701392 }
13711393 end
13721394
1373- defp badpattern ( { :try_else , type } , pattern , _ ) do
1374- { pattern ,
1375- """
1376- the following clause will never match:
1377-
1378- #{ expr_to_string ( pattern ) |> indent ( 4 ) }
1379-
1380- it attempts to match on the result of the try do-block which has incompatible type:
1381-
1382- #{ to_quoted_string ( type ) |> indent ( 4 ) }
1383- """ }
1384- end
1385-
1386- defp badpattern ( { { :case , meta , expr } , [ type ] , [ previous_type ] } , pattern , _ ) do
1395+ defp badpattern ( { { op , meta , expr , type } , args , previous } , _ , _ ) when op in [ :case , :try_else ] do
13871396 cond do
13881397 meta [ :type_check ] == :expr ->
1389- error_type = if previous_type == none ( ) , do: type , else: previous_type
1390-
13911398 { expr ,
13921399 """
13931400 the following conditional expression:
@@ -1396,15 +1403,15 @@ defmodule Module.Types.Pattern do
13961403
13971404 will always evaluate to:
13981405
1399- #{ to_quoted_string ( error_type ) |> indent ( 4 ) }
1406+ #{ to_quoted_string ( type ) |> indent ( 4 ) }
14001407 """ }
14011408
1402- previous_type == none ( ) ->
1403- { pattern ,
1409+ previous == [ ] ->
1410+ { args ,
14041411 """
14051412 the following clause will never match:
14061413
1407- #{ expr_to_string ( pattern ) |> indent ( 4 ) } ->
1414+ #{ args_to_string ( args ) |> indent ( 4 ) } ->
14081415
14091416 because it attempts to match on the result of:
14101417
@@ -1415,46 +1422,46 @@ defmodule Module.Types.Pattern do
14151422 #{ to_quoted_string ( type ) |> indent ( 4 ) }
14161423 """ }
14171424
1418- subtype? ( type , previous_type ) ->
1419- { pattern ,
1425+ args_subtype? ( [ type ] , previous ) ->
1426+ { args ,
14201427 """
14211428 the following clause cannot match because the previous clauses already matched all possible values:
14221429
1423- #{ expr_to_string ( pattern ) |> indent ( 4 ) } ->
1430+ #{ args_to_string ( args ) |> indent ( 4 ) } ->
14241431
14251432 it attempts to match on the result of:
14261433
14271434 #{ expr_to_string ( expr ) |> indent ( 4 ) }
14281435
1429- and the following types have already been matched:
1436+ which has the already matched type :
14301437
1431- #{ to_quoted_string ( previous_type ) |> indent ( 4 ) }
1438+ #{ to_quoted_string ( type ) |> indent ( 4 ) }
14321439 """ }
14331440
14341441 true ->
1435- { pattern ,
1442+ { args ,
14361443 """
14371444 the following clause is redundant:
14381445
1439- #{ expr_to_string ( pattern ) |> indent ( 4 ) } ->
1446+ #{ args_to_string ( args ) |> indent ( 4 ) } ->
14401447
1441- the following types are expected (and have already been matched) :
1448+ previous clauses have already matched on the following types :
14421449
1443- #{ to_quoted_string ( previous_type ) |> indent ( 4 ) }
1450+ #{ previous_to_string ( previous ) }
14441451 """ }
14451452 end
14461453 end
14471454
1448- defp badpattern ( { :receive , [ _type ] , [ previous_type ] } , pattern , _ ) do
1449- { pattern ,
1455+ defp badpattern ( { op , args , previous } , _ , _ ) when op in [ :receive , :try_catch ] do
1456+ { args ,
14501457 """
14511458 the following clause is redundant:
14521459
1453- #{ expr_to_string ( pattern ) |> indent ( 4 ) } ->
1460+ #{ args_to_string ( args ) |> indent ( 4 ) } ->
14541461
14551462 previous clauses have already matched on the following types:
14561463
1457- #{ to_quoted_string ( previous_type ) |> indent ( 4 ) }
1464+ #{ previous_to_string ( previous ) }
14581465 """ }
14591466 end
14601467
@@ -1504,4 +1511,18 @@ defmodule Module.Types.Pattern do
15041511 #{ expr_to_string ( pattern_or_expr ) |> indent ( 4 ) }
15051512 """ }
15061513 end
1514+
1515+ defp args_to_string ( args ) do
1516+ args
1517+ |> Enum . map_join ( ", " , & expr_to_string / 1 )
1518+ |> indent ( 4 )
1519+ end
1520+
1521+ defp previous_to_string ( previous ) do
1522+ Enum . map_join ( previous , "\n " , fn types ->
1523+ types
1524+ |> Enum . map_join ( ", " , & to_quoted_string / 1 )
1525+ |> indent ( 4 )
1526+ end )
1527+ end
15071528end
0 commit comments