Elixir: Dialyzer warnings with 1.5 using with/else statements

Created on 25 Jul 2017  路  15Comments  路  Source: elixir-lang/elixir

Environment

  • 1.5 with otp-20
  • Linux

Current behavior

With 1.5 seems that with statements using an else block drive dialyzer crazy.
Don't know if is a dialyzer problem, but with elixir 1.4.5 it does not happens.

sample code here:
https://github.com/xadhoom/local_return/blob/master/lib/local_return.ex

Basically, when using inspect inside a fun within the with/else block , dialyzer complains about "The created fun has no local return".

I've another case when matching using :ets.lookup within with/else but I've not yet a simple reproducible example, will report later if I can draft an isolated case (basically messages like the pattern 'false' can never match the type [tuple(),...])

Most helpful comment

this is not a logger issue btw, can be rephrased as:

defmodule Dialyzer.With.Orig do
  def with_else do
    my_ext_var = :foo

    with :ok <- ok_or_error(),
         :ok <- ok_or_other_error() do
      :ok
    else
      :error ->
        foo(fn -> my_ext_var end)
        :error
      :other_error ->
        :other_error
    end
  end

  @spec ok_or_error() :: :ok | :error
  defp ok_or_error do
    Enum.random([:ok, :error])
  end

  @spec ok_or_other_error() :: :ok | :other_error
  defp ok_or_other_error do
    Enum.random([:ok, :other_error])
  end

  defp foo(fun) do
    fun.()
  end

end

All 15 comments

ok,

I've a simple example also with :ets inside with/else:
https://github.com/xadhoom/local_return/blob/master/lib/local_return_ets.ex

where dialyzer blames about:

lib/local_return_ets.ex:15: The pattern [] can never match the type 'baz' | 'foo'
lib/local_return_ets.ex:16: The pattern 'foo' can never match the type [tuple(),...]

I'm seeing this as well. I don't recall see it with 1.5.0-rc.0, but I'm rechecking now.

Thanks for the reports! If someone can reproduce it as a test in our suite here, it would be a great help. But, if not, we should be able to extract one from @xadhoom's sample app - which is already great.

This is most likely related to the tail call recursion optimizations that were added after RC 0, #6251

I can confirm that I don't see this in 1.5.0-rc.0.

Yes, this is going to be exactly the tail recursion thing. I'm not sure there's a way to fix it without either marking everything in the else handlers as generated or going for the "tuple" solution.

Thank you @paulswartz . This has been fixed in master and in the v1.5 branch. Keep in mind you need OTP 20 because dialyzer in OTP 19.0 had some regressions and emitted warnings when it should not. Maybe OTP 19.3 is fine though.

We should have a new release in a week or two. We just need to grab the first round of regressions.

mmh seems still present on 1.5.1 (maybe a regression after the fix?)

a simple test module that shows the issue

``` defmodule Test do def test do with :a <- :b do x = 42 fn -> inspect(x) end end end end

@xadhoom which warning are you getting? Looking at the code, Elixir is correct in emitting a warning there.

@josevalim similar as original report, with this specific case:

lib/test.ex:3: The pattern 'a' can never match the type 'b'
lib/test.ex:5: The created fun has no local return

these are dialyzer warnings

I understand that this example is too minimal, I'll try to setup a more complete one (basically I've the same error on my code, I'll extract a meaningful one)

@josevalim here's the error, based on the test case provided here: https://github.com/paulswartz/elixir/commit/c928fe7097430f2a2b9c4a4215331eff146331e0

Dialyzer blames about

lib/test.ex:12: The created fun has no local return

require Logger

defmodule Dialyzer.With.Orig do
  def with_else do
    tolog = :foo

    with :ok <- ok_or_error(),
         :ok <- ok_or_other_error() do
      :ok
    else
      :error ->
        Logger.warn fn -> "foo #{inspect tolog}" end
        :error
      :other_error ->
        :other_error
    end
  end

  @spec ok_or_error() :: :ok | :error
  defp ok_or_error do
    Enum.random([:ok, :error])
  end

  @spec ok_or_other_error() :: :ok | :other_error
  defp ok_or_other_error do
    Enum.random([:ok, :other_error])
  end
end

if the call to inspect inside the logger are removed, everything is ok.

this is not a logger issue btw, can be rephrased as:

defmodule Dialyzer.With.Orig do
  def with_else do
    my_ext_var = :foo

    with :ok <- ok_or_error(),
         :ok <- ok_or_other_error() do
      :ok
    else
      :error ->
        foo(fn -> my_ext_var end)
        :error
      :other_error ->
        :other_error
    end
  end

  @spec ok_or_error() :: :ok | :error
  defp ok_or_error do
    Enum.random([:ok, :error])
  end

  @spec ok_or_other_error() :: :ok | :other_error
  defp ok_or_other_error do
    Enum.random([:ok, :other_error])
  end

  defp foo(fun) do
    fun.()
  end

end
Was this page helpful?
0 / 5 - 0 ratings

Related issues

LucianaMarques picture LucianaMarques  路  3Comments

alexrp picture alexrp  路  4Comments

DEvil0000 picture DEvil0000  路  3Comments

ckampfe picture ckampfe  路  3Comments

sashaafm picture sashaafm  路  3Comments