aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMagnus Lång <[email protected]>2016-02-28 00:25:32 +0100
committerHans Bolinder <[email protected]>2016-04-28 16:16:09 +0200
commite16ff38bd1f235eefa0fbee87e9ee6dd0fc94e2a (patch)
treebc0c8cd399c0f8e070427eee80b4dff3b140148d
parentebd967522612333e52a884181e6132b1ba7e5239 (diff)
downloadotp-e16ff38bd1f235eefa0fbee87e9ee6dd0fc94e2a.tar.gz
otp-e16ff38bd1f235eefa0fbee87e9ee6dd0fc94e2a.tar.bz2
otp-e16ff38bd1f235eefa0fbee87e9ee6dd0fc94e2a.zip
dialyzer_dataflow: Fix try in guards
The assumption that 'try' nodes were only used to wrap entire guards is no longer true. We're still swallowing warnings when the handler returns successfully. Unfortunately, bind_guard/5 would need to be refactored to return a new state in order to generate those warnings.
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl44
-rw-r--r--lib/dialyzer/test/map_SUITE_data/results/guard_update3
2 files changed, 42 insertions, 5 deletions
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 9827fda6e3..f0fa9fbb4e 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -1794,9 +1794,49 @@ bind_guard(Guard, Map, Env, Eval, State) ->
'try' ->
Arg = cerl:try_arg(Guard),
[Var] = cerl:try_vars(Guard),
+ EVars = cerl:try_evars(Guard),
%%?debug("Storing: ~w\n", [Var]),
- NewEnv = dict:store(get_label(Var), Arg, Env),
- bind_guard(cerl:try_body(Guard), Map, NewEnv, Eval, State);
+ Map1 = join_maps_begin(Map),
+ Map2 = mark_as_fresh(EVars, Map1),
+ %% Visit handler first so we know if it should be ignored
+ {{HandlerMap, HandlerType}, HandlerE} =
+ try {bind_guard(cerl:try_handler(Guard), Map2, Env, Eval, State), none}
+ catch throw:HE ->
+ {{Map2, t_none()}, HE}
+ end,
+ BodyEnv = dict:store(get_label(Var), Arg, Env),
+ Wanted = case Eval of pos -> t_atom(true); neg -> t_atom(false);
+ dont_know -> t_any() end,
+ case t_is_none(t_inf(HandlerType, Wanted)) of
+ %% Handler won't save us; pretend it does not exist
+ true -> bind_guard(cerl:try_body(Guard), Map, BodyEnv, Eval, State);
+ false ->
+ {{BodyMap, BodyType}, BodyE} =
+ try {bind_guard(cerl:try_body(Guard), Map1, BodyEnv,
+ Eval, State), none}
+ catch throw:BE ->
+ {{Map1, t_none()}, BE}
+ end,
+ Map3 = join_maps_end([BodyMap, HandlerMap], Map1),
+ case t_is_none(Sup = t_sup(BodyType, HandlerType)) of
+ true ->
+ %% Pick a reason. N.B. We assume that the handler is always
+ %% compiler-generated if the body is; that way, we won't need to
+ %% check.
+ Fatality = case {BodyE, HandlerE} of
+ {{fatal_fail, _}, _} -> fatal_fail;
+ {_, {fatal_fail, _}} -> fatal_fail;
+ _ -> fail
+ end,
+ throw({Fatality,
+ case {BodyE, HandlerE} of
+ {{_, Rsn}, _} when Rsn =/= none -> Rsn;
+ {_, {_,Rsn}} -> Rsn;
+ _ -> none
+ end});
+ false -> {Map3, Sup}
+ end
+ end;
tuple ->
Es0 = cerl:tuple_es(Guard),
{Map1, Es} = bind_guard_list(Es0, Map, Env, dont_know, State),
diff --git a/lib/dialyzer/test/map_SUITE_data/results/guard_update b/lib/dialyzer/test/map_SUITE_data/results/guard_update
index f6200ae2af..e4bc892195 100644
--- a/lib/dialyzer/test/map_SUITE_data/results/guard_update
+++ b/lib/dialyzer/test/map_SUITE_data/results/guard_update
@@ -1,7 +1,4 @@
-guard_update.erl:12: Function t2/0 has no local return
-guard_update.erl:15: Clause guard cannot succeed. The variable M was matched against the type #{'a':=2}
-guard_update.erl:15: Function f2/1 has no local return
guard_update.erl:5: Function t/0 has no local return
guard_update.erl:6: The call guard_update:f(#{'a':=2}) will never return since it differs in the 1st argument from the success typing arguments: (#{'b':=_, ...})
guard_update.erl:8: Clause guard cannot succeed. The variable M was matched against the type #{'a':=2}