File 2018-Remove-unused-cerl_-modules.patch of Package erlang
From c91eaa17e235ec2e9acbb922f2b3bacd792d488f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Fri, 17 Sep 2021 08:39:39 +0200
Subject: [PATCH 18/20] Remove unused cerl_* modules
---
lib/dialyzer/src/Makefile | 4 -
lib/dialyzer/src/cerl_closurean.erl | 856 ------------------------
lib/dialyzer/src/cerl_lib.erl | 457 -------------
lib/dialyzer/src/cerl_pmatch.erl | 620 -----------------
lib/dialyzer/src/cerl_typean.erl | 994 ----------------------------
lib/dialyzer/src/dialyzer.app.src | 6 +-
6 files changed, 1 insertion(+), 2936 deletions(-)
delete mode 100644 lib/dialyzer/src/cerl_closurean.erl
delete mode 100644 lib/dialyzer/src/cerl_lib.erl
delete mode 100644 lib/dialyzer/src/cerl_pmatch.erl
delete mode 100644 lib/dialyzer/src/cerl_typean.erl
diff --git a/lib/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile
index 0847da9d5a..5e69bd147e 100644
--- a/lib/dialyzer/src/Makefile
+++ b/lib/dialyzer/src/Makefile
@@ -47,11 +47,7 @@ DIALYZER_DIR = $(ERL_TOP)/lib/dialyzer
# Target Specs
# ----------------------------------------------------
MODULES = \
- cerl_closurean \
- cerl_lib \
- cerl_pmatch \
cerl_prettypr \
- cerl_typean \
dialyzer \
dialyzer_analysis_callgraph \
dialyzer_behaviours \
diff --git a/lib/dialyzer/src/cerl_closurean.erl b/lib/dialyzer/src/cerl_closurean.erl
deleted file mode 100644
index e4718cb819..0000000000
--- a/lib/dialyzer/src/cerl_closurean.erl
+++ /dev/null
@@ -1,856 +0,0 @@
-%% 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.
-%%
-%% @copyright 2001-2002 Richard Carlsson
-%% @author Richard Carlsson <carlsson.richard@gmail.com>
-%% @doc Closure analysis of Core Erlang programs.
-
-%% TODO: might need a "top" (`any') element for any-length value lists.
-
--module(cerl_closurean).
-
--export([analyze/1, annotate/1]).
-%% The following functions are exported from this module since they
-%% are also used by Dialyzer (file dialyzer/src/dialyzer_dep.erl)
--export([is_escape_op/2, is_escape_op/3, is_literal_op/2, is_literal_op/3]).
-
--import(cerl, [ann_c_apply/3, ann_c_fun/3, ann_c_var/2, apply_args/1,
- apply_op/1, atom_val/1, bitstr_size/1, bitstr_val/1,
- binary_segments/1, c_letrec/2, c_seq/2, c_tuple/1,
- c_nil/0, call_args/1, call_module/1, call_name/1,
- case_arg/1, case_clauses/1, catch_body/1, clause_body/1,
- clause_guard/1, clause_pats/1, cons_hd/1, cons_tl/1,
- fun_body/1, fun_vars/1, get_ann/1, is_c_atom/1,
- let_arg/1, let_body/1, let_vars/1, letrec_body/1,
- letrec_defs/1, module_defs/1, module_defs/1,
- module_exports/1, pat_vars/1, primop_args/1,
- primop_name/1, receive_action/1, receive_clauses/1,
- receive_timeout/1, seq_arg/1, seq_body/1, set_ann/2,
- try_arg/1, try_body/1, try_vars/1, try_evars/1,
- try_handler/1, tuple_es/1, type/1, values_es/1]).
-
--import(cerl_trees, [get_label/1]).
-
-%% ===========================================================================
-
--type label() :: integer() | 'top' | 'external' | 'external_call'.
--type ordset(X) :: [X]. % XXX: TAKE ME OUT
--type labelset() :: ordset(label()).
--type outlist() :: [labelset()] | 'none'.
--type escapes() :: labelset().
-
-%% ===========================================================================
-%% annotate(Tree) -> {Tree1, OutList, Outputs, Escapes, Dependencies, Parents}
-%%
-%% Tree = cerl:cerl()
-%%
-%% Analyzes `Tree' (see `analyze') and appends terms `{callers,
-%% Labels}' and `{calls, Labels}' to the annotation list of each
-%% fun-expression node and apply-expression node of `Tree',
-%% respectively, where `Labels' is an ordered-set list of labels of
-%% fun-expressions in `Tree', possibly also containing the atom
-%% `external', corresponding to the dependency information derived
-%% by the analysis. Any previous such annotations are removed from
-%% `Tree'. `Tree1' is the modified tree; for details on `OutList',
-%% `Outputs' , `Dependencies', `Escapes' and `Parents', see
-%% `analyze'.
-%%
-%% Note: `Tree' must be annotated with labels in order to use this
-%% function; see `analyze' for details.
-
--spec annotate(cerl:cerl()) ->
- {cerl:cerl(), outlist(), dict:dict(),
- escapes(), dict:dict(), dict:dict()}.
-
-annotate(Tree) ->
- {Xs, Out, Esc, Deps, Par} = analyze(Tree),
- F = fun (T) ->
- case type(T) of
- 'fun' ->
- L = get_label(T),
- X = case dict:find(L, Deps) of
- {ok, X1} -> X1;
- error -> set__new()
- end,
- set_ann(T, append_ann(callers,
- set__to_list(X),
- get_ann(T)));
- apply ->
- L = get_label(T),
- X = case dict:find(L, Deps) of
- {ok, X1} -> X1;
- error -> set__new()
- end,
- set_ann(T, append_ann(calls,
- set__to_list(X),
- get_ann(T)));
- _ ->
-%%% set_ann(T, []) % debug
- T
- end
- end,
- {cerl_trees:map(F, Tree), Xs, Out, Esc, Deps, Par}.
-
-append_ann(Tag, Val, [X | Xs]) ->
- if tuple_size(X) >= 1, element(1, X) =:= Tag ->
- append_ann(Tag, Val, Xs);
- true ->
- [X | append_ann(Tag, Val, Xs)]
- end;
-append_ann(Tag, Val, []) ->
- [{Tag, Val}].
-
-%% =====================================================================
-%% analyze(Tree) -> {OutList, Outputs, Escapes, Dependencies, Parents}
-%%
-%% Tree = cerl()
-%% OutList = [LabelSet] | none
-%% Outputs = dict(Label, OutList)
-%% Escapes = LabelSet
-%% Dependencies = dict(Label, LabelSet)
-%% LabelSet = ordset(Label)
-%% Label = integer() | top | external | external_call
-%% Parents = dict(Label, Label)
-%%
-%% Analyzes a module or an expression represented by `Tree'.
-%%
-%% The returned `OutList' is a list of sets of labels of
-%% fun-expressions which correspond to the possible closures in the
-%% value list produced by `Tree' (viewed as an expression; the
-%% "value" of a module contains its exported functions). The atom
-%% `none' denotes missing or conflicting information.
-%%
-%% The atom `external' in any label set denotes any possible
-%% function outside `Tree', including those in `Escapes'. The atom
-%% `top' denotes the top-level expression `Tree'.
-%%
-%% `Outputs' is a mapping from the labels of fun-expressions in
-%% `Tree' to corresponding lists of sets of labels of
-%% fun-expressions (or the atom `none'), representing the possible
-%% closures in the value lists returned by the respective
-%% functions.
-%%
-%% `Dependencies' is a similar mapping from the labels of
-%% fun-expressions and apply-expressions in `Tree' to sets of
-%% labels of corresponding fun-expressions which may contain call
-%% sites of the functions or be called from the call sites,
-%% respectively. Any such label not defined in `Dependencies'
-%% represents an unreachable function or a dead or faulty
-%% application.
-%%
-%% `Escapes' is the set of labels of fun-expressions in `Tree' such
-%% that corresponding closures may be accessed from outside `Tree'.
-%%
-%% `Parents' is a mapping from labels of fun-expressions in `Tree'
-%% to the corresponding label of the nearest containing
-%% fun-expression or top-level expression. This can be used to
-%% extend the dependency graph, for certain analyses.
-%%
-%% Note: `Tree' must be annotated with labels (as done by the
-%% function `cerl_trees:label/1') in order to use this function.
-%% The label annotation `{label, L}' (where L should be an integer)
-%% must be the first element of the annotation list of each node in
-%% the tree. Instances of variables bound in `Tree' which denote
-%% the same variable must have the same label; apart from this,
-%% labels should be unique. Constant literals do not need to be
-%% labeled.
-
--record(state, {vars, out, dep, work, funs, par}).
-
-%% Note: In order to keep our domain simple, we assume that all remote
-%% calls and primops return a single value, if any.
-
-%% We use the terms `closure', `label', `lambda' and `fun-expression'
-%% interchangeably. The exact meaning in each case can be grasped from
-%% the context.
-%%
-%% Rules:
-%% 1) The implicit top level lambda escapes.
-%% 2) A lambda returned by an escaped lambda also escapes.
-%% 3) An escaped lambda can be passed an external lambda as argument.
-%% 4) A lambda passed as argument to an external lambda also escapes.
-%% 5) An argument passed to an unknown operation escapes.
-%% 6) A call to an unknown operation can return an external lambda.
-%%
-%% Escaped lambdas become part of the set of external lambdas, but this
-%% does not need to be represented explicitly.
-
-%% We wrap the given syntax tree T in a fun-expression labeled `top',
-%% which is initially in the set of escaped labels. `top' will be
-%% visited at least once.
-%%
-%% We create a separate function labeled `external', defined as:
-%% "'external'/1 = fun (Escape) -> do apply 'external'/1(apply Escape())
-%% 'external'/1", which will represent any and all functions outside T,
-%% and which returns itself, and contains a recursive call; this models
-%% rules 2 and 4 above. It will be revisited if the set of escaped
-%% labels changes, or at least once. Its parameter `Escape' is a
-%% variable labeled `escape', which will hold the set of escaped labels.
-%% initially it contains `top' and `external'.
-
--spec analyze(cerl:cerl()) ->
- {outlist(), dict:dict(), escapes(), dict:dict(), dict:dict()}.
-
-analyze(Tree) ->
- %% Note that we use different name spaces for variable labels and
- %% function/call site labels, so we can reuse some names here. We
- %% assume that the labeling of Tree only uses integers, not atoms.
- External = ann_c_var([{label, external}], {external, 1}),
- Escape = ann_c_var([{label, escape}], 'Escape'),
- ExtBody = c_seq(ann_c_apply([{label, loop}], External,
- [ann_c_apply([{label, external_call}],
- Escape, [])]),
- External),
- ExtFun = ann_c_fun([{label, external}], [Escape], ExtBody),
-%%% io:fwrite("external fun:\n~s.\n",
-%%% [cerl_prettypr:format(ExtFun, [noann])]),
- Top = ann_c_var([{label, top}], {top, 0}),
- TopFun = ann_c_fun([{label, top}], [], Tree),
-
- %% The "start fun" just makes the initialisation easier. It will not
- %% be marked as escaped, and thus cannot be called.
- StartFun = ann_c_fun([{label, start}], [],
- c_letrec([{External, ExtFun}, {Top, TopFun}],
- c_nil())),
-%%% io:fwrite("start fun:\n~s.\n",
-%%% [cerl_prettypr:format(StartFun, [noann])]),
-
- %% Gather a database of all fun-expressions in Tree and initialise
- %% all their outputs and parameter variables. Bind all module- and
- %% letrec-defined variables to their corresponding labels.
- Funs0 = dict:new(),
- Vars0 = dict:new(),
- Out0 = dict:new(),
- Empty = empty(),
- F = fun (T, S = {Fs, Vs, Os}) ->
- case type(T) of
- 'fun' ->
- L = get_label(T),
- As = fun_vars(T),
- {dict:store(L, T, Fs),
- bind_vars_single(As, Empty, Vs),
- dict:store(L, none, Os)};
- letrec ->
- {Fs, bind_defs(letrec_defs(T), Vs), Os};
- module ->
- {Fs, bind_defs(module_defs(T), Vs), Os};
- _ ->
- S
- end
- end,
- {Funs, Vars, Out} = cerl_trees:fold(F, {Funs0, Vars0, Out0},
- StartFun),
-
- %% Initialise Escape to the minimal set of escaped labels.
- Vars1 = dict:store(escape, from_label_list([top, external]), Vars),
-
- %% Enter the fixpoint iteration at the StartFun.
- St = loop(StartFun, start, #state{vars = Vars1,
- out = Out,
- dep = dict:new(),
- work = init_work(),
- funs = Funs,
- par = dict:new()}),
-%%% io:fwrite("dependencies: ~p.\n",
-%%% [[{X, set__to_list(Y)}
-%%% || {X, Y} <- dict:to_list(St#state.dep)]]),
- {dict:fetch(top, St#state.out),
- tidy_dict([start, top, external], St#state.out),
- dict:fetch(escape, St#state.vars),
- tidy_dict([loop], St#state.dep),
- St#state.par}.
-
-tidy_dict([X | Xs], D) ->
- tidy_dict(Xs, dict:erase(X, D));
-tidy_dict([], D) ->
- D.
-
-loop(T, L, St0) ->
-%%% io:fwrite("analyzing: ~w.\n", [L]),
-%%% io:fwrite("work: ~w.\n", [St0#state.work]),
- Xs0 = dict:fetch(L, St0#state.out),
- {Xs, St1} = visit(fun_body(T), L, St0),
- {W, M} = case equal(Xs0, Xs) of
- true ->
- {St1#state.work, St1#state.out};
- false ->
-%%% io:fwrite("out (~w) changed: ~w <- ~w.\n",
-%%% [L, Xs, Xs0]),
- M1 = dict:store(L, Xs, St1#state.out),
- case dict:find(L, St1#state.dep) of
- {ok, S} ->
- {add_work(set__to_list(S), St1#state.work),
- M1};
- error ->
- {St1#state.work, M1}
- end
- end,
- St2 = St1#state{out = M},
- case take_work(W) of
- {ok, L1, W1} ->
- T1 = dict:fetch(L1, St2#state.funs),
- loop(T1, L1, St2#state{work = W1});
- none ->
- St2
- end.
-
-visit(T, L, St) ->
- case type(T) of
- literal ->
- {[empty()], St};
- var ->
- %% If a variable is not already in the store here, we
- %% initialize it to empty().
- L1 = get_label(T),
- Vars = St#state.vars,
- case dict:find(L1, Vars) of
- {ok, X} ->
- {[X], St};
- error ->
- X = empty(),
- St1 = St#state{vars = dict:store(L1, X, Vars)},
- {[X], St1}
- end;
- 'fun' ->
- %% Must revisit the fun also, because its environment might
- %% have changed. (We don't keep track of such dependencies.)
- L1 = get_label(T),
- St1 = St#state{work = add_work([L1], St#state.work),
- par = set_parent([L1], L, St#state.par)},
- {[singleton(L1)], St1};
- values ->
- visit_list(values_es(T), L, St);
- cons ->
- {Xs, St1} = visit_list([cons_hd(T), cons_tl(T)], L, St),
- {[join_single_list(Xs)], St1};
- tuple ->
- {Xs, St1} = visit_list(tuple_es(T), L, St),
- {[join_single_list(Xs)], St1};
- 'let' ->
- {Xs, St1} = visit(let_arg(T), L, St),
- Vars = bind_vars(let_vars(T), Xs, St1#state.vars),
- visit(let_body(T), L, St1#state{vars = Vars});
- seq ->
- {_, St1} = visit(seq_arg(T), L, St),
- visit(seq_body(T), L, St1);
- apply ->
- {Xs, St1} = visit(apply_op(T), L, St),
- {As, St2} = visit_list(apply_args(T), L, St1),
- case Xs of
- [X] ->
- %% We store the dependency from the call site to the
- %% called functions
- Ls = set__to_list(X),
- Out = St2#state.out,
- Xs1 = join_list([dict:fetch(Lx, Out) || Lx <- Ls]),
- St3 = call_site(Ls, L, As, St2),
- L1 = get_label(T),
- D = dict:store(L1, X, St3#state.dep),
- {Xs1, St3#state{dep = D}};
- none ->
- {none, St2}
- end;
- call ->
- M = call_module(T),
- F = call_name(T),
- {_, St1} = visit(M, L, St),
- {_, St2} = visit(F, L, St1),
- {Xs, St3} = visit_list(call_args(T), L, St2),
- remote_call(M, F, Xs, St3);
- primop ->
- As = primop_args(T),
- {Xs, St1} = visit_list(As, L, St),
- primop_call(atom_val(primop_name(T)), length(Xs), Xs, St1);
- 'case' ->
- {Xs, St1} = visit(case_arg(T), L, St),
- visit_clauses(Xs, case_clauses(T), L, St1);
- 'receive' ->
- X = singleton(external),
- {Xs1, St1} = visit_clauses([X], receive_clauses(T), L, St),
- {_, St2} = visit(receive_timeout(T), L, St1),
- {Xs2, St3} = visit(receive_action(T), L, St2),
- {join(Xs1, Xs2), St3};
- 'try' ->
- {Xs1, St1} = visit(try_arg(T), L, St),
- X = singleton(external),
- Vars = bind_vars(try_vars(T), [X], St1#state.vars),
- {Xs2, St2} = visit(try_body(T), L, St1#state{vars = Vars}),
- Evars = bind_vars(try_evars(T), [X, X, X], St2#state.vars),
- {Xs3, St3} = visit(try_handler(T), L, St2#state{vars = Evars}),
- {join(join(Xs1, Xs2), Xs3), St3};
- 'catch' ->
- {_, St1} = visit(catch_body(T), L, St),
- {[singleton(external)], St1};
- binary ->
- {_, St1} = visit_list(binary_segments(T), L, St),
- {[empty()], St1};
- bitstr ->
- %% The other fields are constant literals.
- {_, St1} = visit(bitstr_val(T), L, St),
- {_, St2} = visit(bitstr_size(T), L, St1),
- {none, St2};
- letrec ->
- %% All the bound funs should be revisited, because the
- %% environment might have changed.
- Ls = [get_label(F) || {_, F} <- letrec_defs(T)],
- St1 = St#state{work = add_work(Ls, St#state.work),
- par = set_parent(Ls, L, St#state.par)},
- visit(letrec_body(T), L, St1);
- module ->
- %% All the exported functions escape, and can thus be passed
- %% any external closures as arguments. We regard a module as
- %% a tuple of function variables in the body of a `letrec'.
- visit(c_letrec(module_defs(T), c_tuple(module_exports(T))),
- L, St)
- end.
-
-visit_clause(T, Xs, L, St) ->
- Vars = bind_pats(clause_pats(T), Xs, St#state.vars),
- {_, St1} = visit(clause_guard(T), L, St#state{vars = Vars}),
- visit(clause_body(T), L, St1).
-
-%% We assume correct value-list typing.
-
-visit_list([T | Ts], L, St) ->
- {Xs, St1} = visit(T, L, St),
- {Xs1, St2} = visit_list(Ts, L, St1),
- X = case Xs of
- [X1] -> X1;
- none -> none
- end,
- {[X | Xs1], St2};
-visit_list([], _L, St) ->
- {[], St}.
-
-visit_clauses(Xs, [T | Ts], L, St) ->
- {Xs1, St1} = visit_clause(T, Xs, L, St),
- {Xs2, St2} = visit_clauses(Xs, Ts, L, St1),
- {join(Xs1, Xs2), St2};
-visit_clauses(_, [], _L, St) ->
- {none, St}.
-
-bind_defs([{V, F} | Ds], Vars) ->
- bind_defs(Ds, dict:store(get_label(V), singleton(get_label(F)),
- Vars));
-bind_defs([], Vars) ->
- Vars.
-
-bind_pats(Ps, none, Vars) ->
- bind_pats_single(Ps, empty(), Vars);
-bind_pats(Ps, Xs, Vars) ->
- if length(Xs) =:= length(Ps) ->
- bind_pats_list(Ps, Xs, Vars);
- true ->
- bind_pats_single(Ps, empty(), Vars)
- end.
-
-bind_pats_list([P | Ps], [X | Xs], Vars) ->
- bind_pats_list(Ps, Xs, bind_vars_single(pat_vars(P), X, Vars));
-bind_pats_list([], [], Vars) ->
- Vars.
-
-bind_pats_single([P | Ps], X, Vars) ->
- bind_pats_single(Ps, X, bind_vars_single(pat_vars(P), X, Vars));
-bind_pats_single([], _X, Vars) ->
- Vars.
-
-bind_vars(Vs, none, Vars) ->
- bind_vars_single(Vs, empty(), Vars);
-bind_vars(Vs, Xs, Vars) ->
- if length(Vs) =:= length(Xs) ->
- bind_vars_list(Vs, Xs, Vars);
- true ->
- bind_vars_single(Vs, empty(), Vars)
- end.
-
-bind_vars_list([V | Vs], [X | Xs], Vars) ->
- bind_vars_list(Vs, Xs, dict:store(get_label(V), X, Vars));
-bind_vars_list([], [], Vars) ->
- Vars.
-
-bind_vars_single([V | Vs], X, Vars) ->
- bind_vars_single(Vs, X, dict:store(get_label(V), X, Vars));
-bind_vars_single([], _X, Vars) ->
- Vars.
-
-%% This handles a call site - adding dependencies and updating parameter
-%% variables with respect to the actual parameters. The 'external'
-%% function is handled specially, since it can get an arbitrary number
-%% of arguments, which must be unified into a single argument.
-
-call_site(Ls, L, Xs, St) ->
-%%% io:fwrite("call site: ~w -> ~w (~w).\n", [L, Ls, Xs]),
- {D, W, V} = call_site(Ls, L, Xs, St#state.dep, St#state.work,
- St#state.vars, St#state.funs),
- St#state{dep = D, work = W, vars = V}.
-
-call_site([external | Ls], T, Xs, D, W, V, Fs) ->
- D1 = add_dep(external, T, D),
- X = join_single_list(Xs),
- case bind_arg(escape, X, V) of
- {V1, true} ->
-%%% io:fwrite("escape changed: ~w <- ~w + ~w.\n",
-%%% [dict:fetch(escape, V1), dict:fetch(escape, V),
-%%% X]),
- {W1, V2} = update_esc(set__to_list(X), W, V1, Fs),
- call_site(Ls, T, Xs, D1, add_work([external], W1), V2, Fs);
- {V1, false} ->
- call_site(Ls, T, Xs, D1, W, V1, Fs)
- end;
-call_site([L | Ls], T, Xs, D, W, V, Fs) ->
- D1 = add_dep(L, T, D),
- Vs = fun_vars(dict:fetch(L, Fs)),
- case bind_args(Vs, Xs, V) of
- {V1, true} ->
- call_site(Ls, T, Xs, D1, add_work([L], W), V1, Fs);
- {V1, false} ->
- call_site(Ls, T, Xs, D1, W, V1, Fs)
- end;
-call_site([], _, _, D, W, V, _) ->
- {D, W, V}.
-
-%% Note that `visit' makes sure all lambdas are visited at least once.
-%% For every called function, we add a dependency from the *called*
-%% function to the function containing the call site.
-
-add_dep(Source, Target, Deps) ->
- case dict:find(Source, Deps) of
- {ok, X} ->
- case set__is_member(Target, X) of
- true ->
- Deps;
- false ->
-%%% io:fwrite("new dep: ~w <- ~w.\n", [Target, Source]),
- dict:store(Source, set__add(Target, X), Deps)
- end;
- error ->
-%%% io:fwrite("new dep: ~w <- ~w.\n", [Target, Source]),
- dict:store(Source, set__singleton(Target), Deps)
- end.
-
-%% If the arity does not match the call, nothing is done here.
-
-bind_args(Vs, Xs, Vars) ->
- if length(Vs) =:= length(Xs) ->
- bind_args(Vs, Xs, Vars, false);
- true ->
- {Vars, false}
- end.
-
-bind_args([V | Vs], [X | Xs], Vars, Ch) ->
- L = get_label(V),
- {Vars1, Ch1} = bind_arg(L, X, Vars, Ch),
- bind_args(Vs, Xs, Vars1, Ch1);
-bind_args([], [], Vars, Ch) ->
- {Vars, Ch}.
-
-bind_args_single(Vs, X, Vars) ->
- bind_args_single(Vs, X, Vars, false).
-
-bind_args_single([V | Vs], X, Vars, Ch) ->
- L = get_label(V),
- {Vars1, Ch1} = bind_arg(L, X, Vars, Ch),
- bind_args_single(Vs, X, Vars1, Ch1);
-bind_args_single([], _, Vars, Ch) ->
- {Vars, Ch}.
-
-bind_arg(L, X, Vars) ->
- bind_arg(L, X, Vars, false).
-
-bind_arg(L, X, Vars, Ch) ->
- X0 = dict:fetch(L, Vars),
- X1 = join_single(X, X0),
- case equal_single(X0, X1) of
- true ->
- {Vars, Ch};
- false ->
-%%% io:fwrite("arg (~w) changed: ~w <- ~w + ~w.\n",
-%%% [L, X1, X0, X]),
- {dict:store(L, X1, Vars), true}
- end.
-
-%% This handles escapes from things like primops and remote calls.
-
-%% escape(none, St) ->
-%% St;
-escape([X], St) ->
- Vars = St#state.vars,
- X0 = dict:fetch(escape, Vars),
- X1 = join_single(X, X0),
- case equal_single(X0, X1) of
- true ->
- St;
- false ->
-%%% io:fwrite("escape changed: ~w <- ~w + ~w.\n", [X1, X0, X]),
-%%% io:fwrite("updating escaping funs: ~w.\n", [set__to_list(X)]),
- Vars1 = dict:store(escape, X1, Vars),
- {W, Vars2} = update_esc(set__to_list(set__subtract(X, X0)),
- St#state.work, Vars1,
- St#state.funs),
- St#state{work = add_work([external], W), vars = Vars2}
- end.
-
-%% For all escaping lambdas, since they might be called from outside the
-%% program, all their arguments may be an external lambda. (Note that we
-%% only have to include the `external' label once per escaping lambda.)
-%% If the escape set has changed, we need to revisit the `external' fun.
-
-update_esc(Ls, W, V, Fs) ->
- update_esc(Ls, singleton(external), W, V, Fs).
-
-%% The external lambda is skipped here - the Escape variable is known to
-%% contain `external' from the start.
-
-update_esc([external | Ls], X, W, V, Fs) ->
- update_esc(Ls, X, W, V, Fs);
-update_esc([L | Ls], X, W, V, Fs) ->
- Vs = fun_vars(dict:fetch(L, Fs)),
- case bind_args_single(Vs, X, V) of
- {V1, true} ->
- update_esc(Ls, X, add_work([L], W), V1, Fs);
- {V1, false} ->
- update_esc(Ls, X, W, V1, Fs)
- end;
-update_esc([], _, W, V, _) ->
- {W, V}.
-
-set_parent([L | Ls], L1, D) ->
- set_parent(Ls, L1, dict:store(L, L1, D));
-set_parent([], _L1, D) ->
- D.
-
-%% Handle primop calls: (At present, we assume that all unknown primops
-%% yield exactly one value. This might have to be changed.)
-
-primop_call(F, A, Xs, St0) ->
- case is_pure_op(F, A) of
- %% XXX: this case is currently not possible -- commented out.
- %% true ->
- %% case is_literal_op(F, A) of
- %% true -> {[empty()], St0};
- %% false -> {[join_single_list(Xs)], St0}
- %% end;
- false ->
- St1 = case is_escape_op(F, A) of
- true -> escape([join_single_list(Xs)], St0);
- false -> St0
- end,
- case is_literal_op(F, A) of
- true -> {none, St1};
- false -> {[singleton(external)], St1}
- end
- end.
-
-%% Handle remote-calls: (At present, we assume that all unknown calls
-%% yield exactly one value. This might have to be changed.)
-
-remote_call(M, F, Xs, St) ->
- case is_c_atom(M) andalso is_c_atom(F) of
- true ->
- remote_call_1(atom_val(M), atom_val(F), length(Xs), Xs, St);
- false ->
- %% Unknown function
- {[singleton(external)], escape([join_single_list(Xs)], St)}
- end.
-
-remote_call_1(M, F, A, Xs, St0) ->
- case is_pure_op(M, F, A) of
- true ->
- case is_literal_op(M, F, A) of
- true -> {[empty()], St0};
- false -> {[join_single_list(Xs)], St0}
- end;
- false ->
- St1 = case is_escape_op(M, F, A) of
- true -> escape([join_single_list(Xs)], St0);
- false -> St0
- end,
- case is_literal_op(M, F, A) of
- true -> {[empty()], St1};
- false -> {[singleton(external)], St1}
- end
- end.
-
-%% Domain: none | [Vs], where Vs = set(integer()).
-
-join(none, Xs2) -> Xs2;
-join(Xs1, none) -> Xs1;
-join(Xs1, Xs2) ->
- if length(Xs1) =:= length(Xs2) ->
- join_1(Xs1, Xs2);
- true ->
- none
- end.
-
-join_1([X1 | Xs1], [X2 | Xs2]) ->
- [join_single(X1, X2) | join_1(Xs1, Xs2)];
-join_1([], []) ->
- [].
-
-empty() -> set__new().
-
-singleton(X) -> set__singleton(X).
-
-from_label_list(X) -> set__from_list(X).
-
-join_single(none, Y) -> Y;
-join_single(X, none) -> X;
-join_single(X, Y) -> set__union(X, Y).
-
-join_list([Xs | Xss]) ->
- join(Xs, join_list(Xss));
-join_list([]) ->
- none.
-
-join_single_list([X | Xs]) ->
- join_single(X, join_single_list(Xs));
-join_single_list([]) ->
- empty().
-
-equal(none, none) -> true;
-equal(none, _) -> false;
-equal(_, none) -> false;
-equal(X1, X2) -> equal_1(X1, X2).
-
-equal_1([X1 | Xs1], [X2 | Xs2]) ->
- equal_single(X1, X2) andalso equal_1(Xs1, Xs2);
-equal_1([], []) -> true;
-equal_1(_, _) -> false.
-
-equal_single(X, Y) -> set__equal(X, Y).
-
-%% Set abstraction for label sets in the domain.
-
-set__new() -> [].
-
-set__singleton(X) -> [X].
-
-set__to_list(S) -> S.
-
-set__from_list(S) -> ordsets:from_list(S).
-
-set__union(X, Y) -> ordsets:union(X, Y).
-
-set__add(X, S) -> ordsets:add_element(X, S).
-
-set__is_member(X, S) -> ordsets:is_element(X, S).
-
-set__subtract(X, Y) -> ordsets:subtract(X, Y).
-
-set__equal(X, Y) -> X =:= Y.
-
-%% A simple but efficient functional queue.
-
-queue__new() -> {[], []}.
-
-queue__put(X, {In, Out}) -> {[X | In], Out}.
-
-queue__get({In, [X | Out]}) -> {ok, X, {In, Out}};
-queue__get({[], _}) -> empty;
-queue__get({In, _}) ->
- [X | In1] = lists:reverse(In),
- {ok, X, {[], In1}}.
-
-%% The work list - a queue without repeated elements.
-
-init_work() ->
- {queue__new(), sets:new([{version, 2}])}.
-
-add_work(Ls, {Q, Set}) ->
- add_work(Ls, Q, Set).
-
-%% Note that the elements are enqueued in order.
-
-add_work([L | Ls], Q, Set) ->
- case sets:is_element(L, Set) of
- true ->
- add_work(Ls, Q, Set);
- false ->
- add_work(Ls, queue__put(L, Q), sets:add_element(L, Set))
- end;
-add_work([], Q, Set) ->
- {Q, Set}.
-
-take_work({Queue0, Set0}) ->
- case queue__get(Queue0) of
- {ok, L, Queue1} ->
- Set1 = sets:del_element(L, Set0),
- {ok, L, {Queue1, Set1}};
- empty ->
- none
- end.
-
-%% Escape operators may let their arguments escape. Unless we know
-%% otherwise, and the function is not pure, we assume this is the case.
-%% Error-raising functions (fault/match_fail) are not considered as
-%% escapes (but throw/exit are). Zero-argument functions need not be
-%% listed.
-
--spec is_escape_op(atom(), arity()) -> boolean().
-
-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().
-
-is_escape_op(erlang, error, 1) -> false;
-is_escape_op(erlang, error, 2) -> false;
-is_escape_op(M, F, A) when is_atom(M), is_atom(F), is_integer(A) -> true.
-
-%% "Literal" operators will never return functional values even when
-%% found in their arguments. Unless we know otherwise, we assume this is
-%% not the case. (More functions can be added to this list, if needed
-%% for better precision. Note that the result of `term_to_binary' still
-%% contains an encoding of the closure.)
-
--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.
-
--spec is_literal_op(atom(), atom(), arity()) -> boolean().
-
-is_literal_op(erlang, '+', 2) -> true;
-is_literal_op(erlang, '-', 2) -> true;
-is_literal_op(erlang, '*', 2) -> true;
-is_literal_op(erlang, '/', 2) -> true;
-is_literal_op(erlang, '=:=', 2) -> true;
-is_literal_op(erlang, '==', 2) -> true;
-is_literal_op(erlang, '=/=', 2) -> true;
-is_literal_op(erlang, '/=', 2) -> true;
-is_literal_op(erlang, '<', 2) -> true;
-is_literal_op(erlang, '=<', 2) -> true;
-is_literal_op(erlang, '>', 2) -> true;
-is_literal_op(erlang, '>=', 2) -> true;
-is_literal_op(erlang, 'and', 2) -> true;
-is_literal_op(erlang, 'or', 2) -> true;
-is_literal_op(erlang, 'not', 1) -> true;
-is_literal_op(erlang, length, 1) -> true;
-is_literal_op(erlang, size, 1) -> true;
-is_literal_op(erlang, fun_info, 1) -> true;
-is_literal_op(erlang, fun_info, 2) -> true;
-is_literal_op(erlang, fun_to_list, 1) -> true;
-is_literal_op(erlang, throw, 1) -> true;
-is_literal_op(erlang, exit, 1) -> true;
-is_literal_op(erlang, error, 1) -> true;
-is_literal_op(erlang, error, 2) -> true;
-is_literal_op(M, F, A) when is_atom(M), is_atom(F), is_integer(A) -> false.
-
-%% Pure functions neither affect the state, nor depend on it.
-
-is_pure_op(F, A) when is_atom(F), is_integer(A) -> false.
-
-is_pure_op(M, F, A) -> erl_bifs:is_pure(M, F, A).
-
-%% =====================================================================
diff --git a/lib/dialyzer/src/cerl_lib.erl b/lib/dialyzer/src/cerl_lib.erl
deleted file mode 100644
index 3a6fb1cf51..0000000000
--- a/lib/dialyzer/src/cerl_lib.erl
+++ /dev/null
@@ -1,457 +0,0 @@
-%% 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.
-%%
-%% @copyright 1999-2002 Richard Carlsson
-%% @author Richard Carlsson <carlsson.richard@gmail.com>
-%% @doc Utility functions for Core Erlang abstract syntax trees.
-%%
-%% <p>Syntax trees are defined in the module <a
-%% href=""><code>cerl</code></a>.</p>
-%%
-%% @type cerl() = cerl:cerl()
-
--module(cerl_lib).
-
--define(NO_UNUSED, true).
-
--export([is_safe_expr/2, reduce_expr/1, is_simple_clause/1,
- is_bool_switch/1, bool_switch_cases/1]).
--ifndef(NO_UNUSED).
--export([is_safe_expr/1, is_pure_expr/1, is_pure_expr/2,
- make_bool_switch/3]).
--endif.
-
-
-%% Test if a clause has a single pattern and an always-true guard.
-
--spec is_simple_clause(cerl:c_clause()) -> boolean().
-
-is_simple_clause(C) ->
- case cerl:clause_pats(C) of
- [_P] ->
- G = cerl:clause_guard(C),
- case cerl_clauses:eval_guard(G) of
- {value, true} -> true;
- _ -> false
- end;
- _ -> false
- end.
-
-%% Creating an if-then-else construct that can be recognized as such.
-%% `Test' *must* be guaranteed to return a boolean.
-
--ifndef(NO_UNUSED).
-make_bool_switch(Test, True, False) ->
- Cs = [cerl:c_clause([cerl:c_atom(true)], True),
- cerl:c_clause([cerl:c_atom(false)], False)],
- cerl:c_case(Test, Cs).
--endif.
-
-%% A boolean switch cannot have a catch-all; only true/false branches.
-
--spec is_bool_switch([cerl:c_clause()]) -> boolean().
-
-is_bool_switch([C1, C2]) ->
- case is_simple_clause(C1) andalso is_simple_clause(C2) of
- true ->
- [P1] = cerl:clause_pats(C1),
- [P2] = cerl:clause_pats(C2),
- case cerl:is_c_atom(P1) andalso cerl:is_c_atom(P2) of
- true ->
- A1 = cerl:concrete(P1),
- A2 = cerl:concrete(P2),
- is_boolean(A1) andalso is_boolean(A2)
- andalso A1 =/= A2;
- false ->
- false
- end;
- false ->
- false
- end;
-is_bool_switch(_) ->
- false.
-
-%% Returns the true-body and the false-body for boolean switch clauses.
-
--spec bool_switch_cases([cerl:c_clause()]) -> {cerl:cerl(), cerl:cerl()}.
-
-bool_switch_cases([C1, C2]) ->
- B1 = cerl:clause_body(C1),
- B2 = cerl:clause_body(C2),
- [P1] = cerl:clause_pats(C1),
- case cerl:concrete(P1) of
- true ->
- {B1, B2};
- false ->
- {B2, B1}
- end.
-
-%%
-%% The type of the check functions like the default check below - XXX: refine
-%%
--type check_fun() :: fun((_, _) -> boolean()).
-
-%% The default function property check always returns `false':
-
-default_check(_Property, _Function) -> false.
-
-
-%% @spec is_safe_expr(Expr::cerl()) -> boolean()
-%%
-%% @doc Returns `true' if `Expr' represents a "safe" Core Erlang
-%% expression, otherwise `false'. An expression is safe if it always
-%% completes normally and does not modify the state (although the return
-%% value may depend on the state).
-%%
-%% Expressions of type `apply', `case', `receive' and `binary' are
-%% always considered unsafe by this function.
-
-%% TODO: update cerl_inline to use these functions instead.
-
--ifndef(NO_UNUSED).
-is_safe_expr(E) ->
- Check = fun default_check/2,
- is_safe_expr(E, Check).
--endif.
-%% @clear
-
--spec is_safe_expr(cerl:cerl(), check_fun()) -> boolean().
-
-is_safe_expr(E, Check) ->
- case cerl:type(E) of
- literal ->
- true;
- var ->
- true;
- 'fun' ->
- true;
- values ->
- is_safe_expr_list(cerl:values_es(E), Check);
- tuple ->
- is_safe_expr_list(cerl:tuple_es(E), Check);
- cons ->
- case is_safe_expr(cerl:cons_hd(E), Check) of
- true ->
- is_safe_expr(cerl:cons_tl(E), Check);
- false ->
- false
- end;
- 'let' ->
- case is_safe_expr(cerl:let_arg(E), Check) of
- true ->
- is_safe_expr(cerl:let_body(E), Check);
- false ->
- false
- end;
- letrec ->
- is_safe_expr(cerl:letrec_body(E), Check);
- seq ->
- case is_safe_expr(cerl:seq_arg(E), Check) of
- true ->
- is_safe_expr(cerl:seq_body(E), Check);
- false ->
- false
- end;
- 'catch' ->
- is_safe_expr(cerl:catch_body(E), Check);
- 'try' ->
- %% If the guarded expression is safe, the try-handler will
- %% never be evaluated, so we need only check the body. If
- %% the guarded expression is pure, but could fail, we also
- %% have to check the handler.
- case is_safe_expr(cerl:try_arg(E), Check) of
- true ->
- is_safe_expr(cerl:try_body(E), Check);
- false ->
- case is_pure_expr(cerl:try_arg(E), Check) of
- true ->
- case is_safe_expr(cerl:try_body(E), Check) of
- true ->
- is_safe_expr(cerl:try_handler(E), Check);
- false ->
- false
- end;
- false ->
- false
- end
- end;
- primop ->
- Name = cerl:atom_val(cerl:primop_name(E)),
- As = cerl:primop_args(E),
- case Check(safe, {Name, length(As)}) of
- true ->
- is_safe_expr_list(As, Check);
- false ->
- false
- end;
- call ->
- Module = cerl:call_module(E),
- Name = cerl:call_name(E),
- case cerl:is_c_atom(Module) and cerl:is_c_atom(Name) of
- true ->
- M = cerl:atom_val(Module),
- F = cerl:atom_val(Name),
- As = cerl:call_args(E),
- case Check(safe, {M, F, length(As)}) of
- true ->
- is_safe_expr_list(As, Check);
- false ->
- false
- end;
- false ->
- false % Call to unknown function
- end;
- _ ->
- false
- end.
-
-is_safe_expr_list([E | Es], Check) ->
- case is_safe_expr(E, Check) of
- true ->
- is_safe_expr_list(Es, Check);
- false ->
- false
- end;
-is_safe_expr_list([], _Check) ->
- true.
-
-
-%% @spec (Expr::cerl()) -> bool()
-%%
-%% @doc Returns `true' if `Expr' represents a "pure" Core Erlang
-%% expression, otherwise `false'. An expression is pure if it does not
-%% affect the state, nor depend on the state, although its evaluation is
-%% not guaranteed to complete normally for all input.
-%%
-%% Expressions of type `apply', `case', `receive' and `binary' are
-%% always considered impure by this function.
-
--ifndef(NO_UNUSED).
-is_pure_expr(E) ->
- Check = fun default_check/2,
- is_pure_expr(E, Check).
--endif.
-%% @clear
-
-is_pure_expr(E, Check) ->
- case cerl:type(E) of
- literal ->
- true;
- var ->
- true;
- 'fun' ->
- true;
- values ->
- is_pure_expr_list(cerl:values_es(E), Check);
- tuple ->
- is_pure_expr_list(cerl:tuple_es(E), Check);
- cons ->
- case is_pure_expr(cerl:cons_hd(E), Check) of
- true ->
- is_pure_expr(cerl:cons_tl(E), Check);
- false ->
- false
- end;
- 'let' ->
- case is_pure_expr(cerl:let_arg(E), Check) of
- true ->
- is_pure_expr(cerl:let_body(E), Check);
- false ->
- false
- end;
- letrec ->
- is_pure_expr(cerl:letrec_body(E), Check);
- seq ->
- case is_pure_expr(cerl:seq_arg(E), Check) of
- true ->
- is_pure_expr(cerl:seq_body(E), Check);
- false ->
- false
- end;
- 'catch' ->
- is_pure_expr(cerl:catch_body(E), Check);
- 'try' ->
- case is_pure_expr(cerl:try_arg(E), Check) of
- true ->
- case is_pure_expr(cerl:try_body(E), Check) of
- true ->
- is_pure_expr(cerl:try_handler(E), Check);
- false ->
- false
- end;
- false ->
- false
- end;
- primop ->
- Name = cerl:atom_val(cerl:primop_name(E)),
- As = cerl:primop_args(E),
- case Check(pure, {Name, length(As)}) of
- true ->
- is_pure_expr_list(As, Check);
- false ->
- false
- end;
- call ->
- Module = cerl:call_module(E),
- Name = cerl:call_name(E),
- case cerl:is_c_atom(Module) and cerl:is_c_atom(Name) of
- true ->
- M = cerl:atom_val(Module),
- F = cerl:atom_val(Name),
- As = cerl:call_args(E),
- case Check(pure, {M, F, length(As)}) of
- true ->
- is_pure_expr_list(As, Check);
- false ->
- false
- end;
- false ->
- false % Call to unknown function
- end;
- _ ->
- false
- end.
-
-is_pure_expr_list([E | Es], Check) ->
- case is_pure_expr(E, Check) of
- true ->
- is_pure_expr_list(Es, Check);
- false ->
- false
- end;
-is_pure_expr_list([], _Check) ->
- true.
-
-
-%% Peephole optimizations
-%%
-%% This is only intended to be a light-weight cleanup optimizer,
-%% removing small things that may e.g. have been generated by other
-%% optimization passes or in the translation from higher-level code.
-%% It is not recursive in general - it only descends until it can do no
-%% more work in the current context.
-%%
-%% To expose hidden cases of final expressions (enabling last call
-%% optimization), we try to remove all trivial let-bindings (`let X = Y
-%% in X', `let X = Y in Y', `let X = Y in let ... in ...', `let X = let
-%% ... in ... in ...', etc.). We do not, however, try to recognize any
-%% other similar cases, even for simple `case'-expressions like `case E
-%% of X -> X end', or simultaneous multiple-value bindings.
-
--spec reduce_expr(cerl:cerl()) -> cerl:cerl().
-
-reduce_expr(E) ->
- Check = fun default_check/2,
- reduce_expr(E, Check).
-
--spec reduce_expr(cerl:cerl(), check_fun()) -> cerl:cerl().
-
-reduce_expr(E, Check) ->
- case cerl:type(E) of
- values ->
- case cerl:values_es(E) of
- [E1] ->
- %% Not really an "optimization" in itself, but
- %% enables other rewritings by removing the wrapper.
- reduce_expr(E1, Check);
- _ ->
- E
- end;
- 'seq' ->
- A = reduce_expr(cerl:seq_arg(E), Check),
- B = reduce_expr(cerl:seq_body(E), Check),
- %% `do <E1> <E2>' is equivalent to `<E2>' if `<E1>' is
- %% "safe" (cannot effect the behaviour in any way).
- case is_safe_expr(A, Check) of
- true ->
- B;
- false ->
- case cerl:is_c_seq(B) of
- true ->
- %% Rewrite `do <E1> do <E2> <E3>' to `do do
- %% <E1> <E2> <E3>' so that the "body" of the
- %% outermost seq-operator is the expression
- %% which produces the final result (i.e.,
- %% E3). This can make other optimizations
- %% easier; see `let'.
- B1 = cerl:seq_arg(B),
- B2 = cerl:seq_body(B),
- cerl:c_seq(cerl:c_seq(A, B1), B2);
- false ->
- cerl:c_seq(A, B)
- end
- end;
- 'let' ->
- A = reduce_expr(cerl:let_arg(E), Check),
- case cerl:is_c_seq(A) of
- true ->
- %% `let X = do <E1> <E2> in Y' is equivalent to `do
- %% <E1> let X = <E2> in Y'. Note that `<E2>' cannot
- %% be a seq-operator, due to the `seq' optimization.
- A1 = cerl:seq_arg(A),
- A2 = cerl:seq_body(A),
- E1 = cerl:update_c_let(E, cerl:let_vars(E),
- A2, cerl:let_body(E)),
- cerl:c_seq(A1, reduce_expr(E1, Check));
- false ->
- B = reduce_expr(cerl:let_body(E), Check),
- Vs = cerl:let_vars(E),
- %% We give up if the body does not reduce to a
- %% single variable. This is not a generic copy
- %% propagation.
- case cerl:type(B) of
- var when length(Vs) =:= 1 ->
- %% We have `let <V1> = <E> in <V2>':
- [V] = Vs,
- N1 = cerl:var_name(V),
- N2 = cerl:var_name(B),
- if N1 =:= N2 ->
- %% `let X = <E> in X' equals `<E>'
- A;
- true ->
- %% `let X = <E> in Y' when X and Y
- %% are different variables is
- %% equivalent to `do <E> Y'.
- reduce_expr(cerl:c_seq(A, B), Check)
- end;
- literal ->
- %% `let X = <E> in T' when T is a literal
- %% term is equivalent to `do <E> T'.
- reduce_expr(cerl:c_seq(A, B), Check);
- _ ->
- cerl:update_c_let(E, Vs, A, B)
- end
- end;
- 'try' ->
- %% Get rid of unnecessary try-expressions.
- A = reduce_expr(cerl:try_arg(E), Check),
- B = reduce_expr(cerl:try_body(E), Check),
- case is_safe_expr(A, Check) of
- true ->
- B;
- false ->
- cerl:update_c_try(E, A, cerl:try_vars(E), B,
- cerl:try_evars(E),
- cerl:try_handler(E))
- end;
- 'catch' ->
- %% Just a simpler form of try-expressions.
- B = reduce_expr(cerl:catch_body(E), Check),
- case is_safe_expr(B, Check) of
- true ->
- B;
- false ->
- cerl:update_c_catch(E, B)
- end;
- _ ->
- E
- end.
diff --git a/lib/dialyzer/src/cerl_pmatch.erl b/lib/dialyzer/src/cerl_pmatch.erl
deleted file mode 100644
index 66fce3c8eb..0000000000
--- a/lib/dialyzer/src/cerl_pmatch.erl
+++ /dev/null
@@ -1,620 +0,0 @@
-%% 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.
-%%
-%% @copyright 2000-2006 Richard Carlsson
-%% @author Richard Carlsson <carlsson.richard@gmail.com>
-%%
-%% @doc Core Erlang pattern matching compiler.
-%%
-%% <p>For reference, see Simon L. Peyton Jones "The Implementation of
-%% Functional Programming Languages", chapter 5 (by Phil Wadler).</p>
-%%
-%% @type cerl() = cerl:cerl().
-%% Abstract Core Erlang syntax trees.
-%% @type cerl_records() = cerl:cerl_records().
-%% An explicit record representation of Core Erlang syntax trees.
-
--module(cerl_pmatch).
-
-%%-define(NO_UNUSED, true).
-
--export([clauses/2]).
--ifndef(NO_UNUSED).
--export([transform/2, core_transform/2, expr/2]).
--endif.
-
--import(lists, [all/2, splitwith/2, foldr/3, keysort/2, foldl/3,
- mapfoldl/3]).
-
--define(binary_id, {binary}).
--define(cons_id, {cons}).
--define(tuple_id, {tuple}).
--define(literal_id(V), V).
-
-
-%% @spec core_transform(Module::cerl_records(), Options::[term()]) ->
-%% cerl_records()
-%%
-%% @doc Transforms a module represented by records. See
-%% <code>transform/2</code> for details.
-%%
-%% <p>Use the compiler option <code>{core_transform, cerl_pmatch}</code>
-%% to insert this function as a compilation pass.</p>
-%%
-%% @see transform/2
-
--ifndef(NO_UNUSED).
--spec core_transform(cerl:c_module(), [_]) -> cerl:c_module().
-
-core_transform(M, Opts) ->
- cerl:to_records(transform(cerl:from_records(M), Opts)).
--endif. % NO_UNUSED
-%% @clear
-
-
-%% @spec transform(Module::cerl(), Options::[term()]) -> cerl()
-%%
-%% @doc Rewrites all <code>case</code>-clauses in <code>Module</code>.
-%% <code>receive</code>-clauses are not affected. Currently, no options
-%% are available.
-%%
-%% @see clauses/2
-%% @see expr/2
-%% @see core_transform/2
-
--ifndef(NO_UNUSED).
--spec transform(cerl:cerl(), [_]) -> cerl:cerl().
-
-transform(M, _Opts) ->
- expr(M, env__empty()).
--endif. % NO_UNUSED
-%% @clear
-
-
-%% @spec clauses(Clauses::[Clause], Env) -> {Expr, Vars}
-%% Clause = cerl()
-%% Expr = cerl()
-%% Vars = [cerl()]
-%% Env = rec_env:environment()
-%%
-%% @doc Rewrites a sequence of clauses to an equivalent expression,
-%% removing as much repeated testing as possible. Returns a pair
-%% <code>{Expr, Vars}</code>, where <code>Expr</code> is the resulting
-%% expression, and <code>Vars</code> is a list of new variables (i.e.,
-%% not already in the given environment) to be bound to the arguments to
-%% the switch. The following is a typical example (assuming
-%% <code>E</code> is a Core Erlang case expression):
-%% <pre>
-%% handle_case(E, Env) ->
-%% Cs = case_clauses(E),
-%% {E1, Vs} = cerl_pmatch(Cs, Env),
-%% c_let(Vs, case_arg(E), E1).
-%% </pre>
-%%
-%% <p>The environment is used for generating new variables which do not
-%% shadow existing bindings.</p>
-%%
-%% @see rec_env
-%% @see expr/2
-%% @see transform/2
-
--spec clauses([cerl:cerl(),...], rec_env:environment()) ->
- {cerl:cerl(), [cerl:cerl()]}.
-
-clauses(Cs, Env) ->
- clauses(Cs, none, Env).
-
-clauses([C | _] = Cs, Else, Env) ->
- Vs = new_vars(cerl:clause_arity(C), Env),
- E = match(Vs, Cs, Else, add_vars(Vs, Env)),
- {E, Vs}.
-
-%% The implementation very closely follows that described in the book.
-
-match([], Cs, Else, _Env) ->
- %% If the "default action" is the atom 'none', it is simply not
- %% added; otherwise it is put in the body of a final catch-all
- %% clause (which is often removed by the below optimization).
- Cs1 = if Else =:= none -> Cs;
- true -> Cs ++ [cerl:c_clause([], Else)]
- end,
- %% This clause reduction is an important optimization. It selects a
- %% clause body if possible, and otherwise just removes dead clauses.
- case cerl_clauses:reduce(Cs1) of
- {true, {C, []}} -> % if we get bindings, something is wrong!
- cerl:clause_body(C);
- {false, Cs2} ->
- %% This happens when guards are nontrivial.
- cerl:c_case(cerl:c_values([]), Cs2)
- end;
-match([V | _] = Vs, Cs, Else, Env) ->
- foldr(fun (CsF, ElseF) ->
- match_var_con(Vs, CsF, ElseF, Env)
- end,
- Else,
- group([unalias(C, V) || C <- Cs], fun is_var_clause/1)).
-
-group([], _F) ->
- [];
-group([X | _] = Xs, F) ->
- group(Xs, F, F(X)).
-
-group(Xs, F, P) ->
- {First, Rest} = splitwith(fun (X) -> F(X) =:= P end, Xs),
- [First | group(Rest, F)].
-
-is_var_clause(C) ->
- cerl:is_c_var(hd(cerl:clause_pats(C))).
-
-%% To avoid code duplication, if the 'Else' expression is too big, we
-%% put it in a local function definition instead, and replace it with a
-%% call. (Note that it is important that 'is_lightweight' does not yield
-%% 'true' for a simple function application, or we will create a lot of
-%% unnecessary extra functions.)
-
-match_var_con(Vs, Cs, none = Else, Env) ->
- match_var_con_1(Vs, Cs, Else, Env);
-match_var_con(Vs, Cs, Else, Env) ->
- case is_lightweight(Else) of
- true ->
- match_var_con_1(Vs, Cs, Else, Env);
- false ->
- F = new_fvar("match_", 0, Env),
- Else1 = cerl:c_apply(F, []),
- Env1 = add_vars([F], Env),
- cerl:c_letrec([{F, cerl:c_fun([], Else)}],
- match_var_con_1(Vs, Cs, Else1, Env1))
- end.
-
-match_var_con_1(Vs, Cs, Else, Env) ->
- case is_var_clause(hd(Cs)) of
- true ->
- match_var(Vs, Cs, Else, Env);
- false ->
- match_con(Vs, Cs, Else, Env)
- end.
-
-match_var([V | Vs], Cs, Else, Env) ->
- Cs1 = [begin
- [P | Ps] = cerl:clause_pats(C),
- G = make_let([P], V, cerl:clause_guard(C)),
- B = make_let([P], V, cerl:clause_body(C)),
- cerl:update_c_clause(C, Ps, G, B)
- end
- || C <- Cs],
- match(Vs, Cs1, Else, Env).
-
-%% Since Erlang is dynamically typed, we must include the possibility
-%% that none of the constructors in the group will match, and in that
-%% case the "Else" code will be executed (unless it is 'none'), in the
-%% body of a final catch-all clause.
-
-match_con([V | Vs], Cs, Else, Env) ->
- case group_con(Cs) of
- [{_, _, Gs}] ->
- %% Don't create a group type switch if there is only one
- %% such group
- make_switch(V, [match_congroup(DG, Vs, CsG, Else, Env)
- || {DG, _, CsG} <- Gs],
- Else, Env);
- Ts ->
- Cs1 = [match_typegroup(T, V, Vs, Gs, Else, Env)
- || {T, _, Gs} <- Ts],
- make_switch(V, Cs1, Else, Env)
- end.
-
-
-match_typegroup(_T, _V, Vs, [{D, _, Cs}], Else, Env) when element(1, D) /= ?binary_id ->
- %% Don't create a group type switch if there is only one constructor
- %% in the group. (Note that this always happens for '[]'.)
- %% Special case for binaries which always get a group switch
- match_congroup(D, Vs, Cs, Else, Env);
-match_typegroup(T, V, Vs, Gs, Else, Env) ->
- Body = make_switch(V, [match_congroup(D, Vs, Cs, Else, Env)
- || {D, _, Cs} <- Gs],
- Else, Env),
- typetest_clause(T, V, Body, Env).
-
-match_congroup({?binary_id, Segs}, Vs, Cs, Else, Env) ->
- Body = match(Vs, Cs, Else, Env),
- cerl:c_clause([make_pat(?binary_id, Segs)], Body);
-
-match_congroup({D, A}, Vs, Cs, Else, Env) ->
- Vs1 = new_vars(A, Env),
- Body = match(Vs1 ++ Vs, Cs, Else, add_vars(Vs1, Env)),
- cerl:c_clause([make_pat(D, Vs1)], Body).
-
-make_switch(V, Cs, Else, Env) ->
- cerl:c_case(V, if Else =:= none -> Cs;
- true -> Cs ++ [cerl:c_clause([new_var(Env)],
- Else)]
- end).
-
-%% We preserve the relative order of different-type constructors as they
-%% were originally listed. This is done by tracking the clause numbers.
-
-group_con(Cs) ->
- {Cs1, _} = mapfoldl(fun (C, N) ->
- [P | Ps] = cerl:clause_pats(C),
- Ps1 = sub_pats(P) ++ Ps,
- G = cerl:clause_guard(C),
- B = cerl:clause_body(C),
- C1 = cerl:update_c_clause(C, Ps1, G, B),
- D = con_desc(P),
- {{D, N, C1}, N + 1}
- end,
- 0, Cs),
- %% Sort and group constructors.
- Css = group(keysort(1, Cs1), fun ({D,_,_}) -> D end),
- %% Sort each group "back" by line number, and move the descriptor
- %% and line number to the wrapper for the group.
- Gs = [finalize_congroup(C) || C <- Css],
- %% Group by type only (put e.g. different-arity tuples together).
- Gss = group(Gs, fun ({D,_,_}) -> con_desc_type(D) end),
- %% Sort and wrap the type groups.
- Ts = [finalize_typegroup(G) || G <- Gss],
- %% Sort type-groups by first clause order
- keysort(2, Ts).
-
-finalize_congroup(Cs) ->
- [{D,N,_}|_] = Cs1 = keysort(2, Cs),
- {D, N, [C || {_,_,C} <- Cs1]}.
-
-finalize_typegroup(Gs) ->
- [{D,N,_}|_] = Gs1 = keysort(2, Gs),
- {con_desc_type(D), N, Gs1}.
-
-%% Since Erlang clause patterns can contain "alias patterns", we must
-%% eliminate these, by turning them into let-definitions in the guards
-%% and bodies of the clauses.
-
-unalias(C, V) ->
- [P | Ps] = cerl:clause_pats(C),
- B = cerl:clause_body(C),
- G = cerl:clause_guard(C),
- unalias(P, V, Ps, B, G, C).
-
-unalias(P, V, Ps, B, G, C) ->
- case cerl:type(P) of
- alias ->
- V1 = cerl:alias_var(P),
- B1 = make_let([V1], V, B),
- G1 = make_let([V1], V, G),
- unalias(cerl:alias_pat(P), V, Ps, B1, G1, C);
- _ ->
- cerl:update_c_clause(C, [P | Ps], G, B)
- end.
-
-%% Generating a type-switch clause
-
-typetest_clause([], _V, E, _Env) ->
- cerl:c_clause([cerl:c_nil()], E);
-typetest_clause(atom, V, E, _Env) ->
- typetest_clause_1(is_atom, V, E);
-typetest_clause(integer, V, E, _Env) ->
- typetest_clause_1(is_integer, V, E);
-typetest_clause(float, V, E, _Env) ->
- typetest_clause_1(is_float, V, E);
-typetest_clause(cons, _V, E, Env) ->
- [V1, V2] = new_vars(2, Env),
- cerl:c_clause([cerl:c_cons(V1, V2)], E); % there is no 'is cons'
-typetest_clause(tuple, V, E, _Env) ->
- typetest_clause_1(is_tuple, V, E);
-typetest_clause(binary, V, E, _Env) ->
- typetest_clause_1(is_binary, V, E).
-
-typetest_clause_1(T, V, E) ->
- cerl:c_clause([V], cerl:c_call(cerl:c_atom('erlang'),
- cerl:c_atom(T), [V]), E).
-
-%% This returns a constructor descriptor, to be used for grouping and
-%% pattern generation. It consists of an identifier term and the arity.
-
-con_desc(E) ->
- case cerl:type(E) of
- cons -> {?cons_id, 2};
- tuple -> {?tuple_id, cerl:tuple_arity(E)};
- binary -> {?binary_id, cerl:binary_segments(E)};
- literal ->
- case cerl:concrete(E) of
- [_|_] -> {?cons_id, 2};
- T when is_tuple(T) -> {?tuple_id, tuple_size(T)};
- V -> {?literal_id(V), 0}
- end;
- _ ->
- throw({bad_constructor, E})
- end.
-
-%% This returns the type class for a constructor descriptor, for
-%% grouping of clauses. It does not distinguish between tuples of
-%% different arity, nor between different values of atoms, integers and
-%% floats.
-
-con_desc_type({?literal_id([]), _}) -> [];
-con_desc_type({?literal_id(V), _}) when is_atom(V) -> atom;
-con_desc_type({?literal_id(V), _}) when is_integer(V) -> integer;
-con_desc_type({?literal_id(V), _}) when is_float(V) -> float;
-con_desc_type({?cons_id, 2}) -> cons;
-con_desc_type({?tuple_id, _}) -> tuple;
-con_desc_type({?binary_id, _}) -> binary.
-
-%% This creates a new constructor pattern from a type descriptor and a
-%% list of variables.
-
-make_pat(?cons_id, [V1, V2]) -> cerl:c_cons(V1, V2);
-make_pat(?tuple_id, Vs) -> cerl:c_tuple(Vs);
-make_pat(?binary_id, Segs) -> cerl:c_binary(Segs);
-make_pat(?literal_id(Val), []) -> cerl:abstract(Val).
-
-%% This returns the list of subpatterns of a constructor pattern.
-
-sub_pats(E) ->
- case cerl:type(E) of
- cons ->
- [cerl:cons_hd(E), cerl:cons_tl(E)];
- tuple ->
- cerl:tuple_es(E);
- binary ->
- [];
- literal ->
- case cerl:concrete(E) of
- [H|T] -> [cerl:abstract(H), cerl:abstract(T)];
- T when is_tuple(T) -> [cerl:abstract(X)
- || X <- tuple_to_list(T)];
- _ -> []
- end;
- _ ->
- throw({bad_constructor_pattern, E})
- end.
-
-%% This avoids generating stupid things like "let X = ... in 'true'",
-%% and "let X = Y in X", keeping the generated code cleaner. It also
-%% prevents expressions from being considered "non-lightweight" when
-%% code duplication is disallowed (see is_lightweight for details).
-
-make_let(Vs, A, B) ->
- cerl_lib:reduce_expr(cerl:c_let(Vs, A, B)).
-
-%% ---------------------------------------------------------------------
-%% Rewriting a module or other expression:
-
-%% @spec expr(Expression::cerl(), Env) -> cerl()
-%% Env = rec_env:environment()
-%%
-%% @doc Rewrites all <code>case</code>-clauses in
-%% <code>Expression</code>. <code>receive</code>-clauses are not
-%% affected.
-%%
-%% <p>The environment is used for generating new variables which do not
-%% shadow existing bindings.</p>
-%%
-%% @see clauses/2
-%% @see rec_env
-
--ifndef(NO_UNUSED).
--spec expr(cerl:cerl(), rec_env:environment()) -> cerl:cerl().
-
-expr(E, Env) ->
- case cerl:type(E) of
- binary ->
- Es = expr_list(cerl:binary_segments(E), Env),
- cerl:update_c_binary(E, Es);
- bitstr ->
- V = expr(cerl:bitstr_val(E), Env),
- Sz = expr(cerl:bitstr_size(E), Env),
- Unit = expr(cerl:bitstr_unit(E), Env),
- Type = expr(cerl:bitstr_type(E), Env),
- cerl:update_c_bitstr(E, V, Sz, Unit, Type, cerl:bitstr_flags(E));
- literal ->
- E;
- var ->
- E;
- values ->
- Es = expr_list(cerl:values_es(E), Env),
- cerl:update_c_values(E, Es);
- cons ->
- H = expr(cerl:cons_hd(E), Env),
- T = expr(cerl:cons_tl(E), Env),
- cerl:update_c_cons(E, H, T);
- tuple ->
- Es = expr_list(cerl:tuple_es(E), Env),
- cerl:update_c_tuple(E, Es);
- 'let' ->
- A = expr(cerl:let_arg(E), Env),
- Vs = cerl:let_vars(E),
- Env1 = add_vars(Vs, Env),
- B = expr(cerl:let_body(E), Env1),
- cerl:update_c_let(E, Vs, A, B);
- seq ->
- A = expr(cerl:seq_arg(E), Env),
- B = expr(cerl:seq_body(E), Env),
- cerl:update_c_seq(E, A, B);
- apply ->
- Op = expr(cerl:apply_op(E), Env),
- As = expr_list(cerl:apply_args(E), Env),
- cerl:update_c_apply(E, Op, As);
- call ->
- M = expr(cerl:call_module(E), Env),
- N = expr(cerl:call_name(E), Env),
- As = expr_list(cerl:call_args(E), Env),
- cerl:update_c_call(E, M, N, As);
- primop ->
- As = expr_list(cerl:primop_args(E), Env),
- cerl:update_c_primop(E, cerl:primop_name(E), As);
- 'case' ->
- A = expr(cerl:case_arg(E), Env),
- Cs = expr_list(cerl:case_clauses(E), Env),
- {E1, Vs} = clauses(Cs, Env),
- make_let(Vs, A, E1);
- clause ->
- Vs = cerl:clause_vars(E),
- Env1 = add_vars(Vs, Env),
- G = expr(cerl:clause_guard(E), Env1),
- B = expr(cerl:clause_body(E), Env1),
- cerl:update_c_clause(E, cerl:clause_pats(E), G, B);
- 'fun' ->
- Vs = cerl:fun_vars(E),
- Env1 = add_vars(Vs, Env),
- B = expr(cerl:fun_body(E), Env1),
- cerl:update_c_fun(E, Vs, B);
- 'receive' ->
- %% NOTE: No pattern matching compilation is done here! The
- %% receive-clauses and patterns cannot be staged as long as
- %% we are working with "normal" Core Erlang.
- Cs = expr_list(cerl:receive_clauses(E), Env),
- T = expr(cerl:receive_timeout(E), Env),
- A = expr(cerl:receive_action(E), Env),
- cerl:update_c_receive(E, Cs, T, A);
- 'try' ->
- A = expr(cerl:try_arg(E), Env),
- Vs = cerl:try_vars(E),
- B = expr(cerl:try_body(E), add_vars(Vs, Env)),
- Evs = cerl:try_evars(E),
- H = expr(cerl:try_handler(E), add_vars(Evs, Env)),
- cerl:update_c_try(E, A, Vs, B, Evs, H);
- 'catch' ->
- B = expr(cerl:catch_body(E), Env),
- cerl:update_c_catch(E, B);
- letrec ->
- Ds = cerl:letrec_defs(E),
- Env1 = add_defs(Ds, Env),
- Ds1 = defs(Ds, Env1),
- B = expr(cerl:letrec_body(E), Env1),
- cerl:update_c_letrec(E, Ds1, B);
- module ->
- Ds = cerl:module_defs(E),
- Env1 = add_defs(Ds, Env),
- Ds1 = defs(Ds, Env1),
- cerl:update_c_module(E, cerl:module_name(E),
- cerl:module_exports(E),
- cerl:module_attrs(E), Ds1)
- end.
-
-expr_list(Es, Env) ->
- [expr(E, Env) || E <- Es].
-
-defs(Ds, Env) ->
- [{V, expr(F, Env)} || {V, F} <- Ds].
--endif. % NO_UNUSED
-%% @clear
-
-%% ---------------------------------------------------------------------
-%% Support functions
-
-new_var(Env) ->
- Name = env__new_vname(Env),
- cerl:c_var(Name).
-
-new_vars(N, Env) ->
- [cerl:c_var(V) || V <- env__new_vnames(N, Env)].
-
-new_fvar(A, N, Env) ->
- Name = env__new_fname(A, N, Env),
- cerl:c_var(Name).
-
-add_vars(Vs, Env) ->
- foldl(fun (V, E) -> env__bind(cerl:var_name(V), [], E) end, Env, Vs).
-
--ifndef(NO_UNUSED).
-add_defs(Ds, Env) ->
- foldl(fun ({V, _F}, E) ->
- env__bind(cerl:var_name(V), [], E)
- end, Env, Ds).
--endif. % NO_UNUSED
-
-%% This decides whether an expression is worth lifting out to a separate
-%% function instead of duplicating the code. In other words, whether its
-%% cost is about the same or smaller than that of a local function call.
-%% Note that variables must always be "lightweight"; otherwise, they may
-%% get lifted out of the case switch that introduces them.
-
-is_lightweight(E) ->
- case get('cerl_pmatch_duplicate_code') of
- never -> cerl:type(E) =:= var; % Avoids all code duplication
- always -> true; % Does not lift code to new functions
- _ -> is_lightweight_1(E)
- end.
-
-is_lightweight_1(E) ->
- case cerl:type(E) of
- var -> true;
- literal -> true;
- 'fun' -> true;
- values -> all(fun is_simple/1, cerl:values_es(E));
- cons -> is_simple(cerl:cons_hd(E))
- andalso is_simple(cerl:cons_tl(E));
- tuple -> all(fun is_simple/1, cerl:tuple_es(E));
- 'let' -> (is_simple(cerl:let_arg(E)) andalso
- is_lightweight_1(cerl:let_body(E)));
- seq -> (is_simple(cerl:seq_arg(E)) andalso
- is_lightweight_1(cerl:seq_body(E)));
- primop ->
- all(fun is_simple/1, cerl:primop_args(E));
- apply ->
- is_simple(cerl:apply_op(E))
- andalso all(fun is_simple/1, cerl:apply_args(E));
- call ->
- is_simple(cerl:call_module(E))
- andalso is_simple(cerl:call_name(E))
- andalso all(fun is_simple/1, cerl:call_args(E));
- _ ->
- %% The default is to lift the code to a new function.
- false
- end.
-
-%% "Simple" things have no (or negligible) runtime cost and are free
-%% from side effects.
-
-is_simple(E) ->
- case cerl:type(E) of
- var -> true;
- literal -> true;
- values -> all(fun is_simple/1, cerl:values_es(E));
- _ -> false
- end.
-
-
-%% ---------------------------------------------------------------------
-%% Abstract datatype: environment()
-
-env__bind(Key, Val, Env) ->
- rec_env:bind(Key, Val, Env).
-
--ifndef(NO_UNUSED).
-%% env__bind_recursive(Ks, Vs, F, Env) ->
-%% rec_env:bind_recursive(Ks, Vs, F, Env).
-
-%% env__lookup(Key, Env) ->
-%% rec_env:lookup(Key, Env).
-
-%% env__get(Key, Env) ->
-%% rec_env:get(Key, Env).
-
-%% env__is_defined(Key, Env) ->
-%% rec_env:is_defined(Key, Env).
-
-env__empty() ->
- rec_env:empty().
--endif. % NO_UNUSED
-
-env__new_vname(Env) ->
- rec_env:new_key(Env).
-
-env__new_vnames(N, Env) ->
- rec_env:new_keys(N, Env).
-
-env__new_fname(F, A, Env) ->
- rec_env:new_key(fun (X) ->
- S = integer_to_list(X),
- {list_to_atom(F ++ S), A}
- end,
- Env).
diff --git a/lib/dialyzer/src/cerl_typean.erl b/lib/dialyzer/src/cerl_typean.erl
deleted file mode 100644
index b0e5c10d7d..0000000000
--- a/lib/dialyzer/src/cerl_typean.erl
+++ /dev/null
@@ -1,994 +0,0 @@
-%% -*- erlang-indent-level: 4 -*-
-%%
-%% 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.
-%%
-%% @copyright 2001-2002 Richard Carlsson
-%% @author Richard Carlsson <carlsson.richard@gmail.com>
-%% @doc Type analysis of Core Erlang programs.
-
-%% TODO: filters must handle conjunctions for better precision!
-%% TODO: should get filters from patterns as well as guards.
-%% TODO: unused functions are being included in the analysis.
-
--module(cerl_typean).
-
--export([core_transform/2, analyze/1, pp_hook/0]).
-%%-export([analyze/2, analyze/5, annotate/1, annotate/2, annotate/5]).
-
--import(erl_types, [t_any/0, t_atom/0, t_atom_vals/1, t_binary/0,
- t_cons/2, t_cons_hd/1, t_cons_tl/1, t_float/0,
- t_fun/0, t_fun/2, t_from_range/2, t_from_term/1,
- t_inf/2, t_integer/0,
- t_is_any/1, t_is_atom/1, t_is_cons/1, t_is_list/1,
- t_is_maybe_improper_list/1, t_is_none/1, t_is_tuple/1,
- t_limit/2, t_list_elements/1, t_maybe_improper_list/0,
- t_none/0, t_number/0, t_pid/0, t_port/0, t_product/1,
- t_reference/0, t_sup/2, t_to_tlist/1, t_tuple/0, t_tuple/1,
- t_tuple_args/1, t_tuple_size/1, t_tuple_subtypes/1]).
-
--import(cerl, [ann_c_fun/3, ann_c_var/2, alias_pat/1, alias_var/1,
- apply_args/1, apply_op/1, atom_val/1, bitstr_size/1,
- bitstr_val/1, bitstr_type/1, bitstr_flags/1, binary_segments/1,
- c_letrec/2, c_nil/0,
- c_values/1, call_args/1, call_module/1, call_name/1,
- case_arg/1, case_clauses/1, catch_body/1, clause_body/1,
- clause_guard/1, clause_pats/1, concrete/1, cons_hd/1,
- cons_tl/1, fun_body/1, fun_vars/1, get_ann/1, int_val/1,
- is_c_atom/1, is_c_int/1, let_arg/1, let_body/1, let_vars/1,
- letrec_body/1, letrec_defs/1, module_defs/1,
- module_defs/1, module_exports/1, pat_vars/1,
- primop_args/1, primop_name/1, receive_action/1,
- receive_clauses/1, receive_timeout/1, seq_arg/1,
- seq_body/1, set_ann/2, try_arg/1, try_body/1,
- try_evars/1, try_handler/1, try_vars/1, tuple_arity/1,
- tuple_es/1, type/1, values_es/1, var_name/1]).
-
--import(cerl_trees, [get_label/1]).
-
--ifdef(DEBUG).
--define(ANNOTATE(X), case erl_types:t_to_string(X) of Q when length(Q) < 255 -> list_to_atom(Q); Q -> Q end).
--else.
--define(ANNOTATE(X), X).
--endif.
-
-%% Limit for type representation depth.
--define(DEF_LIMIT, 3).
-
-
-%% @spec core_transform(Module::cerl_records(), Options::[term()]) ->
-%% cerl_records()
-%%
-%% @doc Annotates a module represented by records with type
-%% information. See <code>annotate/1</code> for details.
-%%
-%% <p>Use the compiler option <code>{core_transform, cerl_typean}</code>
-%% to insert this function as a compilation pass.</p>
-%%
-%% @see module/2
-
--spec core_transform(cerl:cerl(), [term()]) -> cerl:cerl().
-
-core_transform(Code, _Opts) ->
- {Code1, _} = cerl_trees:label(cerl:from_records(Code)),
- %% io:fwrite("Running type analysis..."),
- %% {T1,_} = statistics(runtime),
- {Code2, _, _} = annotate(Code1),
- %% {T2,_} = statistics(runtime),
- %% io:fwrite("(~w ms).\n", [T2 - T1]),
- cerl:to_records(Code2).
-
-
-%% =====================================================================
-%% annotate(Tree) -> {Tree1, Type, Vars}
-%%
-%% Tree = cerl:cerl()
-%%
-%% Analyzes `Tree' (see `analyze') and appends terms `{type, Type}'
-%% to the annotation list of each fun-expression node and
-%% apply-expression node of `Tree', respectively, where `Labels' is
-%% an ordered-set list of labels of fun-expressions in `Tree',
-%% possibly also containing the atom `external', corresponding to
-%% the dependency information derived by the analysis. Any previous
-%% such annotations are removed from `Tree'. `Tree1' is the
-%% modified tree; for details on `OutList', `Outputs' ,
-%% `Dependencies' and `Escapes', see `analyze'.
-%%
-%% Note: `Tree' must be annotated with labels in order to use this
-%% function; see `analyze' for details.
-
-annotate(Tree) ->
- annotate(Tree, ?DEF_LIMIT).
-
-annotate(Tree, Limit) ->
- {_, _, Esc, Dep, Par} = cerl_closurean:analyze(Tree),
- annotate(Tree, Limit, Esc, Dep, Par).
-
-annotate(Tree, Limit, Esc, Dep, Par) ->
- {Type, Out, Vars} = analyze(Tree, Limit, Esc, Dep, Par),
- DelAnn = fun (T) -> set_ann(T, delete_ann(type, get_ann(T))) end,
- SetType = fun (T, Dict) ->
- case dict:find(get_label(T), Dict) of
- {ok, X} ->
- case t_is_any(X) of
- true ->
- DelAnn(T);
- false ->
- set_ann(T, append_ann(type,
- ?ANNOTATE(X),
- get_ann(T)))
- end;
- error ->
- DelAnn(T)
- end
- end,
- F = fun (T) ->
- case type(T) of
- var ->
- SetType(T, Vars);
- apply ->
- SetType(T, Out);
- call ->
- SetType(T, Out);
- primop ->
- SetType(T, Out);
- 'fun' ->
- SetType(T, Out);
- _ ->
- DelAnn(T)
- end
- end,
- {cerl_trees:map(F, Tree), Type, Vars}.
-
-append_ann(Tag, Val, [X | Xs]) ->
- if tuple_size(X) >= 1, element(1, X) =:= Tag ->
- append_ann(Tag, Val, Xs);
- true ->
- [X | append_ann(Tag, Val, Xs)]
- end;
-append_ann(Tag, Val, []) ->
- [{Tag, Val}].
-
-delete_ann(Tag, [X | Xs]) ->
- if tuple_size(X) >= 1, element(1, X) =:= Tag ->
- delete_ann(Tag, Xs);
- true ->
- [X | delete_ann(Tag, Xs)]
- end;
-delete_ann(_, []) ->
- [].
-
-
-%% =====================================================================
-%% analyze(Tree) -> {OutList, Outputs, Dependencies}
-%%
-%% Tree = cerl:cerl()
-%% OutList = [LabelSet] | none
-%% Outputs = dict(integer(), OutList)
-%% Dependencies = dict(integer(), LabelSet)
-%% LabelSet = ordset(Label)
-%% Label = integer() | external
-%%
-%% Analyzes a module or an expression represented by `Tree'.
-%%
-%% The returned `OutList' is a list of sets of labels of
-%% fun-expressions which correspond to the possible closures in the
-%% value list produced by `Tree' (viewed as an expression; the
-%% "value" of a module contains its exported functions). The atom
-%% `none' denotes missing or conflicting information.
-%%
-%% The atom `external' in any label set denotes any possible
-%% function outside `Tree', including those in `Escapes'.
-%%
-%% `Outputs' is a mapping from the labels of fun-expressions in
-%% `Tree' to corresponding lists of sets of labels of
-%% fun-expressions (or the atom `none'), representing the possible
-%% closures in the value lists returned by the respective
-%% functions.
-%%
-%% `Dependencies' is a similar mapping from the labels of
-%% fun-expressions and apply-expressions in `Tree' to sets of
-%% labels of corresponding fun-expressions which may contain call
-%% sites of the functions or be called from the call sites,
-%% respectively. Any such label not defined in `Dependencies'
-%% represents an unreachable function or a dead or faulty
-%% application.
-%%
-%% `Escapes' is the set of labels of fun-expressions in `Tree' such
-%% that corresponding closures may be accessed from outside `Tree'.
-%%
-%% Note: `Tree' must be annotated with labels (as done by the
-%% function `cerl_trees:label/1') in order to use this function.
-%% The label annotation `{label, L}' (where L should be an integer)
-%% must be the first element of the annotation list of each node in
-%% the tree. Instances of variables bound in `Tree' which denote
-%% the same variable must have the same label; apart from this,
-%% labels should be unique. Constant literals do not need to be
-%% labeled.
-
--record(state, {k, vars, out, dep, work, funs, envs}).
-
-%% Note: In order to keep our domain simple, we assume that all remote
-%% calls and primops return a single value, if any.
-
-%% We wrap the given syntax tree T in a fun-expression labeled `top',
-%% which is initially in the set of escaped labels. `top' will be
-%% visited at least once.
-%%
-%% We create a separate function labeled `external', defined as:
-%% "External = fun () -> Any", which will represent any and all
-%% functions outside T, and whose return value has unknown type.
-
--type label() :: integer() | 'external' | 'top'.
--type ordset(X) :: [X]. % XXX: TAKE ME OUT
--type labelset() :: ordset(label()).
--type outlist() :: [labelset()] | 'none'.
-
--spec analyze(cerl:cerl()) -> {outlist(), dict:dict(), dict:dict()}.
-
-analyze(Tree) ->
- analyze(Tree, ?DEF_LIMIT).
-
-analyze(Tree, Limit) ->
- {_, _, Esc, Dep, Par} = cerl_closurean:analyze(Tree),
- analyze(Tree, Limit, Esc, Dep, Par).
-
-analyze(Tree, Limit, Esc0, Dep0, Par) ->
- %% Note that we use different name spaces for variable labels and
- %% function/call site labels. We assume that the labeling of Tree
- %% only uses integers, not atoms.
- LabelExtL = [{label, external}],
- External = ann_c_var(LabelExtL, {external, 1}),
- ExtFun = ann_c_fun(LabelExtL, [], ann_c_var([{label, any}], 'Any')),
-%%% io:fwrite("external fun:\n~s.\n",
-%%% [cerl_prettypr:format(ExtFun, [noann, {paper, 80}])]),
- LabelTopL = [{label, top}],
- Top = ann_c_var(LabelTopL, {top, 0}),
- TopFun = ann_c_fun(LabelTopL, [], Tree),
-
- %% The "start fun" just makes the initialisation easier. It is not
- %% itself in the call graph.
- StartFun = ann_c_fun([{label, start}], [],
- c_letrec([{External, ExtFun}, {Top, TopFun}],
- c_nil())),
-%%% io:fwrite("start fun:\n~s.\n",
-%%% [cerl_prettypr:format(StartFun, [{paper, 80}])]),
-
- %% Gather a database of all fun-expressions in Tree and initialise
- %% their outputs and parameter variables. All escaping functions can
- %% receive any values as inputs. Also add an extra dependency edge
- %% from each fun-expression label to its parent fun-expression.
-%%% io:fwrite("Escape: ~p.\n",[Esc0]),
- Esc = sets:from_list(Esc0, [{version, 2}]),
- Any = t_any(),
- None = t_none(),
- Funs0 = dict:new(),
- Vars0 = dict:store(any, Any, dict:new()),
- Out0 = dict:store(top, None,
- dict:store(external, None, dict:new())),
- Envs0 = dict:store(top, dict:new(),
- dict:store(external, dict:new(), dict:new())),
- F = fun (T, S = {Fs, Vs, Os, Es}) ->
- case type(T) of
- 'fun' ->
- L = get_label(T),
- As = fun_vars(T),
- X = case sets:is_element(L, Esc) of
- true -> Any;
- false -> None
- end,
- {dict:store(L, T, Fs),
- bind_vars_single(As, X, Vs),
- dict:store(L, None, Os),
- dict:store(L, dict:new(), Es)};
- _ ->
- S
- end
- end,
- {Funs, Vars, Out, Envs} = cerl_trees:fold(F, {Funs0, Vars0, Out0,
- Envs0}, StartFun),
-
- %% Add dependencies from funs to their parent funs.
- Dep = lists:foldl(fun ({L, L1}, D) -> add_dep(L, L1, D) end,
- Dep0, dict:to_list(Par)),
-
- %% Enter the fixpoint iteration at the StartFun.
- St = loop(TopFun, top, #state{vars = Vars,
- out = Out,
- dep = Dep,
- work = init_work(),
- funs = Funs,
- envs = Envs,
- k = Limit}),
- {dict:fetch(top, St#state.out),
- tidy_dict([top, external], St#state.out),
- tidy_dict([any], St#state.vars)}.
-
-tidy_dict([X | Xs], D) ->
- tidy_dict(Xs, dict:erase(X, D));
-tidy_dict([], D) ->
- D.
-
-loop(T, L, St0) ->
-%%% io:fwrite("analyzing: ~w.\n",[L]),
-%%% io:fwrite("work: ~w.\n", [Queue0]),
- Env = dict:fetch(L, St0#state.envs),
- X0 = dict:fetch(L, St0#state.out),
- {X1, St1} = visit(fun_body(T), Env, St0),
- X = limit(X1, St1#state.k),
- {W, M} = case equal(X0, X) of
- true ->
- {St1#state.work, St1#state.out};
- false ->
-%%% io:fwrite("out (~w) changed: ~s <- ~s.\n",
-%%% [L, erl_types:t_to_string(X),
-%%% erl_types:t_to_string(X0)]),
- M1 = dict:store(L, X, St1#state.out),
- case dict:find(L, St1#state.dep) of
- {ok, S} ->
-%%% io:fwrite("adding work: ~w.\n", [S]),
- {add_work(S, St1#state.work), M1};
- error ->
- {St1#state.work, M1}
- end
- end,
- St2 = St1#state{out = M},
- case take_work(W) of
- {ok, L1, W1} ->
- T1 = dict:fetch(L1, St2#state.funs),
- loop(T1, L1, St2#state{work = W1});
- none ->
- St2
- end.
-
-visit(T, Env, St) ->
- case type(T) of
- literal ->
- {t_from_term(concrete(T)), St};
- var ->
- %% If a variable is not already in the store at this point,
- %% we initialize it to 'none()'.
- L = get_label(T),
- Vars = St#state.vars,
- case dict:find(L, Vars) of
- {ok, X} ->
- case dict:find(var_name(T), Env) of
- {ok, X1} ->
-%%% io:fwrite("filtered variable reference: ~w:~s.\n",
-%%% [var_name(T), erl_types:t_to_string(X1)]),
- {meet(X, X1), St};
- error ->
- {X, St}
- end;
- error ->
- X = t_none(),
- Vars1 = dict:store(L, X, Vars),
- St1 = St#state{vars = Vars1},
- {X, St1}
- end;
- 'fun' ->
- %% Must revisit the fun also, because its environment might
- %% have changed. (We don't keep track of such dependencies.)
- L = get_label(T),
- Xs = [dict:fetch(get_label(V), St#state.vars)
- || V <- fun_vars(T)],
- X = dict:fetch(L, St#state.out),
- St1 = St#state{work = add_work([L], St#state.work),
- envs = dict:store(L, Env, St#state.envs)},
- {t_fun(Xs, X), St1};
- values ->
- {Xs, St1} = visit_list(values_es(T), Env, St),
- {t_product(Xs), St1};
- cons ->
- {[X1, X2], St1} = visit_list([cons_hd(T), cons_tl(T)], Env, St),
- {t_cons(X1, X2), St1};
- tuple ->
- {Xs, St1} = visit_list(tuple_es(T), Env, St),
- {t_tuple(Xs), St1};
- 'let' ->
- {X, St1} = visit(let_arg(T), Env, St),
- LetVars = let_vars(T),
- St1Vars = St1#state.vars,
- Vars = case t_is_any(X) orelse t_is_none(X) of
- true ->
- bind_vars_single(LetVars, X, St1Vars);
- false ->
- bind_vars(LetVars, t_to_tlist(X), St1Vars)
- end,
- visit(let_body(T), Env, St1#state{vars = Vars});
- seq ->
- {_, St1} = visit(seq_arg(T), Env, St),
- visit(seq_body(T), Env, St1);
- apply ->
- {_F, St1} = visit(apply_op(T), Env, St),
- {As, St2} = visit_list(apply_args(T), Env, St1),
- L = get_label(T),
- Ls = get_deps(L, St#state.dep),
- Out = St2#state.out,
- X = join_list([dict:fetch(L1, Out) || L1 <- Ls]),
- Out1 = dict:store(L, X, Out),
- {X, call_site(Ls, As, St2#state{out = Out1})};
- call ->
- M = call_module(T),
- F = call_name(T),
- As = call_args(T),
- {[X1, X2], St1} = visit_list([M, F], Env, St),
- {Xs, St2} = visit_list(As, Env, St1),
-%%% io:fwrite("call: ~w:~w(~w).\n",[X1,X2,Xs]),
- X = case {t_atom_vals(X1), t_atom_vals(X2)} of
- {[M1], [F1]} ->
- A = length(As),
-%%% io:fwrite("known call: ~w:~w/~w.\n",
-%%% [M1, F1, A]),
- call_type(M1, F1, A, Xs);
- _ ->
- t_any()
- end,
- L = get_label(T),
- {X, St2#state{out = dict:store(L, X, St2#state.out)}};
- primop ->
- As = primop_args(T),
- {Xs, St1} = visit_list(As, Env, St),
- F = atom_val(primop_name(T)),
- A = length(As),
- L = get_label(T),
- X = primop_type(F, A, Xs),
- {X, St1#state{out = dict:store(L, X, St1#state.out)}};
- 'case' ->
- {X, St1} = visit(case_arg(T), Env, St),
- Xs = case t_is_any(X) orelse t_is_none(X) of
- true ->
- [X || _ <- cerl:case_clauses(T)];
- false ->
- t_to_tlist(X)
- end,
- join_visit_clauses(Xs, case_clauses(T), Env, St1);
- 'receive' ->
- Any = t_any(),
- {X1, St1} = join_visit_clauses([Any], receive_clauses(T),
- Env, St),
- {X2, St2} = visit(receive_timeout(T), Env, St1),
- case t_is_atom(X2) andalso (t_atom_vals(X2) =:= [infinity]) of
- true ->
- {X1, St2};
- false ->
- {X3, St3} = visit(receive_action(T), Env, St2),
- {join(X1, X3), St3}
- end;
- 'try' ->
- {X, St1} = visit(try_arg(T), Env, St),
- Any = t_any(),
- Atom = t_atom(),
- TryVars = try_vars(T),
- St1Vars = St1#state.vars,
- Vars = case t_is_any(X) orelse t_is_none(X) of
- true ->
- bind_vars_single(TryVars, X, St1Vars);
- false ->
- bind_vars(TryVars, t_to_tlist(X), St1Vars)
- end,
- {X1, St2} = visit(try_body(T), Env, St1#state{vars = Vars}),
- EVars = bind_vars(try_evars(T), [Atom, Any, Any], St2#state.vars),
- {X2, St3} = visit(try_handler(T), Env, St2#state{vars = EVars}),
- {join(X1, X2), St3};
- 'catch' ->
- {_, St1} = visit(catch_body(T), Env, St),
- {t_any(), St1};
- binary ->
- {_, St1} = visit_list(binary_segments(T), Env, St),
- {t_binary(), St1};
- bitstr ->
- %% The other fields are constant literals.
- {_, St1} = visit(bitstr_val(T), Env, St),
- {_, St2} = visit(bitstr_size(T), Env, St1),
- {t_none(), St2};
- letrec ->
- %% All the bound funs should be revisited, because the
- %% environment might have changed.
- Vars = bind_defs(letrec_defs(T), St#state.vars,
- St#state.out),
- Ls = [get_label(F) || {_, F} <- letrec_defs(T)],
- St1 = St#state{work = add_work(Ls, St#state.work),
- vars = Vars},
- visit(letrec_body(T), Env, St1);
- module ->
- %% We handle a module as a sequence of function variables in
- %% the body of a `letrec'.
- {_, St1} = visit(c_letrec(module_defs(T),
- c_values(module_exports(T))),
- Env, St),
- {t_none(), St1}
- end.
-
-visit_clause(T, Xs, Env, St) ->
- Env1 = Env,
- Vars = bind_pats(clause_pats(T), Xs, St#state.vars),
- G = clause_guard(T),
- {_, St1} = visit(G, Env1, St#state{vars = Vars}),
- Env2 = guard_filters(G, Env1),
- visit(clause_body(T), Env2, St1).
-
-%% We assume correct value-list typing.
-
-visit_list([T | Ts], Env, St) ->
- {X, St1} = visit(T, Env, St),
- {Xs, St2} = visit_list(Ts, Env, St1),
- {[X | Xs], St2};
-visit_list([], _Env, St) ->
- {[], St}.
-
-join_visit_clauses(Xs, [T | Ts], Env, St) ->
- {X1, St1} = visit_clause(T, Xs, Env, St),
- {X2, St2} = join_visit_clauses(Xs, Ts, Env, St1),
- {join(X1, X2), St2};
-join_visit_clauses(_, [], _Env, St) ->
- {t_none(), St}.
-
-bind_defs([{V, F} | Ds], Vars, Out) ->
- Xs = [dict:fetch(get_label(V1), Vars) || V1 <- fun_vars(F)],
- X = dict:fetch(get_label(F), Out),
- bind_defs(Ds, dict:store(get_label(V), t_fun(Xs, X), Vars), Out);
-bind_defs([], Vars, _Out) ->
- Vars.
-
-bind_pats(Ps, Xs, Vars) ->
- if length(Xs) =:= length(Ps) ->
- bind_pats_list(Ps, Xs, Vars);
- true ->
- bind_pats_single(Ps, t_none(), Vars)
- end.
-
-bind_pats_list([P | Ps], [X | Xs], Vars) ->
- Vars1 = bind_pat_vars(P, X, Vars),
- bind_pats_list(Ps, Xs, Vars1);
-bind_pats_list([], [], Vars) ->
- Vars.
-
-bind_pats_single([P | Ps], X, Vars) ->
- bind_pats_single(Ps, X, bind_pat_vars(P, X, Vars));
-bind_pats_single([], _X, Vars) ->
- Vars.
-
-bind_pat_vars(P, X, Vars) ->
- case type(P) of
- var ->
- dict:store(get_label(P), X, Vars);
- literal ->
- Vars;
- cons ->
- case t_is_cons(X) of
- true ->
- %% If X is "nonempty proper list of X1", then the
- %% head has type X1 and the tail has type "proper
- %% list of X1". (If X is just "cons cell of X1",
- %% then both head and tail have type X1.)
- Vars1 = bind_pat_vars(cons_hd(P), t_cons_hd(X),
- Vars),
- bind_pat_vars(cons_tl(P), t_cons_tl(X), Vars1);
- false ->
- case t_is_list(X) of
- true ->
- %% If X is "proper list of X1", then the
- %% head has type X1 and the tail has type
- %% "proper list of X1", i.e., type X.
- Vars1 = bind_pat_vars(cons_hd(P),
- t_list_elements(X),
- Vars),
- bind_pat_vars(cons_tl(P), X, Vars1);
- false ->
- case t_is_maybe_improper_list(X) of
- true ->
- %% If X is "cons cell of X1", both
- %% the head and tail have type X1.
- X1 = t_list_elements(X),
- Vars1 = bind_pat_vars(cons_hd(P),
- X1, Vars),
- bind_pat_vars(cons_tl(P), X1,
- Vars1);
- false ->
- bind_vars_single(pat_vars(P),
- top_or_bottom(X),
- Vars)
- end
- end
- end;
- tuple ->
- case t_is_tuple(X) of
- true ->
- case t_tuple_subtypes(X) of
- unknown ->
- bind_vars_single(pat_vars(P), top_or_bottom(X),
- Vars);
- [Tuple] ->
- case t_tuple_size(Tuple) =:= tuple_arity(P) of
- true ->
- bind_pats_list(tuple_es(P),
- t_tuple_args(Tuple), Vars);
-
- false ->
- bind_vars_single(pat_vars(P),
- top_or_bottom(X), Vars)
- end;
- List when is_list(List) ->
- bind_vars_single(pat_vars(P), top_or_bottom(X),
- Vars)
- end;
- false ->
- bind_vars_single(pat_vars(P), top_or_bottom(X), Vars)
- end;
- binary ->
- bind_pats_single(binary_segments(P), t_none(), Vars);
- bitstr ->
- %% Only the Value field is a new binding. Size is already
- %% bound, and the other fields are constant literals.
- %% We could create a filter for Size being an integer().
- Size = bitstr_size(P),
- ValType =
- case concrete(bitstr_type(P)) of
- float -> t_float();
- binary -> t_binary();
- integer ->
- case is_c_int(Size) of
- false -> t_integer();
- true ->
- SizeVal = int_val(Size),
- Flags = concrete(bitstr_flags(P)),
- case lists:member(signed, Flags) of
- true ->
- t_from_range(-(1 bsl (SizeVal - 1)),
- 1 bsl (SizeVal - 1) - 1);
- false ->
- t_from_range(0,1 bsl SizeVal - 1)
- end
- end
- end,
- bind_pat_vars(bitstr_val(P), ValType, Vars);
- alias ->
- P1 = alias_pat(P),
- Vars1 = bind_pat_vars(P1, X, Vars),
- dict:store(get_label(alias_var(P)), pat_type(P1, Vars1),
- Vars1)
- end.
-
-pat_type(P, Vars) ->
- case type(P) of
- var ->
- dict:fetch(get_label(P), Vars);
- literal ->
- t_from_term(concrete(P));
- cons ->
- t_cons(pat_type(cons_hd(P), Vars),
- pat_type(cons_tl(P), Vars));
- tuple ->
- t_tuple([pat_type(E, Vars) || E <- tuple_es(P)]);
- binary ->
- t_binary();
- alias ->
- pat_type(alias_pat(P), Vars)
- end.
-
-bind_vars(Vs, Xs, Vars) ->
- if length(Vs) =:= length(Xs) ->
- bind_vars_list(Vs, Xs, Vars);
- true ->
- bind_vars_single(Vs, t_none(), Vars)
- end.
-
-bind_vars_list([V | Vs], [X | Xs], Vars) ->
- bind_vars_list(Vs, Xs, dict:store(get_label(V), X, Vars));
-bind_vars_list([], [], Vars) ->
- Vars.
-
-bind_vars_single([V | Vs], X, Vars) ->
- bind_vars_single(Vs, X, dict:store(get_label(V), X, Vars));
-bind_vars_single([], _X, Vars) ->
- Vars.
-
-add_dep(Source, Target, Deps) ->
- case dict:find(Source, Deps) of
- {ok, X} ->
- case set__is_member(Target, X) of
- true ->
- Deps;
- false ->
-%%% io:fwrite("new dep: ~w <- ~w.\n", [Target, Source]),
- dict:store(Source, set__add(Target, X), Deps)
- end;
- error ->
-%%% io:fwrite("new dep: ~w <- ~w.\n", [Target, Source]),
- dict:store(Source, set__singleton(Target), Deps)
- end.
-
-%% This handles a call site, updating parameter variables with respect
-%% to the actual parameters.
-
-call_site(Ls, Xs, St) ->
-%% io:fwrite("call site: ~w ~s.\n",
-%% [Ls, erl_types:t_to_string(erl_types:t_product(Xs))]),
- {W, V} = call_site(Ls, Xs, St#state.work, St#state.vars,
- St#state.funs, St#state.k),
- St#state{work = W, vars = V}.
-
-call_site([L | Ls], Xs, W, V, Fs, Limit) ->
- Vs = fun_vars(dict:fetch(L, Fs)),
- case bind_args(Vs, Xs, V, Limit) of
- {V1, true} ->
- call_site(Ls, Xs, add_work([L], W), V1, Fs, Limit);
- {V1, false} ->
- call_site(Ls, Xs, W, V1, Fs, Limit)
- end;
-call_site([], _, W, V, _, _) ->
- {W, V}.
-
-%% If the arity does not match the call, nothing is done here.
-
-bind_args(Vs, Xs, Vars, Limit) ->
- if length(Vs) =:= length(Xs) ->
- bind_args(Vs, Xs, Vars, Limit, false);
- true ->
- {Vars, false}
- end.
-
-bind_args([V | Vs], [X | Xs], Vars, Limit, Ch) ->
- L = get_label(V),
- {Vars1, Ch1} = bind_arg(L, X, Vars, Limit, Ch),
- bind_args(Vs, Xs, Vars1, Limit, Ch1);
-bind_args([], [], Vars, _Limit, Ch) ->
- {Vars, Ch}.
-
-%% bind_arg(L, X, Vars, Limit) ->
-%% bind_arg(L, X, Vars, Limit, false).
-
-bind_arg(L, X, Vars, Limit, Ch) ->
- X0 = dict:fetch(L, Vars),
- X1 = limit(join(X, X0), Limit),
- case equal(X0, X1) of
- true ->
- {Vars, Ch};
- false ->
-%%% io:fwrite("arg (~w) changed: ~s <- ~s + ~s.\n",
-%%% [L, erl_types:t_to_string(X1),
-%%% erl_types:t_to_string(X0),
-%%% erl_types:t_to_string(X)]),
- {dict:store(L, X1, Vars), true}
- end.
-
-%% Domain: type(), defined in module `erl_types'.
-
-meet(X, Y) -> t_inf(X, Y).
-
-join(X, Y) -> t_sup(X, Y).
-
-join_list([Xs | Xss]) ->
- join(Xs, join_list(Xss));
-join_list([]) ->
- t_none().
-
-equal(X, Y) -> X =:= Y.
-
-limit(X, K) -> t_limit(X, K).
-
-top_or_bottom(T) ->
- case t_is_none(T) of
- true ->
- T;
- false ->
- t_any()
- end.
-
-strict(Xs, T) ->
- case erl_types:any_none(Xs) of
- true ->
- t_none();
- false ->
- T
- end.
-
-%% Set abstraction for label sets.
-
-%% set__new() -> [].
-
-set__singleton(X) -> [X].
-
-%% set__to_list(S) -> S.
-
-%% set__from_list(S) -> ordsets:from_list(S).
-
-%% set__union(X, Y) -> ordsets:union(X, Y).
-
-set__add(X, S) -> ordsets:add_element(X, S).
-
-set__is_member(X, S) -> ordsets:is_element(X, S).
-
-%% set__subtract(X, Y) -> ordsets:subtract(X, Y).
-
-%% set__equal(X, Y) -> X =:= Y.
-
-%% A simple but efficient functional queue.
-
-queue__new() -> {[], []}.
-
-queue__put(X, {In, Out}) -> {[X | In], Out}.
-
-queue__get({In, [X | Out]}) -> {ok, X, {In, Out}};
-queue__get({[], _}) -> empty;
-queue__get({In, _}) ->
- [X | In1] = lists:reverse(In),
- {ok, X, {[], In1}}.
-
-%% The work list - a queue without repeated elements.
-
-init_work() ->
- {queue__put(external, queue__new()), sets:new([{version, 2}])}.
-
-add_work(Ls, {Q, Set}) ->
- add_work(Ls, Q, Set).
-
-%% Note that the elements are enqueued in order.
-
-add_work([L | Ls], Q, Set) ->
- case sets:is_element(L, Set) of
- true ->
- add_work(Ls, Q, Set);
- false ->
- add_work(Ls, queue__put(L, Q), sets:add_element(L, Set))
- end;
-add_work([], Q, Set) ->
- {Q, Set}.
-
-take_work({Queue0, Set0}) ->
- case queue__get(Queue0) of
- {ok, L, Queue1} ->
- Set1 = sets:del_element(L, Set0),
- {ok, L, {Queue1, Set1}};
- empty ->
- none
- end.
-
-get_deps(L, Dep) ->
- case dict:find(L, Dep) of
- {ok, Ls} -> Ls;
- error -> []
- end.
-
-%% Type information for built-in functions. We do not check that the
-%% arguments have the correct type; if the call would actually fail,
-%% rather than return a value, this is a safe overapproximation.
-
-primop_type(match_fail, 1, _) -> t_none();
-primop_type(_, _, Xs) -> strict(Xs, t_any()).
-
-call_type(M, F, A, Xs) ->
- erl_bif_types:type(M, F, A, Xs).
-
-guard_filters(T, Env) ->
- guard_filters(T, Env, dict:new()).
-
-guard_filters(T, Env, Vars) ->
- case type(T) of
- call ->
- M = call_module(T),
- F = call_name(T),
- case is_c_atom(M) andalso is_c_atom(F) of
- true ->
- As = call_args(T),
- case {atom_val(M), atom_val(F), length(As)} of
- {erlang, 'and', 2} ->
- [A1, A2] = As,
- guard_filters(A1, guard_filters(A2, Env));
- {erlang, is_atom, 1} ->
- filter(As, t_atom(), Env);
- {erlang, is_binary, 1} ->
- filter(As, t_binary(), Env);
- {erlang, is_float, 1} ->
- filter(As, t_float(), Env);
- {erlang, is_function, 1} ->
- filter(As, t_fun(), Env);
- {erlang, is_integer, 1} ->
- filter(As, t_integer(), Env);
- {erlang, is_list, 1} ->
- filter(As, t_maybe_improper_list(), Env);
- {erlang, is_number, 1} ->
- filter(As, t_number(), Env);
- {erlang, is_pid, 1} ->
- filter(As, t_pid(), Env);
- {erlang, is_port, 1} ->
- filter(As, t_port(), Env);
- {erlang, is_reference, 1} ->
- filter(As, t_reference(), Env);
- {erlang, is_tuple, 1} ->
- filter(As, t_tuple(), Env);
- _ ->
- Env
- end;
- false ->
- Env
- end;
- var ->
- case dict:find(var_name(T), Vars) of
- {ok, T1} ->
- guard_filters(T1, Env, Vars);
- error ->
- Env
- end;
- 'let' ->
- case let_vars(T) of
- [V] ->
- guard_filters(let_body(T), Env,
- dict:store(var_name(V), let_arg(T),
- Vars));
- _ ->
- Env
- end;
- values ->
- case values_es(T) of
- [T1] ->
- guard_filters(T1, Env, Vars);
- _ ->
- Env
- end;
- _ ->
- Env
- end.
-
-filter(As, X, Env) ->
- [A] = As,
- case type(A) of
- var ->
- V = var_name(A),
- case dict:find(V, Env) of
- {ok, X1} ->
- dict:store(V, meet(X, X1), Env);
- error ->
- dict:store(V, X, Env)
- end;
- _ ->
- Env
- end.
-
-%% Callback hook for cerl_prettypr:
-
--spec pp_hook() -> fun((cerl:cerl(), _, fun((_,_) -> any())) -> any()).
-
-pp_hook() ->
- fun pp_hook/3.
-
-pp_hook(Node, Ctxt, Cont) ->
- As = cerl:get_ann(Node),
- As1 = proplists:delete(type, proplists:delete(label, As)),
- As2 = proplists:delete(typesig, proplists:delete(file, As1)),
- D = Cont(cerl:set_ann(Node, []), Ctxt),
- T = case proplists:lookup(type, As) of
- {type, T0} -> T0;
- none ->
- case proplists:lookup(typesig, As) of
- {typesig, T0} -> T0;
- none -> t_any()
- end
- end,
- D1 = case erl_types:t_is_any(T) of
- true ->
- D;
- false ->
- case cerl:is_literal(Node) of
- true ->
- D;
- false ->
- S = erl_types:t_to_string(T),
- Q = prettypr:beside(prettypr:text("::"),
- prettypr:text(S)),
- prettypr:beside(D, Q)
- end
- end,
- cerl_prettypr:annotate(D1, As2, Ctxt).
-
-%% =====================================================================
diff --git a/lib/dialyzer/src/dialyzer.app.src b/lib/dialyzer/src/dialyzer.app.src
index 3090895190..93d004d2e7 100644
--- a/lib/dialyzer/src/dialyzer.app.src
+++ b/lib/dialyzer/src/dialyzer.app.src
@@ -22,11 +22,7 @@
{application, dialyzer,
[{description, "DIscrepancy AnaLYZer of ERlang programs, version %VSN%"},
{vsn, "%VSN%"},
- {modules, [cerl_closurean,
- cerl_lib,
- cerl_pmatch,
- cerl_prettypr,
- cerl_typean,
+ {modules, [cerl_prettypr,
dialyzer,
dialyzer_analysis_callgraph,
dialyzer_behaviours,
--
2.31.1