[erlang-questions] picky dialyzer

Kostis Sagonas kostis@REDACTED
Thu Jan 13 12:09:51 CET 2011


Ulf Wiger wrote:
> This has me confused:
> 
> Compiled src/jobs_server.erl
> ==> jobs (analyze)
> jobs_server.erl:1251: The pattern {'queue', _, _, {'producer', _}, _, _, _, _, _, _, _, _, _, _} can never match the type #queue{mod::atom(),type::'fifo' | #action{a::'approve' | 'reject'} | #passive{type::'fifo'},group::atom(),...}
> 
> The line in question is 
> 
> q_is_empty(#queue{type = #producer{}}) -> false;
> 
> 
> The #queue{} record definition is:
> 
> -record(producer, {f :: mfa() | function()}).
> -record(passive , {type = fifo   :: fifo}).
> -record(action  , {a = approve   :: approve | reject}).
> 
> -record(queue, {name                 :: any(),
> 		mod                  :: atom(),
> 		type = fifo          :: fifo | #producer{} | #passive{} | #action{},
> 		group                :: atom(),
>                 …}
> 
> Why does dialyzer think that #producer{} is not a valid member of
> the type def for the 'type' attribute?

Because it is very clever!  :D

You are looking at the tree and you are missing the forest... You are 
concentrating at the type definitions you have added, when you should be 
examining the code you wrote. The code of q_is_empty/1 reads:

   q_is_empty(#queue{type = #producer{}}) -> false;
   q_is_empty(#queue{mod = Mod} = Q)       -> Mod:is_empty(Q).

and this is a module-local function, meaning it will only be used from 
within this module. The only place in the module where there are calls 
to this function is from;

   check_queue(#queue{type = #producer{}} = Q, TS, S) ->
       do_check_queue(Q, TS, S);
   check_queue(Q, TS, S) ->
       case q_is_empty(Q) of
           true ->
               ...;
           false ->
               ...
       end.

At the point where the call to q_is_empty/1 is, dialyzer knows that Q 
will be a #queue{} record where type is not a #producer{}.  In other 
words, there will never be any calls that will match the first clause of 
q_is_empty/1.  So, this clause is unreachable.


> The funny thing is that if I rename the #producer{} record to e.g. 
> #prod{} or #produce{}, dialyzer picks it up (but generates a bunch of 
> other problems, partly because the change wasn't done consistently throughout).
> 
> This (or practically the same warning) can be reproduced by pulling the 
> latest version of http://github.com/esl/jobs and running ./rebar analyze

Well, if I pull the latest version from this repository, I get many more 
dialyzer warnings when I analyze this file. For example, dialyzer 
complains that

jobs_server.erl:991: The call 
erlang:spawn_monitor(M::atom(),F::atom(),A::byte()) will never return 
since it differs in the 3rd argument from the success typing arguments: 
(atom(),atom(),[any()])

and it is of course right.

The problem is that your #producer{} definition is a buggy. It reads:

-record(producer, {f = {erlang,error,[undefined_producer]} :: mfa() | 
function()}).

mfa() is an alias for {atom(),atom(),byte()}.

You want to write {atom(),atom(),[term()]} instead of mfa() there.


Cheers,
Kostis


More information about the erlang-questions mailing list