Skip to content

Commit 0bc8a60

Browse files
committed
Turn off refinements on dynamic calls
1 parent 71696e9 commit 0bc8a60

File tree

3 files changed

+51
-23
lines changed

3 files changed

+51
-23
lines changed

lib/elixir/lib/module/types.ex

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,14 +401,25 @@ defmodule Module.Types do
401401
module: module,
402402
# Current function
403403
function: function,
404-
# List of calls to not warn on as undefined or :all or Macro.Env indicating limited remotes
404+
# Handling of undefined remote calls. May be one of:
405+
#
406+
# * List of calls to not warn on as undefined
407+
#
408+
# * The atom `:all` to not mark anything as undefined
409+
#
410+
# * A Macro.Env struct to not mark anything as undefined
411+
# also used to extract structs from
412+
#
405413
no_warn_undefined: no_warn_undefined,
406414
# A tuple with cache information (may be nil)
407415
cache: cache,
408416
# The mode to be used, see the @modes attribute
409417
mode: mode,
410418
# The function for handling local calls
411-
local_handler: handler
419+
local_handler: handler,
420+
# Control if variable refinment is enabled.
421+
# It is disabled only on dynamic dispatches.
422+
refine_vars: true
412423
}
413424
end
414425

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -517,10 +517,10 @@ defmodule Module.Types.Expr do
517517

518518
# var
519519
def of_expr(var, expected, expr, stack, context) when is_var(var) do
520-
if stack.mode == :traversal do
521-
{dynamic(), context}
522-
else
523-
Of.refine_body_var(var, expected, expr, stack, context)
520+
case stack do
521+
%{mode: :traversal} -> {dynamic(), context}
522+
%{refine_vars: false} -> {Of.var(var, context), context}
523+
%{} -> Of.refine_body_var(var, expected, expr, stack, context)
524524
end
525525
end
526526

@@ -715,7 +715,7 @@ defmodule Module.Types.Expr do
715715
{returns, context} =
716716
Enum.map_reduce(mods, context, fn mod, context ->
717717
expr = {remote, [type_check: {:invoked_as, mod, fun, length(args)}] ++ meta, args}
718-
apply_one(mod, fun, args, expected, expr, stack, context)
718+
apply_one(mod, fun, args, expected, expr, %{stack | refine_vars: false}, context)
719719
end)
720720

721721
{Enum.reduce(returns, &union/2), context}

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

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -362,27 +362,51 @@ defmodule Module.Types.ExprTest do
362362
"""
363363
end
364364

365-
test "requires all combinations to be compatible" do
365+
test "requires all combinations to be compatible (except refinements)" do
366+
assert typecheck!(
367+
[condition, arg],
368+
(
369+
# While the code below may raise, it may also always succeed
370+
# if condition and arg are passed in tandem. Therefore, we
371+
# turn off refinement on dynamic calls.
372+
mod = if condition, do: String, else: List
373+
res = mod.to_integer(arg)
374+
{arg, res}
375+
)
376+
) == tuple([dynamic(), integer()])
377+
366378
assert typeerror!(
367-
[condition, string],
379+
[condition],
368380
(
381+
arg = if condition, do: "foo", else: [?f, ?o, ?o]
369382
mod = if condition, do: String, else: List
370-
mod.to_integer(string)
383+
mod.to_integer(arg)
371384
)
372385
)
373386
|> strip_ansi() == ~l"""
374-
incompatible types given to String.to_integer/1:
387+
incompatible types given to List.to_integer/1:
375388
376-
mod.to_integer(string)
377-
#=> invoked as String.to_integer/1
389+
mod.to_integer(arg)
390+
#=> invoked as List.to_integer/1
378391
379392
given types:
380393
381-
dynamic(non_empty_list(integer()))
394+
binary() or non_empty_list(integer())
382395
383396
but expected one of:
384397
385-
binary()
398+
non_empty_list(integer())
399+
400+
where "arg" was given the type:
401+
402+
# type: binary() or non_empty_list(integer())
403+
# from: types_test.ex:LINE-5
404+
arg =
405+
if condition do
406+
"foo"
407+
else
408+
~c"foo"
409+
end
386410
387411
where "mod" was given the type:
388412
@@ -394,13 +418,6 @@ defmodule Module.Types.ExprTest do
394418
else
395419
List
396420
end
397-
398-
where "string" was given the type:
399-
400-
# type: dynamic(non_empty_list(integer()))
401-
# from: types_test.ex:LINE-3
402-
mod.to_integer(string)
403-
#=> invoked as List.to_integer/1
404421
"""
405422
end
406423
end
@@ -968,7 +985,7 @@ defmodule Module.Types.ExprTest do
968985
where "p" was given the type:
969986
970987
# type: %Point{x: integer(), y: nil, z: integer()}
971-
# from: types_test.ex:951
988+
# from: types_test.ex:LINE-4
972989
p = %Point{..., x: 123}
973990
"""
974991
end

0 commit comments

Comments
 (0)