mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-18 10:53:40 +00:00
compiler/prog_data.m:
Delete the simple_call_id data type, since it is isomorphic
to the pf_sym_name_arity type, which more clearly specifies
what is stored inside it.
compiler/prog_out.m:
Rename (all three versions of) simple_call_id_to_string to
pf_sym_name_orig_arity_to_string, both to conform to the change
in the input data type, and to emphasize that the resulting string
will contain the *original* arity of functions (which does not include
the return value), which is one less than the arity in the
pf_sym_name_arity structure (which, in accordance with the
convention inside the compiler that the arity is the length
of the argument list, *does* include the return value).
Delete the provision inside simple_call_id_to_string, now
pf_sym_name_orig_arity_to_string, for special handling of the names
of the predicates we use to implement promises, because it seems that
*none* of the call sites to any of the three versions of this function
can actually pass to it the identity of such a predicate. These calls
refer to a predicate or mode declaration item (which promise predicates
do not have), to clause or foreign_proc items (which again, promise
predicates cannot have) or calls to the predicate (promise predicates
cannot be called).
Delete the exported predicate simple_call_id_to_sym_name_arity.
It was called from exactly one place, inside prog_out.m itself,
and this diff inlines that call.
Avoid unnecessary forwarding of work from prog_out.m to error_util.m.
Delete (all three versions of) write_simple_call_id. The changes
below replace all their (few) uses with calls to
pf_sym_name_orig_arity_to_string.
compiler/error_util.m:
Replace the simple_call() error piece with qual_pf_sym_name_orig_arity,
and add a new version unqual_pf_sym_name_orig_arity. Their names
explicitly say that they print the original arities of functions,
also say whether they strip away any module qualification on the
sym_name inside the pf_sym_name_arity. We prefer the unqual version
in situations where the module qualifier is implicit, which usually means
that it must be the same as the name of the module being compiled, because
it reduces visual clutter for readers of error messages.
Put {qual,unqual}_pf_sym_name_orig_arity next to their most closely
related function symbols, {qual,unqual}_sym_name_arity, in the
format_component type. (This yields a few inconsequential changes
in the order of error_specs when sorted.)
compiler/add_class.m:
compiler/add_clause.m:
compiler/add_foreign_proc.m:
compiler/add_pragma.m:
compiler/add_pragma_tabling.m:
compiler/add_pragma_type_spec.m:
compiler/add_pred.m:
compiler/check_libgrades.m:
compiler/check_parse_tree_type_defns.m:
compiler/check_promise.m:
compiler/check_typeclass.m:
compiler/convert_parse_tree.m:
compiler/equiv_type.m:
compiler/goal_expr_to_goal.m:
compiler/hlds_data.m:
compiler/hlds_desc.m:
compiler/hlds_goal.m:
compiler/hlds_out_pred.m:
compiler/hlds_out_util.m:
compiler/hlds_pred.m:
compiler/inst_util.m:
compiler/llds_out_instr.m:
compiler/make.module_target.m:
compiler/make.program_target.m:
compiler/make_hlds_error.m:
compiler/make_hlds_warn.m:
compiler/mark_tail_calls.m:
compiler/ml_unify_gen_construct.m:
compiler/mlds_to_c_stmt.m:
compiler/mode_errors.m:
compiler/mode_info.m:
compiler/modecheck_util.m:
compiler/module_qual.collect_mq_info.m:
compiler/module_qual.qualify_items.m:
compiler/parse_item.m:
compiler/parse_module.m:
compiler/parse_type_repn.m:
compiler/polymorphism.m:
compiler/post_typecheck.m:
compiler/pre_quantification.m:
compiler/pred_table.m:
compiler/prog_item.m:
compiler/recompilation.version.m:
compiler/term_constr_build.m:
compiler/termination.m:
compiler/type_ctor_info.m:
compiler/typecheck.m:
compiler/typecheck_errors.m:
compiler/typecheck_info.m:
compiler/unify_gen_construct.m:
compiler/unused_imports.m:
Conform to the changes above. Where I am pretty sure that in an error
message, the module qualifier of a name must be the current module,
use unqual_pf_sym_name_orig_arity instead of qual_pf_sym_name_orig_arity.
Add some sanity checks where they seem appropriate.
Replace sequences of io.write_strings with uses of io.format where this
yields clearer code. (This is why there were no remaining calls to
write_simple_call_id.)
Shorten some too-long lines.
In add_pred.m, make the order of some predicate definitions match
the order of the calls to them.
tests/invalid/ambiguous_method.err_exp:
tests/invalid/ambiguous_method_2.err_exp:
tests/invalid/bad_pred_arity.err_exp:
tests/invalid/bad_sv_unify_msg.err_exp:
tests/invalid/bigtest.err_exp:
tests/invalid/bug113.err_exp:
tests/invalid/bug197.err_exp:
tests/invalid/bug278.err_exp:
tests/invalid/bug410.err_exp:
tests/invalid/bug476.err_exp:
tests/invalid/bug487.err_exp:
tests/invalid/complex_constraint_err.err_exp:
tests/invalid/constrained_poly_insts.err_exp:
tests/invalid/errors.err_exp:
tests/invalid/errors1.err_exp:
tests/invalid/errors2.err_exp:
tests/invalid/exported_mode.err_exp:
tests/invalid/field_syntax_error.err_exp:
tests/invalid/foreign_singleton.err_exp:
tests/invalid/funcs_as_preds.err_exp:
tests/invalid/imported_mode.err_exp:
tests/invalid/invalid_binary_literal.err_exp:
tests/invalid/invalid_float_literal.err_exp:
tests/invalid/invalid_hex_literal.err_exp:
tests/invalid/invalid_octal_literal.err_exp:
tests/invalid/make_opt_error.err_exp:
tests/invalid/missing_det_decls.err_exp:
tests/invalid/multimode_syntax.err_exp:
tests/invalid/null_char.err_exp:
tests/invalid/occurs.err_exp:
tests/invalid/record_syntax_errors.err_exp:
tests/invalid/ref_to_implicit_pred.err_exp:
tests/invalid/require_tailrec_1.err_exp:
tests/invalid/require_tailrec_1.err_exp2:
tests/invalid/require_tailrec_2.err_exp:
tests/invalid/require_tailrec_2.err_exp2:
tests/invalid/require_tailrec_3.err_exp:
tests/invalid/require_tailrec_3.err_exp2:
tests/invalid/state_vars_test2.err_exp:
tests/invalid/state_vars_test3.err_exp:
tests/invalid/state_vars_test5.err_exp:
tests/invalid/type_inf_loop.err_exp:
tests/invalid/typeclass_constraint_extra_var.err_exp:
tests/invalid/typeclass_mode_2.err_exp:
tests/invalid/typeclass_test_12.err_exp:
tests/invalid/typeclass_test_2.err_exp:
tests/invalid/typeclass_test_9.err_exp:
tests/invalid/types.err_exp:
tests/invalid/types2.err_exp:
tests/invalid/unbound_type_vars.err_exp:
tests/invalid/with_type.err_exp:
tests/invalid_purity/purity_nonsense.err_exp:
tests/invalid_purity/purity_nonsense2.err_exp:
tests/warnings/double_underscore.exp:
tests/warnings/pragma_source_file.exp:
tests/warnings/singleton_test.exp:
tests/warnings/singleton_test.exp2:
tests/warnings/singleton_test.exp3:
tests/warnings/singleton_test.exp4:
tests/warnings/singleton_test_state_var.exp:
tests/warnings/warn_return.exp:
tests/warnings/warn_return.exp2:
tests/warnings/warn_return.exp3:
tests/warnings/warn_succ_ind.exp:
tests/warnings/warn_succ_ind.exp2:
tests/warnings/warn_succ_ind.exp3:
tests/warnings/warn_succ_ind.exp4:
Update all these expected output files. Most changes are due to
predicate and function names no longer being module qualified when
the module qualification is obvious. A few changes are due to the
change in the relative ordering of the function symbols of the
format_component type.
For singleton_test, warn_return and warn_succ_ind in warnings,
the changes to the non-C .exp files were done by hand, so they
may have the wrong white space.
506 lines
21 KiB
Mathematica
506 lines
21 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2017 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: pre_quantification.m.
|
|
% Main author: zs.
|
|
%
|
|
% This module adds implicit quantifications of some variables just inside
|
|
% trace goal scopes in order to avoid a minor but annoying problem, which can
|
|
% also be confusing for people who come across it the first time.
|
|
%
|
|
% Consider the following code:
|
|
%
|
|
% :- pred p(int::in, int::in, int::out) is det.
|
|
%
|
|
% p(A, B, X) :-
|
|
% trace [io(!IO)] (
|
|
% ToPrint = A,
|
|
% io.format("A = %d\n", [i(ToPrint)], !IO)
|
|
% ),
|
|
% trace [io(!IO)] (
|
|
% ToPrint = B,
|
|
% io.format("B = %d\n", [i(ToPrint)], !IO)
|
|
% ),
|
|
% X = A + B,
|
|
% trace [io(!IO)] (
|
|
% ToPrint = X,
|
|
% io.format("X = %d\n", [i(ToPrint)], !IO)
|
|
% ).
|
|
%
|
|
% Since the variable ToPrint is used inside more than one trace goal,
|
|
% the compiler's usual scope rules consider it to be a nonlocal variable
|
|
% in each trace goal. This is a problem, because the compiler does not allow
|
|
% trace goals to bind variables that are not local to the trace goal.
|
|
% It does this to preserve the ability of the program to work whether or not
|
|
% the trace goal is there, since it can be deleted if either its compile_time
|
|
% or run_time condition turns out to be false.
|
|
%
|
|
% However, in cases like this, where the nonlocal variable occurs *only*
|
|
% inside trace goals, leaving ToPrint unbound if a trace goal is deleted
|
|
% will not affect the execution of the program. Having it bound more than once
|
|
% if the trace goals *aren't* deleted would be a problem, but this is easily
|
|
% fixed by making ToPrint existentially quantified inside each trace goal;
|
|
% that way, each trace goal will bind and use its own copy of ToPrint.
|
|
%
|
|
% The job of this module is to find out which variables in a clause body
|
|
% are used only inside trace goals, and to insert the quantifications
|
|
% into trace goals as necessary to ensure that all such variables
|
|
% will end up being local to each trace goal scope. The part of the compiler
|
|
% that ensures this is the quantification pass, which is invoked on the
|
|
% goal we generate pretty much immediately after we return it.
|
|
%
|
|
% The overall effect of putting this pass before the quantification pass
|
|
% is a minor tweak of the usual rules of quantification, a tweak that
|
|
% makes the rules enforced by the compiler resemble the rules expected
|
|
% by programmers more closely, helping the compiler approach closer to the
|
|
% principle of least astonishment.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module hlds.pre_quantification.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_goal.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred separate_trace_goal_only_locals(hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module counter.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module sparse_bitset.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% The pre-quantification algorithm is based on dividing the clause body
|
|
% into one or more zones, each identified by a non-negative integer.
|
|
% The zones are based on viewing the clause body as a tree:
|
|
%
|
|
% - with each goal being a node,
|
|
% - atomic goals being leaf nodes, and
|
|
% - each compound goal having a subtree for each subgoal.
|
|
%
|
|
% If lambda goals didn't exist, each zone would be identified by its top node.
|
|
%
|
|
% The top node of zone 0 is the root of the tree, while the top node of
|
|
% every other zone is a trace goal, i.e. a scope goal whose scope_reason
|
|
% is trace_goal. Every trace goal is the top node of a zone, with the
|
|
% exception of trace goals that are contained inside other trace goals.
|
|
% (We have no rule against nesting trace goals, though they don't make
|
|
% much sense, and I (zs) have never seen such nesting.)
|
|
%
|
|
% The zone that each node belongs to is the zone whose top node you first find
|
|
% when you traverse the tree upwards from the node. In other words, each zone
|
|
% claims the nodes beneath it, except the ones that are claimed by another
|
|
% zone whose top node is lower down.
|
|
%
|
|
% Since lambda goals do exist, we have to handle them. We do so by considering
|
|
% the lambda goal to be effectively a separate predicate body inside this
|
|
% clause (since the compiler will eventually turn it into that). So the
|
|
% top level of a rhs_lambda_goal is also considered to be in zone 0.
|
|
|
|
:- type zone == int.
|
|
% We could make a zone a uint, but there are issues around the uint type's
|
|
% membership of the enum typeclass, which is needed for the use of
|
|
% sparse_bitset, as discussed on m-rev on 2017 july 3.
|
|
|
|
:- func top_zone = zone.
|
|
:- pragma inline(top_zone/0).
|
|
|
|
top_zone = 0.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% The pre-quantification algorithm has three main steps.
|
|
%
|
|
% - The first step is to find out which variables are used in which zones.
|
|
% This is done by build_vars_to_zones_in_*, which build the VarsToZones map.
|
|
%
|
|
% - The second step is finding out which variables occur only inside
|
|
% trace goals, not outside them. These variables should be local to the
|
|
% trace goals they appear in. If they occur in only one trace goal,
|
|
% ordinary quantification will ensure this. We need to add explicit
|
|
% existential quantifications for them only if they occur inside
|
|
% more than one trace goal. In terms of zones, this means variables
|
|
% that don't occur in zone 0 but do appear in more than one other zone.
|
|
% (We call these variables the "duplicated" variables.) The job of
|
|
% build_zones_to_dup_vars is to map each trace goal zone to the list
|
|
% of variables that needs to have an existential quantifier added for it
|
|
% in the scope of that trace goal, and to return that as ZonesToDupVars.
|
|
%
|
|
% - The third step is to go through the clause body and insert those
|
|
% existential quantification scopes inside every trace goal scope
|
|
% that contains one or more of these "duplicated" variables.
|
|
%
|
|
|
|
% Our parent ensures that we don't get called on clauses that
|
|
% don't contain any trace goals. Most clauses that have trace goals
|
|
% will have only a few of them, so the sparse_bitset(zone) will
|
|
% typically contain only a single node.
|
|
%
|
|
:- type vars_to_zones == map(prog_var, sparse_bitset(zone)).
|
|
|
|
:- type zones_to_dup_vars == map(zone, list(prog_var)).
|
|
|
|
separate_trace_goal_only_locals(Goal0, Goal) :-
|
|
map.init(VarsToZones0),
|
|
counter.init(1, TraceCounter0),
|
|
build_vars_to_zones_in_goal(top_zone, Goal0, TraceCounter0, _,
|
|
VarsToZones0, VarsToZones),
|
|
map.init(ZonesToDupVars0),
|
|
map.foldl(build_zones_to_dup_vars, VarsToZones,
|
|
ZonesToDupVars0, ZonesToDupVars),
|
|
( if map.is_empty(ZonesToDupVars) then
|
|
Goal = Goal0
|
|
else
|
|
add_exist_scopes_for_dup_vars_in_goal(top_zone, ZonesToDupVars,
|
|
Goal0, Goal, TraceCounter0, _)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The first step described in the comment above
|
|
% separate_trace_goal_only_locals.
|
|
%
|
|
:- pred build_vars_to_zones_in_goal(zone::in, hlds_goal::in,
|
|
counter::in, counter::out, vars_to_zones::in, vars_to_zones::out) is det.
|
|
|
|
build_vars_to_zones_in_goal(CurZone, Goal, !TraceCounter, !VarsToZones) :-
|
|
Goal = hlds_goal(GoalExpr, _GoalInfo),
|
|
(
|
|
GoalExpr = plain_call(_PredId, _ProcId, ArgVars, _Builtin,
|
|
_MaybeUnifyContext, _SymName),
|
|
record_vars_in_zone(CurZone, ArgVars, !VarsToZones)
|
|
;
|
|
GoalExpr = generic_call(GenericCall, ArgVars, _Modes,
|
|
_RegTypes, _Detism),
|
|
record_vars_in_zone(CurZone, ArgVars, !VarsToZones),
|
|
(
|
|
GenericCall = higher_order(ClosureVar, _Purity, _CallKind, _Arity),
|
|
record_var_in_zone(CurZone, ClosureVar, !VarsToZones)
|
|
;
|
|
GenericCall = class_method(TypeClassInfoVar, _MethodNum,
|
|
_ClassId, _PFSymNameArity),
|
|
record_var_in_zone(CurZone, TypeClassInfoVar, !VarsToZones)
|
|
;
|
|
GenericCall = event_call(_)
|
|
;
|
|
GenericCall = cast(_)
|
|
)
|
|
;
|
|
GoalExpr = call_foreign_proc(_Attrs, _PredId, _ProcId,
|
|
ForeignArgs, ExtraArgs, _TraceCond, _Impl),
|
|
record_vars_in_zone(CurZone, list.map(foreign_arg_var, ForeignArgs),
|
|
!VarsToZones),
|
|
record_vars_in_zone(CurZone, list.map(foreign_arg_var, ExtraArgs),
|
|
!VarsToZones)
|
|
;
|
|
GoalExpr = unify(LHSVar, RHS, _Mode, _Kind, _Context),
|
|
record_var_in_zone(CurZone, LHSVar, !VarsToZones),
|
|
(
|
|
RHS = rhs_var(RHSVar),
|
|
record_var_in_zone(CurZone, RHSVar, !VarsToZones)
|
|
;
|
|
RHS = rhs_functor(_ConsId, _IsExistConstr, RHSArgVars),
|
|
record_vars_in_zone(CurZone, RHSArgVars, !VarsToZones)
|
|
;
|
|
RHS = rhs_lambda_goal(_Purity, _Groundness, _PredOrFunc,
|
|
_EvalMethod, _NonLocals, LambdaVars, _LambdaModes, _Detism,
|
|
LambdaGoal),
|
|
% The lambda goal is the one clause of the procedure that
|
|
% we will construct from LambdaGoal. We therefore treat it
|
|
% as we treat the top level goal.
|
|
LambdaZone = top_zone,
|
|
record_vars_in_zone(LambdaZone, LambdaVars, !VarsToZones),
|
|
build_vars_to_zones_in_goal(LambdaZone, LambdaGoal,
|
|
!TraceCounter, !VarsToZones)
|
|
)
|
|
;
|
|
( GoalExpr = conj(_ConjType, SubGoals)
|
|
; GoalExpr = disj(SubGoals)
|
|
),
|
|
list.foldl2(build_vars_to_zones_in_goal(CurZone), SubGoals,
|
|
!TraceCounter, !VarsToZones)
|
|
;
|
|
GoalExpr = switch(Var, _CanFail, Cases),
|
|
record_var_in_zone(CurZone, Var, !VarsToZones),
|
|
list.foldl2(build_vars_to_zones_in_case(CurZone), Cases,
|
|
!TraceCounter, !VarsToZones)
|
|
;
|
|
GoalExpr = negation(SubGoal),
|
|
build_vars_to_zones_in_goal(CurZone, SubGoal,
|
|
!TraceCounter, !VarsToZones)
|
|
;
|
|
GoalExpr = scope(Reason, SubGoal),
|
|
( if
|
|
Reason = trace_goal(_, _, _, _, _),
|
|
CurZone = top_zone
|
|
then
|
|
counter.allocate(NewZone, !TraceCounter),
|
|
build_vars_to_zones_in_goal(NewZone, SubGoal,
|
|
!TraceCounter, !VarsToZones)
|
|
else
|
|
build_vars_to_zones_in_goal(CurZone, SubGoal,
|
|
!TraceCounter, !VarsToZones)
|
|
)
|
|
;
|
|
GoalExpr = if_then_else(Vars, Cond, Then, Else),
|
|
record_vars_in_zone(CurZone, Vars, !VarsToZones),
|
|
build_vars_to_zones_in_goal(CurZone, Cond,
|
|
!TraceCounter, !VarsToZones),
|
|
build_vars_to_zones_in_goal(CurZone, Then,
|
|
!TraceCounter, !VarsToZones),
|
|
build_vars_to_zones_in_goal(CurZone, Else,
|
|
!TraceCounter, !VarsToZones)
|
|
;
|
|
GoalExpr = shorthand(ShortHand),
|
|
(
|
|
ShortHand = bi_implication(SubGoalA, SubGoalB),
|
|
build_vars_to_zones_in_goal(CurZone, SubGoalA,
|
|
!TraceCounter, !VarsToZones),
|
|
build_vars_to_zones_in_goal(CurZone, SubGoalB,
|
|
!TraceCounter, !VarsToZones)
|
|
;
|
|
ShortHand = atomic_goal(_GoalType, OuterVars, InnerVars,
|
|
MaybeOutputVars, MainGoal, OrElseGoals, _OrElseInners),
|
|
OuterVars = atomic_interface_vars(OVA, OVB),
|
|
InnerVars = atomic_interface_vars(IVA, IVB),
|
|
record_vars_in_zone(CurZone, [OVA, OVB, IVA, IVB], !VarsToZones),
|
|
(
|
|
MaybeOutputVars = no
|
|
;
|
|
MaybeOutputVars = yes(OutputVars),
|
|
record_vars_in_zone(CurZone, OutputVars, !VarsToZones)
|
|
),
|
|
build_vars_to_zones_in_goal(CurZone, MainGoal,
|
|
!TraceCounter, !VarsToZones),
|
|
list.foldl2(build_vars_to_zones_in_goal(CurZone), OrElseGoals,
|
|
!TraceCounter, !VarsToZones)
|
|
;
|
|
ShortHand = try_goal(MaybeIOStateVars, ResultVar, SubGoal),
|
|
(
|
|
MaybeIOStateVars = no
|
|
;
|
|
MaybeIOStateVars = yes(IOStateVars),
|
|
IOStateVars = try_io_state_vars(ISVA, ISVB),
|
|
record_vars_in_zone(CurZone, [ISVA, ISVB], !VarsToZones)
|
|
),
|
|
record_var_in_zone(CurZone, ResultVar, !VarsToZones),
|
|
build_vars_to_zones_in_goal(CurZone, SubGoal,
|
|
!TraceCounter, !VarsToZones)
|
|
)
|
|
).
|
|
|
|
:- pred build_vars_to_zones_in_case(zone::in, case::in,
|
|
counter::in, counter::out, vars_to_zones::in, vars_to_zones::out) is det.
|
|
|
|
build_vars_to_zones_in_case(CurZone, Case, !TraceCounter, !VarsToZones) :-
|
|
Case = case(_MainConsId, _OtherConsIds, Goal),
|
|
build_vars_to_zones_in_goal(CurZone, Goal, !TraceCounter, !VarsToZones).
|
|
|
|
:- pred record_vars_in_zone(zone::in, list(prog_var)::in,
|
|
vars_to_zones::in, vars_to_zones::out) is det.
|
|
|
|
record_vars_in_zone(_Zone, [], !VarsToZones).
|
|
record_vars_in_zone(Zone, [Var | Vars], !VarsToZones) :-
|
|
record_var_in_zone(Zone, Var, !VarsToZones),
|
|
record_vars_in_zone(Zone, Vars, !VarsToZones).
|
|
|
|
:- pred record_var_in_zone(zone::in, prog_var::in,
|
|
vars_to_zones::in, vars_to_zones::out) is det.
|
|
|
|
record_var_in_zone(Zone, Var, !VarsToZones) :-
|
|
( if map.search(!.VarsToZones, Var, Zones0) then
|
|
% Most variables will occur only in one zone, typically zone 0.
|
|
% However, they will typically occur there more than once.
|
|
% We don't want to set the bit of the zone more than once.
|
|
( if sparse_bitset.contains(Zones0, Zone) then
|
|
true
|
|
else
|
|
sparse_bitset.insert(Zone, Zones0, Zones),
|
|
map.det_update(Var, Zones, !VarsToZones)
|
|
)
|
|
else
|
|
Zones = sparse_bitset.make_singleton_set(Zone),
|
|
map.det_insert(Var, Zones, !VarsToZones)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The second step described in the comment above
|
|
% separate_trace_goal_only_locals.
|
|
%
|
|
:- pred build_zones_to_dup_vars(prog_var::in, sparse_bitset(zone)::in,
|
|
zones_to_dup_vars::in, zones_to_dup_vars::out) is det.
|
|
|
|
build_zones_to_dup_vars(Var, Zones, !ZonesToDupVars) :-
|
|
ZoneList = sparse_bitset.to_sorted_list(Zones),
|
|
( if
|
|
ZoneList = [FirstZone, _SecondZone | _],
|
|
FirstZone \= top_zone
|
|
then
|
|
list.foldl(add_var_to_zone(Var), ZoneList, !ZonesToDupVars)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred add_var_to_zone(prog_var::in, zone::in,
|
|
zones_to_dup_vars::in, zones_to_dup_vars::out) is det.
|
|
|
|
add_var_to_zone(Var, Zone, !ZonesToDupVars) :-
|
|
( if map.search(!.ZonesToDupVars, Zone, DupVars0) then
|
|
DupVars = [Var | DupVars0],
|
|
map.det_update(Zone, DupVars, !ZonesToDupVars)
|
|
else
|
|
map.det_insert(Zone, [Var], !ZonesToDupVars)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The third step described in the comment above
|
|
% separate_trace_goal_only_locals.
|
|
%
|
|
:- pred add_exist_scopes_for_dup_vars_in_goal(zone::in, zones_to_dup_vars::in,
|
|
hlds_goal::in, hlds_goal::out, counter::in, counter::out) is det.
|
|
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars,
|
|
!Goal, !TraceCounter) :-
|
|
!.Goal = hlds_goal(GoalExpr0, GoalInfo),
|
|
(
|
|
( GoalExpr0 = plain_call(_, _, _, _, _, _)
|
|
; GoalExpr0 = generic_call(_, _, _, _, _)
|
|
; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
|
|
),
|
|
GoalExpr = GoalExpr0
|
|
;
|
|
GoalExpr0 = unify(LHSVar, RHS0, Mode, Kind, Context),
|
|
(
|
|
( RHS0 = rhs_var(_)
|
|
; RHS0 = rhs_functor(_, _, _)
|
|
),
|
|
GoalExpr = GoalExpr0
|
|
;
|
|
RHS0 = rhs_lambda_goal(Purity, Groundness, PredOrFunc,
|
|
EvalMethod, NonLocals, LambdaVars, LambdaModes, Detism,
|
|
LambdaGoal0),
|
|
LambdaZone = top_zone,
|
|
add_exist_scopes_for_dup_vars_in_goal(LambdaZone, ZonesToDupVars,
|
|
LambdaGoal0, LambdaGoal, !TraceCounter),
|
|
RHS = rhs_lambda_goal(Purity, Groundness, PredOrFunc,
|
|
EvalMethod, NonLocals, LambdaVars, LambdaModes, Detism,
|
|
LambdaGoal),
|
|
GoalExpr = unify(LHSVar, RHS, Mode, Kind, Context)
|
|
)
|
|
;
|
|
GoalExpr0 = conj(ConjType, SubGoals0),
|
|
list.map_foldl(
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars),
|
|
SubGoals0, SubGoals, !TraceCounter),
|
|
GoalExpr = conj(ConjType, SubGoals)
|
|
;
|
|
GoalExpr0 = disj(SubGoals0),
|
|
list.map_foldl(
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars),
|
|
SubGoals0, SubGoals, !TraceCounter),
|
|
GoalExpr = disj(SubGoals)
|
|
;
|
|
GoalExpr0 = switch(Var, CanFail, Cases0),
|
|
list.map_foldl(
|
|
add_exist_scopes_for_dup_vars_in_case(CurZone, ZonesToDupVars),
|
|
Cases0, Cases, !TraceCounter),
|
|
GoalExpr = switch(Var, CanFail, Cases)
|
|
;
|
|
GoalExpr0 = negation(SubGoal0),
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars,
|
|
SubGoal0, SubGoal, !TraceCounter),
|
|
GoalExpr = negation(SubGoal)
|
|
;
|
|
GoalExpr0 = scope(Reason, SubGoal0),
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars,
|
|
SubGoal0, SubGoal1, !TraceCounter),
|
|
( if
|
|
Reason = trace_goal(_, _, _, _, _),
|
|
CurZone = top_zone
|
|
then
|
|
counter.allocate(NewZone, !TraceCounter),
|
|
( if map.search(ZonesToDupVars, NewZone, DupVars) then
|
|
list.sort(DupVars, SortedDupVars),
|
|
QuantReason = exist_quant(SortedDupVars),
|
|
QuantExpr = scope(QuantReason, SubGoal1),
|
|
SubGoal1 = hlds_goal(_, SubGoalInfo1),
|
|
SubGoal = hlds_goal(QuantExpr, SubGoalInfo1)
|
|
else
|
|
SubGoal = SubGoal1
|
|
)
|
|
else
|
|
SubGoal = SubGoal1
|
|
),
|
|
GoalExpr = scope(Reason, SubGoal)
|
|
;
|
|
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars,
|
|
Cond0, Cond, !TraceCounter),
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars,
|
|
Then0, Then, !TraceCounter),
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars,
|
|
Else0, Else, !TraceCounter),
|
|
GoalExpr = if_then_else(Vars, Cond, Then, Else)
|
|
;
|
|
GoalExpr0 = shorthand(ShortHand0),
|
|
(
|
|
ShortHand0 = bi_implication(SubGoalA0, SubGoalB0),
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars,
|
|
SubGoalA0, SubGoalA, !TraceCounter),
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars,
|
|
SubGoalB0, SubGoalB, !TraceCounter),
|
|
ShortHand = bi_implication(SubGoalA, SubGoalB)
|
|
;
|
|
ShortHand0 = atomic_goal(GoalType, OuterVars, InnerVars,
|
|
MaybeOutputVars, MainGoal0, OrElseGoals0, OrElseInners),
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars,
|
|
MainGoal0, MainGoal, !TraceCounter),
|
|
list.map_foldl(
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars),
|
|
OrElseGoals0, OrElseGoals, !TraceCounter),
|
|
ShortHand = atomic_goal(GoalType, OuterVars, InnerVars,
|
|
MaybeOutputVars, MainGoal, OrElseGoals, OrElseInners)
|
|
;
|
|
ShortHand0 = try_goal(MaybeIOStateVars, ResultVar, SubGoal0),
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars,
|
|
SubGoal0, SubGoal, !TraceCounter),
|
|
ShortHand = try_goal(MaybeIOStateVars, ResultVar, SubGoal)
|
|
),
|
|
GoalExpr = shorthand(ShortHand)
|
|
),
|
|
!:Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
:- pred add_exist_scopes_for_dup_vars_in_case(zone::in, zones_to_dup_vars::in,
|
|
case::in, case::out, counter::in, counter::out) is det.
|
|
|
|
add_exist_scopes_for_dup_vars_in_case(CurZone, ZonesToDupVars,
|
|
!Case, !TraceCounter) :-
|
|
!.Case = case(MainConsId, OtherConsIds, Goal0),
|
|
add_exist_scopes_for_dup_vars_in_goal(CurZone, ZonesToDupVars,
|
|
Goal0, Goal, !TraceCounter),
|
|
!:Case = case(MainConsId, OtherConsIds, Goal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module hlds.pre_quantification.
|
|
%-----------------------------------------------------------------------------%
|