diff options
author | Björn Gustavsson <bjorn@erlang.org> | 2019-11-25 10:02:46 +0100 |
---|---|---|
committer | Björn Gustavsson <bjorn@erlang.org> | 2020-02-06 12:27:30 +0100 |
commit | 1b23945dd7b3f0c8e88692ddfe41de446230ac5d (patch) | |
tree | b4611c08fb9adf5ba9a2c2c3c49641ad55b59782 | |
parent | 9ab69adb8b58384019b20bb60cf81f4051bc2f9f (diff) | |
download | erlang-1b23945dd7b3f0c8e88692ddfe41de446230ac5d.tar.gz |
Teach Dialyzer to handle the refactored Core Erlang representation
Add support to Dialyzer to handle the primops and the new way
of using letrecs.
-rw-r--r-- | lib/dialyzer/src/Makefile | 1 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer.app.src | 1 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_clean_core.erl | 225 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_dataflow.erl | 76 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_dep.erl | 82 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_typesig.erl | 43 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_utils.erl | 4 | ||||
-rw-r--r-- | lib/hipe/cerl/cerl_closurean.erl | 4 |
8 files changed, 351 insertions, 85 deletions
diff --git a/lib/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile index bddd761705..1f5b308c7d 100644 --- a/lib/dialyzer/src/Makefile +++ b/lib/dialyzer/src/Makefile @@ -53,6 +53,7 @@ MODULES = \ dialyzer_callgraph \ dialyzer_cl \ dialyzer_cl_parse \ + dialyzer_clean_core \ dialyzer_codeserver \ dialyzer_contracts \ dialyzer_dataflow \ diff --git a/lib/dialyzer/src/dialyzer.app.src b/lib/dialyzer/src/dialyzer.app.src index e3a0fc967d..36f5d96ea6 100644 --- a/lib/dialyzer/src/dialyzer.app.src +++ b/lib/dialyzer/src/dialyzer.app.src @@ -28,6 +28,7 @@ dialyzer_callgraph, dialyzer_cl, dialyzer_cl_parse, + dialyzer_clean_core, dialyzer_codeserver, dialyzer_contracts, dialyzer_coordinator, diff --git a/lib/dialyzer/src/dialyzer_clean_core.erl b/lib/dialyzer/src/dialyzer_clean_core.erl new file mode 100644 index 0000000000..d591ad3473 --- /dev/null +++ b/lib/dialyzer/src/dialyzer_clean_core.erl @@ -0,0 +1,225 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +-module(dialyzer_clean_core). +-export([clean/1]). + +-spec clean(cerl:cerl()) -> cerl:cerl(). + +clean(Tree) -> + case cerl:type(Tree) of + apply -> + Op = clean(cerl:apply_op(Tree)), + Args = clean_list(cerl:apply_args(Tree)), + cerl:update_c_apply(Tree, Op, Args); + binary -> + Segments = clean_list(cerl:binary_segments(Tree)), + cerl:update_c_binary(Tree, Segments); + bitstr -> + Val = clean(cerl:bitstr_val(Tree)), + Size = clean(cerl:bitstr_size(Tree)), + Unit = cerl:bitstr_unit(Tree), + Type = cerl:bitstr_type(Tree), + Flags = cerl:bitstr_flags(Tree), + cerl:update_c_bitstr(Tree, Val, Size, Unit, Type, Flags); + 'case' -> + Arg = clean(cerl:case_arg(Tree)), + Clauses = clean_clauses(cerl:case_clauses(Tree)), + cerl:update_c_case(Tree, Arg, Clauses); + call -> + Args = clean_list(cerl:call_args(Tree)), + Module = clean(cerl:call_module(Tree)), + Name = clean(cerl:call_name(Tree)), + cerl:update_c_call(Tree, Module, Name, Args); + 'catch' -> + Body = clean(cerl:catch_body(Tree)), + cerl:update_c_catch(Tree, Body); + cons -> + Hd = clean(cerl:cons_hd(Tree)), + Tl = clean(cerl:cons_tl(Tree)), + cerl:update_c_cons_skel(Tree, Hd, Tl); + 'fun' -> + Body = clean(cerl:fun_body(Tree)), + Vars = cerl:fun_vars(Tree), + cerl:update_c_fun(Tree, Vars, Body); + 'let' -> + Arg = clean(cerl:let_arg(Tree)), + Body = clean(cerl:let_body(Tree)), + Vars = cerl:let_vars(Tree), + cerl:update_c_let(Tree, Vars, Arg, Body); + letrec -> + clean_letrec(Tree); + literal -> + Tree; + module -> + Defs = clean_defs(cerl:module_defs(Tree)), + Name = cerl:module_name(Tree), + Exports = cerl:module_exports(Tree), + Attrs = cerl:module_attrs(Tree), + cerl:update_c_module(Tree, Name, Exports, Attrs, Defs); + primop -> + Args = clean_list(cerl:primop_args(Tree)), + Name = cerl:primop_name(Tree), + cerl:update_c_primop(Tree, Name, Args); + 'receive' -> + Clauses = clean_clauses(cerl:receive_clauses(Tree)), + Timeout = clean(cerl:receive_timeout(Tree)), + Action = clean(cerl:receive_action(Tree)), + cerl:update_c_receive(Tree, Clauses, Timeout, Action); + seq -> + Arg = clean(cerl:seq_arg(Tree)), + Body = clean(cerl:seq_body(Tree)), + cerl:update_c_seq(Tree, Arg, Body); + 'try' -> + Arg = clean(cerl:try_arg(Tree)), + Body = clean(cerl:try_body(Tree)), + Handler = clean(cerl:try_handler(Tree)), + Vs = cerl:try_vars(Tree), + Evs = cerl:try_evars(Tree), + Try = cerl:update_c_try(Tree, Arg, Vs, Body, Evs, Handler), + Try; + tuple -> + Elements = clean_list(cerl:tuple_es(Tree)), + cerl:update_c_tuple_skel(Tree, Elements); + map -> + Arg = clean(cerl:map_arg(Tree)), + Entries = clean_map_pairs(cerl:map_es(Tree)), + cerl:update_c_map(Tree, Arg, Entries); + values -> + Elements = clean_list(cerl:values_es(Tree)), + cerl:update_c_values(Tree, Elements); + var -> + Tree + end. + +clean_letrec(Tree) -> + case lists:member(letrec_goto, cerl:get_ann(Tree)) of + true -> + %% This is a restricted form of letrec used to allow rewriting + %% pattern matching without duplicating code. When a letrec is + %% used in this way, Dialyzer will not be able to infer much + %% type information, so we will need to eliminate the letrec. + [{_Name, Fun}] = cerl:letrec_defs(Tree), + FunBody = cerl:fun_body(Fun), + FunBody1 = clean(FunBody), + Body = clean(cerl:letrec_body(Tree)), + case dialyzer_ignore(Body) of + true -> + %% The body of the letrec directly transfer controls to + %% defined function in the letrec. We only need to keep + %% the body of that function. (This is is the code for + %% a receive construct.) + FunBody1; + false -> + %% The body is non-trivial. Here is an example: + %% + %% letrec 'more_matching'/0 = + %% fun () -> + %% case CaseExpr of . . . end + %% end + %% in case CaseExpr of + %% <<..., Tail>> -> + %% case Tail of + %% <<...>> -> . . . + %% _ -> apply 'more_matching'/0() + %% end + %% _ -> apply 'more_matching'/0() + %% end + %% + %% The clauses that invoke `apply` are marked with + %% a `dialyzer_ignore` annotation to indicate that + %% Dialyzer should ignore them. + %% + %% The example is translated like this: + %% + %% case primop:dialyzer_unknown() of + %% 'a' -> + %% case Var of + %% <<..., Tail>> -> + %% case Tail of + %% <<...>> -> . . . + %% end + %% end + %% 'b' -> + %% %% Body of more_matching/0. + %% case Var of . . . end + %% end + %% + PrimopUnknown = cerl:c_primop(cerl:abstract(dialyzer_unknown), []), + Clauses = [cerl:c_clause([cerl:abstract(a)], Body), + cerl:c_clause([cerl:abstract(b)], FunBody1)], + cerl:c_case(PrimopUnknown, Clauses) + end; + false -> + %% This is a plain letrec. (Originating from a list or binary comprehension.) + Defs = clean_defs(cerl:letrec_defs(Tree)), + Body = clean(cerl:letrec_body(Tree)), + cerl:update_c_letrec(Tree, Defs, Body) + end. + +clean_defs(Defs) -> + [{Name, clean(Fun)} || {Name, Fun} <- Defs]. + +clean_clauses([Clause|Tail]) -> + case clean_clause(Clause) of + ignore -> + %% The clause is either annotated with `dialyzer_ignore` or its + %% body is primop that raises an exception. + clean_clauses(Tail); + Clause1 -> + Tail1 = clean_clauses(Tail), + [Clause1|Tail1] + end; +clean_clauses([]) -> + []. + +clean_clause(Clause) -> + Body = cerl:clause_body(Clause), + case dialyzer_ignore(Clause) orelse is_raising_body(Body) of + true -> + ignore; + false -> + G = clean(cerl:clause_guard(Clause)), + Body1 = clean(Body), + Pats = cerl:clause_pats(Clause), + cerl:update_c_clause(Clause, Pats, G, Body1) + end. + +is_raising_body(Body) -> + case cerl:type(Body) of + primop -> + case cerl:atom_val(cerl:primop_name(Body)) of + match_fail -> true; + raise -> true; + _ -> false + end; + _ -> + false + end. + +clean_list(Trees) -> + [clean(Tree) || Tree <- Trees]. + +clean_map_pairs([Pair|Pairs]) -> + Key = clean(cerl:map_pair_key(Pair)), + Val = clean(cerl:map_pair_val(Pair)), + Pairs1 = clean_map_pairs(Pairs), + Op = cerl:map_pair_op(Pair), + Pair1 = cerl:update_c_map_pair(Pair, Op, Key, Val), + [Pair1|Pairs1]; +clean_map_pairs([]) -> + []. + +dialyzer_ignore(Tree) -> + lists:member(dialyzer_ignore, cerl:get_ann(Tree)). diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index 55c77814f8..cff3981393 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -298,15 +298,36 @@ traverse(Tree, Map, State) -> module -> handle_module(Tree, Map, State); primop -> - Type = - case cerl:atom_val(cerl:primop_name(Tree)) of - match_fail -> t_none(); - raise -> t_none(); - bs_init_writable -> t_from_term(<<>>); - build_stacktrace -> erl_bif_types:type(erlang, build_stacktrace, 0); - Other -> erlang:error({'Unsupported primop', Other}) - end, - {State, Map, Type}; + case cerl:atom_val(cerl:primop_name(Tree)) of + match_fail -> + {State, Map, t_none()}; + raise -> + {State, Map, t_none()}; + bs_init_writable -> + {State, Map, t_from_term(<<>>)}; + build_stacktrace -> + {State, Map, erl_bif_types:type(erlang, build_stacktrace, 0)}; + dialyzer_unknown -> + {State, Map, t_any()}; + recv_peek_message -> + {State, Map, t_product([t_boolean(), t_any()])}; + recv_wait_timeout -> + [Arg] = cerl:primop_args(Tree), + {State1, Map1, TimeoutType} = traverse(Arg, Map, State), + Opaques = State1#state.opaques, + case t_is_atom(TimeoutType, Opaques) andalso + t_atom_vals(TimeoutType, Opaques) =:= ['infinity'] of + true -> + {State1, Map1, t_boolean()}; + false -> + {State1, Map1, t_boolean()} + end; + remove_message -> + {State, Map, t_any()}; + timeout -> + {State, Map, t_any()}; + Other -> erlang:error({'Unsupported primop', Other}) + end; 'receive' -> handle_receive(Tree, Map, State); seq -> @@ -967,7 +988,7 @@ handle_call(Tree, Map, State) -> handle_case(Tree, Map, State) -> Arg = cerl:case_arg(Tree), - Clauses = filter_match_fail(cerl:case_clauses(Tree)), + Clauses = cerl:case_clauses(Tree), {State1, Map1, ArgType} = SMA = traverse(Arg, Map, State), case t_is_none_or_unit(ArgType) of true -> SMA; @@ -1084,7 +1105,7 @@ handle_module(Tree, Map, State) -> %%---------------------------------------- handle_receive(Tree, Map, State) -> - Clauses = filter_match_fail(cerl:receive_clauses(Tree)), + Clauses = cerl:receive_clauses(Tree), Timeout = cerl:receive_timeout(Tree), State1 = case is_race_analysis_enabled(State) of @@ -3019,24 +3040,6 @@ is_lc_simple_list(Tree, TreeType, State) -> andalso t_is_list(TreeType) andalso t_is_simple(t_list_elements(TreeType, Opaques), State). -filter_match_fail([Clause] = Cls) -> - Body = cerl:clause_body(Clause), - case cerl:type(Body) of - primop -> - case cerl:atom_val(cerl:primop_name(Body)) of - match_fail -> []; - raise -> []; - _ -> Cls - end; - _ -> Cls - end; -filter_match_fail([H|T]) -> - [H|filter_match_fail(T)]; -filter_match_fail([]) -> - %% This can actually happen, for example in - %% receive after 1 -> ok end - []. - %%% =========================================================================== %%% %%% The State. @@ -3819,7 +3822,20 @@ find_terminals(Tree) -> %% We cannot make assumptions. Say that both are true. {true, true} end; - 'case' -> find_terminals_list(cerl:case_clauses(Tree)); + 'case' -> + case cerl:case_clauses(Tree) of + [] -> + case lists:member(receive_timeout, cerl:get_ann(Tree)) of + true -> + %% Handle a never ending receive without any + %% clauses specially. (Not sure why.) + {false, true}; + false -> + {false, false} + end; + [_|_] -> + find_terminals_list(cerl:case_clauses(Tree)) + end; 'catch' -> find_terminals(cerl:catch_body(Tree)); clause -> find_terminals(cerl:clause_body(Tree)); cons -> {false, true}; diff --git a/lib/dialyzer/src/dialyzer_dep.erl b/lib/dialyzer/src/dialyzer_dep.erl index 36cdc0876c..c8e9256bc8 100644 --- a/lib/dialyzer/src/dialyzer_dep.erl +++ b/lib/dialyzer/src/dialyzer_dep.erl @@ -87,7 +87,8 @@ traverse(Tree, Out, State, CurrentFun) -> true -> %% Op is a variable and should not be marked as escaping %% based on its use. - OpFuns = case map__lookup(cerl_trees:get_label(Op), Out) of + OpLabel = cerl_trees:get_label(Op), + OpFuns = case map__lookup(OpLabel, Out) of none -> output(none); {value, OF} -> OF end, @@ -96,7 +97,13 @@ traverse(Tree, Out, State, CurrentFun) -> State4 = state__add_deps(CurrentFun, OpFuns, State3), State5 = state__store_callsite(cerl_trees:get_label(Tree), OpFuns, length(Args), State4), - {output(set__singleton(external)), State5} + case state__get_rvals(OpLabel, State5) of + 1 -> + {output(set__singleton(external)), State5}; + NumRvals -> + List = lists:duplicate(NumRvals, output(set__singleton(external))), + {output(List), State5} + end end; binary -> {output(none), State}; @@ -137,9 +144,12 @@ traverse(Tree, Out, State, CurrentFun) -> Vars = cerl:let_vars(Tree), Arg = cerl:let_arg(Tree), Body = cerl:let_body(Tree), - {ArgFuns, State1} = traverse(Arg, Out, State, CurrentFun), + OldNumRvals = state__num_rvals(State), + State1 = state__store_num_rvals(length(Vars), State), + {ArgFuns, State2} = traverse(Arg, Out, State1, CurrentFun), Out1 = bind_list(Vars, ArgFuns, Out), - traverse(Body, Out1, State1, CurrentFun); + State3 = state__store_num_rvals(OldNumRvals, State2), + traverse(Body, Out1, State3, CurrentFun); letrec -> Defs = cerl:letrec_defs(Tree), Body = cerl:letrec_body(Tree), @@ -147,14 +157,15 @@ traverse(Tree, Out, State, CurrentFun) -> state__add_letrecs(cerl_trees:get_label(Var), cerl_trees:get_label(Fun), Acc) end, State, Defs), Out1 = bind_defs(Defs, Out), - State2 = traverse_defs(Defs, Out1, State1, CurrentFun), + NumRvals = state__num_rvals(State1), + State2 = traverse_defs(Defs, Out1, State1, CurrentFun, NumRvals), traverse(Body, Out1, State2, CurrentFun); literal -> {output(none), State}; module -> Defs = cerl:module_defs(Tree), Out1 = bind_defs(Defs, Out), - State1 = traverse_defs(Defs, Out1, State, CurrentFun), + State1 = traverse_defs(Defs, Out1, State, CurrentFun, 1), {output(none), State1}; primop -> Args = cerl:primop_args(Tree), @@ -223,14 +234,15 @@ traverse_list([Tree|Left], Out, State, CurrentFun, Acc) -> traverse_list([], _Out, State, _CurrentFun, Acc) -> {output(lists:reverse(Acc)), State}. -traverse_defs([{_, Fun}|Left], Out, State, CurrentFun) -> - {_, State1} = traverse(Fun, Out, State, CurrentFun), - traverse_defs(Left, Out, State1, CurrentFun); -traverse_defs([], _Out, State, _CurrentFun) -> +traverse_defs([{_, Fun}|Left], Out, State, CurrentFun, NumRvals) -> + State1 = state__store_num_rvals(NumRvals, State), + {_, State2} = traverse(Fun, Out, State1, CurrentFun), + traverse_defs(Left, Out, State2, CurrentFun, NumRvals); +traverse_defs([], _Out, State, _CurrentFun, _NumRvals) -> State. traverse_clauses(Clauses, ArgFuns, Out, State, CurrentFun) -> - case filter_match_fail(Clauses) of + case Clauses of [] -> %% Can happen for example with receives used as timouts. {output(none), State}; @@ -249,24 +261,6 @@ traverse_clauses([Clause|Left], ArgFuns, Out, State, CurrentFun, Acc) -> traverse_clauses([], _ArgFuns, _Out, State, _CurrentFun, Acc) -> {merge_outs(Acc), State}. -filter_match_fail([Clause]) -> - Body = cerl:clause_body(Clause), - case cerl:type(Body) of - primop -> - case cerl:atom_val(cerl:primop_name(Body)) of - match_fail -> []; - raise -> []; - _ -> [Clause] - end; - _ -> [Clause] - end; -filter_match_fail([H|T]) -> - [H|filter_match_fail(T)]; -filter_match_fail([]) -> - %% This can actually happen, for example in - %% receive after 1 -> ok end - []. - remote_call(Tree, ArgFuns, State) -> M = cerl:call_module(Tree), F = cerl:call_name(Tree), @@ -483,12 +477,16 @@ all_vars(Tree, AccIn) -> %% -type local_set() :: 'none' | #set{}. +-type rvals() :: #{label() => non_neg_integer()}. -record(state, {deps :: deps(), esc :: local_set(), calls :: calls(), arities :: dict:dict(label() | 'top', arity()), - letrecs :: letrecs()}). + letrecs :: letrecs(), + num_rvals = 1 :: non_neg_integer(), + rvals = #{} :: rvals() + }). state__new(Tree) -> Exports = set__from_list([X || X <- cerl:module_exports(Tree)]), @@ -526,8 +524,11 @@ state__add_deps(From, #output{type = single, content = To}, %% io:format("Adding deps from ~w to ~w\n", [From, set__to_ordsets(To)]), State#state{deps = map__add(From, To, Map)}. -state__add_letrecs(Var, Fun, #state{letrecs = Map} = State) -> - State#state{letrecs = map__store(Var, Fun, Map)}. +state__add_letrecs(Var, Fun, #state{letrecs = Map, + num_rvals = NumRvals, + rvals = Rvals} = State) -> + State#state{letrecs = map__store(Var, Fun, Map), + rvals = Rvals#{Var => NumRvals}}. state__deps(#state{deps = Deps}) -> Deps. @@ -539,6 +540,10 @@ state__add_esc(#output{content = none}, State) -> State; state__add_esc(#output{type = single, content = Set}, #state{esc = Esc} = State) -> + State#state{esc = set__union(Set, Esc)}; +state__add_esc(#output{type = list, content = [H|T]}, + #state{esc = Esc} = State) -> + #output{type = single, content = Set} = merge_outs(T, H), State#state{esc = set__union(Set, Esc)}. state__esc(#state{esc = Esc}) -> @@ -559,6 +564,19 @@ state__store_callsite(From, To, CallArity, state__calls(#state{calls = Calls}) -> Calls. +state__store_num_rvals(NumRval, State) -> + State#state{num_rvals = NumRval}. + +state__num_rvals(#state{num_rvals = NumRvals}) -> + NumRvals. + +state__get_rvals(FunLabel, #state{rvals = Rvals}) -> + case Rvals of + #{FunLabel := NumRvals} -> NumRvals; + #{} -> 1 + end. + + %%------------------------------------------------------------ %% A test function. Not part of the intended interface. %% diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl index dede475f98..5f40f80ae7 100644 --- a/lib/dialyzer/src/dialyzer_typesig.erl +++ b/lib/dialyzer/src/dialyzer_typesig.erl @@ -296,7 +296,7 @@ traverse(Tree, DefinedVars, State) -> {State4, mk_var(Tree)}; 'case' -> Arg = cerl:case_arg(Tree), - Clauses = filter_match_fail(cerl:case_clauses(Tree)), + Clauses = cerl:case_clauses(Tree), {State1, ArgVar} = traverse(Arg, DefinedVars, State), handle_clauses(Clauses, mk_var(Tree), ArgVar, DefinedVars, State1); call -> @@ -423,10 +423,31 @@ traverse(Tree, DefinedVars, State) -> Type = erl_bif_types:type(erlang, build_stacktrace, 0), State1 = state__store_conj(V, sub, Type, State), {State1, V}; + dialyzer_unknown -> + %% See dialyzer_clean_core:clean_letrec/1. + {State, mk_var(Tree)}; + recv_peek_message -> + {State1, Vars} = state__mk_vars(2, State), + {State1, t_product(Vars)}; + recv_wait_timeout -> + [Timeout] = cerl:primop_args(Tree), + case cerl:is_c_atom(Timeout) andalso + cerl:atom_val(Timeout) =:= infinity of + true -> + {State, t_none()}; + false -> + {State1, TimeoutVar} = traverse(Timeout, DefinedVars, State), + State2 = state__store_conj(TimeoutVar, sub, t_timeout(), State1), + {State2, mk_var(Tree)} + end; + remove_message -> + {State, t_any()}; + timeout -> + {State, t_any()}; Other -> erlang:error({'Unsupported primop', Other}) end; 'receive' -> - Clauses = filter_match_fail(cerl:receive_clauses(Tree)), + Clauses = cerl:receive_clauses(Tree), Timeout = cerl:receive_timeout(Tree), case (cerl:is_c_atom(Timeout) andalso (cerl:atom_val(Timeout) =:= infinity)) of @@ -829,24 +850,6 @@ get_plt_constr(MFA, Dst, ArgVars, State) -> get_contract_return(C, ArgTypes) -> dialyzer_contracts:get_contract_return(C, ArgTypes). -filter_match_fail([Clause] = Cls) -> - Body = cerl:clause_body(Clause), - case cerl:type(Body) of - primop -> - case cerl:atom_val(cerl:primop_name(Body)) of - match_fail -> []; - raise -> []; - _ -> Cls - end; - _ -> Cls - end; -filter_match_fail([H|T]) -> - [H|filter_match_fail(T)]; -filter_match_fail([]) -> - %% This can actually happen, for example in - %% receive after 1 -> ok end - []. - %% If there is a significant number of clauses, we cannot apply the %% list subtraction scheme since it causes the analysis to be too %% slow. Typically, this only affects automatically generated files. diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl index 245c099fef..f679f146cc 100644 --- a/lib/dialyzer/src/dialyzer_utils.erl +++ b/lib/dialyzer/src/dialyzer_utils.erl @@ -99,7 +99,7 @@ get_core_from_src(File, Opts) -> case compile:noenv_file(File, Opts ++ src_compiler_opts()) of error -> {error, []}; {error, Errors, _} -> {error, format_errors(Errors)}; - {ok, _, Core} -> {ok, Core} + {ok, _, Core} -> {ok, dialyzer_clean_core:clean(Core)} end. -type get_core_from_beam_ret() :: {'ok', cerl:c_module()} | {'error', string()}. @@ -116,7 +116,7 @@ get_core_from_beam(File, Opts) -> {ok, {Module, [{debug_info, {debug_info_v1, Backend, Metadata}}]}} -> case Backend:debug_info(core_v1, Module, Metadata, Opts ++ src_compiler_opts()) of {ok, Core} -> - {ok, Core}; + {ok, dialyzer_clean_core:clean(Core)}; {error, _} -> {error, " Could not get Core Erlang code for: " ++ File ++ "\n"} end; diff --git a/lib/hipe/cerl/cerl_closurean.erl b/lib/hipe/cerl/cerl_closurean.erl index a2bd7fe0f0..583c5d624a 100644 --- a/lib/hipe/cerl/cerl_closurean.erl +++ b/lib/hipe/cerl/cerl_closurean.erl @@ -797,7 +797,8 @@ take_work({Queue0, Set0}) -> -spec is_escape_op(atom(), arity()) -> boolean(). -is_escape_op(match_fail, 1) -> false; +is_escape_op(match_fail, 1) -> false; +is_escape_op(recv_wait_timeout, 1) -> false; is_escape_op(F, A) when is_atom(F), is_integer(A) -> true. -spec is_escape_op(atom(), atom(), arity()) -> boolean(). @@ -814,6 +815,7 @@ is_escape_op(M, F, A) when is_atom(M), is_atom(F), is_integer(A) -> true. -spec is_literal_op(atom(), arity()) -> boolean(). +is_literal_op(recv_wait_timeout, 1) -> true; is_literal_op(match_fail, 1) -> true; is_literal_op(F, A) when is_atom(F), is_integer(A) -> false. |