Typing violation because of `Application.compile_env/2`

If the code during compile phase conditionally relies on some config, the typing validation would yell at us while compiling. Consider the following code

defmodule TypeTest do
  foo = Application.compile_env(:my_app, :param, true)

  defp fun(arg) do
    case unquote(foo) do
      false -> {:error, arg}
      true -> {:ok, arg}
    end
  end
end

the compilation warns out with

warning: the following clause will never match:
    false
because it attempts to match on the result of:
    true
which has type:
    true

Unfortunately, the famous trick to fool dialyzer does not obviously work here.

  case :erlang.phash2(1, 1) do
    0 -> …
  end

Any idea of how I could have this warning suppressed? In my opinion, the above is more or less common usecase and the warning there is unlikely helpful.

Apparently, at the time being this shuts the type checker up:

defmodule TypeTest do
  foo = Application.compile_env(:my_app, :param, true)

  defp fun(arg) do
    case [unquote(foo), not unquote(foo)] do
      [false, true] -> {:error, arg}
      [true, false] -> {:ok, arg}
    end
  end
end

This happens, because the list in the question becomes non_empty_list(boolean()). Just matching [foo] won’t work because the latter has a type non_empty_list(true).

You can also not generate the unreachable branches to begin with.

defmodule TypeTest do
  foo = Application.compile_env(:my_app, :param, true)

  if foo do
    defp fun(arg), do: {:error, arg}
  else
    defp fun(arg), do: {:ok, arg}
  end
end

Indeed, thanks!

A bit late to the conversation - but this solution will create the same complaint from the compiler →
typing violation found at:

Is there any way to make the compiler aware of other possibilities?

I ran into this today, and the way I solved it was to move the logic from a module attribute using compile_env to an inner-function call using Application.get_env.

# Old way
defmodule MyApp.SomeModule do
  @debug Application.compile_env(:my_app, [__MODULE__, :debug], false)

  def debug?, do: @debug
end

# New way
defmodule MyApp.SomeModule do
  def debug? do
    Application.get_env(:my_app, [__MODULE__, :debug], false)
  end
end

Yeah, but that’s not the isomorphic approach, because in the not-so-contrived example the code might rely on @debug value during compile time all the turtles down.