Re: Unsettling dialyzer errors

Nalin Ranjan <[email protected]>
Newsgroups gmane.comp.lang.erlang.general
Message-ID <CACE+zkLZay5DJiypk2zh7CsoAP+mY-eS1wQzvr=t5HcL1PudSw@mail.gmail.com>
Sorry to have missed one crucial point. Commenting out "case" on L24 and
removing exports, makes "f2" unused leading to a warning, Further to which,
commenting f2 makes the dialyzer happy, but putting a dummy call to f2(V),
in order to by-pass the warning, leads back to square 1.

Something fishy about f2. Got to get the head around it.

Thanks and Regards
Nalin Ranjan

On Sat, Feb 27, 2021 at 7:23 PM Nalin Ranjan <[email protected]> wrote:

> I tried following up a little. Excuse me if there is any discrepancy as I
> am beginning to run dialyzer from now on.
>
> I exported f1 and f2, and it resulted in the following:
>
>
>> *Proceeding with analysis...*
>> *dialyzer_issue.erl:24: The call dialyzer_issue:f2*
>> *         (V :: 'a' | 'b') breaks the contract *
>> *          (t2()) -> {'ok', integer()} | 'error'*
>> * done in 0m0.27s**done (warnings were emitted)*
>
>
> Now I tried yet another thing. I commented out the nested "case" on L24
> returning 'error' atom as the value, instead. This makes dialyzer happy.
> But if I remove the exports, I am back to square 1.
>
> On Sat, Feb 27, 2021 at 4:18 PM Kostis Sagonas <[email protected]> wrote:
>
>> On 2/27/21 10:50 AM, Nicolas Martyanoff wrote:
>> >
>> > I have come across Dialyzer errors which feel incorrect to me. I
>> > managed to reproduce the problem with a minimal example:
>> >
>> >      -module(dialyzer_issue).
>> >
>> >      -export([foo/1]).
>> >
>> >      -type t1() :: a | b.
>> >      -type t2() :: x | y.
>> >
>> >      -spec f1(t1()) -> {ok, integer()} | error.
>> >      f1(a) -> {ok, 1};
>> >      f1(b) -> {ok, 2};
>> >      f1(_) -> error.
>> >
>> >      -spec f2(t2()) -> {ok, integer()} | error.
>> >      f2(x) -> {ok, 3};
>> >      f2(y) -> {ok, 4};
>> >      f2(_) -> error.
>> >
>> >      -spec foo(t1() | t2()) -> integer().
>> >      foo(V) ->
>> >        case f1(V) of
>> >          {ok, N1} ->
>> >            N1;
>> >          error ->
>> >            case f2(V) of
>> >              {ok, N2} ->
>> >                N2;
>> >              error ->
>> >                0
>> >            end
>> >        end.
>> >
>> > The code works as expected:
>> > [dialyzer_issue:foo(N) || N <- [a, b, c, x, y, z]] evaluates to
>> [1,2,0,3,4,0].
>> >
>> > Running Dialyzer (OTP 23.2.2) produces the following errors:
>> >
>> > dialyzer_issue.erl:13: Invalid type specification for function
>> dialyzer_issue:f2/1. The success typing is
>> >            ('a' | 'b') -> 'error'
>> > dialyzer_issue.erl:14: The pattern
>> >            'x' can never match the type
>> >            'a' | 'b'
>> > dialyzer_issue.erl:15: The pattern
>> >            'y' can never match the type
>> >            'a' | 'b'
>> > dialyzer_issue.erl:24: The call dialyzer_issue:f2
>> >           (V :: 'a' | 'b') breaks the contract
>> >            (t2()) -> {'ok', integer()} | 'error'
>> >
>> > It feels wrong to me: foo/1 first calls f1/1 which will always return
>> > a value of type {ok, integer()} for (V :: a | b), therefore f2/1 cannot
>> > be called for a or b. Since the argument of foo/1 is of type
>> > t1() | t2(), I would expect Dialyzer to correctly infer that f2/1 can
>> > only be called with a value of type t2(), since it cannot be called with
>> > a value of type t1().
>> >
>> > The other errors are even more nonsensical but I imagine they derive
>> > from the apparently incorrect last inference.
>> >
>> > I am used to Dialyzer always turning out to be right, but I cannot wrap
>> > my head around this one. Does anyone here see what the problem is ?
>>
>> Yes.
>>
>> The problem is that you have constrained the input arguments of f1/1 and
>> f2/1 too much -- e.g., you have specified that f1/1 only accepts a | b,
>> so dialyzer can disregard the third clause and also the 'error' atom as
>> a return value; similarly for f2/1. Note that returning the atom 'error'
>> on all terms other than a | b is not the same as not-returning, i.e.,
>> not succeeding.
>>
>> To fix the issue, simply change the specs of f1/1 and f2/2 to accept
>> term() as the type of their argument.
>>
>> Better yet, since you are only exporting foo/1 from this module, do NOT
>> write specs for f1/1 and f2/2 and simply let dialyzer do its magic.
>>
>>
>> Kostis
>>
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.