mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-20 11:54:02 +00:00
Class and instance definitions both contain lists of methods,
predicates and/or functions, that each have one or more procedures.
Until now, we represented the methods in class and instance definitions
as lists of nothing more than pred_proc_ids. This fact complicated
several operations,
- partly because there was no simple way to tell which procedures
were part of the same predicate or function, and
- partly because the order of the list is important (we identify
each method procedure in our equivalent of vtables with a number,
which is simply the procedure's position in this list), but there was
absolutely no information about recorded about this.
This diff therefore replaces the lists of pred_proc_ids with lists of
method_infos. Each method_info contains
- the method procedure number, i.e. the vtable index,
- the pred_or_func, sym_name and user arity of the predicate or function
that the method procedure is a part of, to make it simple to test
whether two method_infos represent different modes of the same predicate
or function, or not,
- the original pred_proc_id of the method procedure, which never changes,
and
- the current pred_proc_id, which program transformations *can* change.
compiler/hlds_class.m:
Make the change above in the representations of class and instance
definitions.
Put the fields of both types into a better order, by putting
related fields next to each other.
Put a notag wrapper around method procedure numbers to prevent
accidentally mixing them up with plain integers.
Add some utility functions.
compiler/prog_data.m:
Replace three fields containing pred_or_func, sym_name and arity
in the parse tree representation of instance methods with just one,
which contains all three pieces of info. This makes it easier to operate
on them as a unit.
Change the representation of methods defined by clauses from a list
of clauses to a cord of clauses, since this supports constant-time
append.
compiler/hlds_goal.m:
Switch from plain ints to the new notag representation of method
procedure numbers in method call goals.
compiler/add_class.m:
Simplify the code for adding new classes to the HLDS.
Give some predicates better names.
compiler/check_typeclass.m:
Significantly simplify the code for that generates the pred_infos and
proc_infos implementing all the methods of an instances definition,
and construct lists of method_infos instead of lists of pred_proc_ids.
Give some predicates better names.
Some error messages about problems in instance definitions started with
In instance declaration for class/arity:
while others started with
In instance declaration for class(module_a.foo, module_b.bar):
Replace both with
In instance declaration for class(foo, bar):
because it contains more useful information than the first, and less
non-useful information than the second. Improve the wording of some
error messages.
Factor out some common code.
compiler/prog_mode.m:
compiler/prog_type.m:
compiler/prog_util.m:
Generalize the existing predicates for stripping "builtin.m" module
qualifiers from sym_names, cons_ids, insts, types and modes
to allow also the stripping of *all* module qualifiers. This capability
is now used when we print an instance's type vector as a context
for diagnostics about problems inside instance definitions.
compiler/add_pred.m:
Add a mechanism for returning the pred_id of a newly created pred_info,
whether or not it was declared using a predmode declaration. This
capability is now needed by add_class.m.
Move the code creating an error message into its own function, and export
that function for add_class.m.
compiler/polymorphism_type_info.m:
Fix some comment rot.
compiler/base_typeclass_info.m:
compiler/call_gen.m:
compiler/dead_proc_elim.m:
compiler/deep_profiling.m:
compiler/direct_arg_in_out.m:
compiler/error_msg_inst.m:
compiler/float_regs.m:
compiler/get_dependencies.m:
compiler/higher_order.m:
compiler/hlds_error_util.m:
compiler/hlds_out_goal.m:
compiler/hlds_out_typeclass_table.m:
compiler/instance_method_clauses.m:
compiler/intermod.m:
compiler/make_hlds_error.m:
compiler/ml_call_gen.m:
compiler/mode_errors.m:
compiler/modes.m:
compiler/module_qual.qualify_items.m:
compiler/old_type_constraints.m:
compiler/parse_class.m:
compiler/parse_tree_out.m:
compiler/parse_tree_out_inst.m:
compiler/polymorphism_post_copy.m:
compiler/polymorphism_type_class_info.m:
compiler/prog_item.m:
compiler/prog_rep.m:
compiler/recompilation.usage.m:
compiler/state_var.m:
compiler/type_class_info.m:
compiler/typecheck_debug.m:
compiler/typecheck_error_type_assign.m:
compiler/typecheck_errors.m:
compiler/typecheck_msgs.m:
compiler/unused_imports.m:
compiler/xml_documentation.m:
Conform to the changes above.
tests/invalid/bug476.err_exp:
tests/invalid/tc_err1.err_exp:
tests/invalid/tc_err2.err_exp:
tests/invalid/typeclass_bogus_method.err_exp:
tests/invalid/typeclass_missing_mode.err_exp:
tests/invalid/typeclass_missing_mode_2.err_exp:
tests/invalid/typeclass_mode.err_exp:
tests/invalid/typeclass_mode_2.err_exp:
tests/invalid/typeclass_mode_3.err_exp:
tests/invalid/typeclass_mode_4.err_exp:
tests/invalid/typeclass_test_10.err_exp:
tests/invalid/typeclass_test_3.err_exp:
tests/invalid/typeclass_test_4.err_exp:
tests/invalid/typeclass_test_5.err_exp:
tests/invalid/typeclass_test_9.err_exp:
Expect the updated wording of some error messages.
3602 lines
145 KiB
Mathematica
3602 lines
145 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1996-2012 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: hlds_goal.m.
|
|
% Main authors: fjh, conway.
|
|
%
|
|
% The module defines the part of the HLDS that deals with goals.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module hlds.hlds_goal.
|
|
:- interface.
|
|
|
|
:- import_module hlds.goal_mode.
|
|
:- import_module hlds.hlds_class.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_llds.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.instmap.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.goal_path.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_data_foreign.
|
|
:- import_module parse_tree.prog_data_pragma.
|
|
:- import_module parse_tree.prog_rename.
|
|
:- import_module parse_tree.set_of_var.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module set.
|
|
:- import_module term_context.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type hlds_goals == list(hlds_goal).
|
|
|
|
:- type hlds_goal
|
|
---> hlds_goal(
|
|
hg_expr :: hlds_goal_expr,
|
|
hg_info :: hlds_goal_info
|
|
).
|
|
|
|
:- func get_hlds_goal_expr(hlds_goal) = hlds_goal_expr.
|
|
:- func get_hlds_goal_info(hlds_goal) = hlds_goal_info.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% The types that represent the kinds of goals in the internal form of
|
|
% Mercury programs.
|
|
%
|
|
|
|
:- type hlds_goal_expr
|
|
|
|
---> unify(
|
|
% A unification. Initially only the terms and the context
|
|
% are known. Mode analysis fills in the missing information.
|
|
|
|
% The variable on the left hand side of the unification.
|
|
% NOTE: for convenience, this field is duplicated in the
|
|
% unification structure below.
|
|
unify_lhs :: prog_var,
|
|
|
|
% Whatever is on the right hand side of the unification.
|
|
unify_rhs :: unify_rhs,
|
|
|
|
% The mode of the unification.
|
|
unify_mode :: unify_mode,
|
|
|
|
% This field says what category of unification it is,
|
|
% and contains information specific to each category.
|
|
unify_kind :: unification,
|
|
|
|
% The location of the unification in the original source code
|
|
% (for use in error messages).
|
|
unify_context :: unify_context
|
|
)
|
|
|
|
; plain_call(
|
|
% A predicate call. Initially only the sym_name, arguments,
|
|
% and context are filled in. Type analysis fills in the
|
|
% pred_id. Mode analysis fills in the proc_id and
|
|
% builtin_state fields.
|
|
|
|
% Which predicate are we calling?
|
|
call_pred_id :: pred_id,
|
|
|
|
% Which mode of the predicate?
|
|
call_proc_id :: proc_id,
|
|
|
|
% The list of argument variables.
|
|
call_args :: list(prog_var),
|
|
|
|
% Is the predicate builtin, and if yes,
|
|
% do we generate inline code for it?
|
|
call_builtin :: builtin_state,
|
|
|
|
% Was this predicate call originally a unification?
|
|
% If so, we store the context of the unification.
|
|
call_unify_context :: maybe(call_unify_context),
|
|
|
|
% The name of the predicate.
|
|
call_sym_name :: sym_name
|
|
)
|
|
|
|
; generic_call(
|
|
% A generic call implements operations which are too
|
|
% polymorphic to be written as ordinary predicates in Mercury
|
|
% and require special casing, either because their arity is
|
|
% variable, or they take higher-order arguments of variable
|
|
% arity. This currently includes higher-order calls and
|
|
% class-method calls.
|
|
|
|
gcall_details :: generic_call,
|
|
|
|
% The list of argument variables.
|
|
gcall_args :: list(prog_var),
|
|
|
|
% The modes of the argument variables. For higher_order calls,
|
|
% this field is junk until after mode analysis.
|
|
gcall_modes :: list(mer_mode),
|
|
|
|
% The register type to use for each of the arguments. This is
|
|
% only needed when float registers exist, and is only set after
|
|
% the float reg wrappers pass.
|
|
gcall_reg_types :: arg_reg_type_info,
|
|
|
|
% The determinism of the call.
|
|
gcall_detism :: determinism
|
|
)
|
|
|
|
; call_foreign_proc(
|
|
% Foreign code from a pragma foreign_proc(...) decl.
|
|
|
|
foreign_attr :: pragma_foreign_proc_attributes,
|
|
|
|
% The called predicate and its mode.
|
|
foreign_pred_id :: pred_id,
|
|
foreign_proc_id :: proc_id,
|
|
|
|
foreign_args :: list(foreign_arg),
|
|
|
|
% Extra arguments added when compiler passes such as tabling
|
|
% stuff more code into a foreign proc than the declared
|
|
% interface of the called Mercury procedure would allow.
|
|
foreign_extra_args :: list(foreign_arg),
|
|
|
|
% If set to yes(Cond), then this goal represents the evaluation
|
|
% of the runtime condition of a trace goal. In that case,
|
|
% the goal must be semidet, and the argument lists empty;
|
|
% the actual code in pragma_foreign_proc_impl is ignored
|
|
% and replaced by the evaluation of Cond.
|
|
foreign_trace_cond :: maybe(trace_expr(trace_runtime)),
|
|
|
|
% The actual code of the foreign_proc.
|
|
foreign_impl :: pragma_foreign_proc_impl
|
|
)
|
|
|
|
; conj(conj_type, list(hlds_goal))
|
|
% A conjunction. NOTE: plain conjunctions must be fully flattened
|
|
% before mode analysis. As a general rule, it is a good idea to
|
|
% keep them flattened.
|
|
|
|
; disj(list(hlds_goal))
|
|
% A disjunction.
|
|
% NOTE: disjunctions should be fully flattened.
|
|
|
|
; switch(
|
|
% Deterministic disjunctions are converted into switches
|
|
% by the switch detection pass.
|
|
|
|
% The variable we are switching on.
|
|
switch_var :: prog_var,
|
|
|
|
% Whether or not the switch test itself can fail (i.e. whether
|
|
% or not it covers all the possible cases).
|
|
switch_canfail :: can_fail,
|
|
|
|
switch_cases :: list(case)
|
|
)
|
|
|
|
; negation(hlds_goal)
|
|
% A negation.
|
|
|
|
; scope(
|
|
% A scope which may be the scope of a quantification,
|
|
% or may be introduced by a compiler transformation.
|
|
% See the documentation of scope_reason for what the
|
|
% compiler may do with the scope.
|
|
|
|
scope_reason :: scope_reason,
|
|
scope_goal :: hlds_goal
|
|
)
|
|
|
|
; if_then_else(
|
|
% An if-then-else,
|
|
% `if some <Vars> <Condition> then <Then> else <Else>'.
|
|
% The scope of the locally existentially quantified variables
|
|
% <Vars> is over the <Condition> and the <Then> part,
|
|
% but not the <Else> part.
|
|
|
|
% The locally existentially quantified variables <Vars>.
|
|
ite_exist_vars :: list(prog_var),
|
|
|
|
ite_cond :: hlds_goal, % The <Condition>
|
|
ite_then :: hlds_goal, % The <Then> part
|
|
ite_else :: hlds_goal % The <Else> part
|
|
)
|
|
|
|
; shorthand(shorthand_goal_expr).
|
|
% Goals that stand for some other, usually bigger goal.
|
|
% All shorthand goals are eliminated during or shortly after
|
|
% the construction of the HLDS, so most passes of the compiler
|
|
% will just call error/1 if they occur.
|
|
|
|
:- inst goal_expr_unify for hlds_goal_expr/0
|
|
---> unify(ground, ground, ground, ground, ground).
|
|
:- inst goal_expr_plain_call for hlds_goal_expr/0
|
|
---> plain_call(ground, ground, ground, ground, ground, ground).
|
|
:- inst goal_expr_generic_call for hlds_goal_expr/0
|
|
---> generic_call(ground, ground, ground, ground, ground).
|
|
:- inst goal_expr_foreign_proc for hlds_goal_expr/0
|
|
---> call_foreign_proc(ground, ground, ground, ground, ground,
|
|
ground, ground).
|
|
:- inst goal_expr_conj for hlds_goal_expr/0
|
|
---> conj(ground, ground).
|
|
:- inst goal_expr_plain_conj for hlds_goal_expr/0
|
|
---> conj(bound(plain_conj), ground).
|
|
:- inst goal_expr_parallel_conj for hlds_goal_expr/0
|
|
---> conj(bound(parallel_conj), ground).
|
|
:- inst goal_expr_disj for hlds_goal_expr/0
|
|
---> disj(ground).
|
|
:- inst goal_expr_switch for hlds_goal_expr/0
|
|
---> switch(ground, ground, ground).
|
|
:- inst goal_expr_ite for hlds_goal_expr/0
|
|
---> if_then_else(ground, ground, ground, ground).
|
|
:- inst goal_expr_neg for hlds_goal_expr/0
|
|
---> negation(ground).
|
|
:- inst goal_expr_scope for hlds_goal_expr/0
|
|
---> scope(ground, ground).
|
|
:- inst goal_expr_shorthand for hlds_goal_expr/0
|
|
---> shorthand(ground).
|
|
|
|
:- inst goal_plain_call for hlds_goal/0
|
|
---> hlds_goal(goal_expr_plain_call, ground).
|
|
|
|
:- type conj_type
|
|
---> plain_conj
|
|
; parallel_conj.
|
|
|
|
% These `shorthand' goals are implemented by HLDS --> HLDS transformations
|
|
% that replaces them with equivalent non-shorthand goals.
|
|
%
|
|
:- type shorthand_goal_expr
|
|
---> bi_implication(
|
|
% bi-implication (A <=> B)
|
|
%
|
|
% Note that ordinary implications (A => B) and reverse
|
|
% implications (A <= B) are expanded out before we construct
|
|
% the HLDS. We cannot do that for bi-implications, because
|
|
% if expansion of bi-implications is done before implicit
|
|
% quantification, then the quantification would be wrong.
|
|
%
|
|
% bi_implications are expanded out by quantification.m.
|
|
|
|
hlds_goal,
|
|
hlds_goal
|
|
)
|
|
|
|
; atomic_goal(
|
|
% An atomic goal that will be executed atomically against
|
|
% all running threads using the stm system.
|
|
|
|
% The type of atomic goal. Either a top level atomic goal,
|
|
% or a nested atomic goal. This isn't known until after
|
|
% typechecking.
|
|
atomic_goal_type :: atomic_goal_type,
|
|
|
|
% The variables representing the initial and final versions
|
|
% of the outer state. For top level atomic goals, of type
|
|
% io.state; for nested atomic goals, of type stm_builtin.stm.
|
|
atomic_outer :: atomic_interface_vars,
|
|
|
|
% The variables representing the initial and final versions
|
|
% of the inner state (always of type stm_builtin.stm).
|
|
atomic_inner :: atomic_interface_vars,
|
|
|
|
% List of output variables specified with `var(...)`.
|
|
% These variables should be free when the atomic goal
|
|
% is started and ground when the atomic goal is complete.
|
|
atomic_output_vars :: maybe(list(prog_var)),
|
|
|
|
% The main atomic transaction goal. If any or_else goals
|
|
% also exist, this goal is the first or_else alternative.
|
|
atomic_main_goal :: hlds_goal,
|
|
|
|
% Any later or_else alternative goals.
|
|
orelse_alternatives :: list(hlds_goal),
|
|
|
|
% The same as atomic_inner, but for each corresponding goal
|
|
% in orelse_alternatives. Begins as an empty list, but is
|
|
% filled in when quantification renames the inner stm state
|
|
% variables apart in each of the or_else alternatives.
|
|
orelse_inners :: list(atomic_interface_vars)
|
|
)
|
|
|
|
; try_goal(
|
|
% A try goal.
|
|
|
|
% The variables holding the initial and final I/O states for
|
|
% the goal to be executed in the `try' proper, i.e. not
|
|
% inclusive of the I/O states that may be in the arms following
|
|
% the try. Will be `no' if no `io(_)' component was specified.
|
|
try_maybe_io :: maybe(try_io_state_vars),
|
|
|
|
% The variable that will hold the result of the `try' or
|
|
% `try_io' call.
|
|
try_result_var :: prog_var,
|
|
|
|
% A "pre-transformed" version of the entire try goal.
|
|
% See try_expand.m for details.
|
|
try_goal :: hlds_goal
|
|
).
|
|
|
|
:- type atomic_interface_vars
|
|
---> atomic_interface_vars(
|
|
atomic_initial :: prog_var,
|
|
atomic_final :: prog_var
|
|
).
|
|
|
|
:- type try_io_state_vars
|
|
---> try_io_state_vars(
|
|
try_io_initial :: prog_var,
|
|
try_io_final :: prog_var
|
|
).
|
|
|
|
% If an atomic goal has type unknown_atomic_goal_type, then the conversion
|
|
% predicates to and from the inner variables have not been added yet to the
|
|
% main and orelse goals. If the type is top_level_atomic_goal or
|
|
% nested_atomic_goal, then the conversion predicates *have* been added.
|
|
:- type atomic_goal_type
|
|
---> unknown_atomic_goal_type
|
|
; top_level_atomic_goal
|
|
; nested_atomic_goal.
|
|
|
|
:- type catch_part
|
|
---> catch_part(
|
|
catch_expr :: hlds_goal_expr,
|
|
catch_goal :: hlds_goal
|
|
).
|
|
|
|
% Each scope that is created from the expansion of a ground term above
|
|
% a certain size is classified into one of these four categories.
|
|
% The categories are for scopes that (a) construct a ground term, (b)
|
|
% take an existing ground term and test whether it has a given shape, and
|
|
% (c) everything else (perhaps some parts of the term are matched and some
|
|
% parts are bound, or some invariant listed below is not guaranteed), and
|
|
% (d) scopes that have not yet been classified into one of these three
|
|
% categories.
|
|
%
|
|
% Many parts of the compiler have special code for handling
|
|
% from_ground_term_construct scopes, code that avoids scanning the code
|
|
% inside the scope. This can be a very big win, since that code can be
|
|
% huge. To make this special casing possible, from_ground_term_construct
|
|
% scopes promise the following invariants.
|
|
%
|
|
% 1. The only nonlocal variable of the scope is the one listed in the
|
|
% scope_reason.
|
|
% 2. The shape of the code inside the scope is a plain conjunction of
|
|
% unifications.
|
|
% 3. These unifications are construct unifications whose construct_how
|
|
% field says construct_statically, and in which the nonlocals,
|
|
% instmap_delta and determinism fields of the goal_info are
|
|
% correctly filled in. The nonlocals will be all the variables in the
|
|
% unification, the instmap delta will say that the value being
|
|
% constructed is ground (not unique, because it is static), and the
|
|
% determinism says that the goal is det. The goal_info of the
|
|
% conjunction will be filled in similarly.
|
|
% 4. None of the these unifications constructs a higher order value.
|
|
% 5. The unifications are ordered such that a variable constructed by
|
|
% all unifications except the last occurs exactly once, as a functor
|
|
% argument of a later unification (bottom up order).
|
|
%
|
|
% From_ground_term_deconstruct scopes obey invariants 1 and 2, and they
|
|
% also obey invariant 6:
|
|
%
|
|
% 6. The unifications are ordered such that a variable on the LHS of
|
|
% all unifications except the first occurs exactly once, as a functor
|
|
% argument on the RHS of an *earlier* unification (top down order).
|
|
%
|
|
% From_ground_term_initial scopes obey invariant 1, and they obey weak
|
|
% forms of invariants 2 and 6. The difference is that some of the
|
|
% "unifications" may include things that look like function symbols
|
|
% but are in fact function calls. When typecheck.m discovers that this
|
|
% applies to a unification, it does not remove the scope or change its
|
|
% kind. The post_typecheck phase, executed as part of the purity pass,
|
|
% will eventually change the kind to from_ground_term_other.
|
|
%
|
|
% Up to the first invocation of mode analysis, all from_ground_term scopes
|
|
% will have kind from_ground_term_initial. After that, they will have
|
|
% one of the three other kinds.
|
|
%
|
|
% If any later compiler pass modifies a from_ground_term_construct scope
|
|
% in a way that invalidates these invariants, it must set the kind field
|
|
% of the scope to from_ground_term_other (or from_ground_term_deconstruct).
|
|
% If the original scope had the from_head feature, the code that does this
|
|
% must also attach that feature to all the subgoals of the modified scope,
|
|
% unless we can be sure that it is executed *after* switch detection,
|
|
% which is the only pass that looks for from_head features, and which looks
|
|
% in all scopes *except* from_ground_term_construct scopes.
|
|
%
|
|
% For now, we don't optimize from_ground_term_other scopes, so there are
|
|
% no invariants required of them.
|
|
%
|
|
% An alternative design would be to have the mode checker turn any scope
|
|
% that it currently keeps as from_ground_term_construct into a new kind
|
|
% of generic call, one which basically says "this goal binds this variable
|
|
% to this ground term", with the ground term represented as a ground term,
|
|
% not as a bunch of construction unifications. The advantage of this
|
|
% approach would be that we could delete the local variables of these
|
|
% scopes (of which there can be hundreds of thousands) from the maps stored
|
|
% in the fields of the pred_info and proc_info (such as the varset and the
|
|
% var_types), making lookups and other operations on those maps
|
|
% significantly faster. The drawback would be the need for totally new code
|
|
% in most parts of the compiler to handle this new kind of goal.
|
|
% Using from_ground_term_construct, on the other hand, allows us to keep
|
|
% using the existing code for scopes in e.g. the type checker and the code
|
|
% generator.
|
|
%
|
|
:- type from_ground_term_kind
|
|
---> from_ground_term_initial
|
|
; from_ground_term_construct
|
|
; from_ground_term_deconstruct
|
|
; from_ground_term_other.
|
|
|
|
:- type scope_reason
|
|
---> exist_quant(list(prog_var))
|
|
% The goal inside the scope construct has the listed variables
|
|
% existentially quantified. The compiler may do whatever
|
|
% preserves this fact.
|
|
|
|
; disable_warnings(goal_warning, list(goal_warning))
|
|
% Do not generate any of the listed (one or more) warnings
|
|
% for any goal inside this scope.
|
|
|
|
; promise_solutions(list(prog_var), promise_solutions_kind)
|
|
% Even though the code inside the scope may have multiple
|
|
% solutions, the creator of the scope (which may be the user
|
|
% or a compiler pass) promises that all these solutions are
|
|
% equivalent relative to the relevant equality theory.
|
|
% (This need not be an equality theory known to the compiler.)
|
|
% The scope goal will therefore act as a single solution
|
|
% context, and the determinism of the scope() goal itself
|
|
% will indicate that it cannot succeed more than once.
|
|
%
|
|
% The promise is valid only if the list of outputs of the goal
|
|
% inside the scope is a subset of the variables listed here.
|
|
% If it is not valid, the compiler must emit an error message.
|
|
|
|
; promise_purity(purity)
|
|
% The goal inside the scope implements an interface of the
|
|
% specified purity, even if its implementation uses less pure
|
|
% components.
|
|
%
|
|
% Works the same way as a promise_pure or promise_semipure
|
|
% pragma, except that it applies to arbitrary goals and not
|
|
% just whole procedure bodies.
|
|
|
|
; require_detism(determinism)
|
|
% Require the wrapped subgoal to have the specified determinism.
|
|
% If it does not, report an error.
|
|
% This scope reason should not exist after the first invocation
|
|
% of simplification.
|
|
|
|
; require_complete_switch(prog_var)
|
|
% Require the wrapped subgoal to be a switch on the given variable
|
|
% that has an arm for every function symbol that the variable
|
|
% could be bound to at this point in the code. If it does not,
|
|
% or if the wrapped subgoal is not a switch on the given variable,
|
|
% then report an error.
|
|
% This scope reason should not exist after the first invocation
|
|
% of simplification.
|
|
|
|
; require_switch_arms_detism(prog_var, determinism)
|
|
% Require the wrapped subgoal to be a switch on the given variable,
|
|
% and require every arm of that switch to have a determinism
|
|
% that promises at least as much as the specified determinism.
|
|
% If either condition is not satisfied, report an error.
|
|
% This scope reason should not exist after the first invocation
|
|
% of simplification.
|
|
|
|
; commit(force_pruning)
|
|
% This scope exists to delimit a piece of code
|
|
% with at_most_many components but with no outputs,
|
|
% whose overall determinism is thus at_most_one,
|
|
% or a piece of code that cannot succeed but some of whose
|
|
% components are at_most_many (regardless of the number of
|
|
% outputs).
|
|
%
|
|
% If the argument is force_pruning, then the outer goal will
|
|
% succeed at most once even if the inner goal is impure.
|
|
|
|
; barrier(removable)
|
|
% The scope exists to prevent other compiler passes from
|
|
% arbitrarily moving computations in or out of the scope.
|
|
% This kind of scope can only be introduced by program
|
|
% transformations.
|
|
%
|
|
% The argument says whether other compiler passes are allowed
|
|
% to delete the scope.
|
|
%
|
|
% A non-removable explicit quantification may be introduced
|
|
% to keep related goals together where optimizations that
|
|
% separate the goals can only result in worse behaviour.
|
|
%
|
|
% A barrier says nothing about the determinism of either
|
|
% the inner or the outer goal, or about pruning.
|
|
|
|
; from_ground_term(prog_var, from_ground_term_kind)
|
|
% The goal inside the scope, which should be a conjunction,
|
|
% results from the conversion of one ground term to
|
|
% superhomogeneous form. The variable specifies what the
|
|
% compiler calls that ground term.
|
|
%
|
|
% This kind of scope is intended to be meaningful after
|
|
% mode analysis only if Kind = from_ground_term_construct.
|
|
|
|
; trace_goal(
|
|
trace_compiletime :: maybe(trace_expr(trace_compiletime)),
|
|
trace_runtime :: maybe(trace_expr(trace_runtime)),
|
|
trace_maybe_io :: maybe(string),
|
|
trace_mutable_vars :: list(trace_mutable_var_hlds),
|
|
trace_quant_vars :: list(prog_var)
|
|
)
|
|
% The goal inside the scope is trace code that is executed only
|
|
% conditionally, and should have no effect on the semantics of
|
|
% the program even if executed.
|
|
%
|
|
% The trace goal is removed by simplification if the compile time
|
|
% condition isn't true. If it is true, the code generator will
|
|
% generate code that will execute the goal inside the scope
|
|
% only if the runtime condition is satisfied.
|
|
%
|
|
% The maybe_io and mutable_vars fields are only advisory in the
|
|
% HLDS, since they are fully processed when the corresponding goal
|
|
% in the parse tree is converted to HLDS.
|
|
|
|
; loop_control(
|
|
lc_lc_var :: prog_var,
|
|
lc_lcs_var :: prog_var,
|
|
lc_use_parent_stack :: lc_use_parent_stack
|
|
).
|
|
% The goal inside the scope will be spawned off because the loop
|
|
% control transformation has been applied to this predicate.
|
|
%
|
|
% The goal will be executed by a different context, and the code
|
|
% generator must use the parent stack pointer to communicate with
|
|
% the parent.
|
|
%
|
|
% lc_lc_var identifies the variable that points to the loop
|
|
% control structure.
|
|
%
|
|
% lc_lcs_var identifies the variable that points to the slot in the
|
|
% loop control structure that should be used to spawn off the work
|
|
% within this scope.
|
|
|
|
:- type promise_solutions_kind
|
|
---> equivalent_solutions
|
|
; equivalent_solution_sets
|
|
; equivalent_solution_sets_arbitrary.
|
|
|
|
:- type removable
|
|
---> removable
|
|
; not_removable.
|
|
|
|
:- type force_pruning
|
|
---> force_pruning
|
|
; dont_force_pruning.
|
|
|
|
:- type trace_mutable_var_hlds
|
|
---> trace_mutable_var_hlds(
|
|
tmvh_mutable_name :: string,
|
|
tmvh_state_var_name :: string
|
|
).
|
|
|
|
:- type is_first_disjunct
|
|
---> is_first_disjunct
|
|
; is_not_first_disjunct.
|
|
|
|
:- type lc_use_parent_stack
|
|
---> lc_use_parent_stack_frame
|
|
; lc_create_frame_on_child_stack.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Information for calls.
|
|
%
|
|
|
|
% For all of our current builtin predicates and functions
|
|
% (such as those that do arithmetic), we generate inline instructions.
|
|
% In the past, we had some builtins (such as call/N) for which
|
|
% we generated a call to an out-of-line procedure, but there is
|
|
% no prospect of any new builtins ever requiring that treatment.
|
|
% Therefore currently all builtins are inline builtins.
|
|
%
|
|
:- type builtin_state
|
|
---> inline_builtin
|
|
; not_builtin.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Information for call_foreign_proc.
|
|
%
|
|
|
|
% In the usual case, the arguments of a foreign_proc are the arguments
|
|
% of the call to the predicate whose implementation is in the foreign
|
|
% language. Each such argument is described by a foreign_arg.
|
|
%
|
|
% The arg_var field gives the identity of the actual parameter.
|
|
%
|
|
% The arg_name_mode field gives the foreign variable name and the original
|
|
% mode declaration for the argument; a no means that the argument is not
|
|
% used by the foreign code. (In particular, the type_info variables
|
|
% introduced by polymorphism.m might be represented in this way.)
|
|
%
|
|
% The arg_type field gives the original types of the arguments.
|
|
% (With inlining, the actual type may be an instance of the original type.)
|
|
%
|
|
:- type foreign_arg
|
|
---> foreign_arg(
|
|
arg_var :: prog_var,
|
|
arg_name_mode :: maybe(foreign_arg_name_mode),
|
|
arg_type :: mer_type,
|
|
arg_box_policy :: box_policy
|
|
).
|
|
|
|
:- func foreign_arg_var(foreign_arg) = prog_var.
|
|
:- func foreign_arg_maybe_name_mode(foreign_arg) =
|
|
maybe(foreign_arg_name_mode).
|
|
:- func foreign_arg_type(foreign_arg) = mer_type.
|
|
:- func foreign_arg_box(foreign_arg) = box_policy.
|
|
|
|
:- pred make_foreign_args(list(prog_var)::in,
|
|
list(foreign_arg_name_mode_box)::in, list(mer_type)::in,
|
|
list(foreign_arg)::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Information for generic_calls.
|
|
%
|
|
|
|
:- type generic_call
|
|
---> higher_order(
|
|
ho_call_var :: prog_var,
|
|
ho_call_purity :: purity,
|
|
|
|
% call/N (pred) or apply/N (func)
|
|
ho_call_kind :: pred_or_func,
|
|
|
|
% number of arguments (including the higher-order term)
|
|
ho_call_arity :: pred_form_arity
|
|
)
|
|
|
|
; class_method(
|
|
% The variable that holds the typeclass_info for the instance.
|
|
method_tci :: prog_var,
|
|
|
|
% The number of the called method.
|
|
method_num :: method_proc_num,
|
|
|
|
% The name and arity of the class.
|
|
method_class_id :: class_id,
|
|
|
|
% The name of the called method.
|
|
method_name :: pf_sym_name_arity
|
|
)
|
|
|
|
; event_call(
|
|
event_name :: string
|
|
)
|
|
|
|
; cast(
|
|
% A cast generic_call with two arguments, Input and Output,
|
|
% assigns `Input' to `Output', performing a cast of this kind.
|
|
cast_kind :: cast_kind
|
|
).
|
|
|
|
% The various kinds of casts that we can do.
|
|
%
|
|
:- type cast_kind
|
|
---> unsafe_type_cast
|
|
% An unsafe type cast between ground values.
|
|
|
|
; unsafe_type_inst_cast
|
|
% An unsafe type and inst cast.
|
|
|
|
; equiv_type_cast
|
|
% A safe type cast between equivalent types, in either direction.
|
|
|
|
; exists_cast
|
|
% A safe cast between an internal type_info or typeclass_info
|
|
% variable, for which the bindings of existential type variables
|
|
% are known statically, to an external type_info or typeclass_info
|
|
% head variable, for which they are not. These are used instead of
|
|
% assignments so that the simplification pass does not attempt
|
|
% to merge the two variables, which could lead to inconsistencies
|
|
% in the rtti_varmaps.
|
|
|
|
; subtype_coerce.
|
|
% A coerce expression.
|
|
|
|
% Get a description of a generic_call goal.
|
|
%
|
|
:- pred generic_call_to_id(generic_call::in, generic_call_id::out) is det.
|
|
|
|
% Determine whether a generic_call is calling
|
|
% a predicate or a function.
|
|
%
|
|
:- func generic_call_pred_or_func(generic_call) = pred_or_func.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Information for unifications.
|
|
%
|
|
|
|
% Initially all unifications are represented as
|
|
% unify(LHSVar, RHS, _, Unification, _) where the Unification
|
|
% is initialized to complicated_unify but is not meaningful.
|
|
% Mode analysis makes the Unification field meaningful, which means
|
|
% most unifications end up being constructs, deconstructs, assigns and
|
|
% simple_tests, with only a few remaining as complicated_unify.
|
|
% Until that pass, the compiler should pay attention *only* the RHS field.
|
|
%
|
|
% The lambda pass in the middle end replaces all rhs_lambda_goal
|
|
% unifications with construct unifications using a closure_cons cons_id.
|
|
:- type unify_rhs
|
|
---> rhs_var(prog_var)
|
|
; rhs_functor(
|
|
rhs_cons_id :: cons_id,
|
|
% The `is_existential_construction' field is only used
|
|
% after polymorphism.m strips off the `new ' prefix from
|
|
% existentially typed constructions.
|
|
rhs_is_exist_constr :: is_exist_constr,
|
|
rhs_args :: list(prog_var)
|
|
)
|
|
; rhs_lambda_goal(
|
|
rhs_purity :: purity,
|
|
|
|
% Whether this closure is `ground' or `any'.
|
|
rhs_groundness :: ho_groundness,
|
|
|
|
rhs_p_or_f :: pred_or_func,
|
|
|
|
% Currently, we don't support any other value than `normal'.
|
|
rhs_eval_method :: lambda_eval_method,
|
|
|
|
% The nonlocals of the goal excluding the lambda arg vars,
|
|
% in *some* order, without duplicates. These are the nonlocals
|
|
% "captured" by the lambda: when we construct a closure
|
|
% for the lambda, these variables will go into the closure,
|
|
% in this order. At that time, all these variables must be
|
|
% ground.
|
|
rhs_closure_vars :: list(prog_var),
|
|
|
|
% The arguments of the lambda, and their modes.
|
|
rhs_arg_vars_modes :: assoc_list(prog_var, mer_mode),
|
|
|
|
rhs_detism :: determinism,
|
|
rhs_lambda_goal :: hlds_goal
|
|
).
|
|
|
|
:- inst rhs_lambda_goal for unify_rhs/0
|
|
---> rhs_lambda_goal(ground, ground, ground, ground, ground, ground,
|
|
ground, ground).
|
|
|
|
% Was the constructor originally of the form 'new ctor'(...).
|
|
%
|
|
:- type is_exist_constr
|
|
---> is_not_exist_constr
|
|
; is_exist_constr.
|
|
|
|
% This type contains the fields of a construct unification that are needed
|
|
% only rarely. If a value of this type is bound to no_construct_sub_info,
|
|
% this means the same as construct_sub_info(no, no), but takes less space.
|
|
% This matters because a module has lots of construct unifications.
|
|
:- type construct_sub_info
|
|
---> construct_sub_info(
|
|
% The argument numbers to take the address of.
|
|
take_address_args :: maybe(list(int)),
|
|
|
|
% The value `yes' tells the code generator to reserve an extra
|
|
% slot, at offset -1, to hold an integer giving the size of
|
|
% the term. The argument specifies the value to be put into
|
|
% this slot, either as an integer constant or as the value
|
|
% of a given variable.
|
|
%
|
|
% The value `no' means there is no extra slot, and is the
|
|
% default.
|
|
%
|
|
% The content of this slot is not meaningful before the
|
|
% size_prof pass has been run.
|
|
term_size_slot :: maybe(term_size_value)
|
|
)
|
|
; no_construct_sub_info.
|
|
|
|
:- type unification
|
|
|
|
---> construct(
|
|
% A construction unification is a unification with a functor
|
|
% or lambda expression which binds the LHS variable,
|
|
% e.g. X = f(Y1, Y2) where the top node of X is output,
|
|
%
|
|
% In HLDS dumps, constructions are written as `X <= f(Y1, Y2)'.
|
|
|
|
% The variable being constructed, e.g. X in above example.
|
|
construct_cell_var :: prog_var,
|
|
|
|
% The cons_id of the functor f/1 in the above example.
|
|
%
|
|
% common_standardize_and_record_construct in common.m
|
|
% depends on this cons_id being the same as in the rhs_functor
|
|
% of the unify goal.
|
|
construct_cons_id :: cons_id,
|
|
|
|
% The list of argument variables; [Y1, Y2] in the example.
|
|
% For a unification with a lambda expression, this is the list
|
|
% of the non-local variables of the lambda expression.
|
|
construct_args :: list(prog_var),
|
|
|
|
% The list of modes of the arguments sub-unifications.
|
|
% For a unification with a lambda expression, this is the list
|
|
% of modes of the non-local variables of the lambda expression.
|
|
construct_arg_modes :: list(unify_mode),
|
|
|
|
% Specify whether to allocate statically, to allocate
|
|
% dynamically (and if so, on the heap or in a region),
|
|
% or to reuse an existing cell (and if so, which cell).
|
|
% Constructions for which this field is `reuse_cell(_)'
|
|
% are described as "reconstructions".
|
|
construct_how :: how_to_construct,
|
|
|
|
% Can the cell be allocated in shared data.
|
|
construct_is_unique :: cell_is_unique,
|
|
|
|
construct_sub_info :: construct_sub_info
|
|
)
|
|
|
|
; deconstruct(
|
|
% A deconstruction unification is a unification with a functor
|
|
% for which the LHS variable was already bound,
|
|
% e.g. X = f(Y1, Y2) where the top node of Y is input.
|
|
% Note that deconstruction of lambda expressions is
|
|
% a mode error.
|
|
%
|
|
% In HLDS dump, deconstructions that cannot fail are written
|
|
% as `X => f(Y1, Y2)', while deconstructions that can fail
|
|
% are written as `X ?= f(Y1, Y2)'.
|
|
|
|
% The variable being deconstructed, e.g. X in the example.
|
|
deconstruct_cell_var :: prog_var,
|
|
|
|
% The cons_id of the functor, e.g. f/1 in the example.
|
|
deconstruct_cons_id :: cons_id,
|
|
|
|
% The list of argument variables, e.g. [Y1, Y2] in the example.
|
|
deconstruct_args :: list(prog_var),
|
|
|
|
% The lists of modes of the argument sub-unifications.
|
|
deconstruct_arg_modes :: list(unify_mode),
|
|
|
|
% Whether or not the unification could possibly fail.
|
|
deconstruct_can_fail :: can_fail,
|
|
|
|
% Can we apply compile time GC to this cell? In other words,
|
|
% can we explicitly deallocate it after the deconstruction?
|
|
deconstruct_can_cgc :: can_cgc
|
|
)
|
|
|
|
; assign(
|
|
% X = Y where the top node of X is output.
|
|
%
|
|
% In HLDS dumps, assigments are written as `X := Y'.
|
|
|
|
assign_to_var :: prog_var,
|
|
assign_from_var :: prog_var
|
|
)
|
|
|
|
; simple_test(
|
|
% X = Y where the type of X and Y is an atomic type and
|
|
%
|
|
% In HLDS dumps, simple tests are written as `X == Y'.
|
|
|
|
test_var1 :: prog_var,
|
|
test_var2 :: prog_var
|
|
)
|
|
|
|
; complicated_unify(
|
|
% X = Y where the type of X and Y is not an atomic type,
|
|
% and where the top-level node of both X and Y is input.
|
|
% May involve bi-directional data flow. Implemented using
|
|
% an out-of-line call to a compiler generated unification
|
|
% predicate for that type & mode.
|
|
%
|
|
% There is no special syntax to denote complicated unifications
|
|
% in HLDS dumps.
|
|
%
|
|
% The simplification pass at the end of semantic analysis
|
|
% replaces complicated unifications with other kinds of goals,
|
|
% usually calls. They should not be encountered by any later
|
|
% compiler passes.
|
|
|
|
% The mode of the unification.
|
|
compl_unify_mode :: unify_mode,
|
|
|
|
% Whether or not it could possibly fail.
|
|
compl_unify_can_fail :: can_fail,
|
|
|
|
% When unifying polymorphic types such as map/2, we need to
|
|
% pass type_info variables to the unification procedure for
|
|
% map/2 so that it knows how to unify the polymorphically
|
|
% typed components of the data structure. Likewise for
|
|
% comparison predicates. This field records which type_info
|
|
% variables we will need. This field is set by polymorphism.m.
|
|
% It is used by quantification.m when recomputing the
|
|
% nonlocals. It is also used by modecheck_unify.m, which
|
|
% checks that the type_info variables needed are all ground.
|
|
% It is also checked by simplify.m when it converts
|
|
% complicated unifications into procedure calls.
|
|
|
|
% The type_info variables needed by this unification,
|
|
% if it ends up being a complicated unify.
|
|
compl_unify_typeinfos :: list(prog_var)
|
|
).
|
|
|
|
:- inst unification_construct for unification/0
|
|
---> construct(ground, ground, ground, ground, ground, ground, ground).
|
|
:- inst unification_deconstruct for unification/0
|
|
---> deconstruct(ground, ground, ground, ground, ground, ground).
|
|
:- inst unification_assign for unification/0
|
|
---> assign(ground, ground).
|
|
:- inst unification_simple_test for unification/0
|
|
---> simple_test(ground, ground).
|
|
:- inst unification_complicated_unify for unification/0
|
|
---> complicated_unify(ground, ground, ground).
|
|
|
|
:- type term_size_value
|
|
---> known_size(
|
|
int % The cell being created has this size.
|
|
)
|
|
; dynamic_size(
|
|
prog_var % This variable contains the size of
|
|
% the cell being created.
|
|
).
|
|
|
|
% `can_cgc' iff the cell is available for compile time garbage collection.
|
|
% Compile time garbage collection is when the compiler recognises that
|
|
% a memory cell is no longer needed and can be safely deallocated
|
|
% (by inserting an explicit call to free).
|
|
%
|
|
:- type can_cgc
|
|
---> can_cgc
|
|
; cannot_cgc.
|
|
|
|
% A unify_context describes the location in the original source
|
|
% code of a unification, for use in error messages.
|
|
%
|
|
:- type unify_context
|
|
---> unify_context(
|
|
unify_main_context,
|
|
list(unify_sub_context)
|
|
).
|
|
|
|
% A unify_main_context describes overall location of the
|
|
% unification within a clause
|
|
%
|
|
:- type unify_main_context
|
|
---> umc_explicit
|
|
% An explicit call to =/2.
|
|
|
|
; umc_head(
|
|
% A unification in an argument of a clause head.
|
|
|
|
int % The argument number (first argument == no. 1)
|
|
)
|
|
|
|
; umc_head_result
|
|
% A unification in the function result term of a clause head.
|
|
|
|
; umc_call(
|
|
% A unification in an argument of a predicate call.
|
|
|
|
call_id, % The name and arity of the predicate.
|
|
int % The argument number (first arg == 1).
|
|
)
|
|
|
|
; umc_implicit(
|
|
% A unification added by some syntactic transformation
|
|
% (e.g. for handling state variables).
|
|
|
|
string % Used to explain the source of the unification.
|
|
).
|
|
|
|
% A unify_sub_context describes the location of sub-unification
|
|
% (which is unifying one argument of a term) within a particular
|
|
% unification.
|
|
%
|
|
:- type unify_sub_context
|
|
---> unify_sub_context(
|
|
cons_id, % The functor.
|
|
int % The argument number (first arg == 1).
|
|
).
|
|
|
|
:- type unify_sub_contexts == list(unify_sub_context).
|
|
|
|
% A call_unify_context is used for unifications that get turned into
|
|
% calls to out-of-line unification predicates, and functions. It records
|
|
% which part of the original source code the unification (which may be
|
|
% a function application) occurred in.
|
|
%
|
|
:- type call_unify_context
|
|
---> call_unify_context(
|
|
prog_var, % The LHS of the unification.
|
|
unify_rhs, % The RHS of the unification.
|
|
unify_context % The context of the unification.
|
|
).
|
|
|
|
% Information on how to construct the cell for a construction unification.
|
|
% It is meaningful only if the argument list is not empty.
|
|
:- type how_to_construct
|
|
---> construct_dynamically
|
|
% Allocate a new term on the heap. This is the default.
|
|
|
|
; construct_statically(static_how)
|
|
% Create a static constant in the target language.
|
|
|
|
; construct_in_region(prog_var)
|
|
% Allocate a new term in a region.
|
|
|
|
; reuse_cell(cell_to_reuse).
|
|
% Reuse an existing heap cell.
|
|
|
|
:- type static_how
|
|
---> born_static
|
|
% A compiler pass intentionally created this term as a static term.
|
|
% Examples include terms inside from_ground_term scopes, type_info
|
|
% and typeclass_info structures constructed by polymorphism, and
|
|
% some deep profiling data structures.
|
|
|
|
; marked_static.
|
|
% The term was not born static, but was marked as static by
|
|
% the mark_static_terms pass.
|
|
%
|
|
% That pass is used by the MLDS backend to discover terms that
|
|
% can be put into static storage. It is not used by the LLDS
|
|
% backend, because that backend predates the creation of
|
|
% mark_static_terms.m, and so in that backend, the same
|
|
% optimization is handled as part of code generation by
|
|
% code in var_locn.m.
|
|
%
|
|
% However, even the LLDS backend may get marked_static construction
|
|
% unifications, because the loop invariant hoisting pass invokes
|
|
% mark_static_terms.m to help it do its job. (Ironically, it helps
|
|
% by identifying construction unifications that should *not* be
|
|
% hoisted out of loops, because they take no time at all at runtime
|
|
% anyway.)
|
|
%
|
|
% Note that after the how_to_construct field of a construction
|
|
% unification is set to construct_dynamically(marked_static),
|
|
% that information remains valid *only* until the next compiler
|
|
% pass modifies any of the information that mark_static_terms.m
|
|
% used to arrive at that decision. When targeting the MLDS backend,
|
|
% the simplification pass, and common.m in particular, carefully
|
|
% refrain from making invalidating changes. When targeting the LLDS
|
|
% backend, they can and sometimes do make such invalidating
|
|
% changes, so if any later pass needs to make a distinction between
|
|
% construct_dynamically and construct_statically, it would need
|
|
% to rerun mark_static_terms.m. As of this writing, there is
|
|
% no such later pass.
|
|
%
|
|
% Before 2022 feb 7, a construct unification was allowed to be
|
|
% marked static only if none of the static components it relied on
|
|
% was bound in a branch of an earlier branched control structure.
|
|
% From 2022 feb 7, a construct unification is allowed to be
|
|
% marked static if some of the static components it relies on
|
|
% are bound in a branch of an earlier branched control structure,
|
|
% *provided* that all the branches of that branched control
|
|
% structure whose endpoints are reachable all agree on that
|
|
% binding. The motivation for this change was Mantis bug #544,
|
|
% which involves add_trail_ops.m transforming
|
|
%
|
|
% ... goal that adds an entry to const_var_map ...
|
|
% ... following code ...
|
|
%
|
|
% into
|
|
%
|
|
% (
|
|
% ... trail ops ...
|
|
% ... goal that adds an entry to const_var_map ...
|
|
% ... trail ops ...
|
|
% ;
|
|
% ... trail ops ...
|
|
% fail
|
|
% ),
|
|
% ... following code ...
|
|
%
|
|
% Since the second disjunct cannot succeed, the new policy
|
|
% allows the following code to rely on exactly the same entries
|
|
% in the const_var_map in the transformed code as in the original
|
|
% code.
|
|
|
|
% Information used to perform structure reuse on a cell.
|
|
%
|
|
:- type cell_to_reuse
|
|
---> cell_to_reuse(
|
|
prog_var,
|
|
|
|
% The cell to be reused may be tagged with one of these
|
|
% cons_ids.
|
|
list(cons_id),
|
|
|
|
% Whether the corresponding argument already has
|
|
% the correct value and does not need to be filled in.
|
|
list(needs_update)
|
|
).
|
|
|
|
% Cells marked `cell_is_shared' can be allocated in read-only memory,
|
|
% and can be shared.
|
|
% Cells marked `cell_is_unique' must be writeable, and therefore
|
|
% cannot be shared.
|
|
% `cell_is_unique' is always a safe approximation.
|
|
%
|
|
:- type cell_is_unique
|
|
---> cell_is_unique
|
|
; cell_is_shared.
|
|
|
|
% A unify mode specifies four instantation states:
|
|
%
|
|
% - the initial instantation state of the LHS (li),
|
|
% - the final instantation state of the LHS (lf),
|
|
% - the initial instantation state of the RHS (ri) and
|
|
% - the final instantation state of the RHS (rf).
|
|
%
|
|
% The most unifications, the two final instantiation states are the same
|
|
% (ground, either with or without further information), but in some cases
|
|
% they may be different: for example, one could be unique and the other
|
|
% clobbered.
|
|
%
|
|
:- type unify_mode
|
|
---> unify_modes_li_lf_ri_rf(mer_inst, mer_inst, mer_inst, mer_inst).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Information for switches.
|
|
%
|
|
|
|
:- type case
|
|
---> case(
|
|
% The list of functors for which this case arm is applicable.
|
|
case_first_functor :: cons_id,
|
|
case_later_functors :: list(cons_id),
|
|
|
|
% The code of the switch arm.
|
|
case_goal :: hlds_goal
|
|
).
|
|
|
|
:- type case_id
|
|
---> case_id(int).
|
|
|
|
:- type tagged_case
|
|
---> tagged_case(
|
|
% The list of functors, and their tags, for which
|
|
% this case arm is applicable.
|
|
tagged_case_first_functor :: tagged_cons_id,
|
|
tagged_case_later_functors :: list(tagged_cons_id),
|
|
|
|
% An identifier of the switch arm.
|
|
tagged_case_id :: case_id,
|
|
|
|
% The code of the switch arm.
|
|
tagged_case_goal :: hlds_goal
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Information for all kinds of goals.
|
|
%
|
|
|
|
:- type hlds_goal_info.
|
|
|
|
% Stuff specific to a back-end. At the moment, only the LLDS back-end
|
|
% annotates the HLDS.
|
|
:- type hlds_goal_code_gen_info
|
|
---> no_code_gen_info
|
|
; llds_code_gen_info(
|
|
llds_code_gen :: llds_code_gen_details
|
|
).
|
|
|
|
% This type stores the possible values of a higher order variable
|
|
% at a particular point, as determined by the closure analysis
|
|
% (see closure_analysis.m.) If a variable does not have an entry
|
|
% in the map, then it may take any (valid) value.
|
|
%
|
|
:- type higher_order_value_map == map(prog_var, set(pred_proc_id)).
|
|
|
|
:- type rbmm_goal_info
|
|
---> rbmm_goal_info(
|
|
% The first three fields partition the nonlocal variables
|
|
% of the goal that represent regions.
|
|
%
|
|
% - The first gives the set of regions that are created
|
|
% by code inside the goal.
|
|
% - The second gives the set of regions that were created
|
|
% before the goal, and are removed (though not necessarily
|
|
% destroyed) by code inside the goal.
|
|
% - The third gives the set of regions that were created before
|
|
% the goal and are *not* removed by code inside the goal.
|
|
|
|
created_regions :: set(prog_var),
|
|
removed_regions :: set(prog_var),
|
|
carried_regions :: set(prog_var),
|
|
|
|
% Regions that exist before the goal (i.e. removed or carried
|
|
% regions) that may be allocated into inside the goal.
|
|
allocated_into_regions :: set(prog_var),
|
|
|
|
% Regions that exist before the goal (i.e. removed or carried
|
|
% regions) that may be read from inside the goal.
|
|
used_regions :: set(prog_var)
|
|
).
|
|
|
|
:- func rbmm_info_init = rbmm_goal_info.
|
|
|
|
:- type mode_constr_goal_info
|
|
---> mode_constr_goal_info(
|
|
% Inst_graph nodes that are reachable from variables
|
|
% that occur in the goal.
|
|
mci_occurring_vars :: set_of_progvar,
|
|
|
|
% Inst_graph nodes produced by this goal.
|
|
mci_producing_vars :: set_of_progvar,
|
|
|
|
% Inst_graph nodes consumed by this goal.
|
|
mci_consuming_vars :: set_of_progvar,
|
|
|
|
% The variables that this goal makes visible.
|
|
mci_make_visible_vars :: set_of_progvar,
|
|
|
|
% The variables that this goal needs to be visible
|
|
% before it is executed.
|
|
mci_need_visible_vars :: set_of_progvar
|
|
).
|
|
|
|
% Information about compile-time garbage collection.
|
|
:- type ctgc_goal_info
|
|
---> ctgc_goal_info(
|
|
% The local forward use set: this set contains the variables
|
|
% that are syntactically needed during forward execution.
|
|
% It is computed as the set of instantiated vars (input vars
|
|
% + sum(pre_births), minus the set of dead vars
|
|
% (sum(post_deaths and pre_deaths).
|
|
% The information is needed for determining the direct reuses.
|
|
ctgc_lfu :: set_of_progvar,
|
|
|
|
% The local backward use set. This set contains the
|
|
% instantiated variables that are needed upon backtracking
|
|
% (i.e. syntactically appearing in any nondet call preceding
|
|
% this goal).
|
|
ctgc_lbu :: set_of_progvar,
|
|
|
|
% Any structure reuse information related to this call.
|
|
ctgc_reuse :: reuse_description
|
|
).
|
|
|
|
% Information describing possible kinds of reuse on a per goal basis.
|
|
%
|
|
% - 'no_reuse_info': before CTGC analysis, every goal is annotated with
|
|
% the reuse description 'no_reuse_info', i.e. no information about any
|
|
% reuse.
|
|
%
|
|
% - 'no_possible_reuse': the goal has been analysed and determined to have
|
|
% no reuse opportunity and reanalysis in light of further information
|
|
% within the same module is unnecessary.
|
|
%
|
|
% - 'potential_reuse': the value 'potential_reuse' states that in a reuse
|
|
% version of the procedure to which the goal belongs, this goal may safely
|
|
% be replaced by a goal implementing structure reuse.
|
|
%
|
|
% - 'reuse': the value 'reuse' states that in the current procedure (either
|
|
% the specialised reuse version of a procedure, or the original procedure
|
|
% itself) the current goal can safely be replaced by a goal performing
|
|
% structure reuse.
|
|
%
|
|
% - 'missed_reuse': the value 'missed_reuse' gives some feedback when an
|
|
% opportunity for reuse was missed for some reason (only used for calls).
|
|
%
|
|
:- type reuse_description
|
|
---> no_reuse_info
|
|
; no_possible_reuse
|
|
; missed_reuse(list(missed_message))
|
|
; potential_reuse(short_reuse_description)
|
|
; reuse(short_reuse_description).
|
|
|
|
% A short description of the kind of reuse allowed in the associated
|
|
% goal:
|
|
%
|
|
% - 'cell_died' (only relevant for deconstructions): states that the cell
|
|
% of the deconstruction becomes dead after that deconstruction.
|
|
%
|
|
% - 'cell_reused' (only relevant for constructions): states that it is
|
|
% allowed to reuse a previously discovered dead term for constructing a
|
|
% new term in the given construction. Details of which term is reused are
|
|
% recorded.
|
|
%
|
|
% - 'reuse_call' (only applicable to procedure calls): the called
|
|
% procedure is an optimised procedure w.r.t. CTGC. Records whether the
|
|
% call is conditional or not.
|
|
%
|
|
:- type short_reuse_description
|
|
---> cell_died
|
|
; cell_reused(
|
|
% The dead variable selected for reusing.
|
|
dead_var,
|
|
|
|
% States whether the reuse is conditional.
|
|
is_conditional,
|
|
|
|
% What are the possible cons_ids that the variable
|
|
% to be reused can have.
|
|
list(cons_id),
|
|
|
|
% Which of the fields of the cell to be reused already contain
|
|
% the correct value.
|
|
list(needs_update)
|
|
)
|
|
; reuse_call(
|
|
is_conditional,
|
|
|
|
% Which arguments must not be clobbered; determines the reuse
|
|
% version to call.
|
|
list(int)
|
|
).
|
|
|
|
% Used to represent the fact whether a reuse opportunity is either
|
|
% always safe (unconditional_reuse) or involves a reuse condition to
|
|
% be satisfied (conditional_reuse).
|
|
%
|
|
:- type is_conditional
|
|
---> conditional_reuse
|
|
; unconditional_reuse.
|
|
|
|
:- type needs_update
|
|
---> needs_update
|
|
; does_not_need_update.
|
|
|
|
:- type missed_message == string.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Information about the goal used by the deep profiler.
|
|
%
|
|
:- type dp_goal_info
|
|
---> dp_goal_info(
|
|
goal_is_mdprof_inst,
|
|
maybe(dp_coverage_goal_info)
|
|
).
|
|
|
|
% Was this goal introduced by the deep profiler as instrumentation code?
|
|
%
|
|
:- type goal_is_mdprof_inst
|
|
---> goal_is_mdprof_inst
|
|
; goal_is_not_mdprof_inst.
|
|
|
|
% Information used by the deep profiler to perform coverage profiling.
|
|
% The predicates that operate on these types are in deep_profiling.m.
|
|
%
|
|
:- type dp_coverage_goal_info
|
|
---> dp_coverage_goal_info(
|
|
goal_trivial,
|
|
port_counts_give_coverage_after
|
|
).
|
|
|
|
% A goal is trivial if it is a simple atomic goal (not a call),
|
|
% or it is a non-atomic goal and all its descendants are trivial.
|
|
%
|
|
:- type goal_trivial
|
|
---> goal_is_trivial
|
|
; goal_is_nontrivial.
|
|
|
|
% A goal has port counts that can be used to calculate the coverage at the
|
|
% end of that goal.
|
|
%
|
|
:- type port_counts_give_coverage_after
|
|
---> port_counts_give_coverage_after
|
|
; no_port_counts_give_coverage_after.
|
|
|
|
:- type goal_feature
|
|
---> feature_constraint
|
|
% This is included if the goal is a constraint. See constraint.m
|
|
% for the definition of this.
|
|
|
|
; feature_from_head
|
|
% This goal was originally in the head of the clause, and was
|
|
% put into the body by the superhomogeneous form transformation.
|
|
|
|
; feature_not_impure_for_determinism
|
|
% This goal should not be treated as impure for the purpose of
|
|
% computing its determinism. This is intended to be used by program
|
|
% transformations that insert impure code into existing goals,
|
|
% and wish to keep the old determinism of those goals.
|
|
|
|
; feature_stack_opt
|
|
% This goal was created by stack slot optimization. Other
|
|
% optimizations should assume that it is there for a reason, and
|
|
% therefore should refrain from "optimizing" it away, even though
|
|
% it is a copy of another, previous goal.
|
|
|
|
; feature_tuple_opt
|
|
% This goal was created by the tupling optimization.
|
|
% The comment for the stack slot optimization above applies here.
|
|
|
|
; feature_call_table_gen
|
|
% This goal generates the variable that represents the call table
|
|
% tip. If debugging is enabled, the code generator needs to save
|
|
% the value of this variable in its stack slot as soon as it is
|
|
% generated; this marker tells the code generator when this
|
|
% happens.
|
|
|
|
; feature_preserve_backtrack_into
|
|
% Determinism analysis should preserve backtracking into goals
|
|
% marked with this feature, even if their determinism puts an
|
|
% at_most_zero upper bound on the number of solutions they have.
|
|
|
|
; feature_save_deep_excp_vars
|
|
% This goal generates the deep profiling variables that the
|
|
% exception handler needs to execute the exception port code.
|
|
|
|
; feature_hide_debug_event
|
|
% The events associated with this goal should be hidden. This is
|
|
% used e.g. by the tabling transformation to preserve the set
|
|
% of events generated by a tabled procedure.
|
|
|
|
; feature_deep_self_tail_rec_call
|
|
% This goal represents a self-tail-recursive call. This marker
|
|
% is used by deep profiling.
|
|
|
|
; feature_debug_self_tail_rec_call
|
|
% This goal represents a self-tail-recursive call. This marker
|
|
% is used by the LLDS code generator for generating TAIL events
|
|
% for the debugger.
|
|
|
|
; feature_self_or_mutual_tail_rec_call
|
|
% This goal represents a tail-recursive call, which may be
|
|
% either self-recursive or mutually-recursive (you have to compare
|
|
% the identities of the caller and the callee to figure out which).
|
|
% This marker is used by inlining, and (in the future) by the MLDS
|
|
% code generator.
|
|
|
|
; feature_obvious_nontail_rec_call
|
|
% This goal represents a recursive call that is not a tail call,
|
|
% but we don't necessarily want to generate a warning for it,
|
|
% since it is followed by a later recursive call (which may or
|
|
% may not be a tail call).
|
|
|
|
; feature_keep_constant_binding
|
|
% This feature should only be attached to unsafe_cast goals
|
|
% that cast a value of an user-defined type to an integer.
|
|
% It tells the mode checker that if the first variable is known
|
|
% to be bound to a given constant, then the second variable
|
|
% should be set to the corresponding local tag value.
|
|
|
|
; feature_dont_warn_singleton
|
|
% Don't warn about singletons in this goal. Intended to be used
|
|
% by the state variable transformation, for situations such as the
|
|
% following:
|
|
%
|
|
% p(X, !.S, ...) :-
|
|
% (
|
|
% X = a,
|
|
% !:S = f(!.S, ...)
|
|
% ;
|
|
% X = b,
|
|
% <code A>
|
|
% ),
|
|
% <code B>.
|
|
%
|
|
% The state variable transformation creates a new variable for
|
|
% the new value of !:S in the disjunction. If code A doesn't define
|
|
% !:S, the state variable transformation inserts an unification
|
|
% after it, unifying the variables representing !.S and !:S.
|
|
% If code B doesn't refer to S, then quantification will restrict
|
|
% the scope of the variable representing !:S to each disjunct,
|
|
% and the unification inserted after code A will refer to a
|
|
% singleton variable.
|
|
%
|
|
% Since it is not reasonable to expect the state variable
|
|
% transformation to do the job of quantification as well,
|
|
% we simply make it mark the unifications it creates, and get
|
|
% the singleton warning code to respect it.
|
|
%
|
|
% On the other hand, see the next feature.
|
|
|
|
; feature_state_var_copy
|
|
% This goal is one of the unifications mentioned in the comment
|
|
% immediately above. A post-pass in the state variable
|
|
% transformation deletes unification goals with this feature
|
|
% if the variable on the LHS (which should be the variable
|
|
% representing the updated version of the state variable)
|
|
% if not used in later code.
|
|
%
|
|
% This allows us to report at least some places where the
|
|
% new version of a state variable is a singleton variable
|
|
% (which in practice virtually always means that it is computed,
|
|
% but never used).
|
|
|
|
; feature_duplicated_for_switch
|
|
% This goal was created by switch detection by duplicating
|
|
% the source code written by the user.
|
|
|
|
; feature_mode_check_clauses_goal
|
|
% This goal is the main disjunction of a predicate with the
|
|
% mode_check_clauses pragma. No compiler pass should try to invoke
|
|
% quadratic or worse algorithms on the arms of this goal, since it
|
|
% probably has many arms (possibly several thousand). This feature
|
|
% may be attached to switches as well as disjunctions.
|
|
|
|
; feature_will_not_modify_trail
|
|
% This goal will not modify the trail, so it is safe for the
|
|
% compiler to omit trailing primitives when generating code
|
|
% for this goal.
|
|
|
|
; feature_will_not_call_mm_tabled
|
|
% This goal will never call a procedure that is evaluted using
|
|
% minimal model tabling. It is safe for the code generator to omit
|
|
% the pneg context wrappers when generating code for this goal.
|
|
|
|
; feature_contains_trace
|
|
% This goal contains a scope goal whose scope_reason is
|
|
% trace_goal(...).
|
|
|
|
; feature_pretest_equality
|
|
% This goal is an if-then-else in a compiler-generated
|
|
% type-constructor-specific unify or compare predicate
|
|
% whose condition is a test of whether the two input arguments
|
|
% are equal or not. The goal feature exists because in some
|
|
% circumstances we need to strip off this pretest, and replace
|
|
% the if-then-else with just its else branch.
|
|
|
|
; feature_pretest_equality_condition
|
|
% This goal is the unification in the condition of a
|
|
% pretest-equality if-then-else goal. The goal feature is required
|
|
% to allow pointer comparisons generated by the compiler.
|
|
|
|
; feature_lambda_undetermined_mode
|
|
% This goal is a lambda goal converted from a higher order term
|
|
% for which we don't know the mode of the call to the underlying
|
|
% predicate. These can be produced by the polymorphism
|
|
% transformation but should be removed by the end of mode
|
|
% checking.
|
|
|
|
; feature_contains_stm_inner_outer
|
|
% This goal is a goal inside an atomic scope, for which the calls
|
|
% to convert inner and outer variables have been inserted.
|
|
|
|
; feature_do_not_tailcall
|
|
% This goal is a call that should not be executed as a tail call.
|
|
% Currently this is only used by the loop control optimization
|
|
% since a spawned off task may need to use the parent's stack frame
|
|
% even after the parent makes a tail call.
|
|
|
|
; feature_do_not_warn_implicit_stream
|
|
% Even if this call is to a predicate that operates on an implicit
|
|
% stream, do not generate a warning about that. This feature
|
|
% should be set on calls that are constructed by the compiler
|
|
% - to calls which may possibly be subject to that warning, and
|
|
% - for which the code from which this call has been constructed
|
|
% has already had generated for it all the warnings of this type
|
|
% that it deserved.
|
|
|
|
; feature_lifted_by_cse
|
|
% This goal is a deconstruction unification that has been lifted
|
|
% out of each arm of a switch by cse_detection.m. Used to avoid
|
|
% spurious warnings about the goal inside a scope such as
|
|
% `require_complete_switch [X] (...)' not being a switch
|
|
% in situations where the `...' starts out as a switch on X,
|
|
% but where cse_detection.m turns it into a conjunction,
|
|
% inserting one or more of these lifted-out deconstructions
|
|
% before the original switch.
|
|
|
|
; feature_lambda_from_try.
|
|
% This lambda goal wraps the main part of a try goal,
|
|
% the part that does the main job but may throw an exception.
|
|
% This feaure is used to inform the code that warns about
|
|
% infinite recursion that the lambda goal *will* be executed
|
|
% in context in which it is constructed.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Access predicates for the hlds_goal_info data structure.
|
|
% For documentation on the meaning of the fields that these
|
|
% procedures access, see the definition of the hlds_goal_info type.
|
|
%
|
|
|
|
:- pred goal_info_init(hlds_goal_info::out) is det.
|
|
:- pred goal_info_init(prog_context::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_init(set_of_progvar::in, instmap_delta::in, determinism::in,
|
|
purity::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_init(set_of_progvar::in, instmap_delta::in, determinism::in,
|
|
purity::in, prog_context::in, hlds_goal_info::out) is det.
|
|
|
|
:- pred goal_info_init_context_purity(prog_context::in, purity::in,
|
|
hlds_goal_info::out) is det.
|
|
|
|
:- func impure_init_goal_info(set_of_progvar, instmap_delta, determinism)
|
|
= hlds_goal_info.
|
|
:- func impure_reachable_init_goal_info(set_of_progvar, determinism)
|
|
= hlds_goal_info.
|
|
:- func impure_unreachable_init_goal_info(set_of_progvar, determinism)
|
|
= hlds_goal_info.
|
|
|
|
% Instead of recording the liveness of every variable at every part
|
|
% of the goal, we just keep track of the initial liveness, and of the changes
|
|
% in liveness. Note that when traversing forwards through a goal,
|
|
% deaths must be applied before births. This is necessary to handle
|
|
% certain circumstances where a variable can occur in both the post-death
|
|
% and post-birth sets, or in both the pre-death and pre-birth sets.
|
|
|
|
% See also goal_info_get_code_model in code_model.m.
|
|
:- func goal_info_get_determinism(hlds_goal_info) = determinism.
|
|
:- func goal_info_get_purity(hlds_goal_info) = purity.
|
|
:- func goal_info_get_instmap_delta(hlds_goal_info) = instmap_delta.
|
|
:- func goal_info_get_nonlocals(hlds_goal_info) = set_of_progvar.
|
|
:- func goal_info_get_features(hlds_goal_info) = set(goal_feature).
|
|
:- func goal_info_get_goal_id(hlds_goal_info) = goal_id.
|
|
:- func goal_info_get_code_gen_info(hlds_goal_info) = hlds_goal_code_gen_info.
|
|
|
|
:- func goal_info_get_context(hlds_goal_info) = prog_context.
|
|
:- func goal_info_get_reverse_goal_path(hlds_goal_info) = reverse_goal_path.
|
|
:- func goal_info_get_higher_order_value_map(hlds_goal_info)
|
|
= higher_order_value_map.
|
|
:- func goal_info_get_goal_mode(hlds_goal_info) = goal_mode.
|
|
:- func goal_info_get_maybe_ctgc(hlds_goal_info) = maybe(ctgc_goal_info).
|
|
:- func goal_info_get_maybe_rbmm(hlds_goal_info) = maybe(rbmm_goal_info).
|
|
:- func goal_info_get_maybe_mode_constr(hlds_goal_info)
|
|
= maybe(mode_constr_goal_info).
|
|
:- func goal_info_get_maybe_dp_info(hlds_goal_info) = maybe(dp_goal_info).
|
|
|
|
:- pred goal_info_set_determinism(determinism::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_purity(purity::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_instmap_delta(instmap_delta::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_nonlocals(set_of_progvar::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_features(set(goal_feature)::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_goal_id(goal_id::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_code_gen_info(hlds_goal_code_gen_info::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
|
|
:- pred goal_info_set_context(prog_context::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_reverse_goal_path(reverse_goal_path::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_higher_order_value_map(higher_order_value_map::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_goal_mode(goal_mode::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_maybe_ctgc(maybe(ctgc_goal_info)::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_maybe_rbmm(maybe(rbmm_goal_info)::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_maybe_mode_constr(maybe(mode_constr_goal_info)::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_maybe_dp_info(maybe(dp_goal_info)::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
|
|
:- func goal_info_get_code_gen_nonlocals(hlds_goal_info) = set_of_progvar.
|
|
:- pred goal_info_set_code_gen_nonlocals(set_of_progvar::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The following functions produce an 'unexpected' error when the
|
|
% requested values have not been set.
|
|
%
|
|
:- func goal_info_get_rbmm(hlds_goal_info) = rbmm_goal_info.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func goal_info_get_maybe_lfu(hlds_goal_info) = maybe(set_of_progvar).
|
|
:- func goal_info_get_maybe_lbu(hlds_goal_info) = maybe(set_of_progvar).
|
|
:- func goal_info_get_maybe_reuse(hlds_goal_info) = maybe(reuse_description).
|
|
|
|
% The following functions produce an 'unexpected' error when the
|
|
% requested values have not been set.
|
|
%
|
|
:- func goal_info_get_lfu(hlds_goal_info) = set_of_progvar.
|
|
:- func goal_info_get_lbu(hlds_goal_info) = set_of_progvar.
|
|
:- func goal_info_get_reuse(hlds_goal_info) = reuse_description.
|
|
|
|
:- pred goal_info_set_lfu(set_of_progvar::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_lbu(set_of_progvar::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_reuse(reuse_description::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred goal_info_get_occurring_vars(hlds_goal_info::in, set_of_progvar::out)
|
|
is det.
|
|
:- pred goal_info_get_producing_vars(hlds_goal_info::in, set_of_progvar::out)
|
|
is det.
|
|
:- pred goal_info_get_consuming_vars(hlds_goal_info::in, set_of_progvar::out)
|
|
is det.
|
|
:- pred goal_info_get_make_visible_vars(hlds_goal_info::in,
|
|
set_of_progvar::out) is det.
|
|
:- pred goal_info_get_need_visible_vars(hlds_goal_info::in,
|
|
set_of_progvar::out) is det.
|
|
|
|
:- pred goal_info_set_occurring_vars(set_of_progvar::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_producing_vars(set_of_progvar::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_consuming_vars(set_of_progvar::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_make_visible_vars(set_of_progvar::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_set_need_visible_vars(set_of_progvar::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
|
|
:- func producing_vars(hlds_goal_info) = set_of_progvar.
|
|
:- func 'producing_vars :='(hlds_goal_info, set_of_progvar) = hlds_goal_info.
|
|
|
|
:- func consuming_vars(hlds_goal_info) = set_of_progvar.
|
|
:- func 'consuming_vars :='(hlds_goal_info, set_of_progvar) = hlds_goal_info.
|
|
|
|
:- func make_visible_vars(hlds_goal_info) = set_of_progvar.
|
|
:- func 'make_visible_vars :='(hlds_goal_info, set_of_progvar)
|
|
= hlds_goal_info.
|
|
|
|
:- func need_visible_vars(hlds_goal_info) = set_of_progvar.
|
|
:- func 'need_visible_vars :='(hlds_goal_info, set_of_progvar)
|
|
= hlds_goal_info.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func goal_info_add_nonlocals_make_impure(hlds_goal_info, set_of_progvar)
|
|
= hlds_goal_info.
|
|
:- pred make_impure(hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred add_impurity_if_needed(bool::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type contains_trace_goal
|
|
---> contains_trace_goal
|
|
; contains_no_trace_goal.
|
|
|
|
:- func worst_contains_trace(contains_trace_goal, contains_trace_goal)
|
|
= contains_trace_goal.
|
|
|
|
:- func goal_get_nonlocals(hlds_goal) = set_of_progvar.
|
|
|
|
:- pred goal_set_goal_id(goal_id::in, hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
:- func goal_get_purity(hlds_goal) = purity.
|
|
:- pred goal_set_purity(purity::in, hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
:- pred goal_get_goal_purity(hlds_goal::in,
|
|
purity::out, contains_trace_goal::out) is det.
|
|
:- pred goal_info_get_goal_purity(hlds_goal_info::in,
|
|
purity::out, contains_trace_goal::out) is det.
|
|
|
|
:- pred goal_info_add_feature(goal_feature::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_add_features(list(goal_feature)::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_remove_feature(goal_feature::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
:- pred goal_info_has_feature(hlds_goal_info::in, goal_feature::in) is semidet.
|
|
|
|
% Set the 'goal_is_mdprof_inst' field in the goal_dp_info structure
|
|
% in the given goal info structure.
|
|
%
|
|
:- pred goal_info_set_mdprof_inst(goal_is_mdprof_inst::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
|
|
:- pred goal_set_context(term_context::in, hlds_goal::in, hlds_goal::out)
|
|
is det.
|
|
|
|
:- pred goal_add_feature(goal_feature::in,
|
|
hlds_goal::in, hlds_goal::out) is det.
|
|
:- pred goal_add_features(list(goal_feature)::in,
|
|
hlds_goal::in, hlds_goal::out) is det.
|
|
:- pred goal_remove_feature(goal_feature::in,
|
|
hlds_goal::in, hlds_goal::out) is det.
|
|
:- pred goal_has_feature(hlds_goal::in, goal_feature::in) is semidet.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% The rename_var* predicates take a structure and a mapping from var -> var
|
|
% and apply that translation. If a var in the input structure does not
|
|
% occur as a key in the mapping, then the variable is left unsubstituted
|
|
% (if Must = need_not_rename) or we throw an exception (if Must = must_rename).
|
|
%
|
|
% We keep these predicates here to allow rename_vars_in_goal_info to exploit
|
|
% knowledge of the actual representation of hlds_goal_infos; since
|
|
% hlds_goal_info is an abstract type, this knowledge is not available
|
|
% in any other module.
|
|
%
|
|
% This exploitation also makes the code of rename_vars_in_goal_info depend on
|
|
% the structure of hlds_goal_infos, which makes accidentally forgetting to
|
|
% update that predicate after modifying the hlds_goal_info type much harder.
|
|
|
|
:- pred rename_some_vars_in_goal(prog_var_renaming::in,
|
|
hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
:- pred must_rename_vars_in_goal(prog_var_renaming::in,
|
|
hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
:- pred rename_vars_in_goals(must_rename::in, prog_var_renaming::in,
|
|
list(hlds_goal)::in, list(hlds_goal)::out) is det.
|
|
|
|
:- pred rename_vars_in_goal_expr(must_rename, prog_var_renaming,
|
|
hlds_goal_expr, hlds_goal_expr).
|
|
:- mode rename_vars_in_goal_expr(in, in,
|
|
in(goal_expr_unify), out(goal_expr_unify)) is det.
|
|
:- mode rename_vars_in_goal_expr(in, in,
|
|
in(goal_expr_plain_call), out(goal_expr_plain_call)) is det.
|
|
:- mode rename_vars_in_goal_expr(in, in,
|
|
in(goal_expr_generic_call), out(goal_expr_generic_call)) is det.
|
|
:- mode rename_vars_in_goal_expr(in, in,
|
|
in(goal_expr_foreign_proc), out(goal_expr_foreign_proc)) is det.
|
|
:- mode rename_vars_in_goal_expr(in, in, in, out) is det.
|
|
|
|
:- pred rename_vars_in_goal_info(must_rename::in, prog_var_renaming::in,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
|
|
:- type incremental_rename_map ==
|
|
map(goal_id, assoc_list(prog_var, prog_var)).
|
|
|
|
% Rename the variables in the given goal, incrementally updating the
|
|
% substitution. When we start processing a goal, we look up its goal_id
|
|
% in the provided map. If we find it, we add the given var to var mappings
|
|
% to the substitution we apply to that goal. We do not insist on variables
|
|
% in the goal occurring in the substitution (i.e. we implicitly assume
|
|
% Must = need_not_rename).
|
|
%
|
|
:- pred incremental_rename_vars_in_goal(prog_var_renaming::in,
|
|
incremental_rename_map::in, hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Miscellaneous utility procedures for dealing with HLDS goals.
|
|
%
|
|
|
|
% Convert a goal to a list of conjuncts.
|
|
% If the goal is a conjunction, then return its conjuncts,
|
|
% otherwise return the goal as a singleton list.
|
|
%
|
|
:- pred goal_to_conj_list(hlds_goal::in, list(hlds_goal)::out) is det.
|
|
|
|
% Convert a goal to a list of parallel conjuncts.
|
|
% If the goal is a parallel conjunction, then return its conjuncts,
|
|
% otherwise return the goal as a singleton list.
|
|
%
|
|
:- pred goal_to_par_conj_list(hlds_goal::in, list(hlds_goal)::out) is det.
|
|
|
|
% Convert a goal to a list of disjuncts.
|
|
% If the goal is a disjunction, then return its disjuncts,
|
|
% otherwise return the goal as a singleton list.
|
|
%
|
|
:- pred goal_to_disj_list(hlds_goal::in, list(hlds_goal)::out) is det.
|
|
|
|
% Convert a list of conjuncts to a goal.
|
|
% If the list contains only one goal, then return that goal,
|
|
% otherwise return the conjunction of the conjuncts,
|
|
% with the specified goal_info.
|
|
%
|
|
:- pred conj_list_to_goal(list(hlds_goal)::in, hlds_goal_info::in,
|
|
hlds_goal::out) is det.
|
|
|
|
% Convert a list of parallel conjuncts to a goal.
|
|
% If the list contains only one goal, then return that goal,
|
|
% otherwise return the parallel conjunction of the conjuncts,
|
|
% with the specified goal_info.
|
|
%
|
|
:- pred par_conj_list_to_goal(list(hlds_goal)::in, hlds_goal_info::in,
|
|
hlds_goal::out) is det.
|
|
|
|
% Convert a list of disjuncts to a goal.
|
|
% If the list contains only one goal, then return that goal,
|
|
% otherwise return the disjunction of the disjuncts,
|
|
% with the specified goal_info.
|
|
%
|
|
:- pred disj_list_to_goal(list(hlds_goal)::in, hlds_goal_info::in,
|
|
hlds_goal::out) is det.
|
|
|
|
% Takes a goal and a list of goals, and conjoins them
|
|
% (with a potentially blank goal_info).
|
|
%
|
|
:- pred conjoin_goal_and_goal_list(hlds_goal::in, list(hlds_goal)::in,
|
|
hlds_goal::out) is det.
|
|
|
|
% Conjoin two goals (with a potentially blank goal_info).
|
|
%
|
|
:- pred conjoin_goals(hlds_goal::in, hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
% Negate a goal, eliminating double negations as we go.
|
|
%
|
|
:- pred negate_goal(hlds_goal::in, hlds_goal_info::in, hlds_goal::out) is det.
|
|
|
|
% Return the union of all the nonlocals of a list of goals.
|
|
%
|
|
:- pred goal_list_nonlocals(list(hlds_goal)::in, set_of_progvar::out) is det.
|
|
|
|
% Compute the instmap_delta resulting from applying
|
|
% all the instmap_deltas of the given goals.
|
|
%
|
|
:- pred goal_list_instmap_delta(list(hlds_goal)::in, instmap_delta::out)
|
|
is det.
|
|
|
|
% Compute the determinism of a list of goals.
|
|
%
|
|
:- pred goal_list_determinism(list(hlds_goal)::in, determinism::out) is det.
|
|
|
|
% Compute the purity of a list of goals.
|
|
:- pred goal_list_purity(list(hlds_goal)::in, purity::out) is det.
|
|
|
|
% Change the contexts of the goal_infos of all the sub-goals
|
|
% of the given goal. This is used to ensure that error messages
|
|
% for automatically generated unification procedures have a useful
|
|
% context.
|
|
%
|
|
:- pred set_goal_contexts(prog_context::in, hlds_goal::in, hlds_goal::out)
|
|
is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module parse_tree.prog_detism.
|
|
|
|
:- import_module io.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
get_hlds_goal_expr(Goal) = Goal ^ hg_expr.
|
|
get_hlds_goal_info(Goal) = Goal ^ hg_info.
|
|
|
|
foreign_arg_var(Arg) = Arg ^ arg_var.
|
|
foreign_arg_maybe_name_mode(Arg) = Arg ^ arg_name_mode.
|
|
foreign_arg_type(Arg) = Arg ^ arg_type.
|
|
foreign_arg_box(Arg) = Arg ^ arg_box_policy.
|
|
|
|
make_foreign_args(Vars, NamesModesBoxes, Types, Args) :-
|
|
( if
|
|
Vars = [Var | VarsTail],
|
|
NamesModesBoxes = [NameModeBox | NamesModesBoxesTail],
|
|
Types = [Type | TypesTail]
|
|
then
|
|
make_foreign_args(VarsTail, NamesModesBoxesTail, TypesTail, ArgsTail),
|
|
NameModeBox = foreign_arg_name_mode_box(MaybeNameMode, Box),
|
|
Arg = foreign_arg(Var, MaybeNameMode, Type, Box),
|
|
Args = [Arg | ArgsTail]
|
|
else if
|
|
Vars = [],
|
|
NamesModesBoxes = [],
|
|
Types = []
|
|
then
|
|
Args = []
|
|
else
|
|
unexpected($pred, "unmatched lists")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Predicates dealing with generic_calls.
|
|
%
|
|
|
|
generic_call_to_id(GenericCall, GenericCallId) :-
|
|
(
|
|
GenericCall = higher_order(_, Purity, PorF, PredFormArity),
|
|
GenericCallId = gcid_higher_order(Purity, PorF, PredFormArity)
|
|
;
|
|
GenericCall = class_method(_, _, ClassId, MethodId),
|
|
GenericCallId = gcid_class_method(ClassId, MethodId)
|
|
;
|
|
GenericCall = event_call(EventName),
|
|
GenericCallId = gcid_event_call(EventName)
|
|
;
|
|
GenericCall = cast(CastType),
|
|
GenericCallId = gcid_cast(CastType)
|
|
).
|
|
|
|
generic_call_pred_or_func(GenericCall) = PredOrFunc :-
|
|
(
|
|
GenericCall = higher_order(_, _, PredOrFunc, _)
|
|
;
|
|
GenericCall = class_method(_, _, _, PFSymNameArity),
|
|
PFSymNameArity = pf_sym_name_arity(PredOrFunc, _, _)
|
|
;
|
|
( GenericCall = event_call(_)
|
|
; GenericCall = cast(_)
|
|
),
|
|
PredOrFunc = pf_predicate
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
rbmm_info_init =
|
|
rbmm_goal_info(set.init, set.init, set.init, set.init, set.init).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Information stored with all kinds of goals.
|
|
%
|
|
|
|
% The information we have about a goal that is not specific to
|
|
% a particular *kind* of goal.
|
|
%
|
|
% The hlds_goal_info and hlds_goal_extra_info types constitute
|
|
% a single logical data structure split into two parts for efficiency
|
|
% purposes.
|
|
%
|
|
% The most frequently used fields are in the hlds_goal_info type,
|
|
% while all the other fields are in the hlds_goal_extra_info type.
|
|
|
|
:- type hlds_goal_info
|
|
---> goal_info(
|
|
% The Boehm collector allocates blocks whose sizes are
|
|
% multiples of 2, so we should keep the number of fields
|
|
% in a hlds_goal_info to be a multiple of 2 as well.
|
|
|
|
% The overall determinism of the goal (computed during
|
|
% determinism analysis). Since the determinism analysis problem
|
|
% is undecidable, this may be a conservative approximation.
|
|
/* 1 */ gi_determinism :: determinism,
|
|
|
|
/* 2 */ gi_purity :: purity,
|
|
|
|
% The change in insts over this goal (computed during mode
|
|
% analysis). Since the unreachability problem is undecidable,
|
|
% the instmap_delta may be reachable even when the goal
|
|
% really never succeeds.
|
|
%
|
|
% The following invariant is required by the code generator
|
|
% and is enforced by the final simplification pass:
|
|
%
|
|
% the determinism specifies at_most_zero solns
|
|
% IFF the instmap_delta is unreachable.
|
|
%
|
|
% Before the final simplification pass, the determinism and
|
|
% instmap_delta might not be consistent with regard to
|
|
% unreachability, but both will be conservative approximations,
|
|
% so if either says a goal is unreachable then it is.
|
|
%
|
|
% Normally the instmap_delta will list only the nonlocal
|
|
% variables of the goal.
|
|
/* 3 */ gi_instmap_delta :: instmap_delta,
|
|
|
|
% The non-local vars in the goal, i.e. the variables that
|
|
% occur both inside and outside of the goal (computed by
|
|
% quantification.m). In some circumstances, this may be a
|
|
% conservative approximation: it may be a superset of the
|
|
% real non-locals.
|
|
/* 4 */ gi_nonlocals :: set_of_progvar,
|
|
|
|
% The set of compiler-defined "features" of this goal,
|
|
% which optimisers may wish to know about.
|
|
/* 5 */ gi_features :: set(goal_feature),
|
|
|
|
% An value that uniquely identifies this goal in its procedure.
|
|
/* 6 */ gi_goal_id :: goal_id,
|
|
|
|
/* 7 */ gi_code_gen_info :: hlds_goal_code_gen_info,
|
|
|
|
% Extra information about the goal that doesn't fit in an
|
|
% eight-word cell. Mostly used for information used by
|
|
% various optional analysis passes, e.g closure analysis.
|
|
/* 8 */ gi_extra :: hlds_goal_extra_info
|
|
).
|
|
|
|
:- type hlds_goal_extra_info
|
|
---> extra_goal_info(
|
|
egi_context :: prog_context,
|
|
|
|
egi_rev_goal_path :: reverse_goal_path,
|
|
|
|
egi_ho_value_map :: higher_order_value_map,
|
|
|
|
egi_goal_mode :: goal_mode,
|
|
|
|
% Any information related to structure reuse (CTGC).
|
|
egi_maybe_ctgc :: maybe(ctgc_goal_info),
|
|
|
|
egi_maybe_rbmm :: maybe(rbmm_goal_info),
|
|
|
|
egi_maybe_mode_constr :: maybe(mode_constr_goal_info),
|
|
|
|
egi_maybe_dp :: maybe(dp_goal_info)
|
|
).
|
|
|
|
% i same diff same%
|
|
% 0 389614 2409489 13.919% determinism
|
|
% 1 10027360 13578654 42.478% instmap_delta
|
|
% 2 497826 15861153 3.043% nonlocals
|
|
% 3 10060683 41741 99.587% purity
|
|
% 4 8442 4617849 0.182% features
|
|
% 5 19010 3313840 0.570% goal_id
|
|
% 6 164 2328 6.581% rev_goal_path
|
|
% 7 0 4323322 0.000% code_gen_info
|
|
% 8 1761371 2616953 40.229% context
|
|
% 9 174 3 98.305% ho_values
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pragma inline(pred(goal_info_init/1)).
|
|
|
|
goal_info_init(GoalInfo) :-
|
|
Detism = detism_erroneous,
|
|
instmap_delta_init_unreachable(InstMapDelta),
|
|
NonLocals = set_of_var.init,
|
|
set.init(Features),
|
|
GoalId = goal_id(-1),
|
|
GoalInfo = goal_info(Detism, purity_pure, InstMapDelta, NonLocals,
|
|
Features, GoalId, no_code_gen_info,
|
|
hlds_goal_extra_info_init(dummy_context)).
|
|
|
|
:- pragma inline(pred(goal_info_init/2)).
|
|
|
|
goal_info_init(Context, GoalInfo) :-
|
|
Detism = detism_erroneous,
|
|
instmap_delta_init_unreachable(InstMapDelta),
|
|
NonLocals = set_of_var.init,
|
|
set.init(Features),
|
|
GoalId = goal_id(-1),
|
|
GoalInfo = goal_info(Detism, purity_pure, InstMapDelta, NonLocals,
|
|
Features, GoalId, no_code_gen_info,
|
|
hlds_goal_extra_info_init(Context)).
|
|
|
|
goal_info_init(NonLocals, InstMapDelta, Detism, Purity, GoalInfo) :-
|
|
set.init(Features),
|
|
GoalId = goal_id(-1),
|
|
GoalInfo = goal_info(Detism, Purity, InstMapDelta, NonLocals,
|
|
Features, GoalId, no_code_gen_info,
|
|
hlds_goal_extra_info_init(dummy_context)).
|
|
|
|
goal_info_init(NonLocals, InstMapDelta, Detism, Purity, Context, GoalInfo) :-
|
|
set.init(Features),
|
|
GoalId = goal_id(-1),
|
|
GoalInfo = goal_info(Detism, Purity, InstMapDelta, NonLocals,
|
|
Features, GoalId, no_code_gen_info,
|
|
hlds_goal_extra_info_init(Context)).
|
|
|
|
goal_info_init_context_purity(Context, Purity, GoalInfo) :-
|
|
Detism = detism_erroneous,
|
|
instmap_delta_init_unreachable(InstMapDelta),
|
|
NonLocals = set_of_var.init,
|
|
set.init(Features),
|
|
GoalId = goal_id(-1),
|
|
GoalInfo = goal_info(Detism, Purity, InstMapDelta, NonLocals,
|
|
Features, GoalId, no_code_gen_info,
|
|
hlds_goal_extra_info_init(Context)).
|
|
|
|
:- func hlds_goal_extra_info_init(term_context) = hlds_goal_extra_info.
|
|
|
|
hlds_goal_extra_info_init(Context) = ExtraInfo :-
|
|
HO_Values = map.init,
|
|
ExtraInfo = extra_goal_info(Context, rgp_nil, HO_Values,
|
|
make_dummy_goal_mode, no, no, no, no).
|
|
|
|
:- func ctgc_goal_info_init = ctgc_goal_info.
|
|
|
|
ctgc_goal_info_init =
|
|
ctgc_goal_info(set_of_var.init, set_of_var.init, no_reuse_info).
|
|
|
|
impure_init_goal_info(NonLocals, InstMapDelta, Determinism) = GoalInfo :-
|
|
goal_info_init(NonLocals, InstMapDelta, Determinism, purity_impure,
|
|
GoalInfo0),
|
|
goal_info_add_feature(feature_not_impure_for_determinism,
|
|
GoalInfo0, GoalInfo).
|
|
|
|
impure_reachable_init_goal_info(NonLocals, Determinism) = GoalInfo :-
|
|
instmap_delta_init_reachable(InstMapDelta),
|
|
goal_info_init(NonLocals, InstMapDelta, Determinism, purity_impure,
|
|
GoalInfo).
|
|
|
|
impure_unreachable_init_goal_info(NonLocals, Determinism) = GoalInfo :-
|
|
instmap_delta_init_unreachable(InstMapDelta),
|
|
goal_info_init(NonLocals, InstMapDelta, Determinism, purity_impure,
|
|
GoalInfo0),
|
|
goal_info_add_feature(feature_not_impure_for_determinism,
|
|
GoalInfo0, GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
goal_info_get_determinism(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_determinism.
|
|
goal_info_get_purity(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_purity.
|
|
goal_info_get_instmap_delta(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_instmap_delta.
|
|
goal_info_get_nonlocals(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_nonlocals.
|
|
goal_info_get_features(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_features.
|
|
goal_info_get_goal_id(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_goal_id.
|
|
goal_info_get_code_gen_info(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_code_gen_info.
|
|
|
|
goal_info_get_context(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_extra ^ egi_context.
|
|
goal_info_get_reverse_goal_path(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_extra ^ egi_rev_goal_path.
|
|
goal_info_get_higher_order_value_map(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_extra ^ egi_ho_value_map.
|
|
goal_info_get_goal_mode(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_extra ^ egi_goal_mode.
|
|
goal_info_get_maybe_ctgc(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_extra ^ egi_maybe_ctgc.
|
|
goal_info_get_maybe_rbmm(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_extra ^ egi_maybe_rbmm.
|
|
goal_info_get_maybe_mode_constr(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_extra ^ egi_maybe_mode_constr.
|
|
goal_info_get_maybe_dp_info(GoalInfo) = X :-
|
|
X = GoalInfo ^ gi_extra ^ egi_maybe_dp.
|
|
|
|
goal_info_set_determinism(X, !GoalInfo) :-
|
|
!GoalInfo ^ gi_determinism := X.
|
|
goal_info_set_purity(X, !GoalInfo) :-
|
|
( if X = !.GoalInfo ^ gi_purity then
|
|
true
|
|
else
|
|
!GoalInfo ^ gi_purity := X
|
|
).
|
|
goal_info_set_instmap_delta(X, !GoalInfo) :-
|
|
( if private_builtin.pointer_equal(X, !.GoalInfo ^ gi_instmap_delta) then
|
|
true
|
|
else
|
|
!GoalInfo ^ gi_instmap_delta := X
|
|
).
|
|
goal_info_set_nonlocals(X, !GoalInfo) :-
|
|
!GoalInfo ^ gi_nonlocals := X.
|
|
goal_info_set_features(X, !GoalInfo) :-
|
|
!GoalInfo ^ gi_features := X.
|
|
goal_info_set_goal_id(X, !GoalInfo) :-
|
|
!GoalInfo ^ gi_goal_id := X.
|
|
goal_info_set_code_gen_info(X, !GoalInfo) :-
|
|
!GoalInfo ^ gi_code_gen_info := X.
|
|
|
|
goal_info_set_context(X, !GoalInfo) :-
|
|
!GoalInfo ^ gi_extra ^ egi_context := X.
|
|
goal_info_set_reverse_goal_path(X, !GoalInfo) :-
|
|
!GoalInfo ^ gi_extra ^ egi_rev_goal_path := X.
|
|
goal_info_set_higher_order_value_map(X, !GoalInfo) :-
|
|
Map0 = !.GoalInfo ^ gi_extra ^ egi_ho_value_map,
|
|
( if private_builtin.pointer_equal(X, Map0) then
|
|
true
|
|
else
|
|
!GoalInfo ^ gi_extra ^ egi_ho_value_map := X
|
|
).
|
|
goal_info_set_goal_mode(X, !GoalInfo) :-
|
|
!GoalInfo ^ gi_extra ^ egi_goal_mode := X.
|
|
goal_info_set_maybe_ctgc(X, !GoalInfo) :-
|
|
!GoalInfo ^ gi_extra ^ egi_maybe_ctgc := X.
|
|
goal_info_set_maybe_rbmm(X, !GoalInfo) :-
|
|
!GoalInfo ^ gi_extra ^ egi_maybe_rbmm := X.
|
|
goal_info_set_maybe_mode_constr(X, !GoalInfo) :-
|
|
!GoalInfo ^ gi_extra ^ egi_maybe_mode_constr := X.
|
|
goal_info_set_maybe_dp_info(X, !GoalInfo) :-
|
|
!GoalInfo ^ gi_extra ^ egi_maybe_dp := X.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The code-gen non-locals are always the same as the
|
|
% non-locals when structure reuse is not being performed.
|
|
goal_info_get_code_gen_nonlocals(GoalInfo) =
|
|
goal_info_get_nonlocals(GoalInfo).
|
|
% The code-gen non-locals are always the same as the
|
|
% non-locals when structure reuse is not being performed.
|
|
goal_info_set_code_gen_nonlocals(NonLocals, !GoalInfo) :-
|
|
goal_info_set_nonlocals(NonLocals, !GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
goal_info_get_rbmm(GoalInfo) = RBMM :-
|
|
MaybeRBMM = goal_info_get_maybe_rbmm(GoalInfo),
|
|
(
|
|
MaybeRBMM = yes(RBMM)
|
|
;
|
|
MaybeRBMM = no,
|
|
unexpected($pred, "Requesting unavailable RBMM information.")
|
|
).
|
|
|
|
goal_info_get_maybe_lfu(GoalInfo) = MaybeLFU :-
|
|
MaybeCTGC = GoalInfo ^ gi_extra ^ egi_maybe_ctgc,
|
|
(
|
|
MaybeCTGC = yes(CTGC),
|
|
MaybeLFU = yes(CTGC ^ ctgc_lfu)
|
|
;
|
|
MaybeCTGC = no,
|
|
MaybeLFU = no
|
|
).
|
|
|
|
goal_info_get_maybe_lbu(GoalInfo) = MaybeLBU :-
|
|
MaybeCTGC = GoalInfo ^ gi_extra ^ egi_maybe_ctgc,
|
|
(
|
|
MaybeCTGC = yes(CTGC),
|
|
MaybeLBU = yes(CTGC ^ ctgc_lbu)
|
|
;
|
|
MaybeCTGC = no,
|
|
MaybeLBU = no
|
|
).
|
|
|
|
goal_info_get_maybe_reuse(GoalInfo) = MaybeReuse :-
|
|
MaybeCTGC = GoalInfo ^ gi_extra ^ egi_maybe_ctgc,
|
|
(
|
|
MaybeCTGC = yes(CTGC),
|
|
MaybeReuse = yes(CTGC ^ ctgc_reuse)
|
|
;
|
|
MaybeCTGC = no,
|
|
MaybeReuse = no
|
|
).
|
|
|
|
goal_info_get_lfu(GoalInfo) = LFU :-
|
|
MaybeLFU = goal_info_get_maybe_lfu(GoalInfo),
|
|
(
|
|
MaybeLFU = yes(LFU)
|
|
;
|
|
MaybeLFU = no,
|
|
unexpected($pred,
|
|
"Requesting LFU information while CTGC field not set.")
|
|
).
|
|
|
|
goal_info_get_lbu(GoalInfo) = LBU :-
|
|
MaybeLBU = goal_info_get_maybe_lbu(GoalInfo),
|
|
(
|
|
MaybeLBU = yes(LBU)
|
|
;
|
|
MaybeLBU = no,
|
|
unexpected($pred,
|
|
"Requesting LBU information while CTGC field not set.")
|
|
).
|
|
|
|
goal_info_get_reuse(GoalInfo) = Reuse :-
|
|
MaybeReuse = goal_info_get_maybe_reuse(GoalInfo),
|
|
(
|
|
MaybeReuse = yes(Reuse)
|
|
;
|
|
MaybeReuse = no,
|
|
unexpected($pred,
|
|
"Requesting reuse information while CTGC field not set.")
|
|
).
|
|
|
|
goal_info_set_lfu(LFU, !GoalInfo) :-
|
|
MaybeCTGC0 = !.GoalInfo ^ gi_extra ^ egi_maybe_ctgc,
|
|
(
|
|
MaybeCTGC0 = yes(CTGC0)
|
|
;
|
|
MaybeCTGC0 = no,
|
|
CTGC0 = ctgc_goal_info_init
|
|
),
|
|
CTGC = CTGC0 ^ ctgc_lfu := LFU,
|
|
MaybeCTGC = yes(CTGC),
|
|
!GoalInfo ^ gi_extra ^ egi_maybe_ctgc := MaybeCTGC.
|
|
|
|
goal_info_set_lbu(LBU, !GoalInfo) :-
|
|
MaybeCTGC0 = !.GoalInfo ^ gi_extra ^ egi_maybe_ctgc,
|
|
(
|
|
MaybeCTGC0 = yes(CTGC0)
|
|
;
|
|
MaybeCTGC0 = no,
|
|
CTGC0 = ctgc_goal_info_init
|
|
),
|
|
CTGC = CTGC0 ^ ctgc_lbu := LBU,
|
|
MaybeCTGC = yes(CTGC),
|
|
!GoalInfo ^ gi_extra ^ egi_maybe_ctgc := MaybeCTGC.
|
|
|
|
goal_info_set_reuse(Reuse, !GoalInfo) :-
|
|
MaybeCTGC0 = !.GoalInfo ^ gi_extra ^ egi_maybe_ctgc,
|
|
(
|
|
MaybeCTGC0 = yes(CTGC0)
|
|
;
|
|
MaybeCTGC0 = no,
|
|
CTGC0 = ctgc_goal_info_init
|
|
),
|
|
CTGC = CTGC0 ^ ctgc_reuse := Reuse,
|
|
MaybeCTGC = yes(CTGC),
|
|
!GoalInfo ^ gi_extra ^ egi_maybe_ctgc := MaybeCTGC.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
goal_info_get_occurring_vars(GoalInfo, OccurringVars) :-
|
|
MMCI = GoalInfo ^ gi_extra ^ egi_maybe_mode_constr,
|
|
(
|
|
MMCI = yes(MCI),
|
|
OccurringVars = MCI ^ mci_occurring_vars
|
|
;
|
|
MMCI = no,
|
|
OccurringVars = set_of_var.init
|
|
).
|
|
|
|
goal_info_get_producing_vars(GoalInfo, ProducingVars) :-
|
|
MMCI = GoalInfo ^ gi_extra ^ egi_maybe_mode_constr,
|
|
(
|
|
MMCI = yes(MCI),
|
|
ProducingVars = MCI ^ mci_producing_vars
|
|
;
|
|
MMCI = no,
|
|
ProducingVars = set_of_var.init
|
|
).
|
|
|
|
goal_info_get_consuming_vars(GoalInfo, ConsumingVars) :-
|
|
MMCI = GoalInfo ^ gi_extra ^ egi_maybe_mode_constr,
|
|
(
|
|
MMCI = yes(MCI),
|
|
ConsumingVars = MCI ^ mci_consuming_vars
|
|
;
|
|
MMCI = no,
|
|
ConsumingVars = set_of_var.init
|
|
).
|
|
|
|
goal_info_get_make_visible_vars(GoalInfo, MakeVisibleVars) :-
|
|
MMCI = GoalInfo ^ gi_extra ^ egi_maybe_mode_constr,
|
|
(
|
|
MMCI = yes(MCI),
|
|
MakeVisibleVars = MCI ^ mci_make_visible_vars
|
|
;
|
|
MMCI = no,
|
|
MakeVisibleVars = set_of_var.init
|
|
).
|
|
|
|
goal_info_get_need_visible_vars(GoalInfo, NeedVisibleVars) :-
|
|
MMCI = GoalInfo ^ gi_extra ^ egi_maybe_mode_constr,
|
|
(
|
|
MMCI = yes(MCI),
|
|
NeedVisibleVars = MCI ^ mci_need_visible_vars
|
|
;
|
|
MMCI = no,
|
|
NeedVisibleVars = set_of_var.init
|
|
).
|
|
|
|
goal_info_set_occurring_vars(OccurringVars, !GoalInfo) :-
|
|
MMCI0 = !.GoalInfo ^ gi_extra ^ egi_maybe_mode_constr,
|
|
(
|
|
MMCI0 = yes(MCI0),
|
|
MCI = MCI0 ^ mci_occurring_vars := OccurringVars
|
|
;
|
|
MMCI0 = no,
|
|
set_of_var.init(ProducingVars),
|
|
set_of_var.init(ConsumingVars),
|
|
set_of_var.init(MakeVisibleVars),
|
|
set_of_var.init(NeedVisibleVars),
|
|
MCI = mode_constr_goal_info(OccurringVars, ProducingVars,
|
|
ConsumingVars, MakeVisibleVars, NeedVisibleVars)
|
|
),
|
|
!GoalInfo ^ gi_extra ^ egi_maybe_mode_constr := yes(MCI).
|
|
|
|
goal_info_set_producing_vars(ProducingVars, !GoalInfo) :-
|
|
MMCI0 = !.GoalInfo ^ gi_extra ^ egi_maybe_mode_constr,
|
|
(
|
|
MMCI0 = yes(MCI0),
|
|
MCI = MCI0 ^ mci_producing_vars := ProducingVars
|
|
;
|
|
MMCI0 = no,
|
|
set_of_var.init(OccurringVars),
|
|
set_of_var.init(ConsumingVars),
|
|
set_of_var.init(MakeVisibleVars),
|
|
set_of_var.init(NeedVisibleVars),
|
|
MCI = mode_constr_goal_info(OccurringVars, ProducingVars,
|
|
ConsumingVars, MakeVisibleVars, NeedVisibleVars)
|
|
),
|
|
!GoalInfo ^ gi_extra ^ egi_maybe_mode_constr := yes(MCI).
|
|
|
|
goal_info_set_consuming_vars(ConsumingVars, !GoalInfo) :-
|
|
MMCI0 = !.GoalInfo ^ gi_extra ^ egi_maybe_mode_constr,
|
|
(
|
|
MMCI0 = yes(MCI0),
|
|
MCI = MCI0 ^ mci_consuming_vars := ConsumingVars
|
|
;
|
|
MMCI0 = no,
|
|
set_of_var.init(OccurringVars),
|
|
set_of_var.init(ProducingVars),
|
|
set_of_var.init(MakeVisibleVars),
|
|
set_of_var.init(NeedVisibleVars),
|
|
MCI = mode_constr_goal_info(OccurringVars, ProducingVars,
|
|
ConsumingVars, MakeVisibleVars, NeedVisibleVars)
|
|
),
|
|
!GoalInfo ^ gi_extra ^ egi_maybe_mode_constr := yes(MCI).
|
|
|
|
goal_info_set_make_visible_vars(MakeVisibleVars, !GoalInfo) :-
|
|
MMCI0 = !.GoalInfo ^ gi_extra ^ egi_maybe_mode_constr,
|
|
(
|
|
MMCI0 = yes(MCI0),
|
|
MCI = MCI0 ^ mci_make_visible_vars := MakeVisibleVars
|
|
;
|
|
MMCI0 = no,
|
|
set_of_var.init(OccurringVars),
|
|
set_of_var.init(ProducingVars),
|
|
set_of_var.init(ConsumingVars),
|
|
set_of_var.init(NeedVisibleVars),
|
|
MCI = mode_constr_goal_info(OccurringVars, ProducingVars,
|
|
ConsumingVars, MakeVisibleVars, NeedVisibleVars)
|
|
),
|
|
!GoalInfo ^ gi_extra ^ egi_maybe_mode_constr := yes(MCI).
|
|
|
|
goal_info_set_need_visible_vars(NeedVisibleVars, !GoalInfo) :-
|
|
MMCI0 = !.GoalInfo ^ gi_extra ^ egi_maybe_mode_constr,
|
|
(
|
|
MMCI0 = yes(MCI0),
|
|
MCI = MCI0 ^ mci_need_visible_vars := NeedVisibleVars
|
|
;
|
|
MMCI0 = no,
|
|
set_of_var.init(OccurringVars),
|
|
set_of_var.init(ProducingVars),
|
|
set_of_var.init(ConsumingVars),
|
|
set_of_var.init(MakeVisibleVars),
|
|
MCI = mode_constr_goal_info(OccurringVars, ProducingVars,
|
|
ConsumingVars, MakeVisibleVars, NeedVisibleVars)
|
|
),
|
|
!GoalInfo ^ gi_extra ^ egi_maybe_mode_constr := yes(MCI).
|
|
|
|
producing_vars(GoalInfo) = ProducingVars :-
|
|
goal_info_get_producing_vars(GoalInfo, ProducingVars).
|
|
|
|
'producing_vars :='(GoalInfo0, ProducingVars) = GoalInfo :-
|
|
goal_info_set_producing_vars(ProducingVars, GoalInfo0, GoalInfo).
|
|
|
|
consuming_vars(GoalInfo) = ConsumingVars :-
|
|
goal_info_get_consuming_vars(GoalInfo, ConsumingVars).
|
|
|
|
'consuming_vars :='(GoalInfo0, ConsumingVars) = GoalInfo :-
|
|
goal_info_set_consuming_vars(ConsumingVars, GoalInfo0, GoalInfo).
|
|
|
|
make_visible_vars(GoalInfo) = MakeVisibleVars :-
|
|
goal_info_get_make_visible_vars(GoalInfo, MakeVisibleVars).
|
|
|
|
'make_visible_vars :='(GoalInfo0, MakeVisibleVars) = GoalInfo :-
|
|
goal_info_set_make_visible_vars(MakeVisibleVars, GoalInfo0, GoalInfo).
|
|
|
|
need_visible_vars(GoalInfo) = NeedVisibleVars :-
|
|
goal_info_get_need_visible_vars(GoalInfo, NeedVisibleVars).
|
|
|
|
'need_visible_vars :='(GoalInfo0, NeedVisibleVars) = GoalInfo :-
|
|
goal_info_set_need_visible_vars(NeedVisibleVars, GoalInfo0, GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
goal_info_add_nonlocals_make_impure(!.GoalInfo, NewNonLocals) = !:GoalInfo :-
|
|
NonLocals0 = goal_info_get_nonlocals(!.GoalInfo),
|
|
NonLocals = set_of_var.union(NonLocals0, NewNonLocals),
|
|
goal_info_set_nonlocals(NonLocals, !GoalInfo),
|
|
make_impure(!GoalInfo).
|
|
|
|
make_impure(!GoalInfo) :-
|
|
Purity = goal_info_get_purity(!.GoalInfo),
|
|
(
|
|
Purity = purity_impure
|
|
% We don't add not_impure_for_determinism, since we want to
|
|
% keep the existing determinism.
|
|
;
|
|
( Purity = purity_pure
|
|
; Purity = purity_semipure
|
|
),
|
|
goal_info_set_purity(purity_impure, !GoalInfo),
|
|
goal_info_add_feature(feature_not_impure_for_determinism, !GoalInfo)
|
|
).
|
|
|
|
add_impurity_if_needed(AddedImpurity, !GoalInfo) :-
|
|
(
|
|
AddedImpurity = no
|
|
;
|
|
AddedImpurity = yes,
|
|
make_impure(!GoalInfo)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
worst_contains_trace(contains_trace_goal, contains_trace_goal) =
|
|
contains_trace_goal.
|
|
worst_contains_trace(contains_trace_goal, contains_no_trace_goal) =
|
|
contains_trace_goal.
|
|
worst_contains_trace(contains_no_trace_goal, contains_trace_goal) =
|
|
contains_trace_goal.
|
|
worst_contains_trace(contains_no_trace_goal, contains_no_trace_goal) =
|
|
contains_no_trace_goal.
|
|
|
|
goal_get_nonlocals(hlds_goal(_GoalExpr, GoalInfo)) =
|
|
goal_info_get_nonlocals(GoalInfo).
|
|
|
|
goal_set_goal_id(GoalId, Goal0, Goal) :-
|
|
Goal0 = hlds_goal(GoalExpr, GoalInfo0),
|
|
goal_info_set_goal_id(GoalId, GoalInfo0, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
goal_get_purity(hlds_goal(_GoalExpr, GoalInfo)) =
|
|
goal_info_get_purity(GoalInfo).
|
|
|
|
goal_set_purity(Purity, Goal0, Goal) :-
|
|
Goal0 = hlds_goal(GoalExpr, GoalInfo0),
|
|
goal_info_set_purity(Purity, GoalInfo0, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
goal_get_goal_purity(Goal, Purity, ContainsTraceGoal) :-
|
|
Goal = hlds_goal(_GoalExpr, GoalInfo),
|
|
goal_info_get_goal_purity(GoalInfo, Purity, ContainsTraceGoal).
|
|
|
|
goal_info_get_goal_purity(GoalInfo, Purity, ContainsTraceGoal) :-
|
|
Purity = goal_info_get_purity(GoalInfo),
|
|
( if goal_info_has_feature(GoalInfo, feature_contains_trace) then
|
|
ContainsTraceGoal = contains_trace_goal
|
|
else
|
|
ContainsTraceGoal = contains_no_trace_goal
|
|
).
|
|
|
|
goal_info_add_feature(NewFeature, !GoalInfo) :-
|
|
Features0 = goal_info_get_features(!.GoalInfo),
|
|
set.insert(NewFeature, Features0, Features),
|
|
goal_info_set_features(Features, !GoalInfo).
|
|
|
|
goal_info_add_features(NewFeatures, !GoalInfo) :-
|
|
Features0 = goal_info_get_features(!.GoalInfo),
|
|
set.insert_list(NewFeatures, Features0, Features),
|
|
goal_info_set_features(Features, !GoalInfo).
|
|
|
|
goal_info_remove_feature(OldFeature, !GoalInfo) :-
|
|
Features0 = goal_info_get_features(!.GoalInfo),
|
|
( if set.remove(OldFeature, Features0, Features) then
|
|
goal_info_set_features(Features, !GoalInfo)
|
|
else
|
|
% !.GoalInfo did not have Feature, so there is no need to allocate
|
|
% memory for a new !:GoalInfo.
|
|
true
|
|
).
|
|
|
|
goal_info_has_feature(GoalInfo, Feature) :-
|
|
Features = goal_info_get_features(GoalInfo),
|
|
set.member(Feature, Features).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
goal_info_set_mdprof_inst(IsMDProfInst, !GoalInfo) :-
|
|
goal_info_get_maybe_dp_info(!.GoalInfo) = MaybeDPInfo0,
|
|
(
|
|
MaybeDPInfo0 = yes(dp_goal_info(_, DPCoverageInfo)),
|
|
MaybeDPInfo = yes(dp_goal_info(IsMDProfInst, DPCoverageInfo))
|
|
;
|
|
MaybeDPInfo0 = no,
|
|
MaybeDPInfo = yes(dp_goal_info(IsMDProfInst, no))
|
|
),
|
|
goal_info_set_maybe_dp_info(MaybeDPInfo, !GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
goal_set_context(Context, Goal0, Goal) :-
|
|
Goal0 = hlds_goal(GoalExpr, GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
goal_add_feature(NewFeature, Goal0, Goal) :-
|
|
Goal0 = hlds_goal(GoalExpr, GoalInfo0),
|
|
goal_info_add_feature(NewFeature, GoalInfo0, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
goal_add_features(NewFeatures, Goal0, Goal) :-
|
|
Goal0 = hlds_goal(GoalExpr, GoalInfo0),
|
|
goal_info_add_features(NewFeatures, GoalInfo0, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
goal_remove_feature(OldFeature, Goal0, Goal) :-
|
|
Goal0 = hlds_goal(GoalExpr, GoalInfo0),
|
|
goal_info_remove_feature(OldFeature, GoalInfo0, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
goal_has_feature(hlds_goal(_GoalExpr, GoalInfo), Feature) :-
|
|
goal_info_has_feature(GoalInfo, Feature).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Rename predicates.
|
|
%
|
|
|
|
rename_some_vars_in_goal(Subn, Goal0, Goal) :-
|
|
rename_vars_in_goal(need_not_rename, Subn, Goal0, Goal).
|
|
|
|
must_rename_vars_in_goal(Subn, Goal0, Goal) :-
|
|
rename_vars_in_goal(must_rename, Subn, Goal0, Goal).
|
|
|
|
:- pred rename_vars_in_goal(must_rename::in, prog_var_renaming::in,
|
|
hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
rename_vars_in_goal(Must, Subn, Goal0, Goal) :-
|
|
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
|
|
rename_vars_in_goal_expr(Must, Subn, GoalExpr0, GoalExpr),
|
|
rename_vars_in_goal_info(Must, Subn, GoalInfo0, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
rename_vars_in_goals(_, _, [], []).
|
|
rename_vars_in_goals(Must, Subn, [Goal0 | Goals0], [Goal | Goals]) :-
|
|
rename_vars_in_goal(Must, Subn, Goal0, Goal),
|
|
rename_vars_in_goals(Must, Subn, Goals0, Goals).
|
|
|
|
:- pred rename_vars_in_cases(must_rename::in, prog_var_renaming::in,
|
|
list(case)::in, list(case)::out) is det.
|
|
|
|
rename_vars_in_cases(_Must, _Subn, [], []).
|
|
rename_vars_in_cases(Must, Subn, [Case0 | Cases0], [Case | Cases]) :-
|
|
Case0 = case(MainConsId, OtherConsIds, Goal0),
|
|
rename_vars_in_goal(Must, Subn, Goal0, Goal),
|
|
Case = case(MainConsId, OtherConsIds, Goal),
|
|
rename_vars_in_cases(Must, Subn, Cases0, Cases).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
rename_vars_in_goal_expr(Must, Subn, Expr0, Expr) :-
|
|
(
|
|
Expr0 = conj(ConjType, Goals0),
|
|
rename_vars_in_goals(Must, Subn, Goals0, Goals),
|
|
Expr = conj(ConjType, Goals)
|
|
;
|
|
Expr0 = disj(Goals0),
|
|
rename_vars_in_goals(Must, Subn, Goals0, Goals),
|
|
Expr = disj(Goals)
|
|
;
|
|
Expr0 = switch(Var0, CanFail, Cases0),
|
|
rename_var(Must, Subn, Var0, Var),
|
|
rename_vars_in_cases(Must, Subn, Cases0, Cases),
|
|
Expr = switch(Var, CanFail, Cases)
|
|
;
|
|
Expr0 = if_then_else(Vars0, Cond0, Then0, Else0),
|
|
rename_var_list(Must, Subn, Vars0, Vars),
|
|
rename_vars_in_goal(Must, Subn, Cond0, Cond),
|
|
rename_vars_in_goal(Must, Subn, Then0, Then),
|
|
rename_vars_in_goal(Must, Subn, Else0, Else),
|
|
Expr = if_then_else(Vars, Cond, Then, Else)
|
|
;
|
|
Expr0 = negation(Goal0),
|
|
rename_vars_in_goal(Must, Subn, Goal0, Goal),
|
|
Expr = negation(Goal)
|
|
;
|
|
Expr0 = scope(Reason0, Goal0),
|
|
(
|
|
Reason0 = exist_quant(Vars0),
|
|
rename_var_list(Must, Subn, Vars0, Vars),
|
|
Reason = exist_quant(Vars)
|
|
;
|
|
Reason0 = promise_solutions(Vars0, Kind),
|
|
rename_var_list(Must, Subn, Vars0, Vars),
|
|
Reason = promise_solutions(Vars, Kind)
|
|
;
|
|
Reason0 = require_complete_switch(Var0),
|
|
rename_var(Must, Subn, Var0, Var),
|
|
Reason = require_complete_switch(Var)
|
|
;
|
|
Reason0 = require_switch_arms_detism(Var0, Detism),
|
|
rename_var(Must, Subn, Var0, Var),
|
|
Reason = require_switch_arms_detism(Var, Detism)
|
|
;
|
|
Reason0 = from_ground_term(Var0, Kind),
|
|
rename_var(Must, Subn, Var0, Var),
|
|
Reason = from_ground_term(Var, Kind)
|
|
;
|
|
Reason0 = trace_goal(Flag, Grade, Env, Vars, QuantVars0),
|
|
rename_var_list(Must, Subn, QuantVars0, QuantVars),
|
|
Reason = trace_goal(Flag, Grade, Env, Vars, QuantVars)
|
|
;
|
|
Reason0 = loop_control(LCVar0, LCSVar0, UseParentStack),
|
|
rename_var(Must, Subn, LCVar0, LCVar),
|
|
rename_var(Must, Subn, LCSVar0, LCSVar),
|
|
Reason = loop_control(LCVar, LCSVar, UseParentStack)
|
|
;
|
|
( Reason0 = disable_warnings(_HeadWarnings, _TailWarnings)
|
|
; Reason0 = promise_purity(_Purity)
|
|
; Reason0 = require_detism(_Detism)
|
|
; Reason0 = commit(_ForcePruning)
|
|
; Reason0 = barrier(_Removable)
|
|
),
|
|
Reason = Reason0
|
|
),
|
|
rename_vars_in_goal(Must, Subn, Goal0, Goal),
|
|
Expr = scope(Reason, Goal)
|
|
;
|
|
Expr0 = generic_call(GenericCall0, Args0, Modes, MaybeArgRegs, Det),
|
|
rename_generic_call(Must, Subn, GenericCall0, GenericCall),
|
|
rename_var_list(Must, Subn, Args0, Args),
|
|
Expr = generic_call(GenericCall, Args, Modes, MaybeArgRegs, Det)
|
|
;
|
|
Expr0 = plain_call(PredId, ProcId, Args0, Builtin, CallUC0, Sym),
|
|
rename_var_in_call_unify_context(Must, Subn, CallUC0, CallUC),
|
|
rename_var_list(Must, Subn, Args0, Args),
|
|
Expr = plain_call(PredId, ProcId, Args, Builtin, CallUC, Sym)
|
|
;
|
|
Expr0 = unify(LHS0, RHS0, Mode, Unify0, Context),
|
|
rename_var(Must, Subn, LHS0, LHS),
|
|
rename_var_in_unify_rhs(Must, Subn, RHS0, RHS),
|
|
rename_var_in_unify(Must, Subn, Unify0, Unify),
|
|
Expr = unify(LHS, RHS, Mode, Unify, Context)
|
|
;
|
|
Expr0 = call_foreign_proc(Attrs, PredId, ProcId, Args0, Extra0,
|
|
MTRC, Impl),
|
|
rename_arg_list(Must, Subn, Args0, Args),
|
|
rename_arg_list(Must, Subn, Extra0, Extra),
|
|
Expr = call_foreign_proc(Attrs, PredId, ProcId, Args, Extra,
|
|
MTRC, Impl)
|
|
;
|
|
Expr0 = shorthand(Shorthand0),
|
|
(
|
|
Shorthand0 = atomic_goal(GoalType0, Outer0, Inner0,
|
|
MaybeOutputVars0, MainGoal0, OrElseGoals0, OrElseInners),
|
|
GoalType = GoalType0,
|
|
Outer0 = atomic_interface_vars(OuterDI0, OuterUO0),
|
|
rename_var(Must, Subn, OuterDI0, OuterDI),
|
|
rename_var(Must, Subn, OuterUO0, OuterUO),
|
|
Outer = atomic_interface_vars(OuterDI, OuterUO),
|
|
Inner0 = atomic_interface_vars(InnerDI0, InnerUO0),
|
|
rename_var(Must, Subn, InnerDI0, InnerDI),
|
|
rename_var(Must, Subn, InnerUO0, InnerUO),
|
|
Inner = atomic_interface_vars(InnerDI, InnerUO),
|
|
(
|
|
MaybeOutputVars0 = no,
|
|
MaybeOutputVars = MaybeOutputVars0
|
|
;
|
|
MaybeOutputVars0 = yes(OutputVars0),
|
|
rename_var_list(Must, Subn, OutputVars0, OutputVars),
|
|
MaybeOutputVars = yes(OutputVars)
|
|
),
|
|
rename_vars_in_goal(Must, Subn, MainGoal0, MainGoal),
|
|
rename_vars_in_goals(Must, Subn, OrElseGoals0, OrElseGoals),
|
|
Shorthand = atomic_goal(GoalType, Outer, Inner,
|
|
MaybeOutputVars, MainGoal, OrElseGoals, OrElseInners)
|
|
;
|
|
Shorthand0 = try_goal(MaybeIO0, ResultVar0, SubGoal0),
|
|
(
|
|
MaybeIO0 = yes(try_io_state_vars(IOVarInitial0, IOVarFinal0)),
|
|
rename_var(Must, Subn, IOVarInitial0, IOVarInitial),
|
|
rename_var(Must, Subn, IOVarFinal0, IOVarFinal),
|
|
MaybeIO = yes(try_io_state_vars(IOVarInitial, IOVarFinal))
|
|
;
|
|
MaybeIO0 = no,
|
|
MaybeIO = no
|
|
),
|
|
rename_var(Must, Subn, ResultVar0, ResultVar),
|
|
rename_vars_in_goal(Must, Subn, SubGoal0, SubGoal),
|
|
Shorthand = try_goal(MaybeIO, ResultVar, SubGoal)
|
|
;
|
|
Shorthand0 = bi_implication(LeftGoal0, RightGoal0),
|
|
rename_vars_in_goal(Must, Subn, LeftGoal0, LeftGoal),
|
|
rename_vars_in_goal(Must, Subn, RightGoal0, RightGoal),
|
|
Shorthand = bi_implication(LeftGoal, RightGoal)
|
|
),
|
|
Expr = shorthand(Shorthand)
|
|
).
|
|
|
|
:- pred rename_var_in_unify_rhs(must_rename::in, prog_var_renaming::in,
|
|
unify_rhs::in, unify_rhs::out) is det.
|
|
|
|
rename_var_in_unify_rhs(Must, Subn, RHS0, RHS) :-
|
|
(
|
|
RHS0 = rhs_var(Var0),
|
|
rename_var(Must, Subn, Var0, Var),
|
|
RHS = rhs_var(Var)
|
|
;
|
|
RHS0 = rhs_functor(Functor, E, ArgVars0),
|
|
rename_var_list(Must, Subn, ArgVars0, ArgVars),
|
|
RHS = rhs_functor(Functor, E, ArgVars)
|
|
;
|
|
RHS0 = rhs_lambda_goal(Purity, Groundness, PredOrFunc, EvalMethod,
|
|
NonLocals0, VarsModes0, Det, Goal0),
|
|
rename_var_list(Must, Subn, NonLocals0, NonLocals),
|
|
assoc_list.keys_and_values(VarsModes0, Vars0, Modes),
|
|
rename_var_list(Must, Subn, Vars0, Vars),
|
|
assoc_list.from_corresponding_lists(Vars, Modes, VarsModes),
|
|
rename_vars_in_goal(Must, Subn, Goal0, Goal),
|
|
RHS = rhs_lambda_goal(Purity, Groundness, PredOrFunc, EvalMethod,
|
|
NonLocals, VarsModes, Det, Goal)
|
|
).
|
|
|
|
:- pred rename_var_in_call_unify_context(must_rename::in,
|
|
prog_var_renaming::in,
|
|
maybe(call_unify_context)::in, maybe(call_unify_context)::out) is det.
|
|
|
|
rename_var_in_call_unify_context(Must, Subn,
|
|
MaybeCallUnifyContext0, MaybeCallUnifyContext) :-
|
|
(
|
|
MaybeCallUnifyContext0 = no,
|
|
MaybeCallUnifyContext = no
|
|
;
|
|
MaybeCallUnifyContext0 = yes(CallUnifyContext0),
|
|
CallUnifyContext0 = call_unify_context(LHSVar0, RHS0, UnifyContext),
|
|
rename_var(Must, Subn, LHSVar0, LHSVar),
|
|
rename_var_in_unify_rhs(Must, Subn, RHS0, RHS),
|
|
% The unify_context contains no variables.
|
|
CallUnifyContext = call_unify_context(LHSVar, RHS, UnifyContext),
|
|
MaybeCallUnifyContext = yes(CallUnifyContext)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
rename_vars_in_goal_info(Must, Subn, !GoalInfo) :-
|
|
!.GoalInfo = goal_info(Detism, Purity, InstMapDelta0, NonLocals0,
|
|
Features, GoalPath, CodeGenInfo0, ExtraInfo0),
|
|
|
|
rename_vars_in_set_of_var(Must, Subn, NonLocals0, NonLocals),
|
|
instmap_delta_apply_sub(Must, Subn, InstMapDelta0, InstMapDelta),
|
|
(
|
|
CodeGenInfo0 = no_code_gen_info,
|
|
CodeGenInfo = no_code_gen_info
|
|
;
|
|
CodeGenInfo0 = llds_code_gen_info(LldsInfo0),
|
|
rename_vars_in_llds_code_gen_info(Must, Subn, LldsInfo0, LldsInfo),
|
|
CodeGenInfo = llds_code_gen_info(LldsInfo)
|
|
),
|
|
|
|
ExtraInfo0 = extra_goal_info(Context, RevGoalPath, HO_Values, GoalMode0,
|
|
MaybeCTGC0, MaybeRBMM0, MaybeMCI0, MaybeDPInfo0),
|
|
rename_vars_in_goal_mode(Must, Subn, GoalMode0, GoalMode),
|
|
(
|
|
MaybeCTGC0 = no,
|
|
MaybeCTGC = no
|
|
;
|
|
MaybeCTGC0 = yes(CTGC0),
|
|
CTGC0 = ctgc_goal_info(ForwardUse0, BackwardUse0, ReuseDesc0),
|
|
rename_vars_in_set_of_var(Must, Subn, ForwardUse0, ForwardUse),
|
|
rename_vars_in_set_of_var(Must, Subn, BackwardUse0, BackwardUse),
|
|
(
|
|
( ReuseDesc0 = no_reuse_info
|
|
; ReuseDesc0 = no_possible_reuse
|
|
; ReuseDesc0 = missed_reuse(_)
|
|
),
|
|
ReuseDesc = ReuseDesc0
|
|
;
|
|
ReuseDesc0 = potential_reuse(ShortReuseDesc0),
|
|
rename_vars_in_short_reuse_desc(Must, Subn,
|
|
ShortReuseDesc0, ShortReuseDesc),
|
|
ReuseDesc = potential_reuse(ShortReuseDesc)
|
|
;
|
|
ReuseDesc0 = reuse(ShortReuseDesc0),
|
|
rename_vars_in_short_reuse_desc(Must, Subn,
|
|
ShortReuseDesc0, ShortReuseDesc),
|
|
ReuseDesc = reuse(ShortReuseDesc)
|
|
),
|
|
CTGC = ctgc_goal_info(ForwardUse, BackwardUse, ReuseDesc),
|
|
MaybeCTGC = yes(CTGC)
|
|
),
|
|
(
|
|
MaybeRBMM0 = no,
|
|
MaybeRBMM = no
|
|
;
|
|
MaybeRBMM0 = yes(RBMM0),
|
|
RBMM0 = rbmm_goal_info(Created0, Removed0, Carried0, Alloc0,
|
|
NonAlloc0),
|
|
rename_vars_in_var_set(Must, Subn, Created0, Created),
|
|
rename_vars_in_var_set(Must, Subn, Removed0, Removed),
|
|
rename_vars_in_var_set(Must, Subn, Carried0, Carried),
|
|
rename_vars_in_var_set(Must, Subn, Alloc0, Alloc),
|
|
rename_vars_in_var_set(Must, Subn, NonAlloc0, NonAlloc),
|
|
RBMM = rbmm_goal_info(Created, Removed, Carried, Alloc, NonAlloc),
|
|
MaybeRBMM = yes(RBMM)
|
|
),
|
|
(
|
|
MaybeMCI0 = no,
|
|
MaybeMCI = no
|
|
;
|
|
MaybeMCI0 = yes(MCI0),
|
|
MCI0 = mode_constr_goal_info(Occurring0, Producing0, Consuming0,
|
|
MakeVisible0, NeedVisible0),
|
|
rename_vars_in_set_of_var(Must, Subn, Occurring0, Occurring),
|
|
rename_vars_in_set_of_var(Must, Subn, Producing0, Producing),
|
|
rename_vars_in_set_of_var(Must, Subn, Consuming0, Consuming),
|
|
rename_vars_in_set_of_var(Must, Subn, MakeVisible0, MakeVisible),
|
|
rename_vars_in_set_of_var(Must, Subn, NeedVisible0, NeedVisible),
|
|
MCI = mode_constr_goal_info(Occurring, Producing, Consuming,
|
|
MakeVisible, NeedVisible),
|
|
MaybeMCI = yes(MCI)
|
|
),
|
|
MaybeDPInfo = MaybeDPInfo0,
|
|
ExtraInfo = extra_goal_info(Context, RevGoalPath, HO_Values, GoalMode,
|
|
MaybeCTGC, MaybeRBMM, MaybeMCI, MaybeDPInfo),
|
|
|
|
!:GoalInfo = goal_info(Detism, Purity, InstMapDelta, NonLocals,
|
|
Features, GoalPath, CodeGenInfo, ExtraInfo).
|
|
|
|
:- pred rename_vars_in_short_reuse_desc(must_rename::in, prog_var_renaming::in,
|
|
short_reuse_description::in, short_reuse_description::out) is det.
|
|
|
|
rename_vars_in_short_reuse_desc(Must, Subn, ShortReuseDesc0, ShortReuseDesc) :-
|
|
(
|
|
( ShortReuseDesc0 = cell_died
|
|
; ShortReuseDesc0 = reuse_call(_, _)
|
|
),
|
|
ShortReuseDesc = ShortReuseDesc0
|
|
;
|
|
ShortReuseDesc0 = cell_reused(DeadVar0, IsCond, ConsIds,
|
|
FieldNeedUpdates),
|
|
rename_var(Must, Subn, DeadVar0, DeadVar),
|
|
ShortReuseDesc = cell_reused(DeadVar, IsCond, ConsIds,
|
|
FieldNeedUpdates)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Incremental rename predicates.
|
|
%
|
|
|
|
incremental_rename_vars_in_goal(Subn0, SubnUpdates, Goal0, Goal) :-
|
|
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
|
|
GoalId = goal_info_get_goal_id(GoalInfo0),
|
|
( if map.search(SubnUpdates, GoalId, GoalSubns) then
|
|
trace [compiletime(flag("statevar-subn")), io(!IO)] (
|
|
io.stderr_stream(StdErr, !IO),
|
|
GoalId = goal_id(GoalIdNum),
|
|
io.format(StdErr, "Goal id %d has substitutions\n",
|
|
[i(GoalIdNum)], !IO),
|
|
io.write(StdErr, GoalSubns, !IO),
|
|
io.nl(StdErr, !IO)
|
|
),
|
|
list.foldl(follow_subn_until_fixpoint, GoalSubns, Subn0, Subn)
|
|
else
|
|
Subn = Subn0
|
|
),
|
|
incremental_rename_vars_in_goal_expr(Subn, SubnUpdates,
|
|
GoalExpr0, GoalExpr),
|
|
rename_vars_in_goal_info(need_not_rename, Subn, GoalInfo0, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
:- pred follow_subn_until_fixpoint(pair(prog_var, prog_var)::in,
|
|
prog_var_renaming::in, prog_var_renaming::out) is det.
|
|
|
|
follow_subn_until_fixpoint(FromVar - ToVar, !Subn) :-
|
|
% About the use of stderr_stream: getting either the debug stream,
|
|
% or the data needed to construct it, down to here would be nontrivial.
|
|
% It should be done the next time this trace code is enabled,
|
|
% if there *is* such a time.
|
|
( if map.search(!.Subn, ToVar, SubstitutedToVar) then
|
|
trace [compiletime(flag("statevar-subn")), io(!IO)] (
|
|
FromVarStr = string.string(FromVar),
|
|
ToVarStr = string.string(ToVar),
|
|
SubstitutedToVarStr = string.string(SubstitutedToVar),
|
|
io.stderr_stream(StdErr, !IO),
|
|
io.format(StdErr, "short circuiting %s: %s -> %s\n",
|
|
[s(FromVarStr), s(ToVarStr), s(SubstitutedToVarStr)], !IO)
|
|
),
|
|
follow_subn_until_fixpoint(FromVar - SubstitutedToVar, !Subn)
|
|
else
|
|
trace [compiletime(flag("statevar-subn")), io(!IO)] (
|
|
FromVarStr = string.string(FromVar),
|
|
ToVarStr = string.string(ToVar),
|
|
io.stderr_stream(StdErr, !IO),
|
|
io.format(StdErr, "applied substitution %s to %s\n",
|
|
[s(FromVarStr), s(ToVarStr)], !IO)
|
|
),
|
|
map.det_insert(FromVar, ToVar, !Subn)
|
|
).
|
|
|
|
:- pred incremental_rename_vars_in_goals(prog_var_renaming::in,
|
|
incremental_rename_map::in,
|
|
list(hlds_goal)::in, list(hlds_goal)::out) is det.
|
|
|
|
incremental_rename_vars_in_goals(_, _, [], []).
|
|
incremental_rename_vars_in_goals(Subn, SubnUpdates,
|
|
[Goal0 | Goals0], [Goal | Goals]) :-
|
|
incremental_rename_vars_in_goal(Subn, SubnUpdates, Goal0, Goal),
|
|
incremental_rename_vars_in_goals(Subn, SubnUpdates, Goals0, Goals).
|
|
|
|
:- pred incremental_rename_vars_in_cases(prog_var_renaming::in,
|
|
incremental_rename_map::in, list(case)::in, list(case)::out) is det.
|
|
|
|
incremental_rename_vars_in_cases(_, _, [], []).
|
|
incremental_rename_vars_in_cases(Subn, SubnUpdates,
|
|
[Case0 | Cases0], [Case | Cases]) :-
|
|
Case0 = case(MainConsId, OtherConsIds, Goal0),
|
|
incremental_rename_vars_in_goal(Subn, SubnUpdates, Goal0, Goal),
|
|
Case = case(MainConsId, OtherConsIds, Goal),
|
|
incremental_rename_vars_in_cases(Subn, SubnUpdates, Cases0, Cases).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred incremental_rename_vars_in_goal_expr(prog_var_renaming::in,
|
|
incremental_rename_map::in,
|
|
hlds_goal_expr::in, hlds_goal_expr::out) is det.
|
|
|
|
incremental_rename_vars_in_goal_expr(Subn, SubnUpdates, Expr0, Expr) :-
|
|
(
|
|
Expr0 = conj(ConjType, Goals0),
|
|
incremental_rename_vars_in_goals(Subn, SubnUpdates, Goals0, Goals),
|
|
Expr = conj(ConjType, Goals)
|
|
;
|
|
Expr0 = disj(Goals0),
|
|
incremental_rename_vars_in_goals(Subn, SubnUpdates, Goals0, Goals),
|
|
Expr = disj(Goals)
|
|
;
|
|
Expr0 = switch(Var0, CanFail, Cases0),
|
|
rename_var(need_not_rename, Subn, Var0, Var),
|
|
incremental_rename_vars_in_cases(Subn, SubnUpdates, Cases0, Cases),
|
|
Expr = switch(Var, CanFail, Cases)
|
|
;
|
|
Expr0 = if_then_else(Vars0, Cond0, Then0, Else0),
|
|
rename_var_list(need_not_rename, Subn, Vars0, Vars),
|
|
incremental_rename_vars_in_goal(Subn, SubnUpdates, Cond0, Cond),
|
|
incremental_rename_vars_in_goal(Subn, SubnUpdates, Then0, Then),
|
|
incremental_rename_vars_in_goal(Subn, SubnUpdates, Else0, Else),
|
|
Expr = if_then_else(Vars, Cond, Then, Else)
|
|
;
|
|
Expr0 = negation(Goal0),
|
|
incremental_rename_vars_in_goal(Subn, SubnUpdates, Goal0, Goal),
|
|
Expr = negation(Goal)
|
|
;
|
|
Expr0 = scope(Reason0, Goal0),
|
|
(
|
|
Reason0 = exist_quant(Vars0),
|
|
rename_var_list(need_not_rename, Subn, Vars0, Vars),
|
|
Reason = exist_quant(Vars)
|
|
;
|
|
Reason0 = promise_solutions(Vars0, Kind),
|
|
rename_var_list(need_not_rename, Subn, Vars0, Vars),
|
|
Reason = promise_solutions(Vars, Kind)
|
|
;
|
|
Reason0 = require_complete_switch(Var0),
|
|
rename_var(need_not_rename, Subn, Var0, Var),
|
|
Reason = require_complete_switch(Var)
|
|
;
|
|
Reason0 = require_switch_arms_detism(Var0, Detism),
|
|
rename_var(need_not_rename, Subn, Var0, Var),
|
|
Reason = require_switch_arms_detism(Var, Detism)
|
|
;
|
|
Reason0 = from_ground_term(Var0, Kind),
|
|
rename_var(need_not_rename, Subn, Var0, Var),
|
|
Reason = from_ground_term(Var, Kind)
|
|
;
|
|
Reason0 = trace_goal(Flag, Grade, Env, Vars, QuantVars0),
|
|
rename_var_list(need_not_rename, Subn, QuantVars0, QuantVars),
|
|
Reason = trace_goal(Flag, Grade, Env, Vars, QuantVars)
|
|
;
|
|
Reason0 = loop_control(LCVar0, LCSVar0, UseParentStack),
|
|
rename_var(need_not_rename, Subn, LCVar0, LCVar),
|
|
rename_var(need_not_rename, Subn, LCSVar0, LCSVar),
|
|
Reason = loop_control(LCVar, LCSVar, UseParentStack)
|
|
;
|
|
( Reason0 = disable_warnings(_HeadWarnings, _TailWarnings)
|
|
; Reason0 = promise_purity(_Purity)
|
|
; Reason0 = require_detism(_Detism)
|
|
; Reason0 = commit(_ForcePruning)
|
|
; Reason0 = barrier(_Removable)
|
|
),
|
|
Reason = Reason0
|
|
),
|
|
incremental_rename_vars_in_goal(Subn, SubnUpdates, Goal0, Goal),
|
|
Expr = scope(Reason, Goal)
|
|
;
|
|
Expr0 = generic_call(GenericCall0, Args0, Modes, MaybeArgRegs, Det),
|
|
rename_generic_call(need_not_rename, Subn, GenericCall0, GenericCall),
|
|
rename_var_list(need_not_rename, Subn, Args0, Args),
|
|
Expr = generic_call(GenericCall, Args, Modes, MaybeArgRegs, Det)
|
|
;
|
|
Expr0 = plain_call(PredId, ProcId, Args0, Builtin, Context, Sym),
|
|
rename_var_list(need_not_rename, Subn, Args0, Args),
|
|
Expr = plain_call(PredId, ProcId, Args, Builtin, Context, Sym)
|
|
;
|
|
Expr0 = unify(LHS0, RHS0, Mode, Unify0, Context),
|
|
rename_var(need_not_rename, Subn, LHS0, LHS),
|
|
incremental_rename_var_in_unify_rhs(Subn, SubnUpdates, RHS0, RHS),
|
|
rename_var_in_unify(need_not_rename, Subn, Unify0, Unify),
|
|
Expr = unify(LHS, RHS, Mode, Unify, Context)
|
|
;
|
|
Expr0 = call_foreign_proc(Attrs, PredId, ProcId, Args0, Extra0,
|
|
MTRC, Impl),
|
|
rename_arg_list(need_not_rename, Subn, Args0, Args),
|
|
rename_arg_list(need_not_rename, Subn, Extra0, Extra),
|
|
Expr = call_foreign_proc(Attrs, PredId, ProcId, Args, Extra,
|
|
MTRC, Impl)
|
|
;
|
|
Expr0 = shorthand(Shorthand0),
|
|
(
|
|
Shorthand0 = atomic_goal(GoalType0, Outer0, Inner0,
|
|
MaybeOutputVars0, MainGoal0, OrElseGoals0, OrElseInners),
|
|
GoalType = GoalType0,
|
|
Outer0 = atomic_interface_vars(OuterDI0, OuterUO0),
|
|
rename_var(need_not_rename, Subn, OuterDI0, OuterDI),
|
|
rename_var(need_not_rename, Subn, OuterUO0, OuterUO),
|
|
Outer = atomic_interface_vars(OuterDI, OuterUO),
|
|
Inner0 = atomic_interface_vars(InnerDI0, InnerUO0),
|
|
rename_var(need_not_rename, Subn, InnerDI0, InnerDI),
|
|
rename_var(need_not_rename, Subn, InnerUO0, InnerUO),
|
|
Inner = atomic_interface_vars(InnerDI, InnerUO),
|
|
(
|
|
MaybeOutputVars0 = no,
|
|
MaybeOutputVars = MaybeOutputVars0
|
|
;
|
|
MaybeOutputVars0 = yes(OutputVars0),
|
|
rename_var_list(need_not_rename, Subn,
|
|
OutputVars0, OutputVars),
|
|
MaybeOutputVars = yes(OutputVars)
|
|
),
|
|
incremental_rename_vars_in_goal(Subn, SubnUpdates,
|
|
MainGoal0, MainGoal),
|
|
incremental_rename_vars_in_goals(Subn, SubnUpdates,
|
|
OrElseGoals0, OrElseGoals),
|
|
Shorthand = atomic_goal(GoalType, Outer, Inner,
|
|
MaybeOutputVars, MainGoal, OrElseGoals, OrElseInners)
|
|
;
|
|
Shorthand0 = try_goal(MaybeIO0, ResultVar0, SubGoal0),
|
|
(
|
|
MaybeIO0 = yes(try_io_state_vars(IOVarInitial0, IOVarFinal0)),
|
|
rename_var(need_not_rename, Subn, IOVarInitial0, IOVarInitial),
|
|
rename_var(need_not_rename, Subn, IOVarFinal0, IOVarFinal),
|
|
MaybeIO = yes(try_io_state_vars(IOVarInitial, IOVarFinal))
|
|
;
|
|
MaybeIO0 = no,
|
|
MaybeIO = no
|
|
),
|
|
rename_var(need_not_rename, Subn, ResultVar0, ResultVar),
|
|
incremental_rename_vars_in_goal(Subn, SubnUpdates,
|
|
SubGoal0, SubGoal),
|
|
Shorthand = try_goal(MaybeIO, ResultVar, SubGoal)
|
|
;
|
|
Shorthand0 = bi_implication(LeftGoal0, RightGoal0),
|
|
incremental_rename_vars_in_goal(Subn, SubnUpdates,
|
|
LeftGoal0, LeftGoal),
|
|
incremental_rename_vars_in_goal(Subn, SubnUpdates,
|
|
RightGoal0, RightGoal),
|
|
Shorthand = bi_implication(LeftGoal, RightGoal)
|
|
),
|
|
Expr = shorthand(Shorthand)
|
|
).
|
|
|
|
:- pred incremental_rename_var_in_unify_rhs(prog_var_renaming::in,
|
|
incremental_rename_map::in, unify_rhs::in, unify_rhs::out) is det.
|
|
|
|
incremental_rename_var_in_unify_rhs(Subn, SubnUpdates, RHS0, RHS) :-
|
|
(
|
|
RHS0 = rhs_var(Var0),
|
|
rename_var(need_not_rename, Subn, Var0, Var),
|
|
RHS = rhs_var(Var)
|
|
;
|
|
RHS0 = rhs_functor(Functor, E, ArgVars0),
|
|
rename_var_list(need_not_rename, Subn, ArgVars0, ArgVars),
|
|
RHS = rhs_functor(Functor, E, ArgVars)
|
|
;
|
|
RHS0 = rhs_lambda_goal(Purity, Groundness, PredOrFunc, EvalMethod,
|
|
NonLocals0, VarsModes0, Det, Goal0),
|
|
rename_var_list(need_not_rename, Subn, NonLocals0, NonLocals),
|
|
assoc_list.keys_and_values(VarsModes0, Vars0, Modes),
|
|
rename_var_list(need_not_rename, Subn, Vars0, Vars),
|
|
assoc_list.from_corresponding_lists(Vars, Modes, VarsModes),
|
|
incremental_rename_vars_in_goal(Subn, SubnUpdates, Goal0, Goal),
|
|
RHS = rhs_lambda_goal(Purity, Groundness, PredOrFunc, EvalMethod,
|
|
NonLocals, VarsModes, Det, Goal)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Predicates used to implement both incremental and non-incremental renames.
|
|
%
|
|
|
|
:- pred rename_arg_list(must_rename::in, prog_var_renaming::in,
|
|
list(foreign_arg)::in, list(foreign_arg)::out) is det.
|
|
|
|
rename_arg_list(_Must, _Subn, [], []).
|
|
rename_arg_list(Must, Subn, [Arg0 | Args0], [Arg | Args]) :-
|
|
rename_arg(Must, Subn, Arg0, Arg),
|
|
rename_arg_list(Must, Subn, Args0, Args).
|
|
|
|
:- pred rename_arg(must_rename::in, prog_var_renaming::in,
|
|
foreign_arg::in, foreign_arg::out) is det.
|
|
|
|
rename_arg(Must, Subn, Arg0, Arg) :-
|
|
Arg0 = foreign_arg(Var0, B, C, D),
|
|
rename_var(Must, Subn, Var0, Var),
|
|
Arg = foreign_arg(Var, B, C, D).
|
|
|
|
:- pred rename_var_in_unify(must_rename::in, prog_var_renaming::in,
|
|
unification::in, unification::out) is det.
|
|
|
|
rename_var_in_unify(Must, Subn, Unify0, Unify) :-
|
|
(
|
|
Unify0 = construct(Var0, ConsId, Vars0, Modes, How0, Uniq, SubInfo0),
|
|
rename_var(Must, Subn, Var0, Var),
|
|
rename_var_list(Must, Subn, Vars0, Vars),
|
|
(
|
|
How0 = construct_dynamically,
|
|
How = How0
|
|
;
|
|
How0 = construct_statically(_),
|
|
How = How0
|
|
;
|
|
How0 = reuse_cell(cell_to_reuse(ReuseVar0, B, C)),
|
|
rename_var(Must, Subn, ReuseVar0, ReuseVar),
|
|
How = reuse_cell(cell_to_reuse(ReuseVar, B, C))
|
|
;
|
|
How0 = construct_in_region(RegVar0),
|
|
rename_var(Must, Subn, RegVar0, RegVar),
|
|
How = construct_in_region(RegVar)
|
|
),
|
|
(
|
|
SubInfo0 = construct_sub_info(MTA, MaybeSize0),
|
|
(
|
|
MaybeSize0 = no,
|
|
MaybeSize = no
|
|
;
|
|
MaybeSize0 = yes(Size0),
|
|
(
|
|
Size0 = known_size(_),
|
|
Size = Size0
|
|
;
|
|
Size0 = dynamic_size(SizeVar0),
|
|
rename_var(Must, Subn, SizeVar0, SizeVar),
|
|
Size = dynamic_size(SizeVar)
|
|
),
|
|
MaybeSize = yes(Size)
|
|
),
|
|
SubInfo = construct_sub_info(MTA, MaybeSize)
|
|
;
|
|
SubInfo0 = no_construct_sub_info,
|
|
SubInfo = no_construct_sub_info
|
|
),
|
|
Unify = construct(Var, ConsId, Vars, Modes, How, Uniq, SubInfo)
|
|
;
|
|
Unify0 = deconstruct(Var0, ConsId, Vars0, Modes, Cat, CanCGC),
|
|
rename_var(Must, Subn, Var0, Var),
|
|
rename_var_list(Must, Subn, Vars0, Vars),
|
|
Unify = deconstruct(Var, ConsId, Vars, Modes, Cat, CanCGC)
|
|
;
|
|
Unify0 = assign(L0, R0),
|
|
rename_var(Must, Subn, L0, L),
|
|
rename_var(Must, Subn, R0, R),
|
|
Unify = assign(L, R)
|
|
;
|
|
Unify0 = simple_test(L0, R0),
|
|
rename_var(Must, Subn, L0, L),
|
|
rename_var(Must, Subn, R0, R),
|
|
Unify = simple_test(L, R)
|
|
;
|
|
Unify0 = complicated_unify(Modes, Cat, TypeInfoVars0),
|
|
rename_var_list(Must, Subn, TypeInfoVars0, TypeInfoVars),
|
|
Unify = complicated_unify(Modes, Cat, TypeInfoVars)
|
|
).
|
|
|
|
:- pred rename_generic_call(must_rename::in, prog_var_renaming::in,
|
|
generic_call::in, generic_call::out) is det.
|
|
|
|
rename_generic_call(Must, Subn, Call0, Call) :-
|
|
(
|
|
Call0 = higher_order(Var0, Purity, PredOrFunc, Arity),
|
|
rename_var(Must, Subn, Var0, Var),
|
|
Call = higher_order(Var, Purity, PredOrFunc, Arity)
|
|
;
|
|
Call0 = class_method(Var0, Method, ClassId, MethodId),
|
|
rename_var(Must, Subn, Var0, Var),
|
|
Call = class_method(Var, Method, ClassId, MethodId)
|
|
;
|
|
( Call0 = event_call(_EventName)
|
|
; Call0 = cast(_CastKind)
|
|
),
|
|
Call = Call0
|
|
).
|
|
|
|
% Not currently needed.
|
|
%
|
|
% :- pred rename_var_maps(must_rename::in, prog_var_renaming::in,
|
|
% map(prog_var, T)::in, map(prog_var, T)::out) is det.
|
|
%
|
|
% rename_var_maps(Must, Subn, Map0, Map) :-
|
|
% map.to_assoc_list(Map0, AssocList0),
|
|
% rename_var_maps_2(Must, Subn, AssocList0, AssocList),
|
|
% map.from_assoc_list(AssocList, Map).
|
|
%
|
|
% :- pred rename_var_maps_2(must_rename::in, map(var(V), var(V))::in,
|
|
% assoc_list(var(V), T)::in, assoc_list(var(V), T)::out) is det.
|
|
%
|
|
% rename_var_maps_2(_Must, _Subn, [], []).
|
|
% rename_var_maps_2(Must, Subn,
|
|
% [Var - Item | VarItems], [NewVar - Item | NewVarItems]) :-
|
|
% rename_var(Must, Subn, Var, NewVar),
|
|
% rename_var_maps_2(Must, Subn, VarItems, NewVarItems).
|
|
%
|
|
% :- pred rename_var_pair_list(must_rename::in, prog_var_renaming::in,
|
|
% assoc_list(prog_var, T)::in, list(pair(prog_var, T))::out) is det.
|
|
%
|
|
% rename_var_pair_list(_Must, _Subn, [], []).
|
|
% rename_var_pair_list(Must, Subn,
|
|
% [Var - Item | VarItems], [NewVar - Item | NewVarItems]) :-
|
|
% rename_var(Must, Subn, Var, NewVar),
|
|
% rename_var_pair_list(Must, Subn, VarItems, NewVarItems).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Miscellaneous utility procedures for dealing with HLDS goals.
|
|
%
|
|
|
|
goal_to_conj_list(Goal, ConjList) :-
|
|
( if Goal = hlds_goal(conj(plain_conj, List), _) then
|
|
ConjList = List
|
|
else
|
|
ConjList = [Goal]
|
|
).
|
|
|
|
goal_to_par_conj_list(Goal, ConjList) :-
|
|
( if Goal = hlds_goal(conj(parallel_conj, List), _) then
|
|
ConjList = List
|
|
else
|
|
ConjList = [Goal]
|
|
).
|
|
|
|
goal_to_disj_list(Goal, DisjList) :-
|
|
( if Goal = hlds_goal(disj(List), _) then
|
|
DisjList = List
|
|
else
|
|
DisjList = [Goal]
|
|
).
|
|
|
|
conj_list_to_goal(ConjList, GoalInfo, Goal) :-
|
|
( if ConjList = [Goal0] then
|
|
Goal = Goal0
|
|
else
|
|
Goal = hlds_goal(conj(plain_conj, ConjList), GoalInfo)
|
|
).
|
|
|
|
par_conj_list_to_goal(ConjList, GoalInfo, Goal) :-
|
|
( if ConjList = [Goal0] then
|
|
Goal = Goal0
|
|
else
|
|
Goal = hlds_goal(conj(parallel_conj, ConjList), GoalInfo)
|
|
).
|
|
|
|
disj_list_to_goal(DisjList, GoalInfo, Goal) :-
|
|
( if DisjList = [Goal0] then
|
|
Goal = Goal0
|
|
else
|
|
Goal = hlds_goal(disj(DisjList), GoalInfo)
|
|
).
|
|
|
|
conjoin_goal_and_goal_list(Goal0, Goals, Goal) :-
|
|
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
|
|
( if GoalExpr0 = conj(plain_conj, GoalList0) then
|
|
GoalList = GoalList0 ++ Goals,
|
|
GoalExpr = conj(plain_conj, GoalList)
|
|
else
|
|
GoalExpr = conj(plain_conj, [Goal0 | Goals])
|
|
),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0).
|
|
|
|
conjoin_goals(Goal1, Goal2, Goal) :-
|
|
( if Goal2 = hlds_goal(conj(plain_conj, Goals2), _) then
|
|
GoalList = Goals2
|
|
else
|
|
GoalList = [Goal2]
|
|
),
|
|
conjoin_goal_and_goal_list(Goal1, GoalList, Goal).
|
|
|
|
negate_goal(Goal, GoalInfo, NegatedGoal) :-
|
|
( if
|
|
% Eliminate double negations.
|
|
Goal = hlds_goal(negation(Goal1), _)
|
|
then
|
|
NegatedGoal = Goal1
|
|
else if
|
|
% Convert negated conjunctions of negations into disjunctions.
|
|
Goal = hlds_goal(conj(plain_conj, NegatedGoals), _),
|
|
all_negated(NegatedGoals, UnnegatedGoals)
|
|
then
|
|
NegatedGoal = hlds_goal(disj(UnnegatedGoals), GoalInfo)
|
|
else
|
|
NegatedGoal = hlds_goal(negation(Goal), GoalInfo)
|
|
).
|
|
|
|
:- pred all_negated(list(hlds_goal)::in, list(hlds_goal)::out) is semidet.
|
|
|
|
all_negated([], []).
|
|
all_negated([hlds_goal(negation(Goal), _) | NegatedGoals], [Goal | Goals]) :-
|
|
all_negated(NegatedGoals, Goals).
|
|
all_negated([hlds_goal(conj(plain_conj, NegatedConj), _) | NegatedGoals],
|
|
Goals) :-
|
|
all_negated(NegatedConj, Goals1),
|
|
all_negated(NegatedGoals, Goals2),
|
|
Goals = Goals1 ++ Goals2.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
goal_list_nonlocals(Goals, NonLocals) :-
|
|
GoalNonLocals = list.map(goal_get_nonlocals, Goals),
|
|
set_of_var.union_list(GoalNonLocals, NonLocals).
|
|
|
|
goal_list_instmap_delta(Goals, InstMapDelta) :-
|
|
ApplyDelta = (pred(Goal::in, Delta0::in, Delta::out) is det :-
|
|
Goal = hlds_goal(_, GoalInfo),
|
|
Delta1 = goal_info_get_instmap_delta(GoalInfo),
|
|
instmap_delta_apply_instmap_delta(Delta0, Delta1, test_size, Delta)
|
|
),
|
|
instmap_delta_init_reachable(InstMapDelta0),
|
|
list.foldl(ApplyDelta, Goals, InstMapDelta0, InstMapDelta).
|
|
|
|
goal_list_determinism(Goals, Determinism) :-
|
|
ComputeDeterminism = (pred(Goal::in, Det0::in, Det::out) is det :-
|
|
Goal = hlds_goal(_, GoalInfo),
|
|
Det1 = goal_info_get_determinism(GoalInfo),
|
|
det_conjunction_detism(Det0, Det1, Det)
|
|
),
|
|
list.foldl(ComputeDeterminism, Goals, detism_det, Determinism).
|
|
|
|
goal_list_purity(Goals, GoalsPurity) :-
|
|
ComputePurity = (pred(Goal::in, Purity0::in, Purity::out) is det :-
|
|
Goal = hlds_goal(_, GoalInfo),
|
|
Purity1 = goal_info_get_purity(GoalInfo),
|
|
worst_purity(Purity0, Purity1) = Purity
|
|
),
|
|
list.foldl(ComputePurity, Goals, purity_pure, GoalsPurity).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
set_goal_contexts(Context, Goal0, Goal) :-
|
|
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
(
|
|
GoalExpr0 = conj(ConjType, SubGoals0),
|
|
list.map(set_goal_contexts(Context), SubGoals0, SubGoals),
|
|
GoalExpr = conj(ConjType, SubGoals)
|
|
;
|
|
GoalExpr0 = disj(SubGoals0),
|
|
list.map(set_goal_contexts(Context), SubGoals0, SubGoals),
|
|
GoalExpr = disj(SubGoals)
|
|
;
|
|
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
|
|
set_goal_contexts(Context, Cond0, Cond),
|
|
set_goal_contexts(Context, Then0, Then),
|
|
set_goal_contexts(Context, Else0, Else),
|
|
GoalExpr = if_then_else(Vars, Cond, Then, Else)
|
|
;
|
|
GoalExpr0 = switch(Var, CanFail, Cases0),
|
|
list.map(set_case_contexts(Context), Cases0, Cases),
|
|
GoalExpr = switch(Var, CanFail, Cases)
|
|
;
|
|
GoalExpr0 = scope(Reason, SubGoal0),
|
|
set_goal_contexts(Context, SubGoal0, SubGoal),
|
|
GoalExpr = scope(Reason, SubGoal)
|
|
;
|
|
GoalExpr0 = negation(SubGoal0),
|
|
set_goal_contexts(Context, SubGoal0, SubGoal),
|
|
GoalExpr = negation(SubGoal)
|
|
;
|
|
( GoalExpr0 = plain_call(_, _, _, _, _, _)
|
|
; GoalExpr0 = generic_call(_, _, _, _, _)
|
|
; GoalExpr0 = unify(_, _, _, _, _)
|
|
; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
|
|
),
|
|
GoalExpr = GoalExpr0
|
|
;
|
|
GoalExpr0 = shorthand(ShortHand0),
|
|
(
|
|
ShortHand0 = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
|
|
MainGoal0, OrElseGoals0, OrElseInners),
|
|
set_goal_contexts(Context, MainGoal0, MainGoal),
|
|
list.map(set_goal_contexts(Context), OrElseGoals0, OrElseGoals),
|
|
ShortHand = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
|
|
MainGoal, OrElseGoals, OrElseInners)
|
|
;
|
|
ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
|
|
set_goal_contexts(Context, SubGoal0, SubGoal),
|
|
ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
|
|
;
|
|
ShortHand0 = bi_implication(LHS0, RHS0),
|
|
set_goal_contexts(Context, LHS0, LHS),
|
|
set_goal_contexts(Context, RHS0, RHS),
|
|
ShortHand = bi_implication(LHS, RHS)
|
|
),
|
|
GoalExpr = shorthand(ShortHand)
|
|
),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
:- pred set_case_contexts(prog_context::in, case::in, case::out) is det.
|
|
|
|
set_case_contexts(Context, Case0, Case) :-
|
|
Case0 = case(MainConsId, OtherConsIds, Goal0),
|
|
set_goal_contexts(Context, Goal0, Goal),
|
|
Case = case(MainConsId, OtherConsIds, Goal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module hlds.hlds_goal.
|
|
%-----------------------------------------------------------------------------%
|