[erlang-questions] Dialyzer questions

Prashanth Mundkur pmundkur.erlang@REDACTED
Wed May 4 04:43:04 CEST 2011


Kostis Sagonas wrote:

> Prashanth Mundkur wrote:
> > We've recently been getting the upcoming release of Disco to go through 
> > the Dialyzer wringer, and it has been a very useful exercise.  Thanks to 
> > Kostis' helpful wielding of the cluebat at the SF Erlang Factory!
> > 
> > We are trying to understand the following output (I hope the inline code 
> > pasting below comes out readable):
> 
> You already figured out some answers to your questions, but let me also 
> reply to them for completeness and hopefully also for more explanations.
> 
> > $ cat dial.erl
> > -module(dial).
> > -export([abort/2, extract/2,
> >          diskspace/1, monitor_diskspace/2, refresh_tags/1, 
> > monitor_launch/0]).
> > 
> > % We have helper functions like this:
> > abort(Msg, Code) ->
> >     error_logger:warning_report(Msg),
> >     exit(Code).
> > % which give rise to the following dialyzer message:
> > %
> > %   Function abort/2 only terminates with explicit exception
> > % How do we interpret this message?
> 
> First of all note that you do not get this warning by default. You only 
> get this warning because you explicitly enabled the -Werror_handling
> option.  I quote from the manual:
> 
>    -Werror_handling ***
>       Include warnings for functions
>       that only return by means of an exception.
> 
> You can suppress this warning by adding a spec of the form:
> 
>     -spec abort(some_type(), some_other_type()) -> no_return().
> 
> for this function, which will notify dialyzer that it is indeed your 
> intention that this function will never return a value
> 
> 
> > % Even with a plt that includes stdlib, there is no warning for the
> > % following code:
> > -spec extract(binary(), nonempty_string()) -> 'ok'.
> > extract(Pack, Dir) ->
> >     case zip:extract(Pack, [{cwd, Dir}]) of
> >         {ok, Files} ->
> >             Files;
> >         {error, Reason} ->
> >             exit({error, Reason})
> >     end.
> 
> This happens because module 'zip' does not have -spec information about 
> its functions.  Without such information, dialyzer's analysis infers 
> that zip:extract/2 will either return something of the form {ok, term()} 
> or {error, term()}.  Note that the only thing that dialyzer can infer 
> given this information is that Files will be some term().  Your spec 
> promises is that this term is 'ok'.  In such cases, where dialyzer 
> cannot find any reason not to trust you, it keeps its mouth shut about 
> it.  (Unless you use option -Woverspecs in this case.)

Thanks, this clarifies a lot.

> 
> Now, it's actually the case that I have a spec-ed version of zip.erl 
> which has not made it into OTP yet and in that version I get the 
> following warning in your code:
> 
>     Invalid type specification for function dial:extract/2.
>     The success typing is (binary() | string(),binary() | string()) ->
> 	[binary() | string() | {binary() | string(),binary()}]
> 
> I will see whether this version can be included as in OTP or not...
> 
> 
> > % Here, the spawn_link of monitor_diskspace in monitor_launch() gives:
> > %   The created fun has no local return
> > % whereas the spawn_link of refresh_tags doesn't.  If the spawn_link
> > % of monitor_diskspace() function is commented out, there is no
> > % warning.  It's not clear how to interpret the warning or the
> > % inconsistency.
> > %
> > % Converting the nested list comprehension in monitor_diskspace into a
> > % list:map made the warning go away, at the expense of readability and
> > % perhaps even efficiency.
> 
> You are right. There is some inconsistency in the behavior of the two 
> cases. But note that the funs that are created have no return -- they 
> both call functions that go into an infinite loop and actually they are 
> explicitly spec-ed as no returning. So the warning you get is correct. 
> Still, you would like the two cases to be consistent and ideally also 
> not get this warning, if possible. (So that you do not have to resort to 
> calls outside this module as e.g. using lists:map/2.)
> 
> We spend some time with Stavros today looking into this and Stavros has 
> come up with a way of making sure the treatment of these two cases is 
> consistent and the one you want. When such specs are in place for 
> functions that go into infinite loops, the warnings for uses of them in 
> funs are suppressed.
> 
> Stay tuned and Stavros' patch will soon find its place to an OTP version 
> near you!

Excellent!

> Hope this explains all issues,

Yes, this was very helpful, thanks!  One issue I forgot to ask about was the
following:

$ cat /tmp/dial.erl 
-module(dial).
-export([f/1]).

-type t() :: 'a' | 'b'.
-spec f(atom()) -> t().

f(x) ->
    g();
f(y) ->
    h().

-spec g() -> t().
g() -> a.

-spec h() -> t().
h() -> b.

$ dialyzer /tmp/dial.erl 
dial.erl:12: The specification for dial:g/0 states that the function
  might also return 'b' but the inferred return is 'a'
dial.erl:15: The specification for dial:h/0 states that the function
  might also return 'a' but the inferred return is 'b'

This seems like a common problem when we use helper functions and
don't want to separately type the return values of each of the
helpers.  Both -Wunderspecs and -Woverspecs don't help here.  Is there
an idiomatic way of avoiding this warning, other than omitting the
-specs for the helpers altogether?

--prashanth



More information about the erlang-questions mailing list