Skip to content

Commit d0514df

Browse files
committed
Clean up typing changes
1 parent 325608b commit d0514df

4 files changed

Lines changed: 81 additions & 83 deletions

File tree

lib/elixir/lib/module/types/apply.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1619,7 +1619,7 @@ defmodule Module.Types.Apply do
16191619

16201620
{message, to_trace, hints} =
16211621
case reason do
1622-
{:badarg, domain} ->
1622+
{:badarg, domain, _empty?} ->
16231623
message = """
16241624
incompatible types given on #{mfa_or_call}:
16251625

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

Lines changed: 57 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,8 +1357,8 @@ defmodule Module.Types.Descr do
13571357
Applies a function type to a list of argument types.
13581358
13591359
Returns `{:ok, result}` if the application is valid
1360-
or one `{:badarg, to_succeed_domain}`, `:badfun`,
1361-
`{:badarity, arities}` if not.
1360+
or one `{:badarg, to_succeed_domain, empty?}`, `:badfun`,
1361+
or `{:badarity, arities}`.
13621362
13631363
Note the domain returned by `:badarg` is not the strong
13641364
domain, but the domain that must be satisfied for the
@@ -1442,63 +1442,63 @@ defmodule Module.Types.Descr do
14421442
static? = fun_dynamic == nil and Enum.all?(arguments, fn arg -> not gradual?(arg) end)
14431443
arity = length(arguments)
14441444

1445-
with false <- Enum.any?(arguments, &empty?/1),
1446-
{:ok, domain, static_arrows, dynamic_arrows} <-
1447-
fun_normalize_both(fun_static, fun_dynamic, arity) do
1448-
cond do
1449-
# The domain here is the extended gradual domain computed by
1450-
# fun_normalize_both/3. If the argument does not satisfy it, we
1451-
# check compatibility before rejecting.
1452-
#
1453-
# Compatibility has two cases to avoid a degenerate situation.
1454-
# If the argument is purely dynamic (e.g. dynamic() and bool()),
1455-
# its static part (lower bound) is none(). We do not want
1456-
# none() <= domain to trivially succeed, because that would mean
1457-
# "a diverging argument is accepted by any function", which is true but
1458-
# useless. So when the static part is empty, we instead check
1459-
# that the upper bound overlaps with the domain. When the static
1460-
# part is non-empty, we check it is a subtype of the domain.
1461-
not subtype?(args_domain, domain) ->
1462-
if static? or not compatible?(args_domain, domain),
1463-
do: {:badarg, domain_to_flat_args(domain, arity)},
1464-
else: {:ok, dynamic()}
1465-
1466-
static? ->
1467-
{:ok, fun_apply_static(arguments, static_arrows)}
1468-
1469-
static_arrows == [] ->
1470-
# Purely dynamic function (e.g. dynamic() and (integer() -> integer())).
1471-
# There are no static arrows, so the general mixed formula simplifies:
1472-
# applying none() to anything yields none(), so the static branch
1473-
# vanishes and only the dynamic branch remains.
1474-
# The result is wrapped in dynamic(), so it is safe regardless of argument precision.
1475-
# If the upper-bounded arguments escape the domain, fun_apply_static returns term(),
1476-
# and dynamic(term()) = dynamic(), which brings back to the compatible case.
1477-
arguments = Enum.map(arguments, &upper_bound/1)
1478-
{:ok, dynamic(fun_apply_static(arguments, dynamic_arrows))}
1445+
if Enum.any?(arguments, &empty?/1) do
1446+
{:badarg, arguments, true}
1447+
else
1448+
with {:ok, domain, static_arrows, dynamic_arrows} <-
1449+
fun_normalize_both(fun_static, fun_dynamic, arity) do
1450+
cond do
1451+
# The domain here is the extended gradual domain computed by
1452+
# fun_normalize_both/3. If the argument does not satisfy it, we
1453+
# check compatibility before rejecting.
1454+
#
1455+
# Compatibility has two cases to avoid a degenerate situation.
1456+
# If the argument is purely dynamic (e.g. dynamic() and bool()),
1457+
# its static part (lower bound) is none(). We do not want
1458+
# none() <= domain to trivially succeed, because that would mean
1459+
# "a diverging argument is accepted by any function", which is true but
1460+
# useless. So when the static part is empty, we instead check
1461+
# that the upper bound overlaps with the domain. When the static
1462+
# part is non-empty, we check it is a subtype of the domain.
1463+
not subtype?(args_domain, domain) ->
1464+
if static? or not compatible?(args_domain, domain),
1465+
do: {:badarg, domain_to_flat_args(domain, arity), false},
1466+
else: {:ok, dynamic()}
1467+
1468+
static? ->
1469+
{:ok, fun_apply_static(arguments, static_arrows)}
1470+
1471+
static_arrows == [] ->
1472+
# Purely dynamic function (e.g. dynamic() and (integer() -> integer())).
1473+
# There are no static arrows, so the general mixed formula simplifies:
1474+
# applying none() to anything yields none(), so the static branch
1475+
# vanishes and only the dynamic branch remains.
1476+
# The result is wrapped in dynamic(), so it is safe regardless of argument precision.
1477+
# If the upper-bounded arguments escape the domain, fun_apply_static returns term(),
1478+
# and dynamic(term()) = dynamic(), which brings back to the compatible case.
1479+
arguments = Enum.map(arguments, &upper_bound/1)
1480+
{:ok, dynamic(fun_apply_static(arguments, dynamic_arrows))}
14791481

1480-
true ->
1481-
# Mixed case: union of the static and dynamic results.
1482-
# static_arrows (lower materialization) contain only arrows that are
1483-
# guaranteed to exist at runtime. Static guarantees about the result
1484-
# come from these alone.
1485-
# dynamic_arrows (upper materialization) include dynamically uncertain
1486-
# arrows, so their result is wrapped in dynamic().
1487-
# We use upper_bound on the arguments for both branches. This is sound
1488-
# because the dynamic branch wraps its result in dynamic().
1489-
# It is more strict and informative than using lower_bound in the static part,
1490-
# as it amounts to assuming the worst case of using the statically present arrows.
1491-
arguments = Enum.map(arguments, &upper_bound/1)
1492-
1493-
{:ok,
1494-
union(
1495-
fun_apply_static(arguments, static_arrows),
1496-
dynamic(fun_apply_static(arguments, dynamic_arrows))
1497-
)}
1482+
true ->
1483+
# Mixed case: union of the static and dynamic results.
1484+
# static_arrows (lower materialization) contain only arrows that are
1485+
# guaranteed to exist at runtime. Static guarantees about the result
1486+
# come from these alone.
1487+
# dynamic_arrows (upper materialization) include dynamically uncertain
1488+
# arrows, so their result is wrapped in dynamic().
1489+
# We use upper_bound on the arguments for both branches. This is sound
1490+
# because the dynamic branch wraps its result in dynamic().
1491+
# It is more strict and informative than using lower_bound in the static part,
1492+
# as it amounts to assuming the worst case of using the statically present arrows.
1493+
arguments = Enum.map(arguments, &upper_bound/1)
1494+
1495+
{:ok,
1496+
union(
1497+
fun_apply_static(arguments, static_arrows),
1498+
dynamic(fun_apply_static(arguments, dynamic_arrows))
1499+
)}
1500+
end
14981501
end
1499-
else
1500-
true -> {:badarg, arguments}
1501-
error -> error
15021502
end
15031503
end
15041504

lib/elixir/test/elixir/macro_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ defmodule MacroTest do
628628

629629
test "boolean expressions that raise" do
630630
falsy = length([]) != 0
631-
x = 0
631+
x = Process.get(:unused, 0)
632632

633633
{result, formatted} = dbg_format_no_newline((falsy || x) && 1 / x)
634634

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

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,26 +1103,26 @@ defmodule Module.Types.DescrTest do
11031103

11041104
test "static" do
11051105
# Full static
1106-
assert fun_apply(fun(), [integer()]) == {:badarg, [none()]}
1107-
assert fun_apply(difference(fun(), none_fun(2)), [integer()]) == {:badarg, [none()]}
1106+
assert fun_apply(fun(), [integer()]) == {:badarg, [none()], false}
1107+
assert fun_apply(difference(fun(), none_fun(2)), [integer()]) == {:badarg, [none()], false}
11081108

11091109
# Basic function application scenarios
11101110
assert fun_apply(fun([integer()], atom()), [integer()]) == {:ok, atom()}
1111-
assert fun_apply(fun([integer()], atom()), [float()]) == {:badarg, [integer()]}
1112-
assert fun_apply(fun([integer()], atom()), [term()]) == {:badarg, [integer()]}
1111+
assert fun_apply(fun([integer()], atom()), [float()]) == {:badarg, [integer()], false}
1112+
assert fun_apply(fun([integer()], atom()), [term()]) == {:badarg, [integer()], false}
11131113

11141114
# Union argument type: domain is int | float
11151115
assert fun_apply(fun([union(integer(), float())], atom()), [integer()]) == {:ok, atom()}
11161116
assert fun_apply(fun([union(integer(), float())], atom()), [float()]) == {:ok, atom()}
11171117

11181118
assert fun_apply(fun([union(integer(), float())], atom()), [atom()]) ==
1119-
{:badarg, [union(integer(), float())]}
1119+
{:badarg, [union(integer(), float())], false}
11201120

11211121
# 2-arity function
11221122
assert fun_apply(fun([integer(), atom()], binary()), [integer(), atom()]) == {:ok, binary()}
11231123

11241124
assert fun_apply(fun([integer(), atom()], binary()), [boolean(), atom()]) ==
1125-
{:badarg, [integer(), atom()]}
1125+
{:badarg, [integer(), atom()], false}
11261126

11271127
# Return types
11281128
assert fun_apply(fun([integer()], none()), [integer()]) == {:ok, none()}
@@ -1135,7 +1135,9 @@ defmodule Module.Types.DescrTest do
11351135
|> elem(1)
11361136
|> equal?(atom())
11371137

1138-
assert fun_apply(fun([integer()], atom()), [dynamic(float())]) == {:badarg, [integer()]}
1138+
assert fun_apply(fun([integer()], atom()), [dynamic(float())]) ==
1139+
{:badarg, [integer()], false}
1140+
11391141
assert fun_apply(fun([integer()], atom()), [dynamic(term())]) == {:ok, dynamic()}
11401142

11411143
# Arity mismatches
@@ -1198,7 +1200,7 @@ defmodule Module.Types.DescrTest do
11981200
assert fun_apply(fun, [dynamic(integer())]) == {:ok, union(atom(), dynamic())}
11991201
assert fun_apply(fun, [dynamic(number())]) == {:ok, dynamic()}
12001202
assert fun_apply(fun, [integer()]) == {:ok, dynamic()}
1201-
assert fun_apply(fun, [float()]) == {:badarg, [dynamic(integer())]}
1203+
assert fun_apply(fun, [float()]) == {:badarg, [dynamic(integer())], false}
12021204
end
12031205

12041206
defp dynamic_fun(args, return), do: dynamic(fun(args, return))
@@ -1214,7 +1216,10 @@ defmodule Module.Types.DescrTest do
12141216
assert fun_apply(dynamic_fun([integer()], atom()), [float()]) == {:ok, dynamic()}
12151217
assert fun_apply(dynamic_fun([integer()], atom()), [term()]) == {:ok, dynamic()}
12161218
assert fun_apply(dynamic_fun([integer()], none()), [integer()]) == {:ok, dynamic(none())}
1217-
assert fun_apply(dynamic(fun([integer()], integer())), [none()]) == {:badarg, [none()]}
1219+
1220+
assert fun_apply(dynamic(fun([integer()], integer())), [none()]) ==
1221+
{:badarg, [none()], true}
1222+
12181223
assert fun_apply(dynamic_fun([integer()], term()), [integer()]) == {:ok, dynamic()}
12191224

12201225
# Dynamic return and dynamic args
@@ -1330,15 +1335,17 @@ defmodule Module.Types.DescrTest do
13301335
)
13311336

13321337
assert fun_args |> fun_apply([atom()]) == {:ok, dynamic()}
1333-
assert fun_args |> fun_apply([integer()]) == {:badarg, [dynamic(atom())]}
1338+
assert fun_args |> fun_apply([integer()]) == {:badarg, [dynamic(atom())], false}
13341339

13351340
# ((bool->bool) or dyn(int->int))
13361341
# booleans work, but not integers
13371342
fun_mixed_gdom = union(fun([boolean()], boolean()), dynamic_fun([integer()], integer()))
13381343
assert fun_apply(fun_mixed_gdom, [boolean()]) == {:ok, dynamic()}
13391344
assert fun_apply(fun_mixed_gdom, [dynamic(boolean())]) == {:ok, union(dynamic(), boolean())}
1340-
assert fun_apply(fun_mixed_gdom, [integer()]) == {:badarg, [dynamic(boolean())]}
1341-
assert fun_apply(fun_mixed_gdom, [dynamic(integer())]) == {:badarg, [dynamic(boolean())]}
1345+
assert fun_apply(fun_mixed_gdom, [integer()]) == {:badarg, [dynamic(boolean())], false}
1346+
1347+
assert fun_apply(fun_mixed_gdom, [dynamic(integer())]) ==
1348+
{:badarg, [dynamic(boolean())], false}
13421349

13431350
# Badfun
13441351
assert union(
@@ -2280,24 +2287,15 @@ defmodule Module.Types.DescrTest do
22802287
# This is a test of the map_update_fun/5 with forced?: false parameter.
22812288
# We check that it does not call its typed_fun argument with `none()`
22822289
# due to the key being absent in the map.
2283-
22842290
type = dynamic(difference(open_map(), empty_map()))
2285-
ref = make_ref()
22862291

22872292
fun = fn _optional?, value ->
2288-
send(self(), {ref, value})
2293+
send(self(), :callback_invoked)
22892294
value
22902295
end
22912296

2292-
_ = map_update_fun(type, binary(), fun, false, false)
2293-
2294-
messages = Process.info(self(), :messages) |> elem(1)
2295-
2296-
# Check that the callback was not invoked with `none()`
2297-
refute Enum.any?(messages, fn
2298-
{seen_ref, value} when seen_ref == ref -> empty?(value)
2299-
_ -> false
2300-
end)
2297+
assert map_update_fun(type, binary(), fun, false, false) == {dynamic(none()), type, []}
2298+
refute_received :callback_invoked
23012299
end
23022300

23032301
test "with dynamic atom keys" do

0 commit comments

Comments
 (0)