Dialyzer: Cons will produce an improper list since its 2nd argument is none()

Dániel Szoboszlay dszoboszlay@REDACTED
Sat Feb 15 23:11:46 CET 2020


I was playing a bit with Jesper's example, and I feel Dialyzer behaves very
surprisingly and somewhat inconsistently with reporting this kind of
problems. Here's my extended test program to illustrate the issues:

-module(foo).
-export([foo/0, err/0, errs/0, bar/0, bars/0]).
-record(foo, {x :: integer()}).
-record(bar, {x}).
-type bar() :: #bar{x :: integer()}.
foo() ->
  [ #foo{x = 0},
    #foo{x = false} ].
err() ->
  error(badarg).
errs() ->
  [ #foo{x = 0},
    error(badarg) ].
-spec bar() -> bar().
bar() ->
  #bar{x = false}.
-spec bars() -> [bar()].
bars() ->
  [ #bar{x = 0},
    #bar{x = false} ].


And these are the warnings Dialyzer reports:

foo.erl:6: Function foo/0 has no local return
foo.erl:7: Cons will produce an improper list since its 2nd argument is
          none()
foo.erl:8: Record construction
          #foo{x :: 'false'} violates the declared type of field x ::
          integer()
foo.erl:11: Function errs/0 has no local return
foo.erl:14: Invalid type specification for function foo:bar/0. The success
typing is
          () -> #bar{x :: 'false'}


I find the following things problematic with these warnings:

   - "Function foo/0 has no local return": what the message says is not
   true. There are some type spec issues with foo/0, but it won't crash,
   because the emulator doesn't care about your type specs at runtime.
   - "Cons will produce an improper list": what the message says is not
   true. If the 2nd argument is none(), that is: evaluating that expression
   would raise an exception instead of returning a value, cons will not
   produce anything, the execution will never reach that far.
   - There is no "Function err/0 has no local return" warning, even though
   err/0 has no local return for real. This also feels very inconsistent with
   the fact that there is such a warning for the errs/0 function, which does
   basically the same thing (when considering types).
   - There is no "Cons will produce an improper list" warning for errs/0,
   even though it is exactly the same situation as in case of foo/0. Dialyzer
   feels inconsistent here.
   - There is no "Invalid type specification for function foo:bars/0:
   warning, even though this function clearly violates its spec, and the same
   problem in bar/0 is properly reported. (Note: this one in particular makes
   me very sad, because I prefer keeping type specs outside of record
   declarations precisely to avoid the "Function has no local return"
   warnings. In my experience Dialyzer produces better warnings this way, but
   now I found an example where an obvious problem is not detected by Dialyzer
   due to the field types being declared outside of the record definition.)
   - A minor inconsistency, but some warnings refer to the function in
   which they occur via its local name within the module (foo/0), while others
   use the qualified name (foo:bar/0).

Would it be possible to make Dialyzer more consistent and less surprising
in these cases?

Cheers,
Daniel

On Fri, 14 Feb 2020 at 15:48, Richard Carlsson <carlsson.richard@REDACTED>
wrote:

> Nice answer, Brujo! I think the only confusion here is the warning about
> the improper list; without that one, everything should have been clear. The
> check for improper lists ought to ignore the case when the tail is none(),
> since it means that the result will also be none() anyway.
>
>         /Richard
>
>
> Den fre 14 feb. 2020 kl 14:05 skrev Jesper Eskilson <
> jesper.eskilson@REDACTED>:
>
>> Ah, my question wasn't why the error occurs in the first place, but
>> rather an inquiry if dialyzer oughtn't formulate the error a bit
>> differently. :)
>>
>>
>> On Fri, Feb 14, 2020 at 1:54 PM Fernando Benavides <
>> elbrujohalcon@REDACTED> wrote:
>>
>>> I think what's happening in this case is this…
>>> Dialyzer sees your code (using cons for the list) as…
>>>
>>> main() -> [ #foo{x = 0} | [#foo{x = false} | []] ].
>>>
>>> It then detects the third warning from your email (namely, that you're
>>> using *false* for something that should be an *integer*):
>>> foo.erl:9: Record construction
>>>           #foo{x :: false} violates the declared type of field x ::
>>>           integer()
>>> Therefore, the type of that second part of the list ([#foo{x = false} |
>>> []]) will be *none()*.
>>> And that leads dialyzer to complain with the second warning that you see…
>>> foo.erl:8: Cons will produce an improper list since its 2nd argument is
>>>           none()
>>> Which, in time, produces the first warning…
>>> foo.erl:7: Function main/0 has no local return
>>>
>>> Hope this helps.
>>>
>>> On Fri, Feb 14, 2020 at 1:45 PM Jesper Eskilson <
>>> jesper.eskilson@REDACTED> wrote:
>>>
>>>> Hi,
>>>>
>>>> When dialyzer analyzes this program:
>>>>
>>>>
>>>> -module(foo).
>>>> -export([main/0]).
>>>> -record(foo, {x :: integer()}).
>>>> main() ->
>>>>   [ #foo{x = 0},
>>>>     #foo{x = false} ].
>>>>
>>>>
>>>> it says:
>>>>
>>>> foo.erl:7: Function main/0 has no local return
>>>> foo.erl:8: Cons will produce an improper list since its 2nd argument is
>>>>           none()
>>>> foo.erl:9: Record construction
>>>>           #foo{x :: false} violates the declared type of field x ::
>>>>           integer()
>>>>
>>>>
>>>> It seems like dialyzer assumes that any cons with second argument not
>>>> being a list will produce an improper list, but shouldn't it treat "none()"
>>>> differently?
>>>>
>>>> Is this a bug in dialyzer, or a feature whose usefulness I am unable to
>>>> grasp?
>>>>
>>>> --
>>>>
>>>> *Jesper Eskilson*
>>>> Senior Software Engineer
>>>> Kred Core
>>>> +46-72-855-8421
>>>>
>>>> Klarna Bank AB (publ)
>>>> Sveavägen 46, 111 34 Stockholm
>>>> Tel: +46 8 120 120 00 <+46812012000>
>>>> Reg no: 556737-0431
>>>> klarna.com
>>>>
>>>>
>>>
>>> --
>>>
>>> <https://about.me/elbrujohalcon?promo=email_sig&utm_source=product&utm_medium=email_sig&utm_campaign=gmail_api&utm_content=thumb>
>>> Brujo Benavides
>>> about.me/elbrujohalcon
>>> <https://about.me/elbrujohalcon?promo=email_sig&utm_source=product&utm_medium=email_sig&utm_campaign=gmail_api&utm_content=thumb>
>>>
>>
>>
>> --
>>
>> *Jesper Eskilson*
>> Senior Software Engineer
>> Kred Core
>> +46-72-855-8421
>>
>> Klarna Bank AB (publ)
>> Sveavägen 46, 111 34 Stockholm
>> Tel: +46 8 120 120 00 <+46812012000>
>> Reg no: 556737-0431
>> klarna.com
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20200215/60565d14/attachment.htm>


More information about the erlang-questions mailing list