aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl18
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl16
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/weird6
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning1.erl18
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning2.erl14
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning3.erl19
-rw-r--r--lib/hipe/cerl/erl_types.erl33
7 files changed, 102 insertions, 22 deletions
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index ce292e1140..83ce875280 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -1351,8 +1351,6 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State, Warns) ->
{Msg, Force} =
case t_is_none(ArgType0) of
true ->
- PatString = format_patterns(Pats),
- PatTypes = [PatString, format_type(OrigArgType, State1)],
%% See if this is covered by an earlier clause or if it
%% simply cannot match
OrigArgTypes =
@@ -1360,14 +1358,24 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, State, Warns) ->
true -> Any = t_any(), [Any || _ <- Pats];
false -> t_to_tlist(OrigArgType)
end,
+ PatString = format_patterns(Pats),
+ ArgTypeString = format_type(OrigArgType, State1),
+ BindResOrig =
+ bind_pat_vars(Pats, OrigArgTypes, [], Map1, State1),
Tag =
- case bind_pat_vars(Pats, OrigArgTypes, [], Map1, State1) of
+ case BindResOrig of
{error, bind, _, _, _} -> pattern_match;
{error, record, _, _, _} -> record_match;
{error, opaque, _, _, _} -> opaque_match;
{_, _} -> pattern_match_cov
end,
- {{Tag, PatTypes}, false};
+ PatTypes = case BindResOrig of
+ {error, opaque, _, _, OpaqueType} ->
+ [PatString, ArgTypeString,
+ format_type(OpaqueType, State1)];
+ _ -> [PatString, ArgTypeString]
+ end,
+ {{Tag, PatTypes}, false};
false ->
%% Try to find out if this is a default clause in a list
%% comprehension and supress this. A real Hack(tm)
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index e8d9c06799..4d650592e2 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -2088,8 +2088,6 @@ v2_solve_disjunct(Disj, Map, V2State0) ->
var_occurs_everywhere(V, Masks, NotFailed) ->
ordsets:is_subset(NotFailed, get_mask(V, Masks)).
--dialyzer({no_improper_lists, [v2_solve_disj/10, v2_solve_conj/12]}).
-
v2_solve_disj([I|Is], [C|Cs], I, Map0, V2State0, UL, MapL, Eval, Uneval,
Failed0) ->
Id = C#constraint_list.id,
@@ -2108,10 +2106,10 @@ v2_solve_disj([I|Is], [C|Cs], I, Map0, V2State0, UL, MapL, Eval, Uneval,
end;
v2_solve_disj([], [], _I, _Map, V2State, UL, MapL, Eval, Uneval, Failed) ->
{ok, V2State, lists:reverse(Eval), UL, MapL, lists:reverse(Uneval), Failed};
-v2_solve_disj(every_i, Cs, I, Map, V2State, UL, MapL, Eval, Uneval, Failed) ->
+v2_solve_disj([every_i], Cs, I, Map, V2State, UL, MapL, Eval, Uneval, Failed) ->
NewIs = case Cs of
[] -> [];
- _ -> [I|every_i]
+ _ -> [I, every_i]
end,
v2_solve_disj(NewIs, Cs, I, Map, V2State, UL, MapL, Eval, Uneval, Failed);
v2_solve_disj(Is, [C|Cs], I, Map, V2State, UL, MapL, Eval, Uneval0, Failed) ->
@@ -2207,11 +2205,11 @@ v2_solve_conj([], _Cs, _I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
v2_solve_conj(NewFlags, Cs, 1, Map, Conj, IsFlat, V2State,
[], [], [U|VarsUp], Map, NewFlags)
end;
-v2_solve_conj(every_i, Cs, I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
+v2_solve_conj([every_i], Cs, I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
LastMap, LastFlags) ->
NewIs = case Cs of
[] -> [];
- _ -> [I|every_i]
+ _ -> [I, every_i]
end,
v2_solve_conj(NewIs, Cs, I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp,
LastMap, LastFlags);
@@ -2233,8 +2231,8 @@ add_mask_to_flags(Flags, [Im|M], I, L) when I > Im ->
add_mask_to_flags(Flags, [_|M], _I, L) ->
{umerge_mask(Flags, M), lists:reverse(L)}.
-umerge_mask(every_i, _F) ->
- every_i;
+umerge_mask([every_i]=Is, _F) ->
+ Is;
umerge_mask(Is, F) ->
lists:umerge(Is, F).
@@ -2250,7 +2248,7 @@ get_flags(#v2_state{constr_data = ConData}=V2State0, C) ->
error ->
?debug("get_flags Id=~w Flags=all ~w\n", [Id, length(Cs)]),
V2State = V2State0#v2_state{constr_data = maps:put(Id, {[],[]}, ConData)},
- {V2State, every_i};
+ {V2State, [every_i]};
{ok, failed} ->
{V2State0, failed_list};
{ok, {Part,U}} when U =/= [] ->
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/weird b/lib/dialyzer/test/opaque_SUITE_data/results/weird
new file mode 100644
index 0000000000..d7f57cd152
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/weird
@@ -0,0 +1,6 @@
+
+weird_warning1.erl:15: Matching of pattern {'a', Dict} tagged with a record name violates the declared type of #b{q::queue:queue(_)}
+weird_warning2.erl:13: Matching of pattern <{'b', Queue}, Key, Value> tagged with a record name violates the declared type of <#a{d::dict:dict(_,_)},'my_key','my_value'>
+weird_warning3.erl:14: The call weird_warning3:add_element(#a{d::queue:queue(_)},'my_key','my_value') does not have a term of type #a{d::dict:dict(_,_)} | #b{q::queue:queue(_)} (with opaque subterms) as 1st argument
+weird_warning3.erl:16: The attempt to match a term of type #a{d::queue:queue(_)} against the pattern {'a', Dict} breaks the opacity of queue:queue(_)
+weird_warning3.erl:18: Matching of pattern {'b', Queue} tagged with a record name violates the declared type of #a{d::queue:queue(_)}
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning1.erl b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning1.erl
new file mode 100644
index 0000000000..094138e72b
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning1.erl
@@ -0,0 +1,18 @@
+-module(weird_warning1).
+-export([public_func/0]).
+
+-record(a, {
+ d = dict:new() :: dict:dict()
+ }).
+
+-record(b, {
+ q = queue:new() :: queue:queue()
+ }).
+
+public_func() ->
+ add_element(#b{}, my_key, my_value).
+
+add_element(#a{d = Dict}, Key, Value) ->
+ dict:store(Key, Value, Dict);
+add_element(#b{q = Queue}, Key, Value) ->
+ queue:in({Key, Value}, Queue).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning2.erl b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning2.erl
new file mode 100644
index 0000000000..4e4512157b
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning2.erl
@@ -0,0 +1,14 @@
+-module(weird_warning2).
+-export([public_func/0]).
+
+-record(a, {d = dict:new() :: dict:dict()}).
+
+-record(b, {q = queue:new() :: queue:queue()}).
+
+public_func() ->
+ add_element(#a{}, my_key, my_value).
+
+add_element(#a{d = Dict}, Key, Value) ->
+ dict:store(Key, Value, Dict);
+add_element(#b{q = Queue}, Key, Value) ->
+ queue:in({Key, Value}, Queue).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning3.erl b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning3.erl
new file mode 100644
index 0000000000..b70ca645cb
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning3.erl
@@ -0,0 +1,19 @@
+-module(weird_warning3).
+-export([public_func/0]).
+
+-record(a, {
+ d = dict:new() :: dict:dict()
+ }).
+
+-record(b, {
+ q = queue:new() :: queue:queue()
+ }).
+
+public_func() ->
+ %% Notice that t_to_string() will create "#a{d::queue:queue(_)}".
+ add_element({a, queue:new()}, my_key, my_value).
+
+add_element(#a{d = Dict}, Key, Value) ->
+ dict:store(Key, Value, Dict);
+add_element(#b{q = Queue}, Key, Value) ->
+ queue:in({Key, Value}, Queue).
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 10e97ff54d..a5a3e8c136 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -526,7 +526,8 @@ list_contains_opaque(List, Opaques) ->
lists:any(fun(E) -> t_contains_opaque(E, Opaques) end, List).
%% t_find_opaque_mismatch/2 of two types should only be used if their
-%% t_inf is t_none() due to some opaque type violation.
+%% t_inf is t_none() due to some opaque type violation. However,
+%% 'error' is returned if a structure mismatch is found.
%%
%% The first argument of the function is the pattern and its second
%% argument the type we are matching against the pattern.
@@ -535,22 +536,30 @@ list_contains_opaque(List, Opaques) ->
'error' | {'ok', erl_type(), erl_type()}.
t_find_opaque_mismatch(T1, T2, Opaques) ->
- t_find_opaque_mismatch(T1, T2, T2, Opaques).
+ catch t_find_opaque_mismatch(T1, T2, T2, Opaques).
t_find_opaque_mismatch(?any, _Type, _TopType, _Opaques) -> error;
-t_find_opaque_mismatch(?none, _Type, _TopType, _Opaques) -> error;
+t_find_opaque_mismatch(?none, _Type, _TopType, _Opaques) -> throw(error);
t_find_opaque_mismatch(?list(T1, Tl1, _), ?list(T2, Tl2, _), TopType, Opaques) ->
t_find_opaque_mismatch_ordlists([T1, Tl1], [T2, Tl2], TopType, Opaques);
t_find_opaque_mismatch(T1, ?opaque(_) = T2, TopType, Opaques) ->
case is_opaque_type(T2, Opaques) of
- false -> {ok, TopType, T2};
+ false ->
+ case t_is_opaque(T1) andalso compatible_opaque_types(T1, T2) =/= [] of
+ true -> error;
+ false -> {ok, TopType, T2}
+ end;
true ->
t_find_opaque_mismatch(T1, t_opaque_structure(T2), TopType, Opaques)
end;
t_find_opaque_mismatch(?opaque(_) = T1, T2, TopType, Opaques) ->
%% The generated message is somewhat misleading:
case is_opaque_type(T1, Opaques) of
- false -> {ok, TopType, T1};
+ false ->
+ case t_is_opaque(T2) andalso compatible_opaque_types(T1, T2) =/= [] of
+ true -> error;
+ false -> {ok, TopType, T1}
+ end;
true ->
t_find_opaque_mismatch(t_opaque_structure(T1), T2, TopType, Opaques)
end;
@@ -566,7 +575,11 @@ t_find_opaque_mismatch(?tuple(_, _, _) = T1, ?tuple_set(_) = T2,
t_find_opaque_mismatch_lists(Tuples1, Tuples2, TopType, Opaques);
t_find_opaque_mismatch(T1, ?union(U2), TopType, Opaques) ->
t_find_opaque_mismatch_lists([T1], U2, TopType, Opaques);
-t_find_opaque_mismatch(_T1, _T2, _TopType, _Opaques) -> error.
+t_find_opaque_mismatch(T1, T2, _TopType, Opaques) ->
+ case t_is_none(t_inf(T1, T2, Opaques)) of
+ false -> error;
+ true -> throw(error)
+ end.
t_find_opaque_mismatch_ordlists(L1, L2, TopType, Opaques) ->
List = lists:zipwith(fun(T1, T2) ->
@@ -575,10 +588,11 @@ t_find_opaque_mismatch_ordlists(L1, L2, TopType, Opaques) ->
t_find_opaque_mismatch_list(List).
t_find_opaque_mismatch_lists(L1, L2, _TopType, Opaques) ->
- List = [t_find_opaque_mismatch(T1, T2, T2, Opaques) || T1 <- L1, T2 <- L2],
+ List = [catch t_find_opaque_mismatch(T1, T2, T2, Opaques) ||
+ T1 <- L1, T2 <- L2],
t_find_opaque_mismatch_list(List).
-t_find_opaque_mismatch_list([]) -> error;
+t_find_opaque_mismatch_list([]) -> throw(error);
t_find_opaque_mismatch_list([H|T]) ->
case H of
{ok, _T1, _T2} -> H;
@@ -3055,6 +3069,9 @@ inf_opaque_types(IsOpaque1, T1, IsOpaque2, T2, Opaques) ->
end
end.
+compatible_opaque_types(?opaque(Es1), ?opaque(Es2)) ->
+ [{O1, O2} || O1 <- Es1, O2 <- Es2, is_compat_opaque_names(O1, O2)].
+
is_compat_opaque_names(Opaque1, Opaque2) ->
#opaque{mod = Mod1, name = Name1, args = Args1} = Opaque1,
#opaque{mod = Mod2, name = Name2, args = Args2} = Opaque2,