From 56f9f62b59eda9dcc36bd47efef68b0243e0cfd1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= <egil@erlang.org>
Date: Fri, 28 Nov 2014 02:42:46 +0100
Subject: compiler: Coalesce map keys in dialyzer mode

This fixes a regression introduced in commit 805f9c89fc01220bc1bb0f27e1b68fd4eca688ba
The problem occured with map keys compiled with dialyzer option turned on.

In OTP 17, map keys needs to be literals.
---
 lib/compiler/src/v3_core.erl | 57 +++++++++++++++++++++++++++++++-------------
 1 file changed, 41 insertions(+), 16 deletions(-)

(limited to 'lib')

diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index caf5298d38..59ec0d4199 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -786,23 +786,42 @@ is_valid_map_src(_)         -> false.
 map_pair_list(Es, St) ->
     foldr(fun
 	    ({map_field_assoc,L,K0,V0}, {Ces,Esp,St0}) ->
-		{K,Ep0,St1} = safe(K0, St0),
-		ok = ensure_valid_map_key(K),
+		{K1,Ep0,St1} = safe(K0, St0),
+		K = ensure_valid_map_key(K1),
 		{V,Ep1,St2} = safe(V0, St1),
 		A = lineno_anno(L, St2),
 		Pair = #c_map_pair{op=#c_literal{val=assoc},anno=A,key=K,val=V},
 		{[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2};
 	    ({map_field_exact,L,K0,V0}, {Ces,Esp,St0}) ->
-		{K,Ep0,St1} = safe(K0, St0),
-		ok = ensure_valid_map_key(K),
+		{K1,Ep0,St1} = safe(K0, St0),
+		K = ensure_valid_map_key(K1),
 		{V,Ep1,St2} = safe(V0, St1),
 		A = lineno_anno(L, St2),
 		Pair = #c_map_pair{op=#c_literal{val=exact},anno=A,key=K,val=V},
 		{[Pair|Ces],Ep0 ++ Ep1 ++ Esp,St2}
 	end, {[],[],St}, Es).
 
-ensure_valid_map_key(#c_literal{}) -> ok;
-ensure_valid_map_key(_) -> throw({bad_map,bad_map_key}).
+ensure_valid_map_key(K0) ->
+    case coalesced_map_key(K0) of
+	{ok,K1} -> K1;
+	error   -> throw({bad_map,bad_map_key})
+    end.
+
+coalesced_map_key(#c_literal{}=K) -> {ok,K};
+%% Dialyzer hack redux
+%% DO coalesce tuples and list in maps for dialyzer
+%% Dialyzer tries to break this apart, don't let it
+coalesced_map_key(#c_tuple{}=K) ->
+    case core_lib:is_literal(K) of
+	true  -> {ok,cerl:fold_literal(K)};
+	false -> error
+    end;
+coalesced_map_key(#c_cons{}=K) ->
+    case core_lib:is_literal(K) of
+	true  -> {ok,cerl:fold_literal(K)};
+	false -> error
+    end;
+coalesced_map_key(_) -> error.
 
 %% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}.
 
@@ -1606,17 +1625,23 @@ pattern_alias_map_pair_patterns([Cv]) -> Cv;
 pattern_alias_map_pair_patterns([Cv1,Cv2|Cvs]) ->
     pattern_alias_map_pair_patterns([pat_alias(Cv1,Cv2)|Cvs]).
 
-pattern_map_pair({map_field_exact,L,K,V}, St) ->
+pattern_map_pair({map_field_exact,L,K,V},St) ->
+    #c_map_pair{anno=lineno_anno(L, St),
+		op=#c_literal{val=exact},
+		key=pattern_map_key(K,St),
+		val=pattern(V, St)}.
+
+pattern_map_key(K,St) ->
+    %% Throws 'nomatch' if the key can't be a literal
+    %% this will be a cryptic error message but it is better than nothing
     case expr(K,St) of
-	{#c_literal{}=Key,_,_} ->
-	    #c_map_pair{anno=lineno_anno(L, St),
-			op=#c_literal{val=exact},
-			key=Key,
-			val=pattern(V, St)};
-	_ ->
-	    %% this will throw a cryptic error message
-	    %% but it is better than nothing
-	    throw(nomatch)
+	{Key0,[],_} ->
+	    %% Dialyzer hack redux
+	    case coalesced_map_key(Key0) of
+		{ok,Key1} -> Key1;
+		error     -> throw(nomatch)
+	    end;
+	_ -> throw(nomatch)
     end.
 
 %% pat_bin([BinElement], State) -> [BinSeg].
-- 
cgit v1.2.3


From 6f7dd46137f9651b2f1b1b6f9803cd2434cb87eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= <egil@erlang.org>
Date: Mon, 1 Dec 2014 16:40:13 +0100
Subject: dialyzer: Test recoalesced map keys

---
 lib/dialyzer/test/small_SUITE_data/src/maps1.erl | 36 ++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

(limited to 'lib')

diff --git a/lib/dialyzer/test/small_SUITE_data/src/maps1.erl b/lib/dialyzer/test/small_SUITE_data/src/maps1.erl
index 06ced5b69e..228ffe2c22 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/maps1.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/maps1.erl
@@ -10,6 +10,7 @@
 
 
 -export([recv/3, decode/1]).
+-export([get_my_map/0,is_my_map/1]).
 
 %-record(can_pkt, {id, data :: binary(), timestamp}).
 
@@ -39,3 +40,38 @@ t2() -> ok.
 
 update(#{ id := Id, val := Val } = M, X) when is_integer(Id) ->
     M#{ val := [Val,X] }.
+
+%% key coalescing
+
+-spec get_my_map() -> map().
+
+get_my_map() ->
+    #{labels  => [one, two],
+      number  => 27,
+      [1,2,3] => wer,
+      {4,5,6} => sdf,
+      kvok    => #{
+	<<"wat">> => v,
+	a => qwe,
+	2 => asd,
+	[1,2,3] => wer,
+	{4,5,6} => sdf,
+	"abc" => zxc
+       }
+     }.
+
+-spec is_my_map(map()) -> 'ok'.
+
+is_my_map(#{labels  := [one, two],
+	    number  := 27,
+	    [1,2,3] := wer,
+	    {4,5,6} := sdf,
+	    kvok := #{
+	      <<"wat">> := v,
+	      a := qwe,
+	      2 := asd,
+	      [1,2,3] := wer,
+	      {4,5,6} := sdf,
+	      "abc" := zxc
+	     }
+	   }) -> ok.
-- 
cgit v1.2.3