dialyxir icon indicating copy to clipboard operation
dialyxir copied to clipboard

Unknown error occurred in format_long

Open Sinc63 opened this issue 3 years ago • 8 comments

Unknown error occurred: %FunctionClauseError{args: nil, arity: 1, clauses: nil, function: :format_long, kind: nil, module: Dialyxir.Warnings.OpaqueTypeTest}

Legacy warning: ets.ex:15: The type test is_reference('undefined' | ets:tid()) breaks the opacity of the term 'undefined' | ets:tid()

Source code: @spec alive?() :: boolean def alive?, do: is_reference(whereis())

Precheck

  • Take a look at the open issues and be sure that your issue is not already covered.
  • Be sure your versions of Dialyxir and Erlex are up to date.

Environment

  • Elixir & Erlang/OTP versions (elixir --version): Erlang/OTP 21 [erts-10.3.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

Elixir 1.7.4 (compiled with Erlang/OTP 20)

  • Which version of Dialyxir are you using? (cat mix.lock | grep dialyxir): I upgraded from 1.0.0 to 1.1.0 and still have the issue. erlex 0.2.6

Current behavior

  • Describe current behavior. Include errors, stack traces, and any additional information that might be important here.

Expected behavior

  • A short description of the expected behavior. No error messages from the tool telling me to report an issue against the tool.

Sinc63 avatar Apr 16 '21 20:04 Sinc63

Hi, is there any chance you can share a code-sample that reproduces this problem?

jeremyjh avatar Apr 30 '21 13:04 jeremyjh

sample.ex:

defmodule Sample do
  def hello do
    if alive?(), do: :world,
    else: :goodbye
  end

  @spec alive?() :: boolean
  def alive?, do: is_reference(whereis())

  @spec whereis() :: reference | :undefined
  def whereis, do: :ets.whereis(:sample)
end

dialyxir output: lib/sample.ex:2:no_return Function hello/0 has no local return.


lib/sample.ex:8:no_return Function alive?/0 has no local return.


Please file a bug in https://github.com/jeremyjh/dialyxir/issues with this message.

Unknown error occurred: %FunctionClauseError{args: nil, arity: 1, clauses: nil, function: :format_long, kind: nil, module: Dialyxir.Warnings.OpaqueTypeTest}

Legacy warning: lib/sample.ex:8: The type test is_reference('undefined' | ets:tid()) breaks the opacity of the term 'undefined' | ets:tid()


done (warnings were emitted) Halting VM with exit status 2

Sinc63 avatar May 04 '21 20:05 Sinc63

I just had a look at the code in OpaqueTypeTest. I'm quite intrigued by the fact that current source code, reputedly 2 years old, uses the term opaqueness, where my error message says opacity, and I have dialyxir .1.1.0 which is supposed to be current. Why are the words different? Are you building from a branch these days?

Sinc63 avatar May 04 '21 21:05 Sinc63

Thank you for the sample. We are not using a branch. The word "opacity" in the legacy comment is simply part of the text message that Erlang dialyzer would write to the console. We do not use that message when we format the message in Dialyxir, we use structured information from the term to build the message.

jeremyjh avatar May 05 '21 13:05 jeremyjh

The issue I'm reporting is going to let you eliminate the FunctionClauseError in the Dialyxir code. Is there a good reference that will help me understand how I should fix the actual opacity error? I'm really new to the tool.

Sinc63 avatar May 05 '21 14:05 Sinc63

I do not really know of a good reference. The only reference I recall reading at first was the dialyzer paper linked in the readme and "Learn You Some Erlang" but that was at a time when there was not a single book written on Elixir, so you had to learn Erlang. There probably are books now that explain it in Elixir terms. You can also ask questions on elixirforums.com. It helps to know Haskell, Scala or OCaml (I've done quite a bit of Haskell) but you really have to develop your own understanding and intuitions as success typing is very different from a whole static type system.

In this specific case the issue might simply be that the spec for :ets.whereis has it returning :ets.tid() | :undefined; I think tid() is an opaque term and you are trying to peel back the cover and look inside by typing it as :reference.

jeremyjh avatar May 05 '21 14:05 jeremyjh

I appreciate that input. That could make sense. Now I have an idea what opaque means. I'll do some digging. Maybe the return :undefined is equivalent to is_reference returning false. That is, that if :ets.whereis returns a tid() then the process is up and :undefined means that it is not. That would make alive? just need to be whereis() != :undefined.

Sinc63 avatar May 05 '21 15:05 Sinc63

Yes probably so, you can't call is_reference on a tid(). You should also change the @spec for Sample.whereis to match the return spec for :ets.whereis, e.g. @spec whereis() :: :ets.tid() | :undefined

jeremyjh avatar May 05 '21 15:05 jeremyjh