diff --git a/lib/elixir/lib/kernel.ex b/lib/elixir/lib/kernel.ex index fb7be13484..a6aea9008a 100644 --- a/lib/elixir/lib/kernel.ex +++ b/lib/elixir/lib/kernel.ex @@ -4703,10 +4703,8 @@ defmodule Kernel do false [] -> - quote do - _ = unquote(left) - false - end + # inlined as false in erlang pass + quote(do: :lists.member(unquote(left), [])) [head | tail] = list -> case in_body? do diff --git a/lib/elixir/lib/module/types/apply.ex b/lib/elixir/lib/module/types/apply.ex index d49b30cb34..870d797705 100644 --- a/lib/elixir/lib/module/types/apply.ex +++ b/lib/elixir/lib/module/types/apply.ex @@ -242,7 +242,11 @@ defmodule Module.Types.Apply do {Kernel, :put_elem, [{[open_tuple([]), integer(), term()], dynamic(open_tuple([]))}]}, ## Lists - {:lists, :member, [{[term(), list(term())], boolean()}]}, + {:lists, :member, + [ + {[term(), empty_list()], atom([false])}, + {[term(), non_empty_list(term())], boolean()} + ]}, ## Map {Map, :delete, [{[open_map(), term()], open_map()}]}, diff --git a/lib/elixir/lib/module/types/helpers.ex b/lib/elixir/lib/module/types/helpers.ex index 629cb26df9..f83951cae1 100644 --- a/lib/elixir/lib/module/types/helpers.ex +++ b/lib/elixir/lib/module/types/helpers.ex @@ -329,7 +329,7 @@ defmodule Module.Types.Helpers do end end - {{:., _, [:lists, :member]}, meta, [expr, [_ | _] = args]} = call -> + {{:., _, [:lists, :member]}, meta, [expr, args]} = call when is_list(args) -> if Enum.any?(args, &match?({:|, _, [_, _]}, &1)) do call else diff --git a/lib/elixir/src/elixir_erl_pass.erl b/lib/elixir/src/elixir_erl_pass.erl index 3a42a96c75..19c9bf9d35 100644 --- a/lib/elixir/src/elixir_erl_pass.erl +++ b/lib/elixir/src/elixir_erl_pass.erl @@ -576,6 +576,10 @@ translate_remote('Elixir.String.Chars', to_string, Meta, [Arg], S) -> {clause, Generated, [Var], [[Guard]], [Fast]}, {clause, Generated, [Var], [], [Slow]} ]}, VS}; +translate_remote(lists, member, Meta, [Expr, []], S) -> + Ann = ?ann(Meta), + {TExpr, S1} = translate(Expr, Ann, S), + {{block, Ann, [{match, Ann, {var, Ann, '_'}, TExpr}, {atom, Ann, false}]}, S1}; translate_remote(lists, member, Meta, [Expr, [Head | Tail] = List], S) -> Ann = ?ann(Meta), diff --git a/lib/elixir/test/elixir/kernel_test.exs b/lib/elixir/test/elixir/kernel_test.exs index a74e0cef69..a481b061d3 100644 --- a/lib/elixir/test/elixir/kernel_test.exs +++ b/lib/elixir/test/elixir/kernel_test.exs @@ -461,6 +461,11 @@ defmodule KernelTest do refute 2 in [] refute false in [] refute true in [] + + # make sure optimization still evaluates the left-hand side + # (do not use assert/refute which handle in/2 differently) + send(self(), :foo) in [] + assert_received :foo end test "with expressions on right side" do @@ -697,7 +702,7 @@ defmodule KernelTest do """ # Empty list - assert expand_to_string(quote(do: :x in [])) =~ "_ = :x\nfalse" + assert expand_to_string(quote(do: :x in [])) =~ ":lists.member(:x, [])" assert expand_to_string(quote(do: :x in []), :guard) == "false" # Lists diff --git a/lib/elixir/test/elixir/macro/env_test.exs b/lib/elixir/test/elixir/macro/env_test.exs index 9406462db0..b2ba307ab7 100644 --- a/lib/elixir/test/elixir/macro/env_test.exs +++ b/lib/elixir/test/elixir/macro/env_test.exs @@ -75,7 +75,7 @@ defmodule Macro.EnvTest do test "to_match/1" do quote = quote(do: x in []) - assert {:__block__, [], [{:=, [], [{:_, [], Kernel}, {:x, [], Macro.EnvTest}]}, false]} = + assert {{:., [], [:lists, :member]}, [], [{:x, [], Macro.EnvTest}, []]} = Macro.expand_once(quote, __ENV__) assert Macro.expand_once(quote, Macro.Env.to_match(__ENV__)) == false diff --git a/lib/elixir/test/elixir/module/types/expr_test.exs b/lib/elixir/test/elixir/module/types/expr_test.exs index 1589d05430..7f31e1aeb7 100644 --- a/lib/elixir/test/elixir/module/types/expr_test.exs +++ b/lib/elixir/test/elixir/module/types/expr_test.exs @@ -1818,6 +1818,11 @@ defmodule Module.Types.ExprTest do end test "Kernel.in/2" do + assert typecheck!( + [x], + x in [] + ) == atom([false]) + assert typecheck!( [x], ( diff --git a/lib/elixir/test/elixir/module/types/helpers_test.exs b/lib/elixir/test/elixir/module/types/helpers_test.exs index 144ce090f2..efa09afb32 100644 --- a/lib/elixir/test/elixir/module/types/helpers_test.exs +++ b/lib/elixir/test/elixir/module/types/helpers_test.exs @@ -20,6 +20,8 @@ defmodule Module.Types.HelpersTest do assert expr_to_string(quote(do: :erlang.list_to_atom(a))) == "List.to_atom(a)" assert expr_to_string(quote(do: :erlang.element(1, a))) == "elem(a, 0)" assert expr_to_string(quote(do: :erlang.element(:erlang.+(a, 1), b))) == "elem(b, a)" + assert expr_to_string(quote(do: :lists.member(a, []))) == "a in []" + assert expr_to_string(quote(do: :lists.member(a, [:foo, :bar]))) == "a in [:foo, :bar]" end test "Kernel macros" do diff --git a/lib/elixir/test/erlang/control_test.erl b/lib/elixir/test/erlang/control_test.erl index da310d5309..5648fb84d4 100644 --- a/lib/elixir/test/erlang/control_test.erl +++ b/lib/elixir/test/erlang/control_test.erl @@ -60,6 +60,23 @@ optimized_or_test() -> {clause, _, [{atom, _, true}], [], [{atom, _, true}]}] } = to_erl("is_list([]) or :done"). +optimized_in_test() -> + {'block', _, + [{match,_, + {var, _, '_'}, + {call, _, {remote, _, {atom, _, 'Elixir.IO'}, {atom, _, puts}}, [{atom, _, hi}]}}, + {atom, _, false}] + } = to_erl("IO.puts(:hi) in []"), + {'block', _, + [{match,_, + {var, _, '_1'}, + {call, _, {remote, _, {atom, _, 'Elixir.IO'}, {atom, _, puts}}, [{atom, _, hi}]}}, + {op, _, 'orelse', + {op, _, '=:=', {var, _ , '_1'}, {integer, _, 1}}, + {op, _, '=:=', {var, _ , '_1'}, {integer, _, 2}} + }] + } = to_erl("IO.puts(:hi) in [1, 2]"). + no_after_in_try_test() -> {'try', _, [_], [], [_], []} = to_erl("try do :foo.bar() catch _ -> :ok end").