From 252df5612032cfba71285b5886937e88ba176529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20L=C3=A5ng?= Date: Sat, 27 Feb 2016 17:56:22 +0100 Subject: erl_types: Add a map type representation The type of a map is represented as a three-tuple {Pairs, DefaultKey, DefaultValue}. DefaultKey and DefaultValue are types. Pairs is a list of three-tuples {Key, mandatory | optional, Value}, where Key and Value are types. All types Key must be singleton, or "known at compile time," as the EEP put it. Examples: #{integer()=>list()} {[], integer(), list()} #{a=>char(), b=>atom()} {[{a, optional, char()}, {b, optional, atom()}], none(), none()} map() {[], any(), any()} A more formal description of the representation and its invariants can be found in erl_types.erl Special thanks to Daniel S. McCain (@dsmccain) that co-authored a very early version of this with me back in April 2014, although only the singleton type logic remains from that version. --- .../test/behaviour_SUITE_data/results/supervisor_incorrect_return | 2 +- lib/dialyzer/test/small_SUITE_data/results/maps1 | 6 +++++- lib/dialyzer/test/small_SUITE_data/results/maps_sum | 2 +- lib/dialyzer/test/small_SUITE_data/src/maps1.erl | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) (limited to 'lib/dialyzer') diff --git a/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return b/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return index 89eb295604..9fc91d4e48 100644 --- a/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return +++ b/lib/dialyzer/test/behaviour_SUITE_data/results/supervisor_incorrect_return @@ -1,2 +1,2 @@ -supervisor_incorrect_return.erl:14: The inferred return type of init/1 ({'ok',{{'one_against_one',0,1},[{_,_,_,_,_,_},...]}}) has nothing in common with 'ignore' | {'ok',{{'one_for_all',non_neg_integer(),pos_integer()} | {'one_for_one',non_neg_integer(),pos_integer()} | {'rest_for_one',non_neg_integer(),pos_integer()} | {'simple_one_for_one',non_neg_integer(),pos_integer()} | #{},[{_,{atom(),atom(),'undefined' | [any()]},'permanent' | 'temporary' | 'transient','brutal_kill' | 'infinity' | non_neg_integer(),'supervisor' | 'worker','dynamic' | [atom()]} | #{}]}}, which is the expected return type for the callback of supervisor behaviour +supervisor_incorrect_return.erl:14: The inferred return type of init/1 ({'ok',{{'one_against_one',0,1},[{_,_,_,_,_,_},...]}}) has nothing in common with 'ignore' | {'ok',{{'one_for_all',non_neg_integer(),pos_integer()} | {'one_for_one',non_neg_integer(),pos_integer()} | {'rest_for_one',non_neg_integer(),pos_integer()} | {'simple_one_for_one',non_neg_integer(),pos_integer()} | #{'intensity'=>non_neg_integer(), 'period'=>pos_integer(), 'strategy'=>'one_for_all' | 'one_for_one' | 'rest_for_one' | 'simple_one_for_one'},[{_,{atom(),atom(),'undefined' | [any()]},'permanent' | 'temporary' | 'transient','brutal_kill' | 'infinity' | non_neg_integer(),'supervisor' | 'worker','dynamic' | [atom()]} | #{'id'=>_, 'modules'=>'dynamic' | [atom()], 'restart'=>'permanent' | 'temporary' | 'transient', 'shutdown'=>'brutal_kill' | 'infinity' | non_neg_integer(), 'start'=>{atom(),atom(),'undefined' | [any()]}, 'type'=>'supervisor' | 'worker'}]}}, which is the expected return type for the callback of supervisor behaviour diff --git a/lib/dialyzer/test/small_SUITE_data/results/maps1 b/lib/dialyzer/test/small_SUITE_data/results/maps1 index e88c91f21f..773f037c47 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/maps1 +++ b/lib/dialyzer/test/small_SUITE_data/results/maps1 @@ -1,4 +1,8 @@ +maps1.erl:20: Function recv/3 has no local return +maps1.erl:25: Function decode/1 has no local return +maps1.erl:31: Function t1/0 has no local return +maps1.erl:40: Function update/2 has no local return maps1.erl:43: Function t3/0 has no local return -maps1.erl:44: The call maps1:foo(~{'greger'=>3, ~{'arne'=>'anka'}~=>45}~,1) will never return since it differs in the 2nd argument from the success typing arguments: (#{},'b') +maps1.erl:46: Function foo/2 has no local return maps1.erl:52: The call Mod:'function'(~{'literal'=>'map'}~,'another_arg') requires that Mod is of type atom() not #{} diff --git a/lib/dialyzer/test/small_SUITE_data/results/maps_sum b/lib/dialyzer/test/small_SUITE_data/results/maps_sum index a19c0bba96..bd192bdb93 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/maps_sum +++ b/lib/dialyzer/test/small_SUITE_data/results/maps_sum @@ -1,4 +1,4 @@ -maps_sum.erl:15: Invalid type specification for function maps_sum:wrong1/1. The success typing is (#{}) -> any() +maps_sum.erl:15: Invalid type specification for function maps_sum:wrong1/1. The success typing is (map()) -> any() maps_sum.erl:26: Function wrong2/1 has no local return maps_sum.erl:27: The call lists:foldl(fun((_,_,_) -> any()),0,Data::any()) will never return since it differs in the 1st argument from the success typing arguments: (fun((_,_) -> any()),any(),[any()]) diff --git a/lib/dialyzer/test/small_SUITE_data/src/maps1.erl b/lib/dialyzer/test/small_SUITE_data/src/maps1.erl index bb2f66a498..597358d16a 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/maps1.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/maps1.erl @@ -21,7 +21,7 @@ recv(Packet, Fun, Chan) -> #{id := Can_id, data := Can_data} = P = decode(Packet), Fun(P). --spec decode(<<_:64,_:_*8>>) -> #{id => <<_:11>>,timestamp => char()}. +-spec decode(<<_:64,_:_*8>>) -> #{id => <<_:11>>,timestamp => char(),_ => _}. decode(<<_:12, Len:4, Timestamp:16, 0:3, Id:11/bitstring, 0:18, Data:Len/binary, _/binary>>) -> #{id => Id, data => Data, timestamp => Timestamp}. -- cgit v1.2.3