Skip to content

Commit 245066b

Browse files
committed
Relax handling of __exception__
We were used to assert it to be true but, given the value is not used in any shape, we can relax the definition and simply assert on the presence of the key. Closes #15251.
1 parent 4d30464 commit 245066b

9 files changed

Lines changed: 43 additions & 24 deletions

File tree

lib/elixir/lib/exception.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ defmodule Exception do
2626
@typedoc "The exception type"
2727
@type t :: %{
2828
required(:__struct__) => module,
29-
required(:__exception__) => true,
29+
required(:__exception__) => term,
3030
optional(atom) => any
3131
}
3232

@@ -77,7 +77,7 @@ defmodule Exception do
7777
@doc false
7878
@deprecated "Use Kernel.is_exception/1 instead"
7979
def exception?(term)
80-
def exception?(%_{__exception__: true}), do: true
80+
def exception?(%_{__exception__: _}), do: true
8181
def exception?(_), do: false
8282

8383
@doc """
@@ -89,7 +89,7 @@ defmodule Exception do
8989
return a descriptive error message instead.
9090
"""
9191
@spec message(t) :: String.t()
92-
def message(%module{__exception__: true} = exception) do
92+
def message(%module{__exception__: _} = exception) do
9393
try do
9494
module.message(exception)
9595
rescue
@@ -123,7 +123,7 @@ defmodule Exception do
123123
@spec normalize(:error, any, stacktrace) :: t
124124
@spec normalize(non_error_kind, payload, stacktrace) :: payload when payload: var
125125
def normalize(kind, payload, stacktrace \\ [])
126-
def normalize(:error, %_{__exception__: true} = payload, _stacktrace), do: payload
126+
def normalize(:error, %_{__exception__: _} = payload, _stacktrace), do: payload
127127
def normalize(:error, payload, stacktrace), do: ErlangError.normalize(payload, stacktrace)
128128
def normalize(_kind, payload, _stacktrace), do: payload
129129

lib/elixir/lib/kernel.ex

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2752,7 +2752,7 @@ defmodule Kernel do
27522752
nil ->
27532753
quote do
27542754
case unquote(term) do
2755-
%_{__exception__: true} -> true
2755+
%_{__exception__: _} -> true
27562756
_ -> false
27572757
end
27582758
end
@@ -2764,8 +2764,7 @@ defmodule Kernel do
27642764
quote do
27652765
is_map(unquote(term)) and :erlang.is_map_key(:__struct__, unquote(term)) and
27662766
is_atom(:erlang.map_get(:__struct__, unquote(term))) and
2767-
:erlang.is_map_key(:__exception__, unquote(term)) and
2768-
:erlang.map_get(:__exception__, unquote(term)) == true
2767+
:erlang.is_map_key(:__exception__, unquote(term))
27692768
end
27702769
end
27712770
end
@@ -2792,7 +2791,7 @@ defmodule Kernel do
27922791
case unquote(name) do
27932792
name when is_atom(name) ->
27942793
case unquote(term) do
2795-
%{__struct__: ^name, __exception__: true} -> true
2794+
%{__struct__: ^name, __exception__: _} -> true
27962795
_ -> false
27972796
end
27982797

@@ -2810,8 +2809,7 @@ defmodule Kernel do
28102809
(is_atom(unquote(name)) or :fail) and
28112810
:erlang.is_map_key(:__struct__, unquote(term)) and
28122811
:erlang.map_get(:__struct__, unquote(term)) == unquote(name) and
2813-
:erlang.is_map_key(:__exception__, unquote(term)) and
2814-
:erlang.map_get(:__exception__, unquote(term)) == true
2812+
:erlang.is_map_key(:__exception__, unquote(term))
28152813
end
28162814
end
28172815
end

lib/elixir/lib/kernel/typespec.ex

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,8 +610,7 @@ defmodule Kernel.Typespec do
610610
types =
611611
:lists.map(
612612
fn %{field: field} ->
613-
default_type = if field == :__exception__, do: true, else: quote(do: term())
614-
{field, Keyword.get(fields, field, default_type)}
613+
{field, Keyword.get(fields, field, quote(do: term()))}
615614
end,
616615
struct_info
617616
)

lib/elixir/lib/kernel/utils.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ defmodule Kernel.Utils do
266266
module.exception([])
267267
end
268268

269-
def raise(%_{__exception__: true} = exception) do
269+
def raise(%_{__exception__: _} = exception) do
270270
exception
271271
end
272272

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ defmodule Module.Types.Expr do
1515
list_of_modules = list(atom())
1616

1717
@try_catch atom([:error, :exit, :throw])
18-
@atom_true atom([true])
1918

2019
@caller closed_map(
2120
__struct__: atom([Macro.Env]),
@@ -41,7 +40,7 @@ defmodule Module.Types.Expr do
4140

4241
# We do not make exception dynamic on purpose. If you do a blank rescue,
4342
# then we will assume you need to statically handle all possible exceptions.
44-
@exception open_map(__struct__: atom(), __exception__: @atom_true)
43+
@exception open_map(__struct__: atom(), __exception__: term())
4544

4645
args_or_arity = union(list(term()), integer())
4746

@@ -558,7 +557,7 @@ defmodule Module.Types.Expr do
558557
## Try
559558

560559
defp of_rescue(var, exceptions, body, expr, info, meta, stack, original) do
561-
args = [__exception__: @atom_true]
560+
args = [__exception__: term()]
562561

563562
{structs, context} =
564563
Enum.map_reduce(exceptions, original, fn exception, context ->

lib/elixir/src/elixir_erl_try.erl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ rescue_guards(Meta, Var, Aliases) ->
117117
ElixirGuards =
118118
[erl_and(Meta,
119119
{erl(Meta, '=='), Meta, [{erl(Meta, map_get), Meta, ['__struct__', Var]}, Alias]},
120-
{erl(Meta, map_get), Meta, ['__exception__', Var]}
120+
{erl(Meta, is_map_key), Meta, ['__exception__', Var]}
121121
) || Alias <- Aliases],
122122

123123
{ElixirGuards ++ ErlangGuards, ErlangAliases}.
@@ -214,7 +214,11 @@ erl_rescue_guard_for(Meta, Var, 'Elixir.ErlangError') ->
214214
erl_and(
215215
Meta,
216216
{erl(Meta, is_map), Meta, [Var]},
217-
{erl(Meta, is_map_key), Meta, ['__exception__', Var]}
217+
erl_and(
218+
Meta,
219+
{erl(Meta, is_map_key), Meta, ['__struct__', Var]},
220+
{erl(Meta, is_map_key), Meta, ['__exception__', Var]}
221+
)
218222
),
219223
{erl(Meta, 'not'), Meta, [Condition]};
220224

lib/elixir/test/elixir/kernel/raise_test.exs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,22 @@ defmodule Kernel.RaiseTest do
337337
assert result == "Erlang error: :sample"
338338
end
339339

340+
test "runtime error from Erlang" do
341+
result =
342+
try do
343+
:erlang.error(%{
344+
__struct__: RuntimeError,
345+
__exception__: :does_not_matter,
346+
message: "oops"
347+
})
348+
rescue
349+
x in [ErlangError] -> {:erlang, Exception.message(x)}
350+
x in [RuntimeError] -> {:runtime, Exception.message(x)}
351+
end
352+
353+
assert result == {:runtime, "oops"}
354+
end
355+
340356
test "undefined function error" do
341357
result =
342358
try do

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2287,12 +2287,12 @@ defmodule Module.Types.ExprTest do
22872287
union(
22882288
closed_map(
22892289
__struct__: atom([ArgumentError]),
2290-
__exception__: atom([true]),
2290+
__exception__: term(),
22912291
message: term()
22922292
),
22932293
closed_map(
22942294
__struct__: atom([RuntimeError]),
2295-
__exception__: atom([true]),
2295+
__exception__: term(),
22962296
message: term()
22972297
)
22982298
)
@@ -2309,7 +2309,7 @@ defmodule Module.Types.ExprTest do
23092309
) ==
23102310
open_map(
23112311
__struct__: atom(),
2312-
__exception__: atom([true])
2312+
__exception__: term()
23132313
)
23142314
end
23152315

@@ -2328,15 +2328,15 @@ defmodule Module.Types.ExprTest do
23282328
23292329
given types:
23302330
2331-
%{..., __exception__: true, __struct__: atom()}
2331+
%{..., __exception__: term(), __struct__: atom()}
23322332
23332333
but expected one of:
23342334
23352335
integer()
23362336
23372337
where "e" was given the type:
23382338
2339-
# type: %{..., __exception__: true, __struct__: atom()}
2339+
# type: %{..., __exception__: term(), __struct__: atom()}
23402340
# from: types_test.ex
23412341
rescue e
23422342

lib/elixir/test/elixir/typespec_test.exs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,10 @@ defmodule TypespecTest do
525525
assert {:type, _, :map, [struct, arg1, arg2]} = type
526526
assert {:type, _, :map_field_exact, struct_args} = struct
527527
assert [{:atom, _, :__struct__}, {:atom, _, TypespecSample}] = struct_args
528-
assert {:type, _, :map_field_exact, [{:atom, _, :__exception__}, {:atom, _, true}]} = arg1
528+
529+
assert {:type, _, :map_field_exact, [{:atom, _, :__exception__}, {:type, _, :term, []}]} =
530+
arg1
531+
529532
assert {:type, _, :map_field_exact, [{:atom, _, :message}, {:type, _, :term, []}]} = arg2
530533
end
531534

0 commit comments

Comments
 (0)