diff --git a/lib/elixir/lib/module/types/apply.ex b/lib/elixir/lib/module/types/apply.ex index 3bbd29a915c..c0fb70ea2f5 100644 --- a/lib/elixir/lib/module/types/apply.ex +++ b/lib/elixir/lib/module/types/apply.ex @@ -159,6 +159,7 @@ defmodule Module.Types.Apply do {:erlang, :apply, [{[fun(), list(term())], dynamic()}]}, {:erlang, :apply, [{[atom(), atom(), list(term())], dynamic()}]}, {:erlang, :and, and_signature}, + {:erlang, :andalso, and_signature}, {:erlang, :atom_to_binary, [{[atom()], binary()}]}, {:erlang, :atom_to_list, [{[atom()], list(integer())}]}, {:erlang, :band, [{[integer(), integer()], integer()}]}, @@ -201,6 +202,7 @@ defmodule Module.Types.Apply do {:erlang, :node, [{[pid() |> union(reference()) |> union(port())], atom()}]}, {:erlang, :not, not_signature}, {:erlang, :or, or_signature}, + {:erlang, :orelse, or_signature}, {:erlang, :raise, [{[atom([:error, :exit, :throw]), term(), raise_stacktrace], none()}]}, {:erlang, :rem, [{[integer(), integer()], integer()}]}, {:erlang, :round, [{[union(integer(), float())], integer()}]}, diff --git a/lib/elixir/lib/module/types/pattern.ex b/lib/elixir/lib/module/types/pattern.ex index d7ec664ee55..3b57bc48979 100644 --- a/lib/elixir/lib/module/types/pattern.ex +++ b/lib/elixir/lib/module/types/pattern.ex @@ -1259,7 +1259,7 @@ defmodule Module.Types.Pattern do {union(type, @dynamic_fail), context} end - defp of_remote(fun, _args, call, expected, stack, context) + defp of_remote(fun, _args, call, expected, stack, %{pattern_info: %{}} = context) when fun in [:and, :or, :andalso, :orelse] do {both_domain, abort_domain, always_rhs?} = case fun do diff --git a/lib/elixir/test/elixir/module/types/pattern_test.exs b/lib/elixir/test/elixir/module/types/pattern_test.exs index e0b3741b080..2318cc796ee 100644 --- a/lib/elixir/test/elixir/module/types/pattern_test.exs +++ b/lib/elixir/test/elixir/module/types/pattern_test.exs @@ -1132,6 +1132,29 @@ defmodule Module.Types.PatternTest do assert typecheck!([x = {}], not (x == :foo), x) == dynamic(tuple([])) assert typecheck!([x = {}], x != :foo, x) == dynamic(tuple([])) end + + test "with orelse/andalso nested in comparison" do + # Verify that orelse and andalso guard evaluation works correctly + assert typecheck!([a, b, c], a != (b or c), :ok) == atom([:ok]) + assert typecheck!([a, b, c], a == (b or c), :ok) == atom([:ok]) + assert typecheck!([a, b, c], a !== (b or c), :ok) == atom([:ok]) + assert typecheck!([a, b, c], a === (b or c), :ok) == atom([:ok]) + assert typecheck!([a, b, c], a == hd(b or c), :ok) == atom([:ok]) + assert typecheck!([a, b, c], a != (b and c), :ok) == atom([:ok]) + assert typecheck!([a, b, c], a == (b and c), :ok) == atom([:ok]) + + # Verify type precision is not lost through orelse/andalso re-evaluation + assert typecheck!([a, b], a and b, a) == dynamic(atom([true])) + assert typecheck!([a, b, c], a === (b or c), a) == dynamic(boolean()) + assert typecheck!([a, b, c], a === (b and c), a) == dynamic(boolean()) + assert typecheck!([a], a, a) == dynamic(atom([true])) + assert typecheck!([a, b], a or b, a) == dynamic(boolean()) + assert typecheck!([a, b, c], b and a === (b or c), a) == dynamic(atom([true])) + assert typecheck!([a, b, c], c and a === (b or c), a) == dynamic(atom([true])) + assert typecheck!([a, b, c], b and a === (b and c), a) == dynamic(boolean()) + assert typecheck!([a, b, c], b and a === (not b and c), a) == dynamic(atom([false])) + assert typecheck!([a, b, c], not b and a === (b and c), a) == dynamic(atom([false])) + end end describe "size comparison in guards" do