Skip to content

Commit c90bd0f

Browse files
committed
Ensure all variables are propagated in a bad match, closes #13946
1 parent f2d8064 commit c90bd0f

File tree

4 files changed

+46
-9
lines changed

4 files changed

+46
-9
lines changed

lib/elixir/lib/module/types/helpers.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ defmodule Module.Types.Helpers do
8686
version = meta[:version]
8787

8888
case vars do
89-
%{^version => %{off_traces: [_ | _] = off_traces, name: name, context: context}} ->
89+
%{^version => %{off_traces: off_traces, name: name, context: context}} ->
9090
{:ok,
9191
Map.put(versions, version, %{
9292
type: :variable,

lib/elixir/lib/module/types/of.ex

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,23 @@ defmodule Module.Types.Of do
2626
type
2727
end
2828

29+
@doc """
30+
Marks a variable with error.
31+
"""
32+
def error_var(var, context) do
33+
{var_name, meta, var_context} = var
34+
version = Keyword.fetch!(meta, :version)
35+
36+
data = %{
37+
type: error_type(),
38+
name: var_name,
39+
context: var_context,
40+
off_traces: []
41+
}
42+
43+
put_in(context.vars[version], data)
44+
end
45+
2946
@doc """
3047
Refines the type of a variable.
3148
"""

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

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,15 +123,17 @@ defmodule Module.Types.Pattern do
123123
pattern_vars = Map.to_list(pattern_vars)
124124
changed = :lists.seq(0, length(types) - 1)
125125

126-
case callback.(types, changed, context) do
127-
{:ok, types, context} ->
128-
of_pattern_recur(types, pattern_vars, pattern_args, stack, context, callback)
126+
try do
127+
case callback.(types, changed, context) do
128+
{:ok, types, context} ->
129+
of_pattern_recur(types, pattern_vars, pattern_args, stack, context, callback)
129130

130-
{:error, context} ->
131-
{types, context}
131+
{:error, context} ->
132+
{types, error_vars(pattern_vars, context)}
133+
end
134+
catch
135+
{types, context} -> {types, error_vars(pattern_vars, context)}
132136
end
133-
catch
134-
{types, context} -> {types, context}
135137
end
136138

137139
defp of_pattern_recur(types, vars, args, stack, context, callback) do
@@ -158,6 +160,7 @@ defmodule Module.Types.Pattern do
158160
end
159161

160162
:error ->
163+
# TODO: This should be precised about the operation (case/=/try/etc)
161164
context = Of.incompatible_error(expr, expected, actual, stack, context)
162165
throw({types, context})
163166
end
@@ -193,11 +196,17 @@ defmodule Module.Types.Pattern do
193196
# A simple structural comparison for optimization
194197
{:ok, ^types, context} -> {types, context}
195198
{:ok, types, context} -> of_pattern_recur(types, vars, args, stack, context, callback)
196-
{:error, context} -> {types, context}
199+
{:error, context} -> {types, error_vars(vars, context)}
197200
end
198201
end
199202
end
200203

204+
defp error_vars(vars, context) do
205+
Enum.reduce(vars, context, fn {_version, [[var | _path] | _paths]}, context ->
206+
Of.error_var(var, context)
207+
end)
208+
end
209+
201210
defp of_pattern_intersect(tree, expected, expr, stack, context) do
202211
actual = of_pattern_tree(tree, context)
203212
type = intersection(actual, expected)
@@ -212,6 +221,7 @@ defmodule Module.Types.Pattern do
212221
{:error, error(__MODULE__, {:invalid_pattern, expr, context}, meta, stack, context)}
213222

214223
true ->
224+
# TODO: This should be precised about the operation (case/=/try/etc)
215225
{:error, Of.incompatible_error(expr, expected, actual, stack, context)}
216226
end
217227
end

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ defmodule Module.Types.PatternTest do
6868
b = :bar
6969
"""
7070
end
71+
72+
test "can be access even if they don't match" do
73+
assert typeerror!(
74+
(
75+
# This will never match, info should not be "corrupted"
76+
[info | _] = __ENV__.function
77+
info
78+
)
79+
) =~ "incompatible types in expression"
80+
end
7181
end
7282

7383
describe "structs" do

0 commit comments

Comments
 (0)