diff options
author | Magnus Lång <[email protected]> | 2016-02-28 00:25:32 +0100 |
---|---|---|
committer | Hans Bolinder <[email protected]> | 2016-04-28 16:16:09 +0200 |
commit | e16ff38bd1f235eefa0fbee87e9ee6dd0fc94e2a (patch) | |
tree | bc0c8cd399c0f8e070427eee80b4dff3b140148d | |
parent | ebd967522612333e52a884181e6132b1ba7e5239 (diff) | |
download | otp-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.erl | 44 | ||||
-rw-r--r-- | lib/dialyzer/test/map_SUITE_data/results/guard_update | 3 |
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} |