mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-16 18:03:36 +00:00
The code in make_hlds_warn.m that is intended to generate singleton warnings
hasn't ever been able to handle code containing 'some [...]' goals properly.
The reason is that
- add_clause.m invokes make_hlds_warn.m only *after* it does quantification
on the body of the clause being added to the HLDS, but
- quantification has always replaced all lists of quantified variables
with the empty list.
This meant that
- we never could report code in which the only occurrence of a variable
was in a list of quantified variables, which is something we *should*
warn about, and
- we always did generate a singleton warning for code such as
"some [Val] map.search(Map, Key, Val)", which is something we *should not*
warn about.
This diff fixes this problem.
The main change is a mechanism that allows us to tell quantification.m
to keep lists of quantified variables intact. However, since the rest
of the compiler does not react well to these lists not being empty,
this diff
- gets make_hlds_warn.m to report whether the clause body goal, in which
quantification.m was told to preserve any lists of quantified variables,
*actually contained* any nonempty lists of quantified variables, and
- if it did, then we invoke quantification.m again, this time telling it
to nuke all lists of quantified variables.
This nuking has to be done relatively rarely, because only a very small
fraction of clauses contain any explicit quantification.
(An alternative design would be for make_hlds_warn.m to always nuke
any nonempty list of quantified variables it traversed. However, this would
require *always* rebuilding the clause body goal, which would probably
be slower on average.)
The above is the main change in this diff. However, the change that is
responsible for the bulk of the diff is the addition of a flag to
exist_quant scopes to specify whether that scope was created by the user
or by the compiler. This is needed because if make_hlds_warn.m sees code
such as "some [Val] map.search(Map, Key, Val)", it definitely *should*
generate a warning about Val being singleton (if it does not occur outside
this code) if the "some [Val]" scope was put there by the compiler.
compiler/make_hlds_warn.m:
Treat user-generated exist_quant scopes as before (the old code did
the right thing to generate warnings, it was just given wrong inputs).
Treat compiler-generated exist_quant scopes as if they weren't there,
for warning-generating purposes.
To make this distinction possible, use separate code to handle
exist_quant and promise_solutions scopes.
Record whether the goal traversal has seen any nonempty lists of quantified
variables, and return this info to the caller in add_clause.m.
Encode the nonempty nature of a list in the argument structure of a
predicate.
Update some obsolete terminology in variable and field names.
Clarify the logic of some code.
compiler/quantification.m:
Add the keep_quant/do_not_keep_quant switch described above.
Add some documentation of the predicates to which it is applicable.
Add free_goal_expr_vars, a version of free_goal_vars that takes
only a goal expr, without the goal info. At one point, I thought
this diff needed it. It does not, so the new function is not used,
but there is also not much point in deleting it, Simplify the code
of free_goal_vars, deleting one of its callees after inlining it
at its only call site.
Replace a appended-to-at-the-front-and-then-reversed list with a cord.
compiler/hlds_goal.m:
Add the created-by-user-or-compiler flag to exist_quant scopes.
compiler/add_clause.m:
Move the code that invokes make_hlds_warn.m to warn about singletons
into the clauses_info_add_clause predicate, whose subcontractor
add_clause_transform does the initial quantification. The reason
for this move is that we have never generated singleton variable warnings
for clauses that were read in from .opt files, or for clauses which are
known to have syntax errors. With the new setup, if we such clauses,
clauses_info_add_clause can, and does, tell add_clause_transform
to tell quantification.m to nuke lists of quantified variable
right away. It is only for the clauses we *can* warn about
that clauses_info_add_clause will tell add_clause_transform
to keep those variables, and will then itself invoke the code
in make_hlds_warn.m that warns about singletons, followed, if needed,
by a var-list-nuking reinvocation of quantification.
This centralization of the code relevant to warning code in
clauses_info_add_clause also allows the deletion of several of its
output arguments, since its two callers used those arguments
only to invoke the warning-generation code. It also eliminates
the duplication of code in those two callers.
compiler/instance_method_clauses.m:
Conform to the change in add_clause.m.
compiler/add_foreign_proc.m:
compiler/assertion.m:
compiler/constraint.m:
compiler/cse_detection.m:
compiler/det_analysis.m:
compiler/det_report.m:
compiler/format_call.m:
compiler/goal_expr_to_goal.m:
compiler/goal_util.m:
compiler/hlds_desc.m:
compiler/hlds_out_goal.m:
compiler/interval.m:
compiler/lambda.m:
compiler/mark_tail_calls.m:
compiler/ml_code_gen.m:
compiler/mode_constraints.m:
compiler/modecheck_goal.m:
compiler/polymorphism_goal.m:
compiler/pre_quantification.m:
compiler/purity.m:
compiler/saved_vars.m:
compiler/simplify_goal_scope.m:
compiler/simplify_proc.m:
compiler/state_var.m:
compiler/stm_expand.m:
compiler/superhomogeneous.m:
compiler/switch_detection.m:
compiler/try_expand.m:
compiler/typecheck.m:
compiler/unique_modes.m:
Conform to the change in hlds_goal.m and/or quantification.m.
compiler/options.m:
Add a way to detect the presence of this fix in the installed compiler.
tests/valid/Mmakefile:
Enable the old test case for this problem, some_singleton,
which we haven't passed until now.
tests/warnings/Mmakefile:
Enable the missing_singleton_warning test case, which we haven't passed
until now.
893 lines
37 KiB
Mathematica
893 lines
37 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2001-2008, 2010-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: constraint.m.
|
|
% Main author: stayl.
|
|
%
|
|
% The constraint propagation transformation attempts to improve the efficiency
|
|
% of a generate-and-test style program by statically scheduling constraints as
|
|
% early as possible, where a "constraint" is any pure goal which has no
|
|
% outputs, can fail, cannot loop and cannot throw an exception.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module transform_hlds.constraint.
|
|
:- interface.
|
|
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.instmap.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.var_table.
|
|
|
|
:- import_module bool.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type constraint_info.
|
|
|
|
% propagate_constraints_in_goal pushes constraints left and inward within
|
|
% a single goal. Specialized versions of procedures which are called with
|
|
% constrained outputs are created by deforest.m. Goals which deforest.m
|
|
% should try to propagate into calls are annotated with a `constraint'
|
|
% goal feature.
|
|
%
|
|
:- pred propagate_constraints_in_goal(hlds_goal::in, hlds_goal::out,
|
|
constraint_info::in, constraint_info::out) is det.
|
|
|
|
:- pred constraint_info_init(module_info::in, var_table::in,
|
|
instmap::in, constraint_info::out) is det.
|
|
|
|
:- pred constraint_info_deconstruct(constraint_info::in, module_info::out,
|
|
var_table::out, bool::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.
|
|
:- import_module check_hlds.inst_match.
|
|
:- import_module hlds.goal_form.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.set_of_var.
|
|
|
|
:- import_module cord.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module require.
|
|
:- import_module term.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
propagate_constraints_in_goal(Goal0, Goal, !Info) :-
|
|
% We need to strip off any existing constraint markers first.
|
|
% Constraint markers are meant to indicate where a constraint is meant
|
|
% to be attached to a call, and that deforest.m should consider creating
|
|
% a specialized version for the call. If deforest.m rearranges the goal,
|
|
% the constraints may not remain next to the call.
|
|
strip_constraint_markers(Goal0, Goal1),
|
|
propagate_goal([], Goal1, Goal, !Info).
|
|
|
|
:- pred propagate_goal(list(constraint)::in, hlds_goal::in, hlds_goal::out,
|
|
constraint_info::in, constraint_info::out) is det.
|
|
|
|
propagate_goal(Constraints, Goal0, Goal, !Info) :-
|
|
% We need to treat all single goals as conjunctions so that propagate_conj
|
|
% can move the constraints to the left of the goal if that is allowed.
|
|
Goal0 = hlds_goal(_, GoalInfo0),
|
|
Features0 = goal_info_get_features(GoalInfo0),
|
|
Context = goal_info_get_context(GoalInfo0),
|
|
goal_to_conj_list(Goal0, Goals0),
|
|
propagate_conj(Constraints, Goals0, Goals, !Info),
|
|
goal_list_nonlocals(Goals, NonLocals),
|
|
goal_list_instmap_delta(Goals, Delta),
|
|
goal_list_determinism(Goals, ConjDetism),
|
|
goal_list_purity(Goals, Purity),
|
|
goal_info_init(NonLocals, Delta, ConjDetism, Purity, Context, GoalInfo1),
|
|
goal_info_set_features(Features0, GoalInfo1, GoalInfo),
|
|
conj_list_to_goal(Goals, GoalInfo, Goal).
|
|
|
|
:- pred propagate_conj_sub_goal(list(constraint)::in,
|
|
hlds_goal::in, list(hlds_goal)::out,
|
|
constraint_info::in, constraint_info::out) is det.
|
|
|
|
propagate_conj_sub_goal(Constraints, Goal0, Goals, !Info) :-
|
|
Goal0 = hlds_goal(GoalExpr0, _),
|
|
HasSubGoals = goal_expr_has_subgoals(GoalExpr0),
|
|
(
|
|
HasSubGoals = does_not_have_subgoals
|
|
;
|
|
HasSubGoals = has_subgoals,
|
|
% If a non-empty list of constraints is pushed into a subgoal,
|
|
% quantification, instmap_deltas and determinism need to be recomputed.
|
|
constraint_info_update_changed(Constraints, !Info)
|
|
),
|
|
InstMap0 = !.Info ^ constr_instmap,
|
|
propagate_conj_sub_goal_2(Constraints, Goal0, Goals, !Info),
|
|
!Info ^ constr_instmap := InstMap0.
|
|
|
|
:- pred propagate_conj_sub_goal_2(list(constraint)::in,
|
|
hlds_goal::in, list(hlds_goal)::out,
|
|
constraint_info::in, constraint_info::out) is det.
|
|
|
|
propagate_conj_sub_goal_2(Constraints, Goal0, FinalGoals, !Info) :-
|
|
Goal0 = hlds_goal(GoalExpr, GoalInfo),
|
|
(
|
|
GoalExpr = conj(ConjType, Goals0),
|
|
(
|
|
ConjType = plain_conj,
|
|
propagate_conj(Constraints, Goals0, Goals, !Info),
|
|
FinalGoals = [hlds_goal(conj(ConjType, Goals), GoalInfo)]
|
|
;
|
|
ConjType = parallel_conj,
|
|
% We can't propagate constraints into parallel conjunctions because
|
|
% parallel conjunctions must have determinism det. However, we can
|
|
% propagate constraints *within* the goals of the conjunction.
|
|
flatten_constraints(Constraints, MoreGoals),
|
|
propagate_in_independent_goals([], Goals0, Goals, !Info),
|
|
FinalGoals = [hlds_goal(conj(ConjType, Goals), GoalInfo) |
|
|
MoreGoals]
|
|
)
|
|
;
|
|
GoalExpr = disj(Goals0),
|
|
propagate_in_independent_goals(Constraints, Goals0, Goals, !Info),
|
|
FinalGoals = [hlds_goal(disj(Goals), GoalInfo)]
|
|
;
|
|
GoalExpr = switch(Var, CanFail, Cases0),
|
|
propagate_cases(Var, Constraints, Cases0, Cases, !Info),
|
|
FinalGoals = [hlds_goal(switch(Var, CanFail, Cases), GoalInfo)]
|
|
;
|
|
GoalExpr = if_then_else(Vars, Cond0, Then0, Else0),
|
|
InstMap0 = !.Info ^ constr_instmap,
|
|
% We can't safely propagate constraints into the condition of an
|
|
% if-then-else, because that would change the answers generated
|
|
% by the procedure.
|
|
propagate_goal([], Cond0, Cond, !Info),
|
|
constraint_info_update_goal(Cond, !Info),
|
|
propagate_goal(Constraints, Then0, Then, !Info),
|
|
!Info ^ constr_instmap := InstMap0,
|
|
propagate_goal(Constraints, Else0, Else, !Info),
|
|
FinalGoals =
|
|
[hlds_goal(if_then_else(Vars, Cond, Then, Else), GoalInfo)]
|
|
;
|
|
GoalExpr = scope(Reason, SubGoal0),
|
|
(
|
|
Reason = disable_warnings(_, _),
|
|
% NOTE: This assumes that compiler passes that generate the
|
|
% warnings that could be disabled by this scope have all been run
|
|
% BEFORE program transformations such as constraint propagation.
|
|
% If they haven't been, then the transformations can hide warnings
|
|
% about code by moving them into these scopes, or can caused them
|
|
% to be generated when the user does not want them by moving
|
|
% the warned-about code out of such scopes.
|
|
propagate_goal(Constraints, SubGoal0, SubGoal, !Info),
|
|
FinalGoals = [hlds_goal(scope(Reason, SubGoal), GoalInfo)]
|
|
;
|
|
( Reason = exist_quant(_, _)
|
|
; Reason = from_ground_term(_, from_ground_term_deconstruct)
|
|
; Reason = from_ground_term(_, from_ground_term_other)
|
|
),
|
|
propagate_goal(Constraints, SubGoal0, SubGoal, !Info),
|
|
FinalGoals = [hlds_goal(scope(Reason, SubGoal), GoalInfo)]
|
|
;
|
|
( Reason = promise_solutions(_, _)
|
|
; Reason = promise_purity(_)
|
|
; Reason = commit(_)
|
|
; Reason = barrier(_)
|
|
; Reason = trace_goal(_, _, _, _, _)
|
|
; Reason = loop_control(_, _, _)
|
|
),
|
|
% We can't safely propagate constraints into one of these scopes.
|
|
% However, we can propagate constraints inside the scope goal.
|
|
propagate_goal([], SubGoal0, SubGoal, !Info),
|
|
flatten_constraints(Constraints, ConstraintGoals),
|
|
FinalGoals = [hlds_goal(scope(Reason, SubGoal), GoalInfo) |
|
|
ConstraintGoals]
|
|
;
|
|
( Reason = require_detism(_)
|
|
; Reason = require_complete_switch(_)
|
|
; Reason = require_switch_arms_detism(_, _)
|
|
; Reason = from_ground_term(_, from_ground_term_initial)
|
|
),
|
|
% These scopes should have been deleted or converted by now.
|
|
unexpected($pred, "unexpected scope")
|
|
;
|
|
Reason = from_ground_term(_, from_ground_term_construct),
|
|
% There is no point in either propagating constraints into these
|
|
% scopes or propagating local constraints within these scopes.
|
|
flatten_constraints(Constraints, ConstraintGoals),
|
|
FinalGoals = [hlds_goal(GoalExpr, GoalInfo) | ConstraintGoals]
|
|
)
|
|
;
|
|
GoalExpr = negation(NegGoal0),
|
|
% We can't safely propagate constraints into a negation,
|
|
% because that would change the answers computed by the procedure.
|
|
propagate_goal([], NegGoal0, NegGoal, !Info),
|
|
flatten_constraints(Constraints, ConstraintGoals),
|
|
FinalGoals = [hlds_goal(negation(NegGoal), GoalInfo) | ConstraintGoals]
|
|
;
|
|
( GoalExpr = plain_call(_, _, _, _, _, _)
|
|
; GoalExpr = generic_call(_, _, _, _, _)
|
|
; GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
|
|
; GoalExpr = unify(_, _, _, _, _)
|
|
),
|
|
% Propagate_conj will move the constraints to the left of the call
|
|
% or unification if that is possible, so nothing needs to be done here.
|
|
flatten_constraints(Constraints, ConstraintGoals),
|
|
FinalGoals = [hlds_goal(GoalExpr, GoalInfo) | ConstraintGoals]
|
|
;
|
|
GoalExpr = shorthand(_),
|
|
unexpected($pred, "shorthand")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Put the constant constructions in front of the constraint.
|
|
%
|
|
:- pred flatten_constraints(list(constraint)::in, list(hlds_goal)::out) is det.
|
|
|
|
flatten_constraints(Constraints0, Goals) :-
|
|
list.map(constraint_goals_to_cord, Constraints0, GoalCords),
|
|
Goals = cord_list_to_list(GoalCords).
|
|
|
|
:- pred constraint_goals_to_cord(constraint::in, cord(hlds_goal)::out) is det.
|
|
|
|
constraint_goals_to_cord(Constraint, GoalCord) :-
|
|
Constraint = constraint(ConstraintGoal, _, _, ConstructGoals),
|
|
GoalCord = cord.snoc(cord.from_list(ConstructGoals), ConstraintGoal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred propagate_in_independent_goals(list(constraint)::in,
|
|
list(hlds_goal)::in, list(hlds_goal)::out,
|
|
constraint_info::in, constraint_info::out) is det.
|
|
|
|
propagate_in_independent_goals(_, [], [], !Info).
|
|
propagate_in_independent_goals(Constraints, [Goal0 | Goals0], [Goal | Goals],
|
|
!Info) :-
|
|
InstMap0 = !.Info ^ constr_instmap,
|
|
propagate_goal(Constraints, Goal0, Goal, !Info),
|
|
!Info ^ constr_instmap := InstMap0,
|
|
propagate_in_independent_goals(Constraints, Goals0, Goals, !Info).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred propagate_cases(prog_var::in, list(constraint)::in,
|
|
list(case)::in, list(case)::out,
|
|
constraint_info::in, constraint_info::out) is det.
|
|
|
|
propagate_cases(_, _, [], [], !Info).
|
|
propagate_cases(Var, Constraints, [Case0 | Cases0], [Case | Cases], !Info) :-
|
|
Case0 = case(MainConsId, OtherConsIds, Goal0),
|
|
InstMap0 = !.Info ^ constr_instmap,
|
|
constraint_info_bind_var_to_functors(Var, MainConsId, OtherConsIds, !Info),
|
|
propagate_goal(Constraints, Goal0, Goal, !Info),
|
|
!Info ^ constr_instmap := InstMap0,
|
|
Case = case(MainConsId, OtherConsIds, Goal),
|
|
propagate_cases(Var, Constraints, Cases0, Cases, !Info).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% propagate_conj detects the constraints in a conjunction and moves them
|
|
% to as early as possible in the list. We expend some effort to keep
|
|
% the constraints in the same order as they are encountered, since this
|
|
% should increase the likelihood of allowing us to fold recursive calls.
|
|
%
|
|
:- pred propagate_conj(list(constraint)::in,
|
|
list(hlds_goal)::in, list(hlds_goal)::out,
|
|
constraint_info::in, constraint_info::out) is det.
|
|
|
|
propagate_conj(Constraints, Goals0, Goals, !Info) :-
|
|
constraint_info_update_changed(Constraints, !Info),
|
|
(
|
|
Goals0 = [],
|
|
flatten_constraints(Constraints, Goals)
|
|
;
|
|
Goals0 = [Goal0 | GoalsTail0],
|
|
( if
|
|
GoalsTail0 = [],
|
|
Constraints = []
|
|
then
|
|
propagate_conj_sub_goal([], Goal0, Goals, !Info)
|
|
else
|
|
InstMap0 = !.Info ^ constr_instmap,
|
|
ModuleInfo = !.Info ^ constr_module_info,
|
|
VarTable = !.Info ^ constr_var_table,
|
|
annotate_conj_output_vars(ModuleInfo, VarTable, InstMap0, Goals0,
|
|
[], RevAnnotatedGoals1),
|
|
annotate_conj_constraints(ModuleInfo, RevAnnotatedGoals1,
|
|
Constraints, [], AnnotatedGoals2, !Info),
|
|
propagate_conj_constraints(AnnotatedGoals2, cord.init, GoalsCord,
|
|
!Info),
|
|
Goals = cord.list(GoalsCord)
|
|
)
|
|
).
|
|
|
|
% Annotate each conjunct with the variables it produces.
|
|
%
|
|
:- pred annotate_conj_output_vars(module_info::in, var_table::in, instmap::in,
|
|
list(hlds_goal)::in, annotated_conj::in, annotated_conj::out) is det.
|
|
|
|
annotate_conj_output_vars(_, _, _, [], !RevAnnotatedGoals).
|
|
annotate_conj_output_vars(ModuleInfo, VarTable, InstMap0, [Goal | Goals],
|
|
!RevAnnotatedGoals) :-
|
|
Goal = hlds_goal(_, GoalInfo),
|
|
InstMapDelta = goal_info_get_instmap_delta(GoalInfo),
|
|
|
|
apply_instmap_delta(InstMapDelta, InstMap0, InstMap),
|
|
instmap_changed_vars(ModuleInfo, VarTable, InstMap0, InstMap,
|
|
ChangedVars0),
|
|
|
|
instmap_vars_list(InstMap, InstMapVars),
|
|
|
|
% Restrict the set of changed variables down to the set for which
|
|
% the new inst is not an acceptable substitute for the old. This is done
|
|
% to allow reordering of a goal which uses a variable with inst
|
|
% `ground(shared, no)' with a constraint which just adds information,
|
|
% changing the inst to `bound(shared, ...)'.
|
|
|
|
InCompatible =
|
|
( pred(Var::in) is semidet :-
|
|
instmap_lookup_var(InstMap0, Var, InstBefore),
|
|
instmap_delta_search_var(InstMapDelta, Var, InstAfter),
|
|
lookup_var_type(VarTable, Var, Type),
|
|
not inst_matches_initial(ModuleInfo, Type, InstAfter, InstBefore)
|
|
),
|
|
IncompatibleInstVars = set_of_var.list_to_set(
|
|
list.filter(InCompatible, InstMapVars)),
|
|
|
|
% This will consider variables with inst `any' to be bound by the goal,
|
|
% so goals which have non-locals with inst `any' will not be considered
|
|
% to be constraints. XXX This is too conservative.
|
|
|
|
Bound =
|
|
( pred(Var::in) is semidet :-
|
|
instmap_lookup_var(InstMap0, Var, InstBefore),
|
|
instmap_delta_search_var(InstMapDelta, Var, InstAfter),
|
|
lookup_var_type(VarTable, Var, Type),
|
|
not inst_matches_binding(ModuleInfo, Type, InstAfter, InstBefore)
|
|
),
|
|
BoundVars = set_of_var.list_to_set(list.filter(Bound, InstMapVars)),
|
|
|
|
% Make sure that variables with inst `any' are placed in the changed vars
|
|
% set. XXX This is too conservative, but avoids unexpected reorderings.
|
|
|
|
set_of_var.union(ChangedVars0, BoundVars, ChangedVars),
|
|
|
|
AnnotatedConjunct = annotated_conjunct(Goal, ChangedVars, BoundVars,
|
|
IncompatibleInstVars),
|
|
!:RevAnnotatedGoals = [AnnotatedConjunct | !.RevAnnotatedGoals],
|
|
annotate_conj_output_vars(ModuleInfo, VarTable, InstMap, Goals,
|
|
!RevAnnotatedGoals).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% A conjunction in which each conjunct is annotated with the set of
|
|
% variables whose instantiatedness it changes.
|
|
%
|
|
:- type annotated_conj == list(annotated_conjunct).
|
|
|
|
:- type annotated_conjunct
|
|
---> annotated_conjunct(
|
|
hlds_goal,
|
|
|
|
% All variables returned by instmap_changed_vars.
|
|
set_of_progvar,
|
|
|
|
% All variables returned by instmap_changed_vars for
|
|
% which inst_matches_binding(NewInst, OldInst) fails.
|
|
set_of_progvar,
|
|
|
|
% Variables returned by instmap_changed_vars
|
|
% for which the new inst cannot be substituted
|
|
% for the old as an input to a goal
|
|
% (inst_matches_initial(NewInst, OldInst) fails).
|
|
set_of_progvar
|
|
).
|
|
|
|
% A constraint is a goal that may fail, has no outputs,
|
|
% always terminates and will not throw an exception.
|
|
%
|
|
:- type constraint
|
|
---> constraint(
|
|
% The constraint itself.
|
|
hlds_goal,
|
|
|
|
% All variables returned by instmap_changed_vars.
|
|
set_of_progvar,
|
|
|
|
% Variables returned by instmap_changed_vars
|
|
% for which the new inst cannot be substituted
|
|
% for the old as an input to a goal
|
|
% (inst_matches_initial(NewInst, OldInst) fails).
|
|
set_of_progvar,
|
|
|
|
% Goals to construct constants used by the constraint.
|
|
% (as in X = 2, Y < X). These need to be propagated
|
|
% with the constraint.
|
|
list(hlds_goal)
|
|
).
|
|
|
|
% Conjunction annotated with constraining goals.
|
|
%
|
|
:- type constrained_conj == list(constrained_goal).
|
|
:- type constrained_goal
|
|
---> constrained_goal(hlds_goal, list(constraint)).
|
|
|
|
% Pass backwards over the conjunction, annotating each conjunct
|
|
% with the constraints that should be pushed into it.
|
|
%
|
|
:- pred annotate_conj_constraints(module_info::in, annotated_conj::in,
|
|
list(constraint)::in, constrained_conj::in, constrained_conj::out,
|
|
constraint_info::in, constraint_info::out) is det.
|
|
|
|
annotate_conj_constraints(_, [], Constraints0,
|
|
ConstrainedGoals0, ConstrainedGoals, !Info) :-
|
|
flatten_constraints(Constraints0, Constraints1),
|
|
list.map(add_empty_constraints, Constraints1, NewConstrainedGoals),
|
|
ConstrainedGoals = NewConstrainedGoals ++ ConstrainedGoals0.
|
|
annotate_conj_constraints(ModuleInfo, [Conjunct | RevConjuncts0], Constraints0,
|
|
ConstrainedGoals0, ConstrainedGoals, !Info) :-
|
|
Conjunct = annotated_conjunct(Goal, ChangedVars, OutputVars,
|
|
IncompatibleInstVars),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo),
|
|
NonLocals = goal_info_get_nonlocals(GoalInfo),
|
|
CI_ModuleInfo0 = !.Info ^ constr_module_info,
|
|
goal_can_loop_or_throw_imaf(Goal, GoalCanLoopOrThrow,
|
|
CI_ModuleInfo0, CI_ModuleInfo),
|
|
!Info ^ constr_module_info := CI_ModuleInfo,
|
|
( if
|
|
% Propagate goals that can fail and have no output variables.
|
|
% Propagating cc_nondet goals would be tricky, because we would
|
|
% need to be careful about reordering the constraints (the cc_nondet
|
|
% goal can't be moved before any goals which can fail).
|
|
Detism = goal_info_get_determinism(GoalInfo),
|
|
( Detism = detism_semi
|
|
; Detism = detism_failure
|
|
),
|
|
|
|
% XXX This is probably a bit too conservative. For example,
|
|
% `any->any' moded non-locals are considered to be outputs.
|
|
set_of_var.is_empty(OutputVars),
|
|
|
|
% Don't propagate impure goals.
|
|
goal_info_get_purity(GoalInfo) = purity_pure,
|
|
|
|
% Only propagate goals that cannot loop or throw exceptions.
|
|
GoalCanLoopOrThrow = cannot_loop_or_throw
|
|
then
|
|
% It is a constraint, so add it to the list of constraints
|
|
% to be attached to goals earlier in the conjunction.
|
|
ConstrainedGoals1 = ConstrainedGoals0,
|
|
Constraint = constraint(hlds_goal(GoalExpr, GoalInfo), ChangedVars,
|
|
IncompatibleInstVars, []),
|
|
Constraints1 = [Constraint | Constraints0]
|
|
else if
|
|
% Look for a simple goal which some constraint depends on
|
|
% which can be propagated backwards. This handles cases like
|
|
% X = 2, Y < X. This should only be done for goals which result in
|
|
% no execution at runtime, such as construction of static constants.
|
|
% Currently we only allow constructions of zero arity constants.
|
|
|
|
% Make a renamed copy of the goal, renaming within the constraint
|
|
% as well, so that a copy of the constant doesn't need to be kept
|
|
% on the stack.
|
|
Goal = hlds_goal(unify(_, _, _, Unify, _), _),
|
|
Unify = construct(ConstructVar, _, [], _, _, _, _)
|
|
then
|
|
ConstrainedGoals1 = [constrained_goal(Goal, []) | ConstrainedGoals0],
|
|
add_constant_construction(ConstructVar, Goal,
|
|
Constraints0, Constraints1, !Info),
|
|
|
|
% If the constraint was the only use of the constant, the old goal
|
|
% can be removed. We need to rerun quantification to work that out.
|
|
!Info ^ constr_changed := yes
|
|
else if
|
|
% Prune away the constraints after a goal that cannot succeed
|
|
% -- they can never be executed.
|
|
Detism = goal_info_get_determinism(GoalInfo),
|
|
determinism_components(Detism, _, at_most_zero)
|
|
then
|
|
constraint_info_update_changed(Constraints0, !Info),
|
|
Constraints1 = [],
|
|
ConstrainedGoals1 = [constrained_goal(Goal, []) | ConstrainedGoals0]
|
|
else if
|
|
% Don't propagate constraints into or past impure goals
|
|
% or trace goals.
|
|
Goal = hlds_goal(_, GoalInfo),
|
|
( goal_info_get_purity(GoalInfo) = purity_impure
|
|
; goal_info_has_feature(GoalInfo, feature_contains_trace)
|
|
)
|
|
then
|
|
Constraints1 = [],
|
|
flatten_constraints(Constraints0, ConstraintGoals),
|
|
list.map(add_empty_constraints, [Goal | ConstraintGoals],
|
|
NewConstrainedGoals),
|
|
ConstrainedGoals1 = NewConstrainedGoals ++ ConstrainedGoals0
|
|
else if
|
|
% Don't move goals which can fail before a goal which can loop
|
|
% or throw an exception if `--fully-strict' is set.
|
|
not goal_cannot_loop_or_throw_term_info(ModuleInfo, Goal),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, fully_strict, yes)
|
|
then
|
|
filter_dependent_constraints(NonLocals, ChangedVars,
|
|
Constraints0, DependentConstraints, IndependentConstraints),
|
|
flatten_constraints(IndependentConstraints,
|
|
IndependentConstraintGoals),
|
|
list.map(add_empty_constraints, IndependentConstraintGoals,
|
|
NewConstrainedGoals),
|
|
ConstrainedGoals1 = [attach_constraints(Goal, DependentConstraints)
|
|
| NewConstrainedGoals] ++ ConstrainedGoals0,
|
|
Constraints1 = []
|
|
else
|
|
filter_dependent_constraints(NonLocals, OutputVars,
|
|
Constraints0, DependentConstraints, IndependentConstraints),
|
|
Constraints1 = IndependentConstraints,
|
|
ConstrainedGoals1 = [attach_constraints(Goal, DependentConstraints) |
|
|
ConstrainedGoals0]
|
|
),
|
|
annotate_conj_constraints(ModuleInfo, RevConjuncts0, Constraints1,
|
|
ConstrainedGoals1, ConstrainedGoals, !Info).
|
|
|
|
:- pred add_empty_constraints(hlds_goal::in, constrained_goal::out) is det.
|
|
|
|
add_empty_constraints(Goal, constrained_goal(Goal, [])).
|
|
|
|
:- func attach_constraints(hlds_goal, list(constraint)) = constrained_goal.
|
|
|
|
attach_constraints(Goal, Constraints0) = constrained_goal(Goal, Constraints) :-
|
|
( if Goal = hlds_goal(plain_call(_, _, _, _, _, _), _) then
|
|
list.map(add_constraint_feature_to_constraint,
|
|
Constraints0, Constraints)
|
|
else
|
|
Constraints = Constraints0
|
|
).
|
|
|
|
:- pred add_constraint_feature_to_constraint(constraint::in, constraint::out)
|
|
is det.
|
|
|
|
add_constraint_feature_to_constraint(Constraint0, Constraint) :-
|
|
Constraint0 = constraint(Goal0, ChangedVars, IncompatibleInstVars,
|
|
ConstructGoals0),
|
|
add_constraint_feature_to_goal(Goal0, Goal),
|
|
list.map(add_constraint_feature_to_goal, ConstructGoals0, ConstructGoals),
|
|
Constraint = constraint(Goal, ChangedVars, IncompatibleInstVars,
|
|
ConstructGoals).
|
|
|
|
:- pred add_constraint_feature_to_goal(hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
add_constraint_feature_to_goal(Goal0, Goal) :-
|
|
Goal0 = hlds_goal(GoalExpr, GoalInfo0),
|
|
goal_info_add_feature(feature_constraint, GoalInfo0, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred add_constant_construction(prog_var::in, hlds_goal::in,
|
|
list(constraint)::in, list(constraint)::out,
|
|
constraint_info::in, constraint_info::out) is det.
|
|
|
|
add_constant_construction(_, _, [], [], !Info).
|
|
add_constant_construction(ConstructVar, Construct0,
|
|
[Constraint0 | Constraints0], [Constraint | Constraints], !Info) :-
|
|
Constraint0 = constraint(ConstraintGoal0, ChangedVars,
|
|
IncompatibleInstVars, Constructs0),
|
|
( if
|
|
ConstraintGoal0 = hlds_goal(_, ConstraintInfo),
|
|
ConstraintNonLocals = goal_info_get_nonlocals(ConstraintInfo),
|
|
set_of_var.member(ConstraintNonLocals, ConstructVar)
|
|
then
|
|
VarTable0 = !.Info ^ constr_var_table,
|
|
lookup_var_entry(VarTable0, ConstructVar, ConstructVarEntry),
|
|
ConstructVarEntry = vte(_, ConstructVarType, ConstructVarTypeIsDummy),
|
|
NewVarEntry = vte("", ConstructVarType, ConstructVarTypeIsDummy),
|
|
add_var_entry(NewVarEntry, NewVar, VarTable0, VarTable),
|
|
!Info ^ constr_var_table := VarTable,
|
|
|
|
Subn = map.singleton(ConstructVar, NewVar),
|
|
rename_some_vars_in_goal(Subn, Construct0, Construct),
|
|
Constructs = [Construct | Constructs0],
|
|
rename_some_vars_in_goal(Subn, ConstraintGoal0, ConstraintGoal),
|
|
Constraint = constraint(ConstraintGoal, ChangedVars,
|
|
IncompatibleInstVars, Constructs)
|
|
else
|
|
Constraint = Constraint0
|
|
),
|
|
add_constant_construction(ConstructVar, Construct0,
|
|
Constraints0, Constraints, !Info).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% filter_dependent_constraints(GoalNonLocals, GoalOutputVars,
|
|
% Constraints, DependentConstraints, IndependentConstraints):
|
|
%
|
|
% Find all constraints which depend on the output variables of the
|
|
% current goal in the conjunction being processed. The DependentConstraints
|
|
% should be pushed into the current goal. The IndependentConstraints
|
|
% should be moved to the left of the current goal, if the purity and
|
|
% termination properties of the current goal allow that.
|
|
%
|
|
:- pred filter_dependent_constraints(set_of_progvar::in, set_of_progvar::in,
|
|
list(constraint)::in, list(constraint)::out, list(constraint)::out) is det.
|
|
|
|
filter_dependent_constraints(NonLocals, GoalOutputVars, Constraints,
|
|
DependentConstraints, IndependentConstraints) :-
|
|
filter_dependent_constraints_2(NonLocals, GoalOutputVars, Constraints,
|
|
[], RevDependentConstraints, [], RevIndependentConstraints),
|
|
list.reverse(RevDependentConstraints, DependentConstraints),
|
|
list.reverse(RevIndependentConstraints, IndependentConstraints).
|
|
|
|
:- pred filter_dependent_constraints_2(set_of_progvar::in, set_of_progvar::in,
|
|
list(constraint)::in,
|
|
list(constraint)::in, list(constraint)::out,
|
|
list(constraint)::in, list(constraint)::out) is det.
|
|
|
|
filter_dependent_constraints_2(_NonLocals, _OutputVars, [],
|
|
!RevDependent, !RevIndependent).
|
|
filter_dependent_constraints_2(NonLocals, GoalOutputVars,
|
|
[Constraint | Constraints], !RevDependent, !RevIndependent) :-
|
|
Constraint = constraint(ConstraintGoal, _, IncompatibleInstVars, _),
|
|
ConstraintGoal = hlds_goal(_, ConstraintGoalInfo),
|
|
ConstraintNonLocals = goal_info_get_nonlocals(ConstraintGoalInfo),
|
|
( if
|
|
(
|
|
% A constraint is not independent of a goal if it uses
|
|
% any of the output variables of that goal.
|
|
set_of_var.intersect(ConstraintNonLocals, GoalOutputVars,
|
|
OutputVarsUsedByConstraint),
|
|
not set_of_var.is_empty(OutputVarsUsedByConstraint)
|
|
;
|
|
% A constraint is not independent of a goal if it changes
|
|
% the inst of a non-local of the goal in such a way that
|
|
% the new inst is incompatible with the old inst (e.g. by
|
|
% losing uniqueness).
|
|
set_of_var.intersect(NonLocals, IncompatibleInstVars,
|
|
IncompatibleInstVarsUsedByGoal),
|
|
not set_of_var.is_empty(IncompatibleInstVarsUsedByGoal)
|
|
;
|
|
% A constraint is not independent of a goal if it uses
|
|
% any variables whose instantiatedness is changed
|
|
% by any of the constraints already attached to the goal
|
|
% (the dependent constraints will be attached to the goal
|
|
% to be pushed into it by propagate_conj_sub_goal).
|
|
list.member(EarlierConstraint, !.RevDependent),
|
|
not can_reorder_constraints(EarlierConstraint, Constraint)
|
|
)
|
|
then
|
|
!:RevDependent = [Constraint | !.RevDependent]
|
|
else
|
|
!:RevIndependent = [Constraint | !.RevIndependent]
|
|
),
|
|
filter_dependent_constraints_2(NonLocals, GoalOutputVars, Constraints,
|
|
!RevDependent, !RevIndependent).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred can_reorder_constraints(constraint::in, constraint::in) is semidet.
|
|
|
|
can_reorder_constraints(EarlierConstraint, Constraint) :-
|
|
EarlierConstraint = constraint(_, EarlierChangedVars, _, _),
|
|
Constraint = constraint(ConstraintGoal, _, _, _),
|
|
ConstraintGoal = hlds_goal(_, ConstraintGoalInfo),
|
|
ConstraintNonLocals = goal_info_get_nonlocals(ConstraintGoalInfo),
|
|
set_of_var.intersect(EarlierChangedVars, ConstraintNonLocals,
|
|
EarlierConstraintIntersection),
|
|
set_of_var.is_empty(EarlierConstraintIntersection).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Push the constraints into each conjunct.
|
|
%
|
|
:- pred propagate_conj_constraints(constrained_conj::in,
|
|
cord(hlds_goal)::in, cord(hlds_goal)::out,
|
|
constraint_info::in, constraint_info::out) is det.
|
|
|
|
propagate_conj_constraints([], !GoalsCord, !Info).
|
|
propagate_conj_constraints([ConstrainedGoal0 | ConstrainedGoals0],
|
|
!GoalsCord, !Info) :-
|
|
ConstrainedGoal0 = constrained_goal(Goal, Constraints0),
|
|
filter_complex_constraints(Constraints0,
|
|
SimpleConstraints, ComplexConstraints0),
|
|
propagate_conj_sub_goal(SimpleConstraints, Goal,
|
|
GoalAndSimpleConstraints, !Info),
|
|
constraint_info_update_goal(Goal, !Info),
|
|
flatten_constraints(ComplexConstraints0, ComplexConstraints),
|
|
!:GoalsCord = !.GoalsCord
|
|
++ cord.from_list(GoalAndSimpleConstraints)
|
|
++ cord.from_list(ComplexConstraints),
|
|
propagate_conj_constraints(ConstrainedGoals0, !GoalsCord, !Info).
|
|
|
|
:- pred filter_complex_constraints(list(constraint)::in,
|
|
list(constraint)::out, list(constraint)::out) is det.
|
|
|
|
filter_complex_constraints(Constraints,
|
|
SimpleConstraints, ComplexConstraints) :-
|
|
filter_complex_constraints_2(Constraints,
|
|
[], RevSimpleConstraints, [], RevComplexConstraints),
|
|
list.reverse(RevSimpleConstraints, SimpleConstraints),
|
|
list.reverse(RevComplexConstraints, ComplexConstraints).
|
|
|
|
% Don't attempt to push branched goals into other goals.
|
|
%
|
|
:- pred filter_complex_constraints_2(list(constraint)::in,
|
|
list(constraint)::in, list(constraint)::out,
|
|
list(constraint)::in, list(constraint)::out) is det.
|
|
|
|
filter_complex_constraints_2([],
|
|
!RevSimpleConstraints, !RevComplexConstraints).
|
|
filter_complex_constraints_2([Constraint | Constraints],
|
|
!RevSimpleConstraints, !RevComplexConstraints) :-
|
|
Constraint = constraint(ConstraintGoal, _, _, _),
|
|
( if
|
|
goal_is_simple(ConstraintGoal),
|
|
|
|
% Check whether this simple constraint can be reordered
|
|
% with the complex constraints we have already found.
|
|
(
|
|
list.member(ComplexConstraint, !.RevComplexConstraints)
|
|
=>
|
|
can_reorder_constraints(ComplexConstraint, Constraint)
|
|
)
|
|
then
|
|
!:RevSimpleConstraints = [Constraint | !.RevSimpleConstraints]
|
|
else
|
|
!:RevComplexConstraints = [Constraint | !.RevComplexConstraints]
|
|
),
|
|
filter_complex_constraints_2(Constraints,
|
|
!RevSimpleConstraints, !RevComplexConstraints).
|
|
|
|
:- pred goal_is_simple(hlds_goal::in) is semidet.
|
|
|
|
goal_is_simple(Goal) :-
|
|
Goal = hlds_goal(GoalExpr, _),
|
|
% XXX This code should be replaced with an explicit switch.
|
|
(
|
|
goal_expr_has_subgoals(GoalExpr) = does_not_have_subgoals
|
|
;
|
|
GoalExpr = negation(SubGoal),
|
|
goal_is_simple(SubGoal)
|
|
;
|
|
GoalExpr = scope(Reason, SubGoal),
|
|
( if
|
|
Reason = from_ground_term(_, FGT),
|
|
( FGT = from_ground_term_construct
|
|
; FGT = from_ground_term_deconstruct
|
|
)
|
|
then
|
|
true
|
|
else
|
|
goal_is_simple(SubGoal)
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type constraint_info
|
|
---> constraint_info(
|
|
constr_module_info :: module_info,
|
|
constr_var_table :: var_table,
|
|
constr_instmap :: instmap,
|
|
constr_changed :: bool % has anything changed.
|
|
).
|
|
|
|
constraint_info_init(ModuleInfo, VarTable, InstMap, ConstraintInfo) :-
|
|
ConstraintInfo = constraint_info(ModuleInfo, VarTable, InstMap, no).
|
|
|
|
constraint_info_deconstruct(ConstraintInfo, ModuleInfo, VarTable, Changed) :-
|
|
ConstraintInfo = constraint_info(ModuleInfo, VarTable, _, Changed).
|
|
|
|
:- pred constraint_info_update_goal(hlds_goal::in,
|
|
constraint_info::in, constraint_info::out) is det.
|
|
|
|
constraint_info_update_goal(hlds_goal(_, GoalInfo), !Info) :-
|
|
InstMap0 = !.Info ^ constr_instmap,
|
|
InstMapDelta = goal_info_get_instmap_delta(GoalInfo),
|
|
apply_instmap_delta(InstMapDelta, InstMap0, InstMap),
|
|
!Info ^ constr_instmap := InstMap.
|
|
|
|
:- pred constraint_info_bind_var_to_functors(prog_var::in, cons_id::in,
|
|
list(cons_id)::in, constraint_info::in, constraint_info::out) is det.
|
|
|
|
constraint_info_bind_var_to_functors(Var, MainConsId, OtherConsIds, !Info) :-
|
|
InstMap0 = !.Info ^ constr_instmap,
|
|
ModuleInfo0 = !.Info ^ constr_module_info,
|
|
VarTable = !.Info ^ constr_var_table,
|
|
lookup_var_type(VarTable, Var, Type),
|
|
bind_var_to_functors(Var, Type, MainConsId, OtherConsIds,
|
|
InstMap0, InstMap, ModuleInfo0, ModuleInfo),
|
|
!Info ^ constr_instmap := InstMap,
|
|
!Info ^ constr_module_info := ModuleInfo.
|
|
|
|
% If a non-empty list of constraints is pushed into a subgoal,
|
|
% quantification, instmap_deltas and determinism need to be recomputed.
|
|
%
|
|
:- pred constraint_info_update_changed(list(constraint)::in,
|
|
constraint_info::in, constraint_info::out) is det.
|
|
|
|
constraint_info_update_changed(Constraints, !Info) :-
|
|
(
|
|
Constraints = []
|
|
;
|
|
Constraints = [_ | _],
|
|
!Info ^ constr_changed := yes
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Remove all `constraint' goal features from the goal_infos
|
|
% of all subgoals of the given goal.
|
|
%
|
|
:- pred strip_constraint_markers(hlds_goal::in, hlds_goal::out) is det.
|
|
|
|
strip_constraint_markers(Goal0, Goal) :-
|
|
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
|
|
strip_constraint_markers_expr(GoalExpr0, GoalExpr),
|
|
( if goal_info_has_feature(GoalInfo0, feature_constraint) then
|
|
goal_info_remove_feature(feature_constraint, GoalInfo0, GoalInfo)
|
|
else
|
|
GoalInfo = GoalInfo0
|
|
),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo).
|
|
|
|
:- pred strip_constraint_markers_expr(hlds_goal_expr::in, hlds_goal_expr::out)
|
|
is det.
|
|
|
|
strip_constraint_markers_expr(GoalExpr0, GoalExpr) :-
|
|
(
|
|
GoalExpr0 = conj(ConjType, SubGoals0),
|
|
list.map(strip_constraint_markers, SubGoals0, SubGoals),
|
|
GoalExpr = conj(ConjType, SubGoals)
|
|
;
|
|
GoalExpr0 = disj(SubGoals0),
|
|
list.map(strip_constraint_markers, SubGoals0, SubGoals),
|
|
GoalExpr = disj(SubGoals)
|
|
;
|
|
GoalExpr0 = switch(Var, CanFail, Cases0),
|
|
list.map(strip_constraint_markers_case, Cases0, Cases),
|
|
GoalExpr = switch(Var, CanFail, Cases)
|
|
;
|
|
GoalExpr0 = negation(SubGoal0),
|
|
strip_constraint_markers(SubGoal0, SubGoal),
|
|
GoalExpr = negation(SubGoal)
|
|
;
|
|
GoalExpr0 = scope(Reason, SubGoal0),
|
|
strip_constraint_markers(SubGoal0, SubGoal),
|
|
GoalExpr = scope(Reason, SubGoal)
|
|
;
|
|
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
|
|
strip_constraint_markers(Cond0, Cond),
|
|
strip_constraint_markers(Then0, Then),
|
|
strip_constraint_markers(Else0, Else),
|
|
GoalExpr = if_then_else(Vars, Cond, Then, Else)
|
|
;
|
|
( GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
|
|
; GoalExpr0 = generic_call(_, _, _, _, _)
|
|
; GoalExpr0 = plain_call(_, _, _, _, _, _)
|
|
; GoalExpr0 = unify(_, _, _, _, _)
|
|
; GoalExpr0 = shorthand(_)
|
|
),
|
|
GoalExpr = GoalExpr0
|
|
).
|
|
|
|
:- pred strip_constraint_markers_case(case::in, case::out) is det.
|
|
|
|
strip_constraint_markers_case(Case0, Case) :-
|
|
Case0 = case(MainConsId, OtherConsIds, SubGoal0),
|
|
strip_constraint_markers(SubGoal0, SubGoal),
|
|
Case = case(MainConsId, OtherConsIds, SubGoal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module transform_hlds.constraint.
|
|
%-----------------------------------------------------------------------------%
|