Files
mercury/compiler/goal_util.m
Zoltan Somogyi 43ca8640f5 Record all deleted callees, not just those in trace goals.
This should eliminate some bogus dead procedure warnings about the
procedures called in deleted code.

compiler/hlds_pred.m:
compiler/simplify_info.m:
    Rename the trace_goal_procs fields of proc_infos and simplify_infos
    to deleted_call_callees in order to reflect the expanded use.

compiler/det_util.m:
    When selecting the reachable arms of a switch, return the goals of the
    unreachable arms as well, so the caller can add the procedures called in
    those goals to the set of deleted callees.

compiler/goal_util.m:
    Add utility predicates for computing the set of procedures called in goals.

compiler/simplify_goal_scope.m:
    Use the new utility predicates.

compiler/simplify_goal_conj.m:
    When we delete the tail of a conjunction as unreachable after a goal
    that cannot succeed, record the callees in the deleted conjuncts.

compiler/simplify_goal_disj.m:
    When we delete a disjunct that cannot succeed, record its callees.

compiler/simplify_goal_ite.m:
    When we delete a then-part because the condition cannot succeed,
    or an else-part because the condition cannot fail, record its callees.

compiler/simplify_goal_switch.m:
compiler/switch_detection.m:
    When we delete a switch arm because the switched-on variable cannot have
    the values that would select it, record its callees.

compiler/dead_proc_elim.m:
compiler/hlds_out_pred.m:
compiler/simplify_proc.m:
    Conform to the changes above.
2015-12-13 13:10:54 +11:00

2435 lines
90 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1995-2012 The University of Melbourne.
% Copyright (C) 2015 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File: goal_util.m.
% Main author: conway.
%
% This module provides various utility procedures for manipulating HLDS goals,
% e.g. attaching features to goals.
%
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- module hlds.goal_util.
:- interface.
:- import_module hlds.hlds_clauses.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module hlds.hlds_rtti.
:- import_module hlds.instmap.
:- import_module hlds.pred_table.
:- import_module hlds.vartypes.
:- 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.set_of_var.
:- import_module bool.
:- import_module list.
:- import_module maybe.
:- import_module set.
:- import_module term.
%-----------------------------------------------------------------------------%
% Given a goal and an initial instmap, compute the final instmap that
% results from the initial instmap after execution of the goal.
%
:- pred update_instmap(hlds_goal::in, instmap::in, instmap::out) is det.
% create_renaming(OutputVars, InstMapDelta, !VarTypes, !VarSet,
% UnifyGoals, NewVars, Renaming):
%
% This predicate is intended for use in program transformations
% that need to wrap up semidet goals, replacing Goal with
% ( if Goal' then UnifyGoals, ... else ...), where Goal' has its output
% variables (OutputVars) replaced with new variables (NewVars),
% with the mapping from OutputVars to NewVars being Renaming.
% VarTypes and VarSet are updated for the new variables. The final
% insts of NewVar are taken from the insts of the corresponding
% OutputVar in InstMapDelta (the initial inst is free).
%
:- pred create_renaming(list(prog_var)::in, instmap_delta::in,
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
list(hlds_goal)::out, list(prog_var)::out, prog_var_renaming::out) is det.
% clone_variable(OldVar, OldVarSet, OldVarTypes,
% !VarSet, !VarTypes, !Renaming, CloneVar):
%
% clone_variable typically takes an old variable OldVar, and creates a
% clone of it, adding the clone variable to !VarSet and !VarType, and
% adding a mapping from the old variable to its clone in !Renaming.
% The name and type of the clone are taken from OldVarSet and OldVarTypes.
% However, if OldVar already has a clone, as shown by it already being a
% key in !.Renaming, clone_variable does nothing. Either way, the identity
% of the clone variable is returned in CloneVar.
%
% (This interface will not easily admit uniqueness in the varset and
% vartypes arguments; such is the sacrifice for generality.)
%
:- pred clone_variable(prog_var::in, prog_varset::in, vartypes::in,
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
prog_var_renaming::in, prog_var_renaming::out, prog_var::out) is det.
% clone_variables(OldVars, OldVarSet, OldVarTypes,
% !VarSet, !VarTypes, !Renaming):
%
% Invoke clone_variable on each variable in OldVars.
%
% The caller can find the identity of the clone of each variable in OldVars
% by looking it up in !:Renaming.
%
:- pred clone_variables(list(prog_var)::in, prog_varset::in, vartypes::in,
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
prog_var_renaming::in, prog_var_renaming::out) is det.
% Return all the variables in the goal or goal expression.
% Unlike quantification.goal_vars, this predicate returns
% even the explicitly quantified variables.
%
% Warning: the complexity of this predicate is proportional to the
% size of the goal. Goals can be pretty big. Whatever you want to do,
% if you have a way to do it *without* calling the predicate, you will
% probably want to it that way.
%
:- pred goal_vars(hlds_goal::in, set_of_progvar::out) is det.
% Do the same job as goal_vars, but for a list of goals.
%
:- pred goals_goal_vars(list(hlds_goal)::in, set_of_progvar::out) is det.
% Return all the variables in a generic call.
%
:- pred generic_call_vars(generic_call::in, list(prog_var)::out) is det.
:- type attach_in_from_ground_term
---> attach_in_from_ground_term
; do_not_attach_in_from_ground_term.
% Attach the given goal features to the given goal and all its subgoals,
% except possibly in from_ground_term scopes.
%
:- pred attach_features_to_all_goals(list(goal_feature),
attach_in_from_ground_term, hlds_goal, hlds_goal) is det.
:- mode attach_features_to_all_goals(in,
in(bound(attach_in_from_ground_term)),
in, out) is det.
:- mode attach_features_to_all_goals(in,
in(bound(do_not_attach_in_from_ground_term)), in, out) is det.
% extra_nonlocal_typeinfos(TypeInfoMap, TypeClassInfoMap,
% VarTypes, ExistQVars, NonLocals, NonLocalTypeInfos):
%
% Compute which type-info and type-class-info variables may need to be
% non-local to a goal.
%
% A type-info variable may be non-local to a goal if any of the ordinary
% non-local variables for that goal are polymorphically typed with a type
% that depends on that type-info variable, or if the type-info is for an
% existentially quantified type variable.
%
% In addition, a typeclass-info may be non-local to a goal if any of the
% non-local variables for that goal are polymorphically typed and are
% constrained by the typeclass constraints for that typeclass-info
% variable, or if the type-class-info is for an existential constraint,
% i.e. a constraint which constrains an existentially quantified type
% variable.
%
:- pred extra_nonlocal_typeinfos(rtti_varmaps::in, vartypes::in,
existq_tvars::in, set_of_progvar::in, set_of_progvar::out) is det.
:- type is_leaf
---> is_leaf
; is_not_leaf.
% See whether the given procedure body is that of a leaf procedure.
%
:- func proc_body_is_leaf(hlds_goal) = is_leaf.
% See whether the goal is a branched structure.
%
:- pred goal_is_branched(hlds_goal_expr::in) is semidet.
% Return an indication of the size of the goal.
%
:- pred goal_size(hlds_goal::in, int::out) is det.
% Return an indication of the size of the list of goals.
%
:- pred goals_size(list(hlds_goal)::in, int::out) is det.
% Return an indication of the size of the list of clauses.
%
:- pred clause_list_size(list(clause)::in, int::out) is det.
:- func goals_callees(list(hlds_goal)) = set(pred_proc_id).
:- func goal_callees(hlds_goal) = set(pred_proc_id).
% Test whether the goal calls the given procedure.
%
:- pred goal_calls(hlds_goal, pred_proc_id).
:- mode goal_calls(in, in) is semidet.
:- mode goal_calls(in, out) is nondet.
% Test whether the goal calls the given predicate.
% This is useful before mode analysis when the proc_ids
% have not been determined.
%
:- pred goal_calls_pred_id(hlds_goal, pred_id).
:- mode goal_calls_pred_id(in, in) is semidet.
:- mode goal_calls_pred_id(in, out) is nondet.
% goal_calls_proc_in_list(Goal, PredProcIds):
%
% Returns the subset of PredProcIds that are called from somewhere inside
% Goal via plain_call.
%
:- func goal_calls_proc_in_list(hlds_goal, list(pred_proc_id))
= list(pred_proc_id).
% goal_list_calls_proc_in_list(Goal, PredProcIds):
%
% Returns the subset of PredProcIds that are called from somewhere inside
% Goals via plain_call.
%
:- func goal_list_calls_proc_in_list(list(hlds_goal), list(pred_proc_id))
= list(pred_proc_id).
% Test whether the goal contains a reconstruction
% (a construction where the `construct_how' field is `cell_to_reuse(_)').
%
:- pred goal_contains_reconstruction(hlds_goal::in, bool::out) is det.
% goal_contains_goal(Goal, SubGoal) is true iff Goal contains SubGoal,
% i.e. iff Goal = SubGoal or Goal contains SubGoal as a direct
% or indirect sub-goal.
%
:- pred goal_contains_goal(hlds_goal::in, hlds_goal::out) is multi.
% direct_subgoal(Goal, DirectSubGoal) is true iff DirectSubGoal is
% a direct sub-goal of Goal.
%
:- pred direct_subgoal(hlds_goal_expr::in, hlds_goal::out) is nondet.
% Returns all the predids that are used within a goal.
%
:- pred predids_from_goal(hlds_goal::in, list(pred_id)::out) is det.
% Returns all the predids that are called along with the list of
% arguments.
:- pred predids_with_args_from_goal(hlds_goal::in,
list({pred_id, list(prog_var)})::out) is det.
% Returns all the predids that are used in a list of goals.
%
:- pred predids_from_goals(list(hlds_goal)::in, list(pred_id)::out) is det.
% Returns all the procedures that are used within a goal.
%
:- pred pred_proc_ids_from_goal(hlds_goal::in, list(pred_proc_id)::out) is det.
:- type goal_is_atomic
---> goal_is_atomic
; goal_is_nonatomic.
% Returns whether a goal is atomic. This is undefined for shorthand goals.
%
:- pred goal_is_atomic(hlds_goal::in, goal_is_atomic::out) is det.
%-----------------------------------------------------------------------------%
% Convert a switch back into a disjunction. This is needed
% for the magic set transformation.
% This aborts if any of the constructors are existentially typed.
%
:- pred switch_to_disjunction(prog_var::in, list(case)::in,
instmap::in, list(hlds_goal)::out, prog_varset::in, prog_varset::out,
vartypes::in, vartypes::out, module_info::in, module_info::out) is det.
% Convert a case into a conjunction by adding a tag test
% (deconstruction unification) to the case goal.
% This aborts if the constructor is existentially typed.
%
:- pred case_to_disjunct(prog_var::in, hlds_goal::in, instmap::in,
cons_id::in, hlds_goal::out, prog_varset::in, prog_varset::out,
vartypes::in, vartypes::out, module_info::in, module_info::out) is det.
%-----------------------------------------------------------------------------%
% Flatten the conjuncts of a conjunction.
% Flattens only one level.
% XXX Why not all levels recursively, as flatten_disj does?
%
:- pred flatten_conj(list(hlds_goal)::in, list(hlds_goal)::out) is det.
% Flatten the disjuncts of a disjunction.
% Flattens all levels recursively.
%
:- pred flatten_disj(list(hlds_goal)::in, list(hlds_goal)::out) is det.
% Create a conjunction of the specified type using the specified goals,
% This fills in the hlds_goal_info.
%
:- pred create_conj_from_list(list(hlds_goal)::in, conj_type::in,
hlds_goal::out) is det.
% Create a conjunction of the specified type using the specified two goals.
% This fills in the hlds_goal_info.
%
:- pred create_conj(hlds_goal::in, hlds_goal::in, conj_type::in,
hlds_goal::out) is det.
% can_reorder_goals_old(ModuleInfo, VarTypes, FullyStrict,
% InstmapBeforeGoal1, Goal1, InstmapBeforeGoal2, Goal2).
%
% Goals can be reordered if
% - the goals are independent
% - the goals are not impure
% - any possible change in termination behaviour is allowed according
% to the semantics options.
%
% NOTE: this version is deprecated; new code should use the following
% version because it supports the intermodule-analysis framework.
%
:- pred can_reorder_goals_old(module_info::in, vartypes::in, bool::in,
instmap::in, hlds_goal::in, instmap::in, hlds_goal::in) is semidet.
% can_reorder_goals(VarTypes, FullyStrict, InstmapBeforeGoal1, Goal1,
% InstmapBeforeGoal2, Goal2, Result, !ModuleInfo).
%
% Result is `yes' if the goals can be reordered; no otherwise.
%
% Goals can be reordered if
% - the goals are independent
% - the goals are not impure
% - any possible change in termination behaviour is allowed according
% to the semantics options.
%
% NOTE: new code should use this version as it supports the
% intermodule-analysis framework.
%
:- pred can_reorder_goals(vartypes::in, bool::in, instmap::in,
hlds_goal::in, instmap::in, hlds_goal::in, bool::out,
module_info::in, module_info::out) is det.
% reordering_maintains_termination_old(ModuleInfo, FullyStrict,
% Goal1, Goal2).
%
% Succeeds if any possible change in termination behaviour from reordering
% the goals is allowed according to the semantics options.
% The information computed by termination and exception analyses is used
% when making this decision.
%
% NOTE: this version is deprecated; new code should use the following
% version because it supports the intermodule-analysis framework.
%
:- pred reordering_maintains_termination_old(module_info::in, bool::in,
hlds_goal::in, hlds_goal::in) is semidet.
% reordering_maintains_termination(FullyStrict, Goal1, Goal2, Result,
% !ModuleInfo, !IO).
%
% Result is `yes' if any possible change in termination behaviour from
% reordering the goals is allowed according to the semantics options.
% The information computed by termination and exception analyses is used
% when making this decision.
%
% NOTE: new code should use this version as it supports the
% intermodule-analysis framework.
%
:- pred reordering_maintains_termination(bool::in, hlds_goal::in,
hlds_goal::in, bool::out, module_info::in, module_info::out) is det.
% generate_simple_call(ModuleName, ProcName, PredOrFunc, ModeNo, Detism,
% Purity, Args, Features, InstMapDelta, ModuleInfo, Context, CallGoal):
%
% Generate a call to a builtin procedure (e.g. from the private_builtin
% or table_builtin module). This is used by HLDS->HLDS transformation
% passes that introduce calls to builtin procedures.
%
% If ModeNo = only_mode, then the predicate must have exactly one
% procedure; an error is raised if this is not the case.
%
% If ModeNo = mode_no(N) then the Nth procedure is used, counting from 0.
%
:- pred generate_simple_call(module_name::in, string::in, pred_or_func::in,
mode_no::in, determinism::in, purity::in, list(prog_var)::in,
list(goal_feature)::in, instmap_delta::in,
module_info::in, term.context::in, hlds_goal::out) is det.
% generate_foreign_proc(ModuleName, ProcName, PredOrFunc, ModeNo, Detism,
% Purity, Attributes, Args, ExtraArgs, MaybeTraceRuntimeCond, Code,
% Features, InstMapDelta, ModuleInfo, Context, CallGoal):
%
% generate_foreign_proc is similar to generate_simple_call,
% but also assumes that the called predicate is defined via a
% foreign_proc, that the foreign_proc's arguments are as given in
% Args, its attributes are Attributes, and its code is Code.
% As well as returning a foreign_code instead of a call, effectively
% inlining the call, generate_foreign_proc also passes ExtraArgs
% as well as Args.
%
:- pred generate_foreign_proc(module_name::in, string::in, pred_or_func::in,
mode_no::in, determinism::in, purity::in,
pragma_foreign_proc_attributes::in,
list(foreign_arg)::in, list(foreign_arg)::in,
maybe(trace_expr(trace_runtime))::in, string::in,
list(goal_feature)::in, instmap_delta::in,
module_info::in, term.context::in, hlds_goal::out) is det.
% Generate a cast goal. The input and output insts are just ground.
%
:- pred generate_cast(cast_kind::in, prog_var::in, prog_var::in,
prog_context::in, hlds_goal::out) is det.
% This version takes input and output inst arguments, which may be
% necessary when casting, say, solver type values with inst any,
% or casting between enumeration types and ints.
%
:- pred generate_cast_with_insts(cast_kind::in, prog_var::in, prog_var::in,
mer_inst::in, mer_inst::in, prog_context::in, hlds_goal::out) is det.
%-----------------------------------------------------------------------------%
:- pred foreign_proc_uses_variable(pragma_foreign_proc_impl::in, string::in)
is semidet.
%-----------------------------------------------------------------------------%
:- func maybe_strip_equality_pretest(hlds_goal) = hlds_goal.
%-----------------------------------------------------------------------------%
:- type maybe_transformed_goal
---> ok(hlds_goal)
; error(string)
; goal_not_found.
% Locate the goal described by the goal path and use its first argument to
% transform that goal before rebuilding the goal tree and returning.
% If the goal is not found, the result is no. If the result of the higher
% order argument is no, then the result is no.
%
:- pred maybe_transform_goal_at_goal_path(
pred(hlds_goal, maybe_error(hlds_goal))::in(pred(in, out) is det),
forward_goal_path::in, hlds_goal::in, maybe_transformed_goal::out) is det.
% As above, except that we also compute the instmap during the traversal so
% that the transformation expressed by the higher order value can use the
% instmap at that point within the goal tree.
%
:- pred maybe_transform_goal_at_goal_path_with_instmap(
pred(instmap, hlds_goal, maybe_error(hlds_goal))::
in(pred(in, in, out) is det),
forward_goal_path::in, instmap::in, hlds_goal::in,
maybe_transformed_goal::out) is det.
% Transform the given goal and all its children according to the higher
% order argument. Children are transformed before their parents, therefore
% the higher order argument will receive a goal with children that have
% already been transformed.
%
:- pred transform_all_goals(
pred(hlds_goal, hlds_goal)::in(pred(in, out) is det),
hlds_goal::in, hlds_goal::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module check_hlds.
:- import_module check_hlds.inst_util.
:- import_module check_hlds.type_util.
:- import_module hlds.goal_form.
:- import_module parse_tree.prog_detism.
:- import_module parse_tree.prog_mode.
:- import_module parse_tree.prog_type.
:- import_module parse_tree.prog_util.
:- import_module int.
:- import_module map.
:- import_module pair.
:- import_module require.
:- import_module solutions.
:- import_module string.
:- import_module varset.
%-----------------------------------------------------------------------------%
update_instmap(hlds_goal(_GoalExpr0, GoalInfo0), !InstMap) :-
DeltaInstMap = goal_info_get_instmap_delta(GoalInfo0),
instmap.apply_instmap_delta(!.InstMap, DeltaInstMap, !:InstMap).
%-----------------------------------------------------------------------------%
create_renaming(OrigVars, InstMapDelta, !VarSet, !VarTypes, Unifies, NewVars,
Renaming) :-
create_renaming_2(OrigVars, InstMapDelta, !VarSet, !VarTypes,
[], RevUnifies, [], RevNewVars, map.init, Renaming),
list.reverse(RevNewVars, NewVars),
list.reverse(RevUnifies, Unifies).
:- pred create_renaming_2(list(prog_var)::in, instmap_delta::in,
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
list(hlds_goal)::in, list(hlds_goal)::out,
list(prog_var)::in, list(prog_var)::out,
prog_var_renaming::in, prog_var_renaming::out) is det.
create_renaming_2([], _, !VarSet, !VarTypes, !RevUnifies, !RevNewVars,
!Renaming).
create_renaming_2([OrigVar | OrigVars], InstMapDelta, !VarSet, !VarTypes,
!RevUnifies, !RevNewVars, !Renaming) :-
varset.new_var(NewVar, !VarSet),
lookup_var_type(!.VarTypes, OrigVar, Type),
add_var_type(NewVar, Type, !VarTypes),
instmap_delta_lookup_var(InstMapDelta, OrigVar, NewInst),
Mode = ((NewInst -> NewInst) - (free -> NewInst)),
UnifyInfo = assign(OrigVar, NewVar),
UnifyContext = unify_context(umc_explicit, []),
GoalExpr = unify(OrigVar, rhs_var(NewVar), Mode, UnifyInfo, UnifyContext),
set_of_var.list_to_set([OrigVar, NewVar], NonLocals),
UnifyInstMapDelta = instmap_delta_from_assoc_list([OrigVar - NewInst]),
goal_info_init(NonLocals, UnifyInstMapDelta, detism_det, purity_pure,
term.context_init, GoalInfo),
Goal = hlds_goal(GoalExpr, GoalInfo),
!:RevUnifies = [Goal | !.RevUnifies],
map.det_insert(OrigVar, NewVar, !Renaming),
!:RevNewVars = [NewVar | !.RevNewVars],
create_renaming_2(OrigVars, InstMapDelta, !VarSet, !VarTypes,
!RevUnifies, !RevNewVars, !Renaming).
%-----------------------------------------------------------------------------%
clone_variable(Var, OldVarNames, OldVarTypes, !VarSet, !VarTypes, !Renaming,
CloneVar) :-
( if map.search(!.Renaming, Var, CloneVarPrime) then
CloneVar = CloneVarPrime
else
( if varset.search_name(OldVarNames, Var, Name) then
varset.new_named_var(Name, CloneVar, !VarSet)
else
varset.new_var(CloneVar, !VarSet)
),
map.det_insert(Var, CloneVar, !Renaming),
( if search_var_type(OldVarTypes, Var, VarType) then
add_var_type(CloneVar, VarType, !VarTypes)
else
% This should never happen after typechecking, but may happen
% before it.
true
)
).
clone_variables([], _, _, !VarSet, !VarTypes, !Renaming).
clone_variables([Var | Vars], OldVarNames, OldVarTypes, !VarSet, !VarTypes,
!Renaming) :-
clone_variable(Var, OldVarNames, OldVarTypes, !VarSet, !VarTypes,
!Renaming, _CloneVar),
clone_variables(Vars, OldVarNames, OldVarTypes, !VarSet, !VarTypes,
!Renaming).
%-----------------------------------------------------------------------------%
goal_vars(Goal, !:Set) :-
!:Set = set_of_var.init,
goal_vars_acc(Goal, !Set).
goals_goal_vars(Goals, !:Set) :-
!:Set = set_of_var.init,
goals_goal_vars_acc(Goals, !Set).
:- pred goal_vars_acc(hlds_goal::in,
set_of_progvar::in, set_of_progvar::out) is det.
goal_vars_acc(Goal, !Set) :-
Goal = hlds_goal(GoalExpr, _GoalInfo),
(
GoalExpr = unify(Var, RHS, _, Unif, _),
set_of_var.insert(Var, !Set),
(
Unif = construct(_, _, _, _, CellToReuse, _, _),
( if CellToReuse = reuse_cell(cell_to_reuse(Var, _, _)) then
set_of_var.insert(Var, !Set)
else
true
)
;
( Unif = deconstruct(_, _, _, _, _, _)
; Unif = assign(_, _)
; Unif = simple_test(_, _)
; Unif = complicated_unify(_, _, _)
)
),
rhs_goal_vars_acc(RHS, !Set)
;
GoalExpr = generic_call(GenericCall, ArgVars, _, _, _),
generic_call_vars(GenericCall, GenericCallVars),
set_of_var.insert_list(GenericCallVars, !Set),
set_of_var.insert_list(ArgVars, !Set)
;
GoalExpr = plain_call(_, _, ArgVars, _, _, _),
set_of_var.insert_list(ArgVars, !Set)
;
( GoalExpr = conj(_, Goals)
; GoalExpr = disj(Goals)
),
goals_goal_vars_acc(Goals, !Set)
;
GoalExpr = switch(Var, _Det, Cases),
set_of_var.insert(Var, !Set),
cases_goal_vars_acc(Cases, !Set)
;
GoalExpr = scope(Reason, SubGoal),
(
Reason = exist_quant(Vars),
set_of_var.insert_list(Vars, !Set)
;
Reason = promise_solutions(Vars, _),
set_of_var.insert_list(Vars, !Set)
;
Reason = from_ground_term(Var, _),
set_of_var.insert(Var, !Set)
;
( Reason = require_complete_switch(Var)
; Reason = require_switch_arms_detism(Var, _)
),
set_of_var.insert(Var, !Set)
;
Reason = loop_control(LCVar, LCSVar, _),
set_of_var.insert(LCVar, !Set),
set_of_var.insert(LCSVar, !Set)
;
( Reason = promise_purity(_)
; Reason = require_detism(_)
; Reason = commit(_)
; Reason = barrier(_)
; Reason = trace_goal(_, _, _, _, _)
)
),
goal_vars_acc(SubGoal, !Set)
;
GoalExpr = negation(SubGoal),
goal_vars_acc(SubGoal, !Set)
;
GoalExpr = if_then_else(Vars, Cond, Then, Else),
set_of_var.insert_list(Vars, !Set),
goal_vars_acc(Cond, !Set),
goal_vars_acc(Then, !Set),
goal_vars_acc(Else, !Set)
;
GoalExpr = call_foreign_proc(_, _, _, Args, ExtraArgs, _, _),
ArgVars = list.map(foreign_arg_var, Args),
ExtraVars = list.map(foreign_arg_var, ExtraArgs),
set_of_var.insert_list(ArgVars, !Set),
set_of_var.insert_list(ExtraVars, !Set)
;
GoalExpr = shorthand(Shorthand),
(
Shorthand = atomic_goal(_, Outer, Inner, MaybeOutputVars,
MainGoal, OrElseGoals, _),
Outer = atomic_interface_vars(OuterDI, OuterUO),
set_of_var.insert(OuterDI, !Set),
set_of_var.insert(OuterUO, !Set),
Inner = atomic_interface_vars(InnerDI, InnerUO),
set_of_var.insert(InnerDI, !Set),
set_of_var.insert(InnerUO, !Set),
(
MaybeOutputVars = no
;
MaybeOutputVars = yes(OutputVars),
set_of_var.insert_list(OutputVars, !Set)
),
goal_vars_acc(MainGoal, !Set),
goals_goal_vars_acc(OrElseGoals, !Set)
;
Shorthand = try_goal(_, _, SubGoal),
% The IO and Result variables would be in SubGoal.
goal_vars_acc(SubGoal, !Set)
;
Shorthand = bi_implication(LeftGoal, RightGoal),
goal_vars_acc(LeftGoal, !Set),
goal_vars_acc(RightGoal, !Set)
)
).
:- pred goals_goal_vars_acc(list(hlds_goal)::in,
set_of_progvar::in, set_of_progvar::out) is det.
goals_goal_vars_acc([], !Set).
goals_goal_vars_acc([Goal | Goals], !Set) :-
goal_vars_acc(Goal, !Set),
goals_goal_vars_acc(Goals, !Set).
:- pred cases_goal_vars_acc(list(case)::in,
set_of_progvar::in, set_of_progvar::out) is det.
cases_goal_vars_acc([], !Set).
cases_goal_vars_acc([case(_, _, Goal) | Cases], !Set) :-
goal_vars_acc(Goal, !Set),
cases_goal_vars_acc(Cases, !Set).
:- pred rhs_goal_vars_acc(unify_rhs::in,
set_of_progvar::in, set_of_progvar::out) is det.
rhs_goal_vars_acc(RHS, !Set) :-
(
RHS = rhs_var(X),
set_of_var.insert(X, !Set)
;
RHS = rhs_functor(_Functor, _, ArgVars),
set_of_var.insert_list(ArgVars, !Set)
;
RHS = rhs_lambda_goal(_, _, _, _, NonLocals, LambdaVars, _, _, Goal),
set_of_var.insert_list(NonLocals, !Set),
set_of_var.insert_list(LambdaVars, !Set),
goal_vars_acc(Goal, !Set)
).
generic_call_vars(higher_order(Var, _, _, _), [Var]).
generic_call_vars(class_method(Var, _, _, _), [Var]).
generic_call_vars(event_call(_), []).
generic_call_vars(cast(_), []).
%-----------------------------------------------------------------------------%
attach_features_to_all_goals(Features, InFromGroundTerm, Goal0, Goal) :-
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
attach_features_to_goal_expr(Features, InFromGroundTerm,
GoalExpr0, GoalExpr),
list.foldl(goal_info_add_feature, Features, GoalInfo0, GoalInfo),
Goal = hlds_goal(GoalExpr, GoalInfo).
:- pred attach_features_to_goals(list(goal_feature),
attach_in_from_ground_term, list(hlds_goal), list(hlds_goal)) is det.
:- mode attach_features_to_goals(in,
in(bound(attach_in_from_ground_term)), in, out) is det.
:- mode attach_features_to_goals(in,
in(bound(do_not_attach_in_from_ground_term)), in, out) is det.
attach_features_to_goals(_Features, _InFromGroundTerm, [], []).
attach_features_to_goals(Features, InFromGroundTerm,
[Goal0 | Goals0], [Goal | Goals]) :-
attach_features_to_all_goals(Features, InFromGroundTerm, Goal0, Goal),
attach_features_to_goals(Features, InFromGroundTerm, Goals0, Goals).
:- pred attach_features_to_cases(list(goal_feature),
attach_in_from_ground_term, list(case), list(case)) is det.
:- mode attach_features_to_cases(in,
in(bound(attach_in_from_ground_term)), in, out) is det.
:- mode attach_features_to_cases(in,
in(bound(do_not_attach_in_from_ground_term)), in, out) is det.
attach_features_to_cases(_Features, _InFromGroundTerm, [], []).
attach_features_to_cases(Features, InFromGroundTerm,
[Case0 | Cases0], [Case | Cases]) :-
Case0 = case(MainConsId, OtherConsIds, Goal0),
attach_features_to_all_goals(Features, InFromGroundTerm, Goal0, Goal),
Case = case(MainConsId, OtherConsIds, Goal),
attach_features_to_cases(Features, InFromGroundTerm, Cases0, Cases).
:- pred attach_features_to_goal_expr(list(goal_feature),
attach_in_from_ground_term, hlds_goal_expr, hlds_goal_expr) is det.
:- mode attach_features_to_goal_expr(in,
in(bound(attach_in_from_ground_term)), in, out) is det.
:- mode attach_features_to_goal_expr(in,
in(bound(do_not_attach_in_from_ground_term)), in, out) is det.
attach_features_to_goal_expr(Features, InFromGroundTerm,
GoalExpr0, GoalExpr) :-
(
( GoalExpr0 = plain_call(_, _, _, _, _, _)
; GoalExpr0 = generic_call(_, _, _, _, _)
; GoalExpr0 = unify(_, _, _, _, _)
; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
),
GoalExpr = GoalExpr0
;
GoalExpr0 = conj(ConjType, Goals0),
attach_features_to_goals(Features, InFromGroundTerm, Goals0, Goals),
GoalExpr = conj(ConjType, Goals)
;
GoalExpr0 = disj(Goals0),
attach_features_to_goals(Features, InFromGroundTerm, Goals0, Goals),
GoalExpr = disj(Goals)
;
GoalExpr0 = switch(Var, CanFail, Cases0),
attach_features_to_cases(Features, InFromGroundTerm, Cases0, Cases),
GoalExpr = switch(Var, CanFail, Cases)
;
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
attach_features_to_all_goals(Features, InFromGroundTerm, Cond0, Cond),
attach_features_to_all_goals(Features, InFromGroundTerm, Then0, Then),
attach_features_to_all_goals(Features, InFromGroundTerm, Else0, Else),
GoalExpr = if_then_else(Vars, Cond, Then, Else)
;
GoalExpr0 = negation(SubGoal0),
attach_features_to_all_goals(Features, InFromGroundTerm,
SubGoal0, SubGoal),
GoalExpr = negation(SubGoal)
;
GoalExpr0 = scope(Reason, SubGoal0),
( if Reason = from_ground_term(_, _) then
(
InFromGroundTerm = do_not_attach_in_from_ground_term,
SubGoal = SubGoal0
;
InFromGroundTerm = attach_in_from_ground_term,
attach_features_to_all_goals(Features, InFromGroundTerm,
SubGoal0, SubGoal)
)
else
attach_features_to_all_goals(Features, InFromGroundTerm,
SubGoal0, SubGoal)
),
GoalExpr = scope(Reason, SubGoal)
;
GoalExpr0 = shorthand(ShortHand0),
(
ShortHand0 = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
MainGoal0, OrElseGoals0, OrElseInners),
attach_features_to_all_goals(Features, InFromGroundTerm,
MainGoal0, MainGoal),
attach_features_to_goals(Features, InFromGroundTerm,
OrElseGoals0, OrElseGoals),
ShortHand = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
MainGoal, OrElseGoals, OrElseInners)
;
ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
attach_features_to_all_goals(Features, InFromGroundTerm,
SubGoal0, SubGoal),
ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
;
ShortHand0 = bi_implication(GoalA0, GoalB0),
attach_features_to_all_goals(Features, InFromGroundTerm,
GoalA0, GoalA),
attach_features_to_all_goals(Features, InFromGroundTerm,
GoalB0, GoalB),
ShortHand = bi_implication(GoalA, GoalB)
),
GoalExpr = shorthand(ShortHand)
).
%-----------------------------------------------------------------------------%
extra_nonlocal_typeinfos(RttiVarMaps, VarTypes, ExistQVars,
NonLocals, NonLocalTypeInfos) :-
% Find all non-local type vars. That is, type vars that are existentially
% quantified or type vars that appear in the type of a non-local prog_var.
set_of_var.to_sorted_list(NonLocals, NonLocalsList),
lookup_var_types(VarTypes, NonLocalsList, NonLocalsTypes),
type_vars_list(NonLocalsTypes, NonLocalTypeVarsList0),
NonLocalTypeVarsList = ExistQVars ++ NonLocalTypeVarsList0,
set_of_var.list_to_set(NonLocalTypeVarsList, NonLocalTypeVars),
% Find all the type_infos that are non-local, that is, type_infos for
% type vars that are non-local in the above sense.
TypeVarToProgVar = (func(TypeVar) = ProgVar :-
rtti_lookup_type_info_locn(RttiVarMaps, TypeVar, Locn),
type_info_locn_var(Locn, ProgVar)
),
NonLocalTypeInfoVars = set_of_var.list_to_set(
list.map(TypeVarToProgVar, NonLocalTypeVarsList)),
% Find all the typeclass_infos that are non-local. These include
% all typeclass_infos that constrain a type variable that is non-local
% in the above sense.
solutions.solutions(
( pred(Var::out) is nondet :-
% Search through all arguments of all constraints
% that the goal could have used.
rtti_varmaps_reusable_constraints(RttiVarMaps, Constraints),
list.member(Constraint, Constraints),
Constraint = constraint(_ClassName, ArgTypes),
type_list_contains_var(ArgTypes, TypeVar),
set_of_var.member(NonLocalTypeVars, TypeVar),
% We found a constraint that is non-local. Include the variable
% holding its typeclass_info.
rtti_lookup_typeclass_info_var(RttiVarMaps, Constraint, Var)
), NonLocalTypeClassInfoVarsList),
set_of_var.sorted_list_to_set(NonLocalTypeClassInfoVarsList,
NonLocalTypeClassInfoVars),
NonLocalTypeInfos = set_of_var.union(NonLocalTypeInfoVars,
NonLocalTypeClassInfoVars).
%-----------------------------------------------------------------------------%
proc_body_is_leaf(hlds_goal(GoalExpr, _)) = IsLeaf :-
(
GoalExpr = unify(_, _, _, UnifyKind, _),
(
UnifyKind = complicated_unify(_, _, _),
IsLeaf = is_not_leaf
;
( UnifyKind = construct(_, _, _, _, _, _, _)
; UnifyKind = deconstruct(_, _, _, _, _, _)
; UnifyKind = assign(_, _)
; UnifyKind = simple_test(_, _)
),
IsLeaf = is_leaf
)
;
( GoalExpr = plain_call(_, _, _, _, _, _)
; GoalExpr = generic_call(_, _, _, _, _)
; GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
),
IsLeaf = is_not_leaf
;
( GoalExpr = conj(_, Goals)
; GoalExpr = disj(Goals)
),
IsLeaf = proc_body_is_leaf_goals(Goals)
;
GoalExpr = negation(SubGoal),
IsLeaf = proc_body_is_leaf(SubGoal)
;
GoalExpr = scope(Reason, SubGoal),
( if
Reason = from_ground_term(_, FGT),
( FGT = from_ground_term_construct
; FGT = from_ground_term_deconstruct
)
then
IsLeaf = is_leaf
else
IsLeaf = proc_body_is_leaf(SubGoal)
)
;
GoalExpr = switch(_, _, Cases),
IsLeaf = proc_body_is_leaf_cases(Cases)
;
GoalExpr = if_then_else(_, Cond, Then, Else),
( if
proc_body_is_leaf(Cond) = is_leaf,
proc_body_is_leaf(Then) = is_leaf,
proc_body_is_leaf(Else) = is_leaf
then
IsLeaf = is_leaf
else
IsLeaf = is_not_leaf
)
;
GoalExpr = shorthand(ShortHand),
(
( ShortHand = atomic_goal(_, _, _, _, _, _, _)
; ShortHand = try_goal(_, _, _)
),
IsLeaf = is_not_leaf
;
ShortHand = bi_implication(GoalA, GoalB),
( if
proc_body_is_leaf(GoalA) = is_leaf,
proc_body_is_leaf(GoalB) = is_leaf
then
IsLeaf = is_leaf
else
IsLeaf = is_not_leaf
)
)
).
:- func proc_body_is_leaf_goals(list(hlds_goal)) = is_leaf.
proc_body_is_leaf_goals([]) = is_leaf.
proc_body_is_leaf_goals([Goal | Goals]) = IsLeaf :-
( if
proc_body_is_leaf(Goal) = is_leaf,
proc_body_is_leaf_goals(Goals) = is_leaf
then
IsLeaf = is_leaf
else
IsLeaf = is_not_leaf
).
:- func proc_body_is_leaf_cases(list(case)) = is_leaf.
proc_body_is_leaf_cases([]) = is_leaf.
proc_body_is_leaf_cases([Case | Cases]) = IsLeaf :-
Case = case(_, _, Goal),
( if
proc_body_is_leaf(Goal) = is_leaf,
proc_body_is_leaf_cases(Cases) = is_leaf
then
IsLeaf = is_leaf
else
IsLeaf = is_not_leaf
).
%-----------------------------------------------------------------------------%
goal_is_branched(if_then_else(_, _, _, _)).
goal_is_branched(switch(_, _, _)).
goal_is_branched(disj(_)).
%-----------------------------------------------------------------------------%
goal_size(hlds_goal(GoalExpr, _), Size) :-
goal_expr_size(GoalExpr, Size).
goals_size([], 0).
goals_size([Goal | Goals], Size) :-
goal_size(Goal, Size1),
goals_size(Goals, Size2),
Size = Size1 + Size2.
clause_list_size(Clauses, GoalSize) :-
list.foldl(clause_size_increment, Clauses, 0, GoalSize0),
( if Clauses = [_] then
GoalSize = GoalSize0
else
% Add one for the disjunction.
GoalSize = GoalSize0 + 1
).
:- pred clause_size_increment(clause::in, int::in, int::out) is det.
clause_size_increment(Clause, Size0, Size) :-
goal_size(Clause ^ clause_body, ClauseSize),
Size = Size0 + ClauseSize.
:- pred cases_size(list(case)::in, int::out) is det.
cases_size([], 0).
cases_size([case(_, _, Goal) | Cases], Size) :-
goal_size(Goal, Size1),
cases_size(Cases, Size2),
Size = Size1 + Size2.
:- pred goal_expr_size(hlds_goal_expr::in, int::out) is det.
goal_expr_size(GoalExpr, Size) :-
(
( GoalExpr = plain_call(_, _, _, _, _, _)
; GoalExpr = generic_call(_, _, _, _, _)
; GoalExpr = unify(_, _, _, _, _)
; GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
),
Size = 1
;
GoalExpr = conj(ConjType, Goals),
goals_size(Goals, InnerSize),
(
ConjType = plain_conj,
Size = InnerSize
;
ConjType = parallel_conj,
Size = InnerSize + 1
)
;
GoalExpr = disj(Goals),
goals_size(Goals, Size1),
Size = Size1 + 1
;
GoalExpr = switch(_, _, Cases),
cases_size(Cases, Size1),
Size = Size1 + 1
;
GoalExpr = if_then_else(_, Cond, Then, Else),
goal_size(Cond, Size1),
goal_size(Then, Size2),
goal_size(Else, Size3),
Size = Size1 + Size2 + Size3 + 1
;
GoalExpr = negation(SubGoal),
goal_size(SubGoal, Size1),
Size = Size1 + 1
;
GoalExpr = scope(Reason, SubGoal),
( if Reason = from_ground_term(_, from_ground_term_construct) then
% These scopes get turned into a single assignment.
Size = 1
else
goal_size(SubGoal, Size1),
Size = Size1 + 1
)
;
GoalExpr = shorthand(ShortHand),
(
ShortHand = atomic_goal(_, _, _, _, MainGoal, OrElseGoals, _),
goal_size(MainGoal, Size1),
goals_size(OrElseGoals, Size2),
Size = Size1 + Size2 + 1
;
ShortHand = try_goal(_, _, SubGoal),
% Hopefully this size isn't too important as the SubGoal is not yet
% in the final form.
goal_size(SubGoal, Size)
;
ShortHand = bi_implication(GoalA, GoalB),
goal_size(GoalA, Size1),
goal_size(GoalB, Size2),
Size = Size1 + Size2 + 1
)
).
%-----------------------------------------------------------------------------%
goals_callees(Goals) = CalleesSet :-
CalleeSets = list.map(goal_callees, Goals),
CalleesSet = set.union_list(CalleeSets).
goal_callees(Goal) = CalleesSet :-
% We need the lambda because this is not the only mode of goal_calls/2.
GoalCalls =
( pred(PredProcId::out) is nondet :-
goal_calls(Goal, PredProcId)
),
solutions(GoalCalls, CalleesList),
set.sorted_list_to_set(CalleesList, CalleesSet).
%-----------------------------------------------------------------------------%
%
% We could implement goal_calls as
% goal_calls(Goal, proc(PredId, ProcId)) :-
% goal_contains_subgoal(Goal, call(PredId, ProcId, _, _, _, _)).
% but the following is more efficient in the (in, in) mode
% since it avoids creating any choice points.
%
% XXX STM
% split this predicate into two:
% goal_calls_this_proc(Goal, PredProcId) = bool
% all_called_procs_in_goal(Goal) = cord(pred_proc_id)
goal_calls(hlds_goal(GoalExpr, _), PredProcId) :-
goal_expr_calls(GoalExpr, PredProcId).
:- pred goals_calls(list(hlds_goal), pred_proc_id).
:- mode goals_calls(in, in) is semidet.
:- mode goals_calls(in, out) is nondet.
goals_calls([Goal | Goals], PredProcId) :-
(
goal_calls(Goal, PredProcId)
;
goals_calls(Goals, PredProcId)
).
:- pred cases_calls(list(case), pred_proc_id).
:- mode cases_calls(in, in) is semidet.
:- mode cases_calls(in, out) is nondet.
cases_calls([case(_, _, Goal) | Cases], PredProcId) :-
(
goal_calls(Goal, PredProcId)
;
cases_calls(Cases, PredProcId)
).
:- pred goal_expr_calls(hlds_goal_expr, pred_proc_id).
:- mode goal_expr_calls(in, in) is semidet.
:- mode goal_expr_calls(in, out) is nondet.
goal_expr_calls(GoalExpr, PredProcId) :-
require_complete_switch [GoalExpr]
(
GoalExpr = conj(_ConjType, Conjuncts),
goals_calls(Conjuncts, PredProcId)
;
GoalExpr = disj(Disjuncts),
goals_calls(Disjuncts, PredProcId)
;
GoalExpr = switch(_, _, Cases),
cases_calls(Cases, PredProcId)
;
GoalExpr = if_then_else(_, Cond, Then, Else),
( goal_calls(Cond, PredProcId)
; goal_calls(Then, PredProcId)
; goal_calls(Else, PredProcId)
)
;
GoalExpr = negation(SubGoal),
goal_calls(SubGoal, PredProcId)
;
GoalExpr = scope(Reason, SubGoal),
( if
Reason = from_ground_term(_, FGT),
( FGT = from_ground_term_construct
; FGT = from_ground_term_deconstruct
)
then
% These goals contain only construction and deconstruction
% unifications respectively.
fail
else
goal_calls(SubGoal, PredProcId)
)
;
GoalExpr = plain_call(PredId, ProcId, _, _, _, _),
PredProcId = proc(PredId, ProcId)
;
( GoalExpr = generic_call(_, _, _, _, _)
; GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
; GoalExpr = unify(_, _, _, _, _)
; GoalExpr = shorthand(_)
),
fail
).
%-----------------------------------------------------------------------------%
%
% We could implement goal_calls_pred_id as
% goal_calls_pred_id(Goal, PredId) :-
% goal_contains_subgoal(Goal, call(PredId, _, _, _, _, _)).
% but the following is more efficient in the (in, in) mode
% since it avoids creating any choice points.
%
goal_calls_pred_id(hlds_goal(GoalExpr, _), PredId) :-
goal_expr_calls_pred_id(GoalExpr, PredId).
:- pred goals_calls_pred_id(list(hlds_goal), pred_id).
:- mode goals_calls_pred_id(in, in) is semidet.
:- mode goals_calls_pred_id(in, out) is nondet.
goals_calls_pred_id([Goal | Goals], PredId) :-
(
goal_calls_pred_id(Goal, PredId)
;
goals_calls_pred_id(Goals, PredId)
).
:- pred cases_calls_pred_id(list(case), pred_id).
:- mode cases_calls_pred_id(in, in) is semidet.
:- mode cases_calls_pred_id(in, out) is nondet.
cases_calls_pred_id([case(_, _, Goal) | Cases], PredId) :-
(
goal_calls_pred_id(Goal, PredId)
;
cases_calls_pred_id(Cases, PredId)
).
:- pred goal_expr_calls_pred_id(hlds_goal_expr, pred_id).
:- mode goal_expr_calls_pred_id(in, in) is semidet.
:- mode goal_expr_calls_pred_id(in, out) is nondet.
goal_expr_calls_pred_id(GoalExpr, PredId) :-
require_complete_switch [GoalExpr]
(
GoalExpr = conj(_ConjType, Conjuncts),
goals_calls_pred_id(Conjuncts, PredId)
;
GoalExpr = disj(Disjuncts),
goals_calls_pred_id(Disjuncts, PredId)
;
GoalExpr = switch(_, _, Cases),
cases_calls_pred_id(Cases, PredId)
;
GoalExpr = if_then_else(_, Cond, Then, Else),
( goal_calls_pred_id(Cond, PredId)
; goal_calls_pred_id(Then, PredId)
; goal_calls_pred_id(Else, PredId)
)
;
GoalExpr = negation(SubGoal),
goal_calls_pred_id(SubGoal, PredId)
;
GoalExpr = scope(Reason, SubGoal),
( if
Reason = from_ground_term(_, FGT),
( FGT = from_ground_term_construct
; FGT = from_ground_term_deconstruct
)
then
% These goals contain only construction and deconstruction
% unifications respectively.
fail
else
goal_calls_pred_id(SubGoal, PredId)
)
;
GoalExpr = plain_call(PredId, _, _, _, _, _)
;
( GoalExpr = generic_call(_, _, _, _, _)
; GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
; GoalExpr = unify(_, _, _, _, _)
; GoalExpr = shorthand(_)
),
fail
).
%-----------------------------------------------------------------------------%
goal_calls_proc_in_list(Goal, PredProcIds) = CalledPredProcIds :-
goal_calls_proc_in_list_2(Goal, PredProcIds, set.init, CalledSet),
set.to_sorted_list(CalledSet, CalledPredProcIds).
goal_list_calls_proc_in_list(Goals, PredProcIds) = CalledPredProcIds :-
goal_list_calls_proc_in_list_2(Goals, PredProcIds, set.init, CalledSet),
set.to_sorted_list(CalledSet, CalledPredProcIds).
:- pred goal_calls_proc_in_list_2(hlds_goal::in, list(pred_proc_id)::in,
set(pred_proc_id)::in, set(pred_proc_id)::out) is det.
goal_calls_proc_in_list_2(hlds_goal(GoalExpr, _GoalInfo), PredProcIds,
!CalledSet) :-
(
GoalExpr = unify(_, _, _, _, _)
;
GoalExpr = plain_call(PredId, ProcId, _, _, _, _),
( if list.member(proc(PredId, ProcId), PredProcIds) then
set.insert(proc(PredId, ProcId), !CalledSet)
else
true
)
;
GoalExpr = generic_call(_, _, _, _, _)
;
GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
;
GoalExpr = conj(_, Goals),
goal_list_calls_proc_in_list_2(Goals, PredProcIds, !CalledSet)
;
GoalExpr = disj(Goals),
goal_list_calls_proc_in_list_2(Goals, PredProcIds, !CalledSet)
;
GoalExpr = switch(_, _, Cases),
case_list_calls_proc_in_list(Cases, PredProcIds, !CalledSet)
;
GoalExpr = if_then_else(_, Cond, Then, Else),
goal_calls_proc_in_list_2(Cond, PredProcIds, !CalledSet),
goal_calls_proc_in_list_2(Then, PredProcIds, !CalledSet),
goal_calls_proc_in_list_2(Else, PredProcIds, !CalledSet)
;
GoalExpr = negation(Goal),
goal_calls_proc_in_list_2(Goal, PredProcIds, !CalledSet)
;
GoalExpr = scope(Reason, Goal),
( if
Reason = from_ground_term(_, FGT),
( FGT = from_ground_term_construct
; FGT = from_ground_term_deconstruct
)
then
% These goals contain only construction unifications.
true
else
goal_calls_proc_in_list_2(Goal, PredProcIds, !CalledSet)
)
;
GoalExpr = shorthand(ShortHand),
(
ShortHand = atomic_goal(_, _, _, _, MainGoal, OrElseGoals, _),
goal_calls_proc_in_list_2(MainGoal, PredProcIds, !CalledSet),
goal_list_calls_proc_in_list_2(OrElseGoals, PredProcIds,
!CalledSet)
;
ShortHand = try_goal(_, _, SubGoal),
goal_calls_proc_in_list_2(SubGoal, PredProcIds, !CalledSet)
;
ShortHand = bi_implication(_, _),
unexpected($module, $pred, "bi_implication")
)
).
:- pred goal_list_calls_proc_in_list_2(list(hlds_goal)::in,
list(pred_proc_id)::in, set(pred_proc_id)::in, set(pred_proc_id)::out)
is det.
goal_list_calls_proc_in_list_2([], _, !CalledSet).
goal_list_calls_proc_in_list_2([Goal | Goals], PredProcIds, !CalledSet) :-
goal_calls_proc_in_list_2(Goal, PredProcIds, !CalledSet),
goal_list_calls_proc_in_list_2(Goals, PredProcIds, !CalledSet).
:- pred case_list_calls_proc_in_list(list(case)::in, list(pred_proc_id)::in,
set(pred_proc_id)::in, set(pred_proc_id)::out) is det.
case_list_calls_proc_in_list([], _, !CalledSet).
case_list_calls_proc_in_list([Case | Cases], PredProcIds, !CalledSet) :-
Case = case(_, _, Goal),
goal_calls_proc_in_list_2(Goal, PredProcIds, !CalledSet),
case_list_calls_proc_in_list(Cases, PredProcIds, !CalledSet).
%-----------------------------------------------------------------------------%
goal_contains_reconstruction(Goal, ContainsReconstruction) :-
Goal = hlds_goal(GoalExpr, _),
(
GoalExpr = conj(_ConjType, Conjuncts),
goals_contain_reconstruction(Conjuncts, ContainsReconstruction)
;
GoalExpr = disj(Disjuncts),
goals_contain_reconstruction(Disjuncts, ContainsReconstruction)
;
GoalExpr = switch(_, _, Cases),
cases_contain_reconstruction(Cases, ContainsReconstruction)
;
GoalExpr = if_then_else(_, Cond, Then, Else),
( if
( goal_contains_reconstruction(Cond, yes)
; goal_contains_reconstruction(Then, yes)
; goal_contains_reconstruction(Else, yes)
)
then
ContainsReconstruction = yes
else
ContainsReconstruction = no
)
;
GoalExpr = negation(SubGoal),
goal_contains_reconstruction(SubGoal, ContainsReconstruction)
;
GoalExpr = scope(Reason, SubGoal),
( if
Reason = from_ground_term(_, FGT),
( FGT = from_ground_term_construct
; FGT = from_ground_term_deconstruct
)
then
% Construct scopes contain only construction unifications
% that do no reuse. Deconstruct scopes do not contain
% any constructions at all.
ContainsReconstruction = no
else
goal_contains_reconstruction(SubGoal, ContainsReconstruction)
)
;
GoalExpr = unify(_, _, _, Unify, _),
( if
Unify = construct(_, _, _, _, HowToConstruct, _, _),
HowToConstruct = reuse_cell(_)
then
ContainsReconstruction = yes
else
ContainsReconstruction = no
)
;
( GoalExpr = plain_call(_, _, _, _, _, _)
; GoalExpr = generic_call(_, _, _, _, _)
; GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
),
ContainsReconstruction = no
;
GoalExpr = shorthand(Shorthand),
(
Shorthand = bi_implication(GoalA, GoalB),
( if
goal_contains_reconstruction(GoalA, yes),
goal_contains_reconstruction(GoalB, yes)
then
ContainsReconstruction = yes
else
ContainsReconstruction = no
)
;
Shorthand = atomic_goal(_AtomicGoalType, _OuterVars, _InnerVars,
_OutputVars, MainGoal, OrElseGoals, _Inners),
( if
goal_contains_reconstruction(MainGoal, yes),
goals_contain_reconstruction(OrElseGoals, yes)
then
ContainsReconstruction = yes
else
ContainsReconstruction = no
)
;
Shorthand = try_goal(_MaybeTryIOStateVars, _ResultVar, SubGoal),
goal_contains_reconstruction(SubGoal, ContainsReconstruction)
)
).
:- pred goals_contain_reconstruction(list(hlds_goal)::in, bool::out) is det.
goals_contain_reconstruction([], no).
goals_contain_reconstruction([Goal | Goals], ContainsReconstruction) :-
goal_contains_reconstruction(Goal, HeadContainsReconstruction),
(
HeadContainsReconstruction = yes,
ContainsReconstruction = yes
;
HeadContainsReconstruction = no,
goals_contain_reconstruction(Goals, ContainsReconstruction)
).
:- pred cases_contain_reconstruction(list(case)::in, bool::out) is det.
cases_contain_reconstruction([], no).
cases_contain_reconstruction([Case | Cases], ContainsReconstruction) :-
Case = case(_, _, CaseGoal),
goal_contains_reconstruction(CaseGoal, HeadContainsReconstruction),
(
HeadContainsReconstruction = yes,
ContainsReconstruction = yes
;
HeadContainsReconstruction = no,
cases_contain_reconstruction(Cases, ContainsReconstruction)
).
%-----------------------------------------------------------------------------%
goal_contains_goal(Goal, Goal).
goal_contains_goal(hlds_goal(GoalExpr, _), SubGoal) :-
direct_subgoal(GoalExpr, DirectSubGoal),
goal_contains_goal(DirectSubGoal, SubGoal).
direct_subgoal(scope(_, Goal), Goal).
direct_subgoal(negation(Goal), Goal).
direct_subgoal(if_then_else(_, Cond, Then, Else), Goal) :-
( Goal = Cond
; Goal = Then
; Goal = Else
).
direct_subgoal(conj(_ConjType, ConjList), Goal) :-
list.member(Goal, ConjList).
direct_subgoal(disj(DisjList), Goal) :-
list.member(Goal, DisjList).
direct_subgoal(switch(_, _, CaseList), Goal) :-
list.member(Case, CaseList),
Case = case(_, _, Goal).
%-----------------------------------------------------------------------------%
switch_to_disjunction(_, [], _, [], !VarSet, !VarTypes, !ModuleInfo).
switch_to_disjunction(Var, [Case | Cases], InstMap, Goals,
!VarSet, !VarTypes, !ModuleInfo) :-
Case = case(MainConsId, OtherConsIds, CaseGoal),
case_to_disjunct(Var, CaseGoal, InstMap, MainConsId, MainDisjunctGoal,
!VarSet, !VarTypes, !ModuleInfo),
list.map_foldl3(case_to_disjunct(Var, CaseGoal, InstMap),
OtherConsIds, OtherDisjunctGoals, !VarSet, !VarTypes, !ModuleInfo),
switch_to_disjunction(Var, Cases, InstMap, CasesGoals, !VarSet, !VarTypes,
!ModuleInfo),
Goals = [MainDisjunctGoal | OtherDisjunctGoals] ++ CasesGoals.
case_to_disjunct(Var, CaseGoal, InstMap, ConsId, Disjunct, !VarSet, !VarTypes,
!ModuleInfo) :-
ConsArity = cons_id_arity(ConsId),
varset.new_vars(ConsArity, ArgVars, !VarSet),
lookup_var_type(!.VarTypes, Var, VarType),
type_util.get_cons_id_arg_types(!.ModuleInfo, VarType, ConsId, ArgTypes),
vartypes_add_corresponding_lists(ArgVars, ArgTypes, !VarTypes),
instmap_lookup_var(InstMap, Var, Inst0),
( if
inst_expand(!.ModuleInfo, Inst0, Inst1),
get_arg_insts(Inst1, ConsId, ConsArity, ArgInsts1)
then
ArgInsts = ArgInsts1
else
unexpected($module, $pred, "get_arg_insts failed")
),
InstToUniMode =
( pred(ArgInst::in, ArgUniMode::out) is det :-
ArgUniMode = ((ArgInst - free) -> (ArgInst - ArgInst))
),
list.map(InstToUniMode, ArgInsts, UniModes),
UniMode = (Inst0 -> Inst0) - (Inst0 -> Inst0),
UnifyContext = unify_context(umc_explicit, []),
Unification = deconstruct(Var, ConsId, ArgVars, UniModes, can_fail,
cannot_cgc),
RHS = rhs_functor(ConsId, is_not_exist_constr, ArgVars),
ExtraGoalExpr = unify(Var, RHS, UniMode, Unification, UnifyContext),
NonLocals = set_of_var.make_singleton(Var),
instmap_delta_init_reachable(ExtraInstMapDelta0),
instmap_delta_bind_var_to_functor(Var, VarType, ConsId, InstMap,
ExtraInstMapDelta0, ExtraInstMapDelta, !ModuleInfo),
goal_info_init(NonLocals, ExtraInstMapDelta,
detism_semi, purity_pure, ExtraGoalInfo),
% Conjoin the test and the rest of the case.
goal_to_conj_list(CaseGoal, CaseGoalConj),
GoalList = [hlds_goal(ExtraGoalExpr, ExtraGoalInfo) | CaseGoalConj],
% Work out the nonlocals, instmap_delta and determinism
% of the entire conjunction.
CaseGoal = hlds_goal(_, CaseGoalInfo),
CaseNonLocals0 = goal_info_get_nonlocals(CaseGoalInfo),
set_of_var.insert(Var, CaseNonLocals0, CaseNonLocals),
CaseInstMapDelta = goal_info_get_instmap_delta(CaseGoalInfo),
instmap_delta_apply_instmap_delta(ExtraInstMapDelta, CaseInstMapDelta,
test_size, InstMapDelta),
CaseDetism0 = goal_info_get_determinism(CaseGoalInfo),
det_conjunction_detism(detism_semi, CaseDetism0, Detism),
CasePurity = goal_info_get_purity(CaseGoalInfo),
goal_info_init(CaseNonLocals, InstMapDelta, Detism, CasePurity,
CombinedGoalInfo),
Disjunct = hlds_goal(conj(plain_conj, GoalList), CombinedGoalInfo).
%-----------------------------------------------------------------------------%
flatten_conj([], []).
flatten_conj([Goal | Goals0], FlatConj) :-
flatten_conj(Goals0, FlatConjTail),
( if Goal = hlds_goal(conj(plain_conj, SubGoals), _) then
FlatConj = SubGoals ++ FlatConjTail
else
FlatConj = [Goal | FlatConjTail]
).
flatten_disj(Disjuncts, FlatDisjuncts) :-
list.foldr(flatten_disj_acc, Disjuncts, [], FlatDisjuncts).
:- pred flatten_disj_acc(hlds_goal::in,
list(hlds_goal)::in, list(hlds_goal)::out) is det.
flatten_disj_acc(Disjunct, !FlatDisjuncts) :-
( if Disjunct = hlds_goal(disj(SubDisjs), _GoalInfo) then
list.foldr(flatten_disj_acc, SubDisjs, !FlatDisjuncts)
else
!:FlatDisjuncts = [Disjunct | !.FlatDisjuncts]
).
%-----------------------------------------------------------------------------%
create_conj(GoalA, GoalB, Type, ConjGoal) :-
create_conj_from_list([GoalA, GoalB], Type, ConjGoal).
create_conj_from_list(Conjuncts, ConjType, ConjGoal) :-
(
Conjuncts = [HeadGoal | TailGoals],
(
TailGoals = [ _ | _ ],
ConjGoalExpr = conj(ConjType, Conjuncts),
goal_list_nonlocals(Conjuncts, NonLocals),
goal_list_instmap_delta(Conjuncts, InstMapDelta),
goal_list_determinism(Conjuncts, Detism),
goal_list_purity(Conjuncts, Purity),
HeadGoal = hlds_goal(_, HeadGoalInfo),
Context = goal_info_get_context(HeadGoalInfo),
goal_info_init(NonLocals, InstMapDelta, Detism, Purity, Context,
ConjGoalInfo),
ConjGoal = hlds_goal(ConjGoalExpr, ConjGoalInfo)
;
TailGoals = [],
ConjGoal = HeadGoal
)
;
Conjuncts = [],
unexpected($module, $pred, "empty conjunction")
).
%-----------------------------------------------------------------------------%
can_reorder_goals_old(ModuleInfo, VarTypes, FullyStrict,
InstmapBeforeEarlierGoal, EarlierGoal,
InstmapBeforeLaterGoal, LaterGoal) :-
% The logic here is mostly duplicated in can_reorder_goals below
% and in pd_can_reorder_goals in pd_util.m.
EarlierGoal = hlds_goal(_, EarlierGoalInfo),
LaterGoal = hlds_goal(_, LaterGoalInfo),
% Impure goals and trace goals cannot be reordered.
goal_info_get_goal_purity(EarlierGoalInfo, EarlierPurity, EarlierTrace),
goal_info_get_goal_purity(LaterGoalInfo, LaterPurity, LaterTrace),
EarlierPurity \= purity_impure,
LaterPurity \= purity_impure,
EarlierTrace = contains_no_trace_goal,
LaterTrace = contains_no_trace_goal,
reordering_maintains_termination_old(ModuleInfo, FullyStrict,
EarlierGoal, LaterGoal),
% Don't reorder the goals if the later goal depends on the outputs
% of the current goal.
not goal_depends_on_earlier_goal(LaterGoal, EarlierGoal,
InstmapBeforeEarlierGoal, VarTypes, ModuleInfo),
% Don't reorder the goals if the later goal changes the instantiatedness
% of any of the non-locals of the earlier goal. This is necessary if the
% later goal clobbers any of the non-locals of the earlier goal, and
% avoids rerunning full mode analysis in other cases.
not goal_depends_on_earlier_goal(EarlierGoal, LaterGoal,
InstmapBeforeLaterGoal, VarTypes, ModuleInfo).
can_reorder_goals(VarTypes, FullyStrict, InstmapBeforeEarlierGoal,
EarlierGoal, InstmapBeforeLaterGoal, LaterGoal, CanReorder,
!ModuleInfo) :-
% The logic here is mostly duplicated in can_reorder_goals_old above
% and in pd_can_reorder_goals in pd_util.m.
EarlierGoal = hlds_goal(_, EarlierGoalInfo),
LaterGoal = hlds_goal(_, LaterGoalInfo),
% Impure goals and trace goals cannot be reordered.
goal_info_get_goal_purity(EarlierGoalInfo, EarlierPurity, EarlierTrace),
goal_info_get_goal_purity(LaterGoalInfo, LaterPurity, LaterTrace),
( if
( EarlierPurity = purity_impure
; LaterPurity = purity_impure
; EarlierTrace = contains_trace_goal
; LaterTrace = contains_trace_goal
)
then
CanReorder = no
else
reordering_maintains_termination(FullyStrict,
EarlierGoal, LaterGoal, MaintainsTermination, !ModuleInfo),
(
MaintainsTermination = no,
CanReorder = no
;
MaintainsTermination = yes,
( if
% Don't reorder the goals if the later goal depends on the
% outputs of the current goal.
%
goal_depends_on_earlier_goal(LaterGoal, EarlierGoal,
InstmapBeforeEarlierGoal, VarTypes, !.ModuleInfo)
then
CanReorder = no
else if
% Don't reorder the goals if the later goal changes the
% instantiatedness of any of the non-locals of the earlier
% goal. This is necessary if the later goal clobbers any of
% the non-locals of the earlier goal, and avoids rerunning
% full mode analysis in other cases.
%
goal_depends_on_earlier_goal(EarlierGoal, LaterGoal,
InstmapBeforeLaterGoal, VarTypes, !.ModuleInfo)
then
CanReorder = no
else
CanReorder = yes
)
)
).
reordering_maintains_termination_old(ModuleInfo, FullyStrict,
EarlierGoal, LaterGoal) :-
EarlierGoal = hlds_goal(_, EarlierGoalInfo),
LaterGoal = hlds_goal(_, LaterGoalInfo),
EarlierDetism = goal_info_get_determinism(EarlierGoalInfo),
determinism_components(EarlierDetism, EarlierCanFail, _),
LaterDetism = goal_info_get_determinism(LaterGoalInfo),
determinism_components(LaterDetism, LaterCanFail, _),
% If --fully-strict was specified, don't convert (can_loop, can_fail)
% into (can_fail, can_loop).
( if
FullyStrict = yes,
not goal_cannot_loop_or_throw(EarlierGoal)
then
LaterCanFail = cannot_fail
else
true
),
% Don't convert (can_fail, can_loop) into (can_loop, can_fail), since
% this could worsen the termination properties of the program.
(
EarlierCanFail = can_fail,
goal_cannot_loop_or_throw(ModuleInfo, LaterGoal)
;
EarlierCanFail = cannot_fail
).
reordering_maintains_termination(FullyStrict, EarlierGoal, LaterGoal,
MaintainsTermination, !ModuleInfo) :-
EarlierGoal = hlds_goal(_, EarlierGoalInfo),
LaterGoal = hlds_goal(_, LaterGoalInfo),
EarlierDetism = goal_info_get_determinism(EarlierGoalInfo),
determinism_components(EarlierDetism, EarlierCanFail, _),
LaterDetism = goal_info_get_determinism(LaterGoalInfo),
determinism_components(LaterDetism, LaterCanFail, _),
% If --fully-strict was specified, don't convert (can_loop, can_fail) into
% (can_fail, can_loop).
goal_can_loop_or_throw(EarlierGoal, EarlierCanLoopOrThrow, !ModuleInfo),
( if
FullyStrict = yes,
EarlierCanLoopOrThrow = can_loop_or_throw,
LaterCanFail = can_fail
then
MaintainsTermination = no
else
% Don't convert (can_fail, can_loop) into (can_loop, can_fail), since
% this could worsen the termination properties of the program.
%
goal_can_loop_or_throw(LaterGoal, LaterCanLoopOrThrow,
!ModuleInfo),
( if
EarlierCanFail = can_fail,
LaterCanLoopOrThrow = can_loop_or_throw
then
MaintainsTermination = no
else
MaintainsTermination = yes
)
).
% If the earlier goal changes the instantiatedness of a variable
% that is used in the later goal, then the later goal depends on
% the earlier goal.
%
% This code does work on the alias branch.
%
:- pred goal_depends_on_earlier_goal(hlds_goal::in, hlds_goal::in, instmap::in,
vartypes::in, module_info::in) is semidet.
goal_depends_on_earlier_goal(LaterGoal, EarlierGoal, InstMapBeforeEarlierGoal,
VarTypes, ModuleInfo) :-
LaterGoal = hlds_goal(_, LaterGoalInfo),
EarlierGoal = hlds_goal(_, EarlierGoalInfo),
EarlierInstMapDelta = goal_info_get_instmap_delta(EarlierGoalInfo),
instmap.apply_instmap_delta(InstMapBeforeEarlierGoal,
EarlierInstMapDelta, InstMapAfterEarlierGoal),
instmap_changed_vars(InstMapBeforeEarlierGoal, InstMapAfterEarlierGoal,
VarTypes, ModuleInfo, EarlierChangedVars),
LaterGoalNonLocals = goal_info_get_nonlocals(LaterGoalInfo),
set_of_var.intersect(EarlierChangedVars, LaterGoalNonLocals, Intersection),
not set_of_var.is_empty(Intersection).
%-----------------------------------------------------------------------------%
generate_simple_call(ModuleName, ProcName, PredOrFunc, ModeNo, Detism, Purity,
Args, Features, InstMapDelta0, ModuleInfo, Context, Goal) :-
list.length(Args, Arity),
lookup_builtin_pred_proc_id(ModuleInfo, ModuleName, ProcName,
PredOrFunc, Arity, ModeNo, PredId, ProcId),
% builtin_state only uses this to work out whether
% this is the "recursive" clause generated for the compiler
% for each builtin, so an invalid pred_id won't cause problems.
InvalidPredId = invalid_pred_id,
BuiltinState = builtin_state(ModuleInfo, InvalidPredId, PredId, ProcId),
GoalExpr = plain_call(PredId, ProcId, Args, BuiltinState, no,
qualified(ModuleName, ProcName)),
set_of_var.list_to_set(Args, NonLocals),
determinism_components(Detism, _CanFail, NumSolns),
(
NumSolns = at_most_zero,
instmap_delta_init_unreachable(InstMapDelta)
;
( NumSolns = at_most_one
; NumSolns = at_most_many
; NumSolns = at_most_many_cc
),
InstMapDelta = InstMapDelta0
),
module_info_pred_info(ModuleInfo, PredId, PredInfo),
pred_info_get_purity(PredInfo, PredPurity),
expect(unify(Purity, PredPurity), $module,
"generate_simple_call: purity disagreement"),
goal_info_init(NonLocals, InstMapDelta, Detism, Purity, Context,
GoalInfo0),
list.foldl(goal_info_add_feature, Features, GoalInfo0, GoalInfo),
Goal = hlds_goal(GoalExpr, GoalInfo).
generate_foreign_proc(ModuleName, ProcName, PredOrFunc, ModeNo, Detism,
Purity, Attributes, Args, ExtraArgs, MaybeTraceRuntimeCond, Code,
Features, InstMapDelta0, ModuleInfo, Context, Goal) :-
list.length(Args, Arity),
lookup_builtin_pred_proc_id(ModuleInfo, ModuleName, ProcName,
PredOrFunc, Arity, ModeNo, PredId, ProcId),
GoalExpr = call_foreign_proc(Attributes, PredId, ProcId, Args, ExtraArgs,
MaybeTraceRuntimeCond, fp_impl_ordinary(Code, no)),
ArgVars = list.map(foreign_arg_var, Args),
ExtraArgVars = list.map(foreign_arg_var, ExtraArgs),
Vars = ArgVars ++ ExtraArgVars,
set_of_var.list_to_set(Vars, NonLocals),
determinism_components(Detism, _CanFail, NumSolns),
(
NumSolns = at_most_zero,
instmap_delta_init_unreachable(InstMapDelta)
;
( NumSolns = at_most_one
; NumSolns = at_most_many
; NumSolns = at_most_many_cc
),
InstMapDelta = InstMapDelta0
),
module_info_pred_info(ModuleInfo, PredId, PredInfo),
pred_info_get_purity(PredInfo, PredPurity),
expect(unify(Purity, PredPurity), $module,
"generate_simple_call: purity disagreement"),
goal_info_init(NonLocals, InstMapDelta, Detism, Purity, Context,
GoalInfo0),
list.foldl(goal_info_add_feature, Features, GoalInfo0, GoalInfo),
Goal = hlds_goal(GoalExpr, GoalInfo).
generate_cast(CastType, InArg, OutArg, Context, Goal) :-
Ground = ground_inst,
generate_cast_with_insts(CastType, InArg, OutArg, Ground, Ground, Context,
Goal).
generate_cast_with_insts(CastType, InArg, OutArg, InInst, OutInst, Context,
Goal) :-
set_of_var.list_to_set([InArg, OutArg], NonLocals),
InstMapDelta = instmap_delta_from_assoc_list([OutArg - OutInst]),
goal_info_init(NonLocals, InstMapDelta, detism_det, purity_pure, Context,
GoalInfo),
GoalExpr = generic_call(cast(CastType), [InArg, OutArg],
[in_mode(InInst), out_mode(OutInst)], arg_reg_types_unset, detism_det),
Goal = hlds_goal(GoalExpr, GoalInfo).
%-----------------------------------------------------------------------------%
predids_from_goals(Goals, PredIds) :-
(
Goals = [],
PredIds = []
;
Goals = [Goal | Rest],
predids_from_goal(Goal, PredIds0),
predids_from_goals(Rest, PredIds1),
PredIds = PredIds0 ++ PredIds1
).
predids_from_goal(Goal, PredIds) :-
% Explicit lambda expression needed since goal_calls_pred_id
% has multiple modes.
P = ( pred(PredId::out) is nondet :-
goal_calls_pred_id(Goal, PredId)
),
solutions.solutions(P, PredIds).
predids_with_args_from_goal(Goal, List) :-
solutions(
( pred({PredId, Args}::out) is nondet :-
goal_contains_goal(Goal, hlds_goal(SubGoal, _)),
SubGoal = plain_call(PredId, _, Args, _, _, _)
), List).
pred_proc_ids_from_goal(Goal, PredProcIds) :-
P = ( pred(PredProcId::out) is nondet :-
goal_calls(Goal, PredProcId)
),
solutions.solutions(P, PredProcIds).
goal_is_atomic(Goal, GoalIsAtomic) :-
GoalExpr = Goal ^ hlds_goal_expr,
(
( GoalExpr = unify(_, _, _, _, _)
; GoalExpr = plain_call(_, _, _, _, _, _)
; GoalExpr = generic_call(_, _, _, _, _)
; GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
),
GoalIsAtomic = goal_is_atomic
;
( GoalExpr = conj(_, _)
; GoalExpr = disj(_)
; GoalExpr = switch(_, _, _)
; GoalExpr = negation(_)
; GoalExpr = scope(_, _)
; GoalExpr = if_then_else(_, _, _, _)
),
GoalIsAtomic = goal_is_nonatomic
;
GoalExpr = shorthand(_),
unexpected($module, $pred, "shorthand")
).
%-----------------------------------------------------------------------------%
foreign_proc_uses_variable(Impl, VarName) :-
Impl = fp_impl_ordinary(ForeignBody, _),
string.sub_string_search(ForeignBody, VarName, _).
%-----------------------------------------------------------------------------%
maybe_strip_equality_pretest(Goal0) = Goal :-
% The if_then_else constructed by unify_proc is sometimes wrapped up
% in conjunctions.
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
(
( GoalExpr0 = unify(_, _, _, _, _)
; GoalExpr0 = plain_call(_, _, _, _, _, _)
; GoalExpr0 = generic_call(_, _, _, _, _)
; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
),
Goal = Goal0
;
GoalExpr0 = conj(ConjType, Goals0),
Goals = list.map(maybe_strip_equality_pretest, Goals0),
GoalExpr = conj(ConjType, Goals),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = disj(SubGoals0),
SubGoals = list.map(maybe_strip_equality_pretest, SubGoals0),
GoalExpr = disj(SubGoals),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = switch(Var, CanFail, Cases0),
Cases = list.map(maybe_strip_equality_pretest_case, Cases0),
GoalExpr = switch(Var, CanFail, Cases),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
( if goal_info_has_feature(GoalInfo0, feature_pretest_equality) then
Goal = Else0
else
Cond = maybe_strip_equality_pretest(Cond0),
Then = maybe_strip_equality_pretest(Then0),
Else = maybe_strip_equality_pretest(Else0),
GoalExpr = if_then_else(Vars, Cond, Then, Else),
Goal = hlds_goal(GoalExpr, GoalInfo0)
)
;
GoalExpr0 = negation(SubGoal0),
SubGoal = maybe_strip_equality_pretest(SubGoal0),
GoalExpr = negation(SubGoal),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = scope(Reason, SubGoal0),
( if
Reason = from_ground_term(_, FGT),
( FGT = from_ground_term_construct
; FGT = from_ground_term_deconstruct
)
then
Goal = Goal0
else
SubGoal = maybe_strip_equality_pretest(SubGoal0),
GoalExpr = scope(Reason, SubGoal),
Goal = hlds_goal(GoalExpr, GoalInfo0)
)
;
GoalExpr0 = shorthand(ShortHand0),
(
ShortHand0 = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
MainGoal0, OrElseGoals0, OrElseInners),
MainGoal = maybe_strip_equality_pretest(MainGoal0),
OrElseGoals = list.map(maybe_strip_equality_pretest, OrElseGoals0),
ShortHand = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
MainGoal, OrElseGoals, OrElseInners),
GoalExpr = shorthand(ShortHand),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
SubGoal = maybe_strip_equality_pretest(SubGoal0),
ShortHand = try_goal(MaybeIO, ResultVar, SubGoal),
GoalExpr = shorthand(ShortHand),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
ShortHand0 = bi_implication(_, _),
unexpected($module, $pred, "bi_implication")
)
).
:- func maybe_strip_equality_pretest_case(case) = case.
maybe_strip_equality_pretest_case(Case0) = Case :-
Case0 = case(MainConsId, OtherConsIds, Goal0),
Goal = maybe_strip_equality_pretest(Goal0),
Case = case(MainConsId, OtherConsIds, Goal).
%-----------------------------------------------------------------------------%
maybe_transform_goal_at_goal_path(TransformP, TargetGoalPath,
Goal0, MaybeGoal) :-
(
TargetGoalPath = fgp_nil,
TransformP(Goal0, MaybeGoal0),
maybe_error_to_maybe_transformed_goal(MaybeGoal0, MaybeGoal)
;
TargetGoalPath = fgp_cons(FirstStep, LaterPath),
GoalExpr0 = Goal0 ^ hlds_goal_expr,
(
( GoalExpr0 = unify(_, _, _, _, _)
; GoalExpr0 = plain_call(_, _, _, _, _, _)
; GoalExpr0 = generic_call(_, _, _, _, _)
; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
),
% This search should never reach an atomic goal.
MaybeGoal = goal_not_found
;
GoalExpr0 = conj(ConjType, Conjs0),
( if
FirstStep = step_conj(ConjNum),
list.index1(Conjs0, ConjNum, Conj0)
then
maybe_transform_goal_at_goal_path(TransformP, LaterPath,
Conj0, MaybeConj),
(
MaybeConj = ok(Conj),
list.det_replace_nth(Conjs0, ConjNum, Conj, Conjs),
GoalExpr = conj(ConjType, Conjs),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeConj = error(_)
; MaybeConj = goal_not_found
),
MaybeGoal = MaybeConj
)
else
MaybeGoal = goal_not_found
)
;
GoalExpr0 = disj(Disjs0),
( if
FirstStep = step_disj(DisjNum),
list.index1(Disjs0, DisjNum, Disj0)
then
maybe_transform_goal_at_goal_path(TransformP, LaterPath,
Disj0, MaybeDisj),
(
MaybeDisj = ok(Disj),
list.det_replace_nth(Disjs0, DisjNum, Disj, Disjs),
GoalExpr = disj(Disjs),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeDisj = error(_)
; MaybeDisj = goal_not_found
),
MaybeGoal = MaybeDisj
)
else
MaybeGoal = goal_not_found
)
;
GoalExpr0 = switch(Var, CanFail, Cases0),
( if
FirstStep = step_switch(CaseNum, _MaybeNumConstructors),
list.index1(Cases0, CaseNum, Case0)
then
CaseGoal0 = Case0 ^ case_goal,
maybe_transform_goal_at_goal_path(TransformP, LaterPath,
CaseGoal0, MaybeCaseGoal),
(
MaybeCaseGoal = ok(CaseGoal),
Case = Case0 ^ case_goal := CaseGoal,
list.det_replace_nth(Cases0, CaseNum, Case, Cases),
GoalExpr = switch(Var, CanFail, Cases),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeCaseGoal = error(_)
; MaybeCaseGoal = goal_not_found
),
MaybeGoal = MaybeCaseGoal
)
else
MaybeGoal = goal_not_found
)
;
GoalExpr0 = negation(SubGoal0),
( if FirstStep = step_neg then
maybe_transform_goal_at_goal_path(TransformP, LaterPath,
SubGoal0, MaybeSubGoal),
(
MaybeSubGoal = ok(SubGoal),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := negation(SubGoal))
;
( MaybeSubGoal = error(_)
; MaybeSubGoal = goal_not_found
),
MaybeGoal = MaybeSubGoal
)
else
MaybeGoal = goal_not_found
)
;
GoalExpr0 = scope(Reason, SubGoal0),
( if FirstStep = step_scope(_MaybeCut) then
maybe_transform_goal_at_goal_path(TransformP, LaterPath,
SubGoal0, MaybeSubGoal),
(
MaybeSubGoal = ok(SubGoal),
GoalExpr = scope(Reason, SubGoal),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeSubGoal = error(_)
; MaybeSubGoal = goal_not_found
),
MaybeGoal = MaybeSubGoal
)
else
MaybeGoal = goal_not_found
)
;
GoalExpr0 = if_then_else(ExistVars, Cond0, Then0, Else0),
( if FirstStep = step_ite_cond then
maybe_transform_goal_at_goal_path(TransformP, LaterPath,
Cond0, MaybeCond),
(
MaybeCond = ok(Cond),
GoalExpr = if_then_else(ExistVars, Cond, Then0, Else0),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeCond = error(_)
; MaybeCond = goal_not_found
),
MaybeGoal = MaybeCond
)
else if FirstStep = step_ite_then then
maybe_transform_goal_at_goal_path(TransformP, LaterPath,
Then0, MaybeThen),
(
MaybeThen = ok(Then),
GoalExpr = if_then_else(ExistVars, Cond0, Then, Else0),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeThen = error(_)
; MaybeThen = goal_not_found
),
MaybeGoal = MaybeThen
)
else if FirstStep = step_ite_else then
maybe_transform_goal_at_goal_path(TransformP, LaterPath,
Else0, MaybeElse),
(
MaybeElse = ok(Else),
GoalExpr = if_then_else(ExistVars, Cond0, Then0, Else),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeElse = error(_)
; MaybeElse = goal_not_found
),
MaybeGoal = MaybeElse
)
else
MaybeGoal = goal_not_found
)
;
GoalExpr0 = shorthand(_),
unexpected($module, $pred, "shorthand")
)
).
maybe_transform_goal_at_goal_path_with_instmap(TransformP, TargetGoalPath,
Instmap0, Goal0, MaybeGoal) :-
(
TargetGoalPath = fgp_nil,
TransformP(Instmap0, Goal0, MaybeGoal0),
maybe_error_to_maybe_transformed_goal(MaybeGoal0, MaybeGoal)
;
TargetGoalPath = fgp_cons(FirstStep, LaterPath),
GoalExpr0 = Goal0 ^ hlds_goal_expr,
(
( GoalExpr0 = unify(_, _, _, _, _)
; GoalExpr0 = plain_call(_, _, _, _, _, _)
; GoalExpr0 = generic_call(_, _, _, _, _)
; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
),
% This search should never reach an atomic goal.
MaybeGoal = goal_not_found
;
GoalExpr0 = conj(ConjType, Conjs0),
( if
FirstStep = step_conj(ConjNum),
list.index1(Conjs0, ConjNum, Conj0)
then
list.take_upto(ConjNum - 1, Conjs0, HeadConjs),
HeadInstdeltas = map(
(func(G) =
goal_info_get_instmap_delta(G ^ hlds_goal_info)),
HeadConjs),
foldl(apply_instmap_delta_sv, HeadInstdeltas,
Instmap0, Instmap),
maybe_transform_goal_at_goal_path_with_instmap(TransformP,
LaterPath, Instmap, Conj0, MaybeConj),
(
MaybeConj = ok(Conj),
list.det_replace_nth(Conjs0, ConjNum, Conj, Conjs),
GoalExpr = conj(ConjType, Conjs),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeConj = error(_)
; MaybeConj = goal_not_found
),
MaybeGoal = MaybeConj
)
else
MaybeGoal = goal_not_found
)
;
GoalExpr0 = disj(Disjs0),
( if
FirstStep = step_disj(DisjNum),
list.index1(Disjs0, DisjNum, Disj0)
then
maybe_transform_goal_at_goal_path_with_instmap(TransformP,
LaterPath, Instmap0, Disj0, MaybeDisj),
(
MaybeDisj = ok(Disj),
list.det_replace_nth(Disjs0, DisjNum, Disj, Disjs),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := disj(Disjs))
;
( MaybeDisj = error(_)
; MaybeDisj = goal_not_found
),
MaybeGoal = MaybeDisj
)
else
MaybeGoal = goal_not_found
)
;
GoalExpr0 = switch(Var, CanFail, Cases0),
( if
FirstStep = step_switch(CaseNum, _MaybeNumConstructors),
list.index1(Cases0, CaseNum, Case0)
then
CaseGoal0 = Case0 ^ case_goal,
maybe_transform_goal_at_goal_path_with_instmap(TransformP,
LaterPath, Instmap0, CaseGoal0, MaybeCaseGoal),
(
MaybeCaseGoal = ok(CaseGoal),
Case = Case0 ^ case_goal := CaseGoal,
list.det_replace_nth(Cases0, CaseNum, Case, Cases),
GoalExpr = switch(Var, CanFail, Cases),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeCaseGoal = error(_)
; MaybeCaseGoal = goal_not_found
),
MaybeGoal = MaybeCaseGoal
)
else
MaybeGoal = goal_not_found
)
;
GoalExpr0 = negation(SubGoal0),
( if FirstStep = step_neg then
maybe_transform_goal_at_goal_path_with_instmap(TransformP,
LaterPath, Instmap0, SubGoal0, MaybeSubGoal),
(
MaybeSubGoal = ok(SubGoal),
GoalExpr = negation(SubGoal),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeSubGoal = error(_)
; MaybeSubGoal = goal_not_found
),
MaybeGoal = MaybeSubGoal
)
else
MaybeGoal = goal_not_found
)
;
GoalExpr0 = scope(Reason, SubGoal0),
( if FirstStep = step_scope(_MaybeCut) then
maybe_transform_goal_at_goal_path_with_instmap(TransformP,
LaterPath, Instmap0, SubGoal0, MaybeSubGoal),
(
MaybeSubGoal = ok(SubGoal),
GoalExpr = scope(Reason, SubGoal),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeSubGoal = error(_)
; MaybeSubGoal = goal_not_found
),
MaybeGoal = MaybeSubGoal
)
else
MaybeGoal = goal_not_found
)
;
GoalExpr0 = if_then_else(ExistVars, Cond0, Then0, Else0),
( if FirstStep = step_ite_cond then
maybe_transform_goal_at_goal_path_with_instmap(TransformP,
LaterPath, Instmap0, Cond0, MaybeCond),
(
MaybeCond = ok(Cond),
GoalExpr = if_then_else(ExistVars, Cond, Then0, Else0),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeCond = error(_)
; MaybeCond = goal_not_found
),
MaybeGoal = MaybeCond
)
else if FirstStep = step_ite_then then
apply_instmap_delta_sv(
goal_info_get_instmap_delta(Cond0 ^ hlds_goal_info),
Instmap0, Instmap),
maybe_transform_goal_at_goal_path_with_instmap(TransformP,
LaterPath, Instmap, Then0, MaybeThen),
(
MaybeThen = ok(Then),
GoalExpr = if_then_else(ExistVars, Cond0, Then, Else0),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeThen = error(_)
; MaybeThen = goal_not_found
),
MaybeGoal = MaybeThen
)
else if FirstStep = step_ite_else then
maybe_transform_goal_at_goal_path_with_instmap(TransformP,
LaterPath, Instmap0, Else0, MaybeElse),
(
MaybeElse = ok(Else),
GoalExpr = if_then_else(ExistVars, Cond0, Then0, Else),
MaybeGoal = ok(Goal0 ^ hlds_goal_expr := GoalExpr)
;
( MaybeElse = error(_)
; MaybeElse = goal_not_found
),
MaybeGoal = MaybeElse
)
else
MaybeGoal = goal_not_found
)
;
GoalExpr0 = shorthand(_),
unexpected($module, $pred, "shorthand")
)
).
:- pred maybe_error_to_maybe_transformed_goal(maybe_error(hlds_goal)::in,
maybe_transformed_goal::out) is det.
maybe_error_to_maybe_transformed_goal(ok(Goal), ok(Goal)).
maybe_error_to_maybe_transformed_goal(error(Error), error(Error)).
transform_all_goals(TransformP, Goal0, Goal) :-
GoalExpr0 = Goal0 ^ hlds_goal_expr,
(
( GoalExpr0 = unify(_, _, _, _, _)
; GoalExpr0 = plain_call(_, _, _, _, _, _)
; GoalExpr0 = generic_call(_, _, _, _, _)
; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
),
GoalExpr = GoalExpr0
;
GoalExpr0 = conj(ConjType, Conjs0),
list.map(transform_all_goals(TransformP), Conjs0, Conjs),
GoalExpr = conj(ConjType, Conjs)
;
GoalExpr0 = disj(Disjs0),
list.map(transform_all_goals(TransformP), Disjs0, Disjs),
GoalExpr = disj(Disjs)
;
GoalExpr0 = switch(Var, CanFail, Cases0),
list.map(
( pred(Case0::in, Case::out) is det :-
GoalI0 = Case0 ^ case_goal,
transform_all_goals(TransformP, GoalI0, GoalI),
Case = Case0 ^ case_goal := GoalI
), Cases0, Cases),
GoalExpr = switch(Var, CanFail, Cases)
;
GoalExpr0 = negation(SubGoal0),
transform_all_goals(TransformP, SubGoal0, SubGoal),
GoalExpr = negation(SubGoal)
;
GoalExpr0 = scope(Reason, SubGoal0),
transform_all_goals(TransformP, SubGoal0, SubGoal),
GoalExpr = scope(Reason, SubGoal)
;
GoalExpr0 = if_then_else(ExistVars, Cond0, Then0, Else0),
transform_all_goals(TransformP, Cond0, Cond),
transform_all_goals(TransformP, Then0, Then),
transform_all_goals(TransformP, Else0, Else),
GoalExpr = if_then_else(ExistVars, Cond, Then, Else)
;
GoalExpr0 = shorthand(_),
unexpected($module, $pred, "shorthand")
),
Goal1 = Goal0 ^ hlds_goal_expr := GoalExpr,
TransformP(Goal1, Goal).
%-----------------------------------------------------------------------------%
:- end_module hlds.goal_util.
%-----------------------------------------------------------------------------%