Why does Dialiszer not find this code incorrect?

I created the snippet below based on this tutorial. The last two lines ( feed_squid(FeederRP)and feed_red_panda(FeederSquid)) obviously violate certain restrictions, but Dialyzer finds them in order. This is rather disappointing, because this is exactly the type of error that I want to catch with a tool that performs static analysis.

The tutorial has an explanation:

Before functions are called with the wrong feeder, they are first called correct. Starting with R15B01, Dialyzer will not find an error with this code. The observed behavior is that as soon as the call to this function succeeds inside the function body, Dialyzer will ignore later errors in the same code unit.

What is the reason for this behavior? I understand that the philosophy behind success is to β€œnever cry like a wolf,” but in the current scenario, Dialyzer explicitly ignores intentionally defined function specifications (after he sees that the functions were called correctly earlier). I understand that the code does not crash at runtime. Can I somehow make Dialyzer always take my features seriously? If not, is there a tool that can do this?

-module(zoo).
-export([main/0]).

-type red_panda() :: bamboo | birds | eggs | berries.
-type squid() :: sperm_whale.
-type food(A) :: fun(() -> A).

-spec feeder(red_panda) -> food(red_panda());
            (squid) -> food(squid()).
feeder(red_panda) ->
    fun() ->
            element(random:uniform(4), {bamboo, birds, eggs, berries})
    end;
feeder(squid) ->
    fun() -> sperm_whale end.

-spec feed_red_panda(food(red_panda())) -> red_panda().
feed_red_panda(Generator) ->
    Food = Generator(),
    io:format("feeding ~p to the red panda~n", [Food]),
    Food.

-spec feed_squid(food(squid())) -> squid().
feed_squid(Generator) ->
    Food = Generator(),
    io:format("throwing ~p in the squid aquarium~n", [Food]),
    Food.

main() ->
    %% Random seeding
    <<A:32, B:32, C:32>> = crypto:rand_bytes(12),
    random:seed(A, B, C),
    %% The zoo buys a feeder for both the red panda and squid
    FeederRP = feeder(red_panda),
    FeederSquid = feeder(squid),
    %% Time to feed them!
    feed_squid(FeederSquid),
    feed_red_panda(FeederRP),
    %% This should not be right!
    feed_squid(FeederRP),
    feed_red_panda(FeederSquid).
+5
source share
2 answers

Minimizing the example quite a bit, I have these two versions:

The first that Dialyzer can call:

-module(zoo).
-export([main/0]).

-type red_panda_food() :: bamboo.
-type squid_food()     :: sperm_whale.

-spec feed_squid(fun(() -> squid_food())) -> squid_food().
feed_squid(Generator) -> Generator().

main() ->
    %% The zoo buys a feeder for both the red panda and squid
    FeederRP = fun() -> bamboo end,
    FeederSquid = fun() -> sperm_whale end,
    %% CRITICAL POINT %%
    %% This should not be right!
    feed_squid(FeederRP),
    %% Time to feed them!
    feed_squid(FeederSquid)

Then one that has no warnings:

    [...]
    %% CRITICAL POINT %%
    %% Time to feed them!
    feed_squid(FeederSquid)
    %% This should not be right!
    feed_squid(FeederRP).

The dialog box warnings for the version that it can catch are as follows:

zoo.erl:7: The contract zoo:feed_squid(fun(() -> squid_food())) -> squid_food() cannot be right because the inferred return for feed_squid(FeederRP::fun(() -> 'bamboo')) on line 15 is 'bamboo'
zoo.erl:10: Function main/0 has no local return

... , .

, , Dialyzer , feed_squid/1 fun() -> bamboo fun() -> none() (, , , feed_squid/1, ). , , Dialyzer , .

Dialyzer , -Woverspecs:

zoo.erl:7: Type specification zoo:feed_squid(fun(() -> squid_food())) -> squid_food() is a subtype of the success typing: zoo:feed_squid(fun(() -> 'bamboo' | 'sperm_whale')) -> 'bamboo' | 'sperm_whale'

... , ! /, , , , Dialyzer . , , , Dialyzer ( ).

: !

, , , . feed_squid/1 (fun() -> any()) -> any(). feed_squid/1 FeederRP , , bamboo, main/0. feed_squid/1 FeederSquid sperm_whale, FeederSquid, FeederRP sperm_whale OR bamboo. FeederRP, - sperm_whale OR bamboo. spec, promises, sperm_whale, Dialyzer . , fun() -> bamboo | sperm_whale , fun() -> bamboo, fun() -> bamboo. spec (fun() -> sperm_whale), Dialyzer , fun() -> none(). feed_squid/1 (, Dialyzer ), , sperm_whale, !

"" , , , , , "" fun(...) -> none().

+4

(, . ).

"" , . " " "", "", . . . , , , .

:)

. : " , ?". feed_red_panda/1 , fun (() -> A) A. feed_red_panda(fun erlang:now/0), . , main/0 . .

: " ?". , . . , , . : food(red_panda()), food(squid()). , , . , , . , , , - . -, .

, , spec-. , . , . , .

, , . , , . , .

, " ". . . , , Erlang . , .

+2

All Articles