From 3d05725ceb26611ac8c19cc01df715089dc322d5 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 1 Sep 2017 11:44:36 +0200 Subject: dialyzer: Modify handling of singleton map key types The test case loop.erl shows that there is a problem with certain singleton key types. Here the internal representation toggles between #{a | b => ...} and #{a => ..., b => ...} The choice is to turn #{a | b => ...} into #{a => ..., b => ...} early (t_from_form()). The aim is to keep as much info as possible (in pairs). However, including complex singleton keys (tuples, maps) in this scheme is potentially too costly, and a bit complicated. So one more choice is made: let atoms and number (and nothing else) be singleton types, and let complex keys go into the default key. --- lib/dialyzer/test/map_SUITE_data/results/loop | 4 + .../test/map_SUITE_data/results/opaque_key | 1 + lib/dialyzer/test/map_SUITE_data/src/loop.erl | 92 ++++++++++++++++++++++ .../src/opaque_key/opaque_key_adt.erl | 2 +- 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 lib/dialyzer/test/map_SUITE_data/results/loop create mode 100644 lib/dialyzer/test/map_SUITE_data/src/loop.erl (limited to 'lib/dialyzer/test/map_SUITE_data') diff --git a/lib/dialyzer/test/map_SUITE_data/results/loop b/lib/dialyzer/test/map_SUITE_data/results/loop new file mode 100644 index 0000000000..2e956a5709 --- /dev/null +++ b/lib/dialyzer/test/map_SUITE_data/results/loop @@ -0,0 +1,4 @@ + +loop.erl:63: The call loop:start_timer(#loop{state::'idle' | 'waiting',queues::#{'category1'=>#queue{limit::non_neg_integer(),buffer::[any()]}, 'category2'=>#queue{limit::non_neg_integer(),buffer::[any()]}},counters::#{'counter1':=10, 2:=10}}) does not have a term of type #loop{state::'idle' | 'waiting',timer::timer:tref(),queues::#{'category1'=>#queue{limit::non_neg_integer(),buffer::[any()]}, 'category2'=>#queue{limit::non_neg_integer(),buffer::[any()]}},counters::#{'counter1'=>non_neg_integer(), 2=>non_neg_integer()}} (with opaque subterms) as 1st argument +loop.erl:67: Function wait/1 has no local return +loop.erl:85: Record construction #loop{state::'idle' | 'waiting',timer::{'error',_} | {'ok',timer:tref()},queues::#{'category1'=>#queue{limit::non_neg_integer(),buffer::[any()]}, 'category2'=>#queue{limit::non_neg_integer(),buffer::[any()]}},counters::#{'counter1'=>non_neg_integer(), 2=>non_neg_integer()}} violates the declared type of field timer::'undefined' | timer:tref() diff --git a/lib/dialyzer/test/map_SUITE_data/results/opaque_key b/lib/dialyzer/test/map_SUITE_data/results/opaque_key index 2ae0e0c5c6..8d6379b5e0 100644 --- a/lib/dialyzer/test/map_SUITE_data/results/opaque_key +++ b/lib/dialyzer/test/map_SUITE_data/results/opaque_key @@ -1,4 +1,5 @@ +opaque_key_adt.erl:35: Invalid type specification for function opaque_key_adt:s2/0. The success typing is () -> #{3:='a'} opaque_key_adt.erl:41: Invalid type specification for function opaque_key_adt:s4/0. The success typing is () -> #{1:='a'} opaque_key_adt.erl:44: Invalid type specification for function opaque_key_adt:s5/0. The success typing is () -> #{2:=3} opaque_key_adt.erl:56: Invalid type specification for function opaque_key_adt:smt1/0. The success typing is () -> #{3:='a'} diff --git a/lib/dialyzer/test/map_SUITE_data/src/loop.erl b/lib/dialyzer/test/map_SUITE_data/src/loop.erl new file mode 100644 index 0000000000..c861052d9f --- /dev/null +++ b/lib/dialyzer/test/map_SUITE_data/src/loop.erl @@ -0,0 +1,92 @@ +-module(loop). + +-export([timeout/2]). + +-export([idle/2, waiting/2]). + +-type request_category() :: category1 | category2. + +-type counter() :: counter1 | 2. + +-type counters() :: #{counter() => non_neg_integer()}. + +-record(queue, {limit = 0 :: non_neg_integer(), + buffer = [] :: list()}). + +-type request_queues() :: #{request_category() => #queue{}}. + +-record(?MODULE, + {state = idle :: idle | waiting, + timer = undefined :: undefined | timer:tref(), + queues = #{category1 => #queue{}, + category2 => #queue{}} :: request_queues(), + counters = new_counters() :: counters()}). +-spec timeout(Ref, Timer :: timer:tref()) -> {noreply, Ref}. +timeout(Ref, Timer) -> + handle_message(Ref, {timeout, Timer}). + +-type message() :: {reset, request_category()} + | {timeout, timer:tref()}. + +-spec handle_message(Ref, Message :: message()) -> + {reply, boolean(), Ref} | {noreply, Ref}. +handle_message(Ref, Msg) -> + MV = #?MODULE{state = State} = get(mv), + case apply(?MODULE, State, [Msg, MV]) of + {reply, Result, NewMV} -> + put(mv, NewMV), + {reply, Result, Ref}; + {noreply, NewMV} -> + put(mv, NewMV), + {noreply, Ref} + end. + +-spec idle(Message :: message(), #?MODULE{}) -> + {reply, boolean(), #?MODULE{}} | {noreply, #?MODULE{}}. +idle({reset, Category}, MV = #?MODULE{queues = Queues}) -> + case Queues of + #{Category := #queue{limit = 0}} -> + {reply, false, MV}; + _ -> + wait(MV) + end; +idle(_, MV) -> + {noreply, MV}. + +-spec waiting(Message :: message(), #?MODULE{}) -> + {reply, boolean(), #?MODULE{}} | {noreply, #?MODULE{}}. +waiting({reset, _Category}, MV = #?MODULE{}) -> + NewMV = stop_timer(MV), + {noreply, NewMV#?MODULE{state = idle}}; +waiting({timeout, Timer}, #?MODULE{timer = Timer} = MV) -> + %% The opaque warning is an effect of the call to timer:send_after(). + {noreply, start_timer(MV#?MODULE{timer = undefined, + counters = new_counters()})}. + +-spec wait(#?MODULE{}) -> {noreply, #?MODULE{}}. +wait(MV) -> + {noreply, start_timer(MV#?MODULE{state = waiting})}. + +-spec stop_timer(#?MODULE{}) -> #?MODULE{}. +stop_timer(MV) -> + case MV#?MODULE.timer of + undefined -> + MV; + Timer -> + timer:cancel(Timer), + MV#?MODULE{timer = undefined} + end. + +-spec start_timer(MV :: #?MODULE{}) -> #?MODULE{}. +start_timer(MV) -> + case MV#?MODULE.timer of + undefined -> + %% Note: timer:send_after() returns {ok, TRef} | {error, _}. + MV#?MODULE{timer = timer:send_after(1000, ?MODULE)}; + _Timer -> + start_timer(stop_timer(MV)) + end. + +-spec new_counters() -> counters(). +new_counters() -> + #{counter1 => 10, 2 => 10}. diff --git a/lib/dialyzer/test/map_SUITE_data/src/opaque_key/opaque_key_adt.erl b/lib/dialyzer/test/map_SUITE_data/src/opaque_key/opaque_key_adt.erl index b98c713c6b..9228cfa413 100644 --- a/lib/dialyzer/test/map_SUITE_data/src/opaque_key/opaque_key_adt.erl +++ b/lib/dialyzer/test/map_SUITE_data/src/opaque_key/opaque_key_adt.erl @@ -33,7 +33,7 @@ s0() -> #{}. s1() -> #{3 => a}. -spec s2() -> s(atom() | 3). -s2() -> #{3 => a}. %% Contract breakage (not found) +s2() -> #{3 => a}. %% Contract breakage -spec s3() -> s(atom() | 3). s3() -> #{3 => 5, a => 6, 7 => 8}. -- cgit v1.2.3