Skip to content

Commit 52296e0

Browse files
committed
Improve case error message and improve performance
1 parent d8eed78 commit 52296e0

4 files changed

Lines changed: 58 additions & 34 deletions

File tree

lib/elixir/lib/module/types/descr.ex

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2767,16 +2767,9 @@ defmodule Module.Types.Descr do
27672767
if subtype?(v2, v1), do: :right_subtype_of_left
27682768
end
27692769

2770-
defp map_top?(bdd_leaf(:open, fields)) when map_size(fields) == 0, do: true
2771-
defp map_top?(_), do: false
2772-
2773-
defp map_intersection(bdd1, bdd2) do
2774-
cond do
2775-
map_top?(bdd1) and is_tuple(bdd2) -> bdd2
2776-
map_top?(bdd2) and is_tuple(bdd1) -> bdd1
2777-
true -> map_bdd_intersection(bdd1, bdd2)
2778-
end
2779-
end
2770+
defp map_intersection(bdd_leaf(:open, []), bdd), do: bdd
2771+
defp map_intersection(bdd, bdd_leaf(:open, [])), do: bdd
2772+
defp map_intersection(bdd1, bdd2), do: map_bdd_intersection(bdd1, bdd2)
27802773

27812774
# A variant of bdd_intersection/3 that only continues if the maps are closed
27822775
# or both sides are leafs.
@@ -2853,6 +2846,9 @@ defmodule Module.Types.Descr do
28532846
end
28542847
end
28552848

2849+
defp map_difference(bdd_leaf(:open, []), bdd2),
2850+
do: bdd_negation(bdd2)
2851+
28562852
defp map_difference(bdd1, bdd2),
28572853
do: bdd_difference(bdd1, bdd2, &map_leaf_disjoint?/2)
28582854

@@ -4343,6 +4339,9 @@ defmodule Module.Types.Descr do
43434339

43444340
defp tuple_new(tag, elements), do: bdd_leaf(tag, elements)
43454341

4342+
defp tuple_intersection(bdd_leaf(:open, []), bdd), do: bdd
4343+
defp tuple_intersection(bdd, bdd_leaf(:open, [])), do: bdd
4344+
43464345
defp tuple_intersection(bdd1, bdd2) do
43474346
bdd_intersection(bdd1, bdd2, &tuple_leaf_intersection/2)
43484347
end
@@ -4401,6 +4400,12 @@ defmodule Module.Types.Descr do
44014400
end
44024401
end
44034402

4403+
defp tuple_difference(bdd_leaf(:open, []), bdd_leaf(:open, [])),
4404+
do: :bdd_bot
4405+
4406+
defp tuple_difference(bdd_leaf(:open, []), bdd2),
4407+
do: bdd_negation(bdd2)
4408+
44044409
defp tuple_difference(bdd1, bdd2),
44054410
do: bdd_difference(bdd1, bdd2, &tuple_leaf_disjoint?/2)
44064411

lib/elixir/lib/module/types/expr.ex

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -734,11 +734,10 @@ defmodule Module.Types.Expr do
734734
{failed?, context} = reset_failed(context, failed?)
735735
{patterns, guards} = extract_head(head)
736736

737-
clause_domain = difference(domain, previous)
738-
info = {:case, case_meta, clause_domain, case_expr, previous}
737+
info = {:case, case_meta, domain, case_expr, previous}
739738

740739
{trees, precise?, context} =
741-
Pattern.of_head(patterns, guards, [clause_domain], info, meta, stack, context)
740+
Pattern.of_head(patterns, guards, [domain], [previous], info, meta, stack, context)
742741

743742
# It failed, let's try to detect if it was due a previous or current clause.
744743
# The current clause will be easier to understand, so we prefer that

lib/elixir/lib/module/types/pattern.ex

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,11 @@ defmodule Module.Types.Pattern do
127127
4. Then we propagate all dependencies to refine variables
128128
129129
"""
130-
def of_head(patterns, guards, expected, tag, meta, stack, %{vars: vars} = context) do
130+
def of_head(patterns, guards, expected, previous \\ nil, tag, meta, stack, context) do
131+
%{vars: vars} = context
131132
stack = %{stack | meta: meta}
132133

133-
case of_pattern_args(patterns, expected, tag, stack, context) do
134+
case of_pattern_args(patterns, expected, previous, tag, stack, context) do
134135
{trees, pattern_precise?, changed, context} ->
135136
{guard_precise?, context} = of_guards(guards, changed, vars, stack, context)
136137
{trees, pattern_precise? and guard_precise?, context}
@@ -157,20 +158,23 @@ defmodule Module.Types.Pattern do
157158
[]
158159
end
159160

160-
defp of_pattern_args([], [], _tag, _stack, context) do
161+
defp of_pattern_args([], [], _previous, _tag, _stack, context) do
161162
{[], true, [], context}
162163
end
163164

164-
defp of_pattern_args(patterns, expected, tag, stack, context) do
165+
defp of_pattern_args(patterns, expected, previous, tag, stack, context) do
165166
context = init_pattern_info(context, [])
166167

167168
{trees, precise?, context} =
168169
of_pattern_args_zip(patterns, expected, 0, [], true, stack, context)
169170

170171
{pattern_info, context} = pop_pattern_info(context)
171172

172-
case of_pattern_intersect(trees, 0, [], pattern_info, tag, stack, context) do
173-
{_types, changed, context} -> {trees, precise?, changed, context}
173+
with {:ok, types} <- of_pattern_intersect(trees, 0, [], pattern_info, tag, stack, context),
174+
{_types, changed, context} <-
175+
of_pattern_refine(types, previous, pattern_info, tag, stack, context) do
176+
{trees, precise?, changed, context}
177+
else
174178
{:error, context} -> {trees, context}
175179
end
176180
end
@@ -210,8 +214,11 @@ defmodule Module.Types.Pattern do
210214
args = [{tree, expected, expr}]
211215
tag = {:match, expected}
212216

213-
case of_pattern_intersect(args, 0, [], pattern_info, tag, stack, context) do
214-
{[type], changed, context} -> {type, of_changed(changed, stack, context)}
217+
with {:ok, types} <- of_pattern_intersect(args, 0, [], pattern_info, tag, stack, context),
218+
{[type], changed, context} <-
219+
of_pattern_refine(types, nil, pattern_info, tag, stack, context) do
220+
{type, of_changed(changed, stack, context)}
221+
else
215222
{:error, context} -> {expected, context}
216223
end
217224
end
@@ -228,11 +235,12 @@ defmodule Module.Types.Pattern do
228235
{pattern_info, context} = pop_pattern_info(context)
229236
args = [{tree, expected, pattern}]
230237

231-
case of_pattern_intersect(args, 0, [], pattern_info, tag, stack, context) do
232-
{_types, changed, context} ->
233-
{_precise?, context} = of_guards(guards, changed, vars, stack, context)
234-
context
235-
238+
with {:ok, types} <- of_pattern_intersect(args, 0, [], pattern_info, tag, stack, context),
239+
{_types, changed, context} <-
240+
of_pattern_refine(types, nil, pattern_info, tag, stack, context) do
241+
{_precise?, context} = of_guards(guards, changed, vars, stack, context)
242+
context
243+
else
236244
{:error, context} ->
237245
context
238246
end
@@ -251,8 +259,20 @@ defmodule Module.Types.Pattern do
251259
end
252260
end
253261

254-
defp of_pattern_intersect([], _index, acc, pattern_info, tag, stack, context) do
255-
types = Enum.reverse(acc)
262+
defp of_pattern_intersect([], _index, acc, _pattern_info, _tag, _stack, _context) do
263+
{:ok, Enum.reverse(acc)}
264+
end
265+
266+
defp of_pattern_refine(types, previous, pattern_info, tag, stack, context) do
267+
types =
268+
case previous do
269+
nil ->
270+
types
271+
272+
[previous] ->
273+
[type] = types
274+
[difference(type, previous)]
275+
end
256276

257277
try do
258278
pattern_info
@@ -1381,11 +1401,11 @@ defmodule Module.Types.Pattern do
13811401
true ->
13821402
{pattern,
13831403
"""
1384-
the following clause cannot match because previous clauses already matched this pattern:
1404+
the following clause is redundant:
13851405
13861406
#{expr_to_string(pattern) |> indent(4)} ->
13871407
1388-
the following types have already been matched:
1408+
the following types are expected (and have already been matched):
13891409
13901410
#{to_quoted_string(previous_type) |> indent(4)}
13911411
"""}

lib/elixir/test/elixir/module/types/expr_test.exs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,11 +1713,11 @@ defmodule Module.Types.ExprTest do
17131713
:ok -> 2
17141714
end
17151715
) == ~l"""
1716-
the following clause cannot match because previous clauses already matched this pattern:
1716+
the following clause is redundant:
17171717
17181718
:ok ->
17191719
1720-
the following types have already been matched:
1720+
the following types are expected (and have already been matched):
17211721
17221722
:ok
17231723
"""
@@ -1729,11 +1729,11 @@ defmodule Module.Types.ExprTest do
17291729
{x, y} when is_integer(x) and is_integer(y) -> 2
17301730
end
17311731
) =~ ~l"""
1732-
the following clause cannot match because previous clauses already matched this pattern:
1732+
the following clause is redundant:
17331733
17341734
{x, y} when is_integer(x) and is_integer(y) ->
17351735
1736-
the following types have already been matched:
1736+
the following types are expected (and have already been matched):
17371737
17381738
{integer(), integer()}
17391739
"""

0 commit comments

Comments
 (0)