From 2cc38dc4dcf9533a23862e16805fb0aa820a9019 Mon Sep 17 00:00:00 2001 From: sabiwara Date: Sat, 23 May 2026 09:10:42 +0900 Subject: [PATCH 1/6] Inline in empty list in erlang pass --- lib/elixir/lib/kernel.ex | 6 ++---- lib/elixir/src/elixir_erl_pass.erl | 3 +++ lib/elixir/test/elixir/kernel_test.exs | 2 +- lib/elixir/test/elixir/macro/env_test.exs | 2 +- lib/elixir/test/elixir/module/types/expr_test.exs | 5 +++++ 5 files changed, 12 insertions(+), 6 deletions(-) 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/src/elixir_erl_pass.erl b/lib/elixir/src/elixir_erl_pass.erl index 3a42a96c75..850da1d400 100644 --- a/lib/elixir/src/elixir_erl_pass.erl +++ b/lib/elixir/src/elixir_erl_pass.erl @@ -576,6 +576,9 @@ 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), + {{atom, Ann, false}, S}; 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..d41e5f15d6 100644 --- a/lib/elixir/test/elixir/kernel_test.exs +++ b/lib/elixir/test/elixir/kernel_test.exs @@ -697,7 +697,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..77e4b647ac 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 [] + ) == boolean() + assert typecheck!( [x], ( From 5eade00629a3776319c81a85f021c0bad75ca6e2 Mon Sep 17 00:00:00 2001 From: sabiwara Date: Sat, 23 May 2026 18:01:53 +0900 Subject: [PATCH 2/6] Ensure we still evaluate the left-handside --- lib/elixir/src/elixir_erl_pass.erl | 9 +++++++-- lib/elixir/test/elixir/kernel_test.exs | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/elixir/src/elixir_erl_pass.erl b/lib/elixir/src/elixir_erl_pass.erl index 850da1d400..bf5d1f0bf1 100644 --- a/lib/elixir/src/elixir_erl_pass.erl +++ b/lib/elixir/src/elixir_erl_pass.erl @@ -576,9 +576,14 @@ 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) -> +translate_remote(lists, member, Meta, [Expr, []], S) -> Ann = ?ann(Meta), - {{atom, Ann, false}, S}; + {TExpr, S1} = translate(Expr, Ann, S), + {VarName, S2} = elixir_erl_var:build('_', S1), + Generated = erl_anno:set_generated(true, Ann), + Var = {var, Generated, VarName}, + Block = {block, Generated, [{match, Generated, Var, TExpr}, {atom, Ann, false}]}, + {Block, S2}; 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 d41e5f15d6..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 From 34762e47aaf533dd52f263f3ad2016a97135d29b Mon Sep 17 00:00:00 2001 From: sabiwara Date: Sat, 23 May 2026 18:39:11 +0900 Subject: [PATCH 3/6] Add tests for inlining in --- lib/elixir/test/erlang/control_test.erl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/elixir/test/erlang/control_test.erl b/lib/elixir/test/erlang/control_test.erl index da310d5309..3d938be338 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, _, '_1'}, + {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"). From 596eeda50446550650a1072b3d515a6a9b0b0f40 Mon Sep 17 00:00:00 2001 From: sabiwara Date: Sat, 23 May 2026 18:42:21 +0900 Subject: [PATCH 4/6] Update type helpers --- lib/elixir/lib/module/types/helpers.ex | 2 +- lib/elixir/test/elixir/module/types/helpers_test.exs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) 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/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 From 58a96eefc01a3f98b5debd0ae878c8f6770fcb82 Mon Sep 17 00:00:00 2001 From: sabiwara Date: Sat, 23 May 2026 18:51:06 +0900 Subject: [PATCH 5/6] Refine type to false for empty list --- lib/elixir/lib/module/types/apply.ex | 6 +++++- lib/elixir/test/elixir/module/types/expr_test.exs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) 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/test/elixir/module/types/expr_test.exs b/lib/elixir/test/elixir/module/types/expr_test.exs index 77e4b647ac..7f31e1aeb7 100644 --- a/lib/elixir/test/elixir/module/types/expr_test.exs +++ b/lib/elixir/test/elixir/module/types/expr_test.exs @@ -1821,7 +1821,7 @@ defmodule Module.Types.ExprTest do assert typecheck!( [x], x in [] - ) == boolean() + ) == atom([false]) assert typecheck!( [x], From eb03c094a4b29206641f1bbe545ad731161fb95a Mon Sep 17 00:00:00 2001 From: sabiwara Date: Sun, 24 May 2026 08:32:25 +0900 Subject: [PATCH 6/6] Do not build var or use generated --- lib/elixir/src/elixir_erl_pass.erl | 6 +----- lib/elixir/test/erlang/control_test.erl | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/elixir/src/elixir_erl_pass.erl b/lib/elixir/src/elixir_erl_pass.erl index bf5d1f0bf1..19c9bf9d35 100644 --- a/lib/elixir/src/elixir_erl_pass.erl +++ b/lib/elixir/src/elixir_erl_pass.erl @@ -579,11 +579,7 @@ translate_remote('Elixir.String.Chars', to_string, Meta, [Arg], S) -> translate_remote(lists, member, Meta, [Expr, []], S) -> Ann = ?ann(Meta), {TExpr, S1} = translate(Expr, Ann, S), - {VarName, S2} = elixir_erl_var:build('_', S1), - Generated = erl_anno:set_generated(true, Ann), - Var = {var, Generated, VarName}, - Block = {block, Generated, [{match, Generated, Var, TExpr}, {atom, Ann, false}]}, - {Block, S2}; + {{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/erlang/control_test.erl b/lib/elixir/test/erlang/control_test.erl index 3d938be338..5648fb84d4 100644 --- a/lib/elixir/test/erlang/control_test.erl +++ b/lib/elixir/test/erlang/control_test.erl @@ -63,7 +63,7 @@ optimized_or_test() -> optimized_in_test() -> {'block', _, [{match,_, - {var, _, '_1'}, + {var, _, '_'}, {call, _, {remote, _, {atom, _, 'Elixir.IO'}, {atom, _, puts}}, [{atom, _, hi}]}}, {atom, _, false}] } = to_erl("IO.puts(:hi) in []"),