Files
mercury/compiler/build_mode_constraints.m
Mark Brown 7f5a08eb37 Split parts of inst_match.m into a new module, inst_test.m
Most modules that imported inst_match did so in order to use
predicates such as inst_is_ground to test properties of insts.
These predicates are split into a new module, leaving the more
complex parts of inst_match to be imported in fewer places.
This makes it easier to change inst_match (for example, to
address mantis bug 264) without unintentional changes to
the rest of the compiler.

compiler/inst_test.m:
    New module containing code from inst_match.m.

compiler/check_hlds.m:
    Include the new module.

compiler/inst_match.m:
    Move code to the new module.

compiler/inst_util.m:
    Move inst_expand and inst_expand_and_remove_constrained_inst_vars
    here rather than the new module, since they make more sense here.

compiler/build_mode_constraints.m:
compiler/cse_detection.m:
compiler/deforest.m:
compiler/delay_construct.m:
compiler/delay_partial_inst.m:
compiler/dep_par_conj.m:
compiler/det_report.m:
compiler/fact_table.m:
compiler/float_regs.m:
compiler/goal_util.m:
compiler/interval.m:
compiler/loop_inv.m:
compiler/modecheck_goal.m:
compiler/pd_util.m:
compiler/prog_rep.m:
compiler/simplify_goal_call.m:
compiler/size_prof.m:
compiler/stm_expand.m:
compiler/structure_sharing.domain.m:
compiler/switch_detection.m:
compiler/term_util.m:
compiler/trace_gen.m:
compiler/unify_proc.m:
compiler/unneeded_code.m:
    Only import inst_test.

compiler/common.m:
compiler/instmap.m:
compiler/mode_util.m:
compiler/modecheck_call.m:
compiler/modecheck_unify.m:
compiler/modecheck_util.m:
compiler/modes.m:
compiler/simplify_goal_disj.m:
    Import inst_test in addition to inst_match.

compiler/lco.m:
compiler/simplify_goal_switch.m:
    Import inst_test and inst_util, but not inst_match.
2015-11-06 20:52:25 +11:00

1100 lines
48 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2004-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: build_mode_constraints.m.
% Main author: richardf.
%
% This module contains predicates and data structures needed for
% traversing the HLDS and building a list of abstract constraint formulae to
% describe variable producers in a Mercury program.
%
%-----------------------------------------------------------------------------%
:- module check_hlds.build_mode_constraints.
:- interface.
:- import_module check_hlds.abstract_mode_constraints.
:- import_module hlds.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_pred.
:- import_module hlds.hlds_module.
:- import_module mdbcomp.
:- import_module mdbcomp.goal_path.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.set_of_var.
:- import_module bimap.
:- import_module list.
%-----------------------------------------------------------------------------%
% This represents the conjunction of the constraints it contains
% (which typically will refer to only one predicate).
%
:- type mode_constraints == pred_p_c_constraints.
% A store of information about mode constraint variables
% associated with constraints based mode analysis of a module
% (not just of a single predicate).
%
:- type mc_var_info
---> mc_var_info(
% Produces constraint variables for producer/consumer
% constraints.
mc_varset :: mc_varset,
% Bimap between constraint variables and the abstract
% representation of the proposition they represent.
rep_var_map :: mc_var_map
).
% A map between the constraint variables (mc_var) and what they represent,
% i.e. the proposition that some program variable is produced at some goal
% (in a particular predicate).
%
% This provides a quick way of looking up the constraint variable we need
% when we want to constrain the position in which a program variable
% can be produced.
%
:- type mc_var_map == bimap(mc_rep_var, mc_var).
% Just a conveniently descriptive name.
%
:- type args == list(prog_var).
% Just a conveniently descriptive name.
%
:- type nonlocals == set_of_progvar.
% In order to uniquely distinguish a prog_var that may not be
% unique amongst predicates, this data structure is used to specify
% the predicate to which this prog_var is intended to apply.
%
:- type mc_prog_var ---> prog_var `in` pred_id.
% An abstract representation of a mode constraint variable.
%
% (ProgVar `in` PredId) `at` GoalId represents the constraint variable
% of the proposition that ProgVar is produced at goal GoalId
% in predicate PredId.
%
:- type mc_rep_var ---> mc_prog_var `at` goal_id.
%-----------------------------------------------------------------------------%
% add_mc_vars_for_scc_heads(ModuleInfo, SCC, !VarInfo)
%
% For each HeadVariable of each predicate PredId, in the supplied
% SCC, this predicate adds to VarInfo the constraint variable for
% HeadVariable `in` PredId `at` []. In other words, it creates the
% constraint variables that describe whether or not a head variable
% is produced by a call to the predicate.
%
% (We do this all at once because intra-SCC calls refer to the production
% of program variables at the head of the callee, and the caller doesn't
% neccessarily have access to the relevant prog_varset to create the
% necessary constraint variables.)
%
:- pred add_mc_vars_for_scc_heads(module_info::in, list(pred_id)::in,
mc_var_info::in, mc_var_info::out) is det.
% add_mc_vars_for_goal(PredId, ProgVarset, Goal, !VarInfo)
%
% For each nonlocal of Goal, makes sure a constraint variable exists
% in VarInfo representing the proposition that the nonlocal is produced
% at that Goal. Other variables are either local and must be produced
% at the current path or do not occur in the goal and so are not produced
% at the current path (although some such constraint variables are
% created later so as to simplify constraint generation).
%
% NOTE: this predicate *does* recurse on subgoals.
%
:- pred add_mc_vars_for_goal(pred_id::in, prog_varset::in, hlds_goal::in,
mc_var_info::in, mc_var_info::out) is det.
% add_clauses_constraints(ModuleInfo, PredId, PredInfo, !VarInfo,
% !Constraints)
%
% Adds to Constraints the constraints for the body of predicate PredId,
% from the module described in ModuleInfo. (PredInfo should be the unpacked
% info for PredId from ModuleInfo, and VarInfo is included to keep track
% of mode constraint variables for that module.)
%
:- pred add_clauses_constraints(module_info::in, pred_id::in, pred_info::in,
mc_var_info::in, mc_var_info::out,
mode_constraints::in, mode_constraints::out) is det.
% mode_decls_constraints(ModuleInfo, VarMap, PredId, Decls,
% HeadVarsList, Constraints)
%
% Constraints is the disjunction of the constraints for individual declared
% modes being satisfied, i.e.
% disj([ConstraintsForMode1, ..., ConstraintsForModen])
%
% The constraints for a predicate with declared modes is the disjunction
% of the constraints for each declared mode. If, according to the mode
% declaration, a variable is not initially free, then it is constrained
% so that it cannot be produced by a call to this predicate; if a variable
% is initially free and becomes not free then it is constrained so as
% to be produced by the predicate. Otherwise it is free -> free and is
% constrained so as to not be produced.
%
% NOTE: if Decls is an empty list, this is interpreted as there being
% no modes for which the predicate can be executed and the returned
% constraints are [disj([])] which cannot be satisfied. Predicates
% with mode inference requested should not be handled by this.
%
:- pred mode_decls_constraints(module_info::in, mc_var_map::in, pred_id::in,
list(list(mer_mode))::in, list(args)::in, list(mc_constraint)::out) is det.
% add_mode_decl_constraints(ModuleInfo, PredId, ProcId,
% Decl, Args, !VarInfo, !Constraints)
%
% Constrains (in the Constraints) the production of Args at the head
% of predicate PredId when called in mode ProcId with declaration Decl.
% If, according to the mode declaration, a variable is not initially free,
% then it is constrained so that it cannot be produced by a call to this
% predicate; if a variable is initially free and becomes not free then
% it is constrained so as to be produced by the predicate. Otherwise it is
% free -> free and is constrained so as to not be produced.
%
:- pred add_mode_decl_constraints(module_info::in, pred_id::in, proc_id::in,
list(mer_mode)::in, args::in, mc_var_info::in, mc_var_info::out,
mode_constraints::in, mode_constraints::out) is det.
% Creates a new mc_var_info structure (with no mode constraint
% variables stored in it).
%
:- func var_info_init = mc_var_info.
% rep_var_to_string(ProgVarset, ProgVar `in` PredId `at` GoalId)
%
% returns a string representation of the constraint variable with
% the abstract representation given, in format "ProgVarName.GoalId" -
% the ProgVarset should have the ProgVarName in it for ProgVar,
% and should therefore be taken from predicate PredId.
%
:- func rep_var_to_string(prog_varset, mc_rep_var) = (string).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module check_hlds.inst_test.
:- import_module check_hlds.mode_util.
:- import_module hlds.hlds_args.
:- import_module hlds.hlds_clauses.
:- import_module hlds.hlds_desc.
:- import_module bool.
:- import_module io.
:- import_module map.
:- import_module maybe.
:- import_module multi_map.
:- import_module require.
:- import_module string.
:- import_module term.
:- import_module varset.
%-----------------------------------------------------------------------------%
:- type conj_constraints_info
---> conj_constraints_info(
% Keys are program variables local to the conjunction.
% They are mapped to constraint variables representing
% the proposition that they are produced at each conjunct,
% but only for conjuncts they appear in/are nonlocal to.
locals_positions :: conjunct_production_map,
% Keys are program variables nonlocal to the conjunction.
% They are mapped to constraint variables representing
% the proposition that they are produced at each conjunct,
% but only for conjuncts they appear in/are nonlocal to.
nonlocals_positions :: conjunct_production_map
).
% Map from program variables to corresponding constraint variables.
% The constraint variables should represent a set of propositions
% that the program variable they are mapped from is produced at
% some conjunct in a single conjunction.
%
% ie if multi_map.member(ConjunctProductionMap, ProgVar, MCVar)
% then MCVar should represent the proposition that ProgVar is
% produced at some specific conjunct, and all such `MCVar's should
% refer to the same conjuction.
%
:- type conjunct_production_map == multi_map(prog_var, mc_var).
%-----------------------------------------------------------------------------%
add_mc_vars_for_scc_heads(ModuleInfo, PredIds, !VarInfo) :-
list.foldl(add_mc_vars_for_pred_head(ModuleInfo), PredIds, !VarInfo).
% add_mc_vars_for_pred_head(ModuleInfo, PredId, !VarInfo)
%
% For each HeadVariable of predicate PredId this predicate adds to
% VarInfo the constraint variable for HeadVariable `in` PredId `at`
% []. In other words, it creates the constraint variables that
% describe whether or not a head variable is produced by a call to
% the predicate.
%
:- pred add_mc_vars_for_pred_head(module_info::in, pred_id::in,
mc_var_info::in, mc_var_info::out) is det.
add_mc_vars_for_pred_head(ModuleInfo, PredId, !VarInfo) :-
module_info_pred_info(ModuleInfo, PredId, PredInfo),
pred_info_get_clauses_info(PredInfo, ClausesInfo),
clauses_info_get_headvar_list(ClausesInfo, HeadVars),
clauses_info_get_varset(ClausesInfo, ProgVarset),
list.foldl(add_mc_var_for_pred_head(ProgVarset, PredId), HeadVars,
!VarInfo).
% add_mc_var_for_pred_head(ProgVarset, PredId, ProgVar, !VarInfo)
%
% For ProgVar, a head variable of predicate PredId, this predicate
% adds to VarInfo the constraint variable for ProgVar `in` PredId
% `at` []. In other words, it creates the constraint variable that
% describes whether or not ProgVar is produced by a call to the
% predicate. ProgVarset should contain ProgVar, and a string name
% for it.
%
:- pred add_mc_var_for_pred_head(prog_varset::in, pred_id::in, prog_var::in,
mc_var_info::in, mc_var_info::out) is det.
add_mc_var_for_pred_head(ProgVarset, PredId, HeadVar, !VarInfo) :-
prog_var_at_path(ProgVarset, PredId, HeadVar, goal_id(0), _, !VarInfo).
%-----------------------------------------------------------------------------%
add_mc_vars_for_goal(PredId, ProgVarset, Goal, !VarInfo) :-
Goal = hlds_goal(GoalExpr, GoalInfo),
Nonlocals = goal_info_get_nonlocals(GoalInfo),
GoalId = goal_info_get_goal_id(GoalInfo),
set_of_var.to_sorted_list(Nonlocals, NlsList),
prog_vars_at_path(ProgVarset, PredId, NlsList, GoalId, _, !VarInfo),
% Switch on GoalExpr for recursion
(
GoalExpr = conj(ConjType, Goals),
(
ConjType = plain_conj,
list.foldl(add_mc_vars_for_goal(PredId, ProgVarset), Goals,
!VarInfo)
;
ConjType = parallel_conj
% XXX handle this
)
;
GoalExpr = switch(_, _, Cases),
Goals = list.map(func(case(_, _, CaseGoal)) = CaseGoal, Cases),
list.foldl(add_mc_vars_for_goal(PredId, ProgVarset), Goals, !VarInfo)
;
GoalExpr = disj(Goals),
list.foldl(add_mc_vars_for_goal(PredId, ProgVarset), Goals, !VarInfo)
;
GoalExpr = negation(SubGoal),
add_mc_vars_for_goal(PredId, ProgVarset, SubGoal, !VarInfo)
;
GoalExpr = scope(_, SubGoal),
add_mc_vars_for_goal(PredId, ProgVarset, SubGoal, !VarInfo)
;
GoalExpr = if_then_else(_, Cond, Then, Else),
Goals = [Cond, Then, Else],
list.foldl(add_mc_vars_for_goal(PredId, ProgVarset), Goals, !VarInfo)
;
GoalExpr = shorthand(ShortHand),
(
ShortHand = atomic_goal(_, _, _, _, _, _, _),
sorry($module, $pred, "NYI: atomic_goal")
;
ShortHand = bi_implication(_, _),
unexpected($module, $pred, "bi_implication")
;
ShortHand = try_goal(_, _, _),
sorry($module, $pred, "try_goal")
)
;
( GoalExpr = plain_call(_, _, _, _, _, _)
; GoalExpr = generic_call(_, _, _, _, _)
; GoalExpr = call_foreign_proc(_, _, _, _, _, _, _)
; GoalExpr = unify(_, _, _, _, _)
)
).
%-----------------------------------------------------------------------------%
add_clauses_constraints(ModuleInfo, PredId, PredInfo, !VarInfo,
!Constraints) :-
pred_info_get_clauses_info(PredInfo, ClausesInfo),
clauses_info_get_headvars(ClausesInfo, HeadVars),
clauses_info_get_clauses_rep(ClausesInfo, ClausesRep, _ItemNumbers),
get_clause_list_maybe_repeated(ClausesRep, Clauses),
clauses_info_get_varset(ClausesInfo, ProgVarset),
(
% If the clause list is empty, then there are no goals
% to produce constraints for.
Clauses = []
;
Clauses = [FirstClause | _],
% Use the first clause for the context of the top level
% goal constraints.
Context = clause_context(FirstClause),
% We consider all clauses for all procedures.
% Though some may not be applicable, overall the wasted effort
% should not be large.
Goals = list.map(clause_body, Clauses),
list.foldl(add_mc_vars_for_goal(PredId, ProgVarset), Goals, !VarInfo),
% Temporarily form the disjunction implied by the goal path
% annotations.
MainGoal = disj(Goals),
Nonlocals = set_of_var.list_to_set(proc_arg_vector_to_list(HeadVars)),
add_goal_expr_constraints(ModuleInfo, ProgVarset, PredId, MainGoal,
Context, goal_id(0), Nonlocals, !VarInfo, !Constraints)
).
% add_goal_constraints(ModuleInfo, ProgVarset, PredId, Goal,
% !VarInfo, !Constraints)
%
% Adds goal constraints to Constraints to describe the producing and
% consuming goals for program variables nonlocal to Goal in
% predicate PredId of the module described in ModuleInfo.
% VarInfo is used to keep track of the meaning of any constraint
% variables used.
%
:- pred add_goal_constraints(module_info::in, prog_varset::in, pred_id::in,
hlds_goal::in, mc_var_info::in, mc_var_info::out, mode_constraints::in,
mode_constraints::out) is det.
add_goal_constraints(ModuleInfo, ProgVarset, PredId, Goal,
!VarInfo, !Constraints) :-
Goal = hlds_goal(GoalExpr, GoalInfo),
Nonlocals = goal_info_get_nonlocals(GoalInfo),
GoalId = goal_info_get_goal_id(GoalInfo),
Context = goal_info_get_context(GoalInfo),
trace [compile_time(flag("goal_mode_constraints")), io(!IO)] (
GoalDesc = describe_goal(ModuleInfo, ProgVarset, Goal),
io.format("constraints for %s:\n", [s(GoalDesc)], !IO)
),
add_goal_expr_constraints(ModuleInfo, ProgVarset, PredId, GoalExpr,
Context, GoalId, Nonlocals, !VarInfo, !Constraints).
% add_goal_expr_constraints(ModuleInfo, ProgVarset, PredId, GoalExpr,
% Context, GoalId, Nonlocals, !VarInfo, !Constraints)
%
% Adds goal constraints to Constraints to describe the producing and
% consuming goals for Nonlocals, with respect to GoalExpr and its
% subgoals.
% Context and GoalId refer to the position of GoalExpr, in the
% source file for the module described in ModuleInfo, and in the
% body of predicate PredId respectively.
% VarInfo is used to keep track of the meaning of any constraint
% variables used. ProgVarset should contain string names for
% all of the variables in Nonlocals.
%
:- pred add_goal_expr_constraints(module_info::in, prog_varset::in,
pred_id::in, hlds_goal_expr::in, prog_context::in, goal_id::in,
nonlocals::in, mc_var_info::in, mc_var_info::out,
mode_constraints::in, mode_constraints::out) is det.
add_goal_expr_constraints(ModuleInfo, ProgVarset, PredId, GoalExpr,
Context, GoalId, Nonlocals, !VarInfo, !Constraints) :-
(
GoalExpr = conj(ConjType, Goals),
(
ConjType = plain_conj,
list.foldl(
add_goal_nonlocals_to_conjunct_production_maps(VarMap, PredId,
Nonlocals),
Goals, conj_constraints_info_init, ConjConstraintsInfo),
VarMap = rep_var_map(!.VarInfo),
list.foldl2(add_goal_constraints(ModuleInfo, ProgVarset, PredId),
Goals, !VarInfo, !Constraints),
map.foldl(
add_local_var_conj_constraints(!.VarInfo ^ mc_varset, Context),
locals_positions(ConjConstraintsInfo), !Constraints),
map.foldl2(
add_nonlocal_var_conj_constraints(ProgVarset, PredId,
Context, GoalId),
nonlocals_positions(ConjConstraintsInfo),
!VarInfo, !Constraints)
;
ConjType = parallel_conj,
% XXX Need to do something here.
sorry($module, $pred, "par_conj")
)
;
GoalExpr = plain_call(CalleePredId, _, Args, _, _, _),
module_info_pred_info(ModuleInfo, CalleePredId, CalleePredInfo),
% The predicate we are in now is the caller.
CallerPredId = PredId,
( pred_info_infer_modes(CalleePredInfo) ->
% No modes declared so just constrain the hearvars
pred_info_get_clauses_info(CalleePredInfo, CalleeClausesInfo),
clauses_info_get_headvar_list(CalleeClausesInfo, CalleeHeadVars),
add_mode_infer_callee(CalleePredId, !Constraints),
add_call_headvar_constraints(ProgVarset, Context, GoalId,
CallerPredId, Args, CalleePredId, CalleeHeadVars,
!VarInfo, !Constraints)
;
% At least one declared mode
pred_info_get_proc_table(CalleePredInfo, CalleeProcTable),
map.values(CalleeProcTable, CalleeProcInfos),
list.map(proc_info_get_argmodes, CalleeProcInfos,
CalleeArgModeDecls),
add_call_mode_decls_constraints(ModuleInfo, ProgVarset, Context,
CallerPredId, CalleeArgModeDecls, GoalId, Args, !VarInfo,
!Constraints)
)
;
GoalExpr = generic_call(Details, _, _, _, _),
% XXX Need to do something here.
(
% XXX Need to do something here.
Details = higher_order(_, _, _, _),
sorry($module, $pred, "higher_order generic_call")
;
% XXX Need to do something here.
Details = class_method(_, _, _, _),
sorry($module, $pred, "class_method generic_call")
;
% XXX We need to impose the constraint that all the argument
% variables are bound elsewhere.
Details = event_call(_),
sorry($module, $pred, "event_call generic_call")
;
% No mode constraints
Details = cast(_)
)
;
GoalExpr = switch(_, _, _),
unexpected($module, $pred, "switch")
;
GoalExpr = unify(LHSvar, RHS, _Mode, _Kind, _UnifyContext),
prog_var_at_path(ProgVarset, PredId, LHSvar, GoalId,
LHSvarProducedHere, !VarInfo),
(
RHS = rhs_var(RHSvar),
% Goal: LHSvar = RHSvar
% At most one of the left and right hand sides of a unification
% is produced at the unification.
prog_var_at_path(ProgVarset, PredId, RHSvar, GoalId,
RHSvarProducedHere, !VarInfo),
not_both(!.VarInfo ^ mc_varset, Context,
LHSvarProducedHere, RHSvarProducedHere, !Constraints)
;
RHS = rhs_functor(_Functor, _IsExistConstr, Args),
prog_vars_at_path(ProgVarset, PredId, Args, GoalId,
ArgsProducedHere, !VarInfo),
(
ArgsProducedHere = [OneArgProducedHere, _Two| _],
% Goal: LHSvar = functor(Args)
% (a): If one arg is produced here, then they all are.
% (b): At most one side of the unification is produced.
equivalent(!.VarInfo ^ mc_varset ,Context, ArgsProducedHere,
!Constraints),
not_both(!.VarInfo ^ mc_varset, Context,
LHSvarProducedHere, OneArgProducedHere, !Constraints)
;
ArgsProducedHere = [OneArgProducedHere],
% Goal: LHSvar = functor(Arg)
% At most one side of the unification is produced.
not_both(!.VarInfo ^ mc_varset, Context,
LHSvarProducedHere, OneArgProducedHere, !Constraints)
;
ArgsProducedHere = []
% Goal: LHSvar = functor
% In this case, LHSvar need not be produced
% - it could be a test, so no constraints.
)
;
RHS = rhs_lambda_goal(_, _, _, _, _, _, _, _, _),
sorry($module, $pred, "unify with lambda goal")
)
;
GoalExpr = disj(Goals),
nonlocals_at_path_and_subpaths(ProgVarset, PredId, GoalId,
DisjunctGoalIds, Nonlocals, NonlocalsHere, NonlocalsAtDisjuncts,
!VarInfo),
GoalInfos = list.map(get_hlds_goal_info, Goals),
DisjunctGoalIds = list.map(goal_info_get_goal_id, GoalInfos),
list.foldl2(add_goal_constraints(ModuleInfo, ProgVarset, PredId),
Goals, !VarInfo, !Constraints),
% A variable bound at any disjunct is bound for the disjunction
% as a whole. If a variable can be bound at one disjunct
% it must be able to be bound at any.
EquivVarss = list.map_corresponding(list.cons, NonlocalsHere,
NonlocalsAtDisjuncts),
list.foldl(equivalent(!.VarInfo ^ mc_varset, Context), EquivVarss,
!Constraints)
;
GoalExpr = negation(Goal),
Goal = hlds_goal(_, NegatedGoalInfo),
NegatedGoalId = goal_info_get_goal_id(NegatedGoalInfo),
VarMap = rep_var_map(!.VarInfo),
set_of_var.fold(cons_prog_var_at_path(VarMap, PredId, GoalId),
Nonlocals, [], NonlocalsAtId),
set_of_var.fold(cons_prog_var_at_path(VarMap, PredId, NegatedGoalId),
Nonlocals, NonlocalsAtId, NonlocalsConstraintVars),
add_goal_constraints(ModuleInfo, ProgVarset, PredId, Goal, !VarInfo,
!Constraints),
% The variables non-local to the negation are not to be produced
% at the negation or any deeper, so we constrain their mode constraint
% variables for these positions to `no'.
list.foldl(equiv_no(!.VarInfo ^ mc_varset, Context),
NonlocalsConstraintVars, !Constraints)
;
GoalExpr = scope(_Reason, Goal),
Goal = hlds_goal(_, SomeGoalInfo),
SomeGoalId = goal_info_get_goal_id(SomeGoalInfo),
% If a program variable is produced by the sub-goal of the some
% statement, it is produced at the main goal as well
% - here we pair up equivalent mode constraint vars and
% then constrain them to reflect this.
NonlocalsList = set_of_var.to_sorted_list(Nonlocals),
prog_vars_at_path(ProgVarset, PredId, NonlocalsList, GoalId,
NonlocalsHere, !VarInfo),
prog_vars_at_path(ProgVarset, PredId, NonlocalsList, SomeGoalId,
NonlocalsAtSubGoal, !VarInfo),
EquivVarss = list.map_corresponding(
func(NlAtId, NlAtSubId) = [NlAtId, NlAtSubId],
NonlocalsHere, NonlocalsAtSubGoal),
list.foldl(equivalent(!.VarInfo ^ mc_varset, Context), EquivVarss,
!Constraints),
add_goal_constraints(ModuleInfo, ProgVarset, PredId, Goal, !VarInfo,
!Constraints)
;
GoalExpr = if_then_else(ExistVars, Cond, Then, Else),
Cond = hlds_goal(_, CondInfo),
Then = hlds_goal(_, ThenInfo),
Else = hlds_goal(_, ElseInfo),
CondId = goal_info_get_goal_id(CondInfo),
ThenId = goal_info_get_goal_id(ThenInfo),
ElseId = goal_info_get_goal_id(ElseInfo),
prog_vars_at_path(ProgVarset, PredId, NonlocalsList, GoalId,
NonlocalsHere, !VarInfo),
prog_vars_at_path(ProgVarset, PredId, NonlocalsList, CondId,
NonlocalsAtCond, !VarInfo),
prog_vars_at_path(ProgVarset, PredId, NonlocalsList, ThenId,
NonlocalsAtThen, !VarInfo),
prog_vars_at_path(ProgVarset, PredId, NonlocalsList, ElseId,
NonlocalsAtElse, !VarInfo),
NonlocalsList = set_of_var.to_sorted_list(Nonlocals),
% The existentially quantified variables shared between the condition
% and the then-part have special constraints.
CondNonlocals = goal_info_get_nonlocals(CondInfo),
ThenNonlocals = goal_info_get_nonlocals(ThenInfo),
list.filter(set_of_var.contains(CondNonlocals),
ExistVars, NonlocalToCond),
list.filter(set_of_var.contains(ThenNonlocals),
NonlocalToCond, LocalAndShared),
prog_vars_at_path(ProgVarset, PredId, LocalAndShared, CondId,
LocalAndSharedAtCond, !VarInfo),
prog_vars_at_path(ProgVarset, PredId, LocalAndShared, ThenId,
LocalAndSharedAtThen, !VarInfo),
add_goal_constraints(ModuleInfo, ProgVarset, PredId, Cond, !VarInfo,
!Constraints),
add_goal_constraints(ModuleInfo, ProgVarset, PredId, Then, !VarInfo,
!Constraints),
add_goal_constraints(ModuleInfo, ProgVarset, PredId, Else, !VarInfo,
!Constraints),
% If a variable is to be produced at this path,
% the then and else parts must be able to produce it.
% Here we group constraint variables into lists to be
% constrained equivalent to reflect this.
EquivVarss = list.map_corresponding3(func(A, B, C) = [A, B, C],
NonlocalsHere, NonlocalsAtThen, NonlocalsAtElse),
list.foldl(equivalent(!.VarInfo ^ mc_varset, Context), EquivVarss,
!Constraints),
% No nonlocal is produced in the condition.
list.foldl(equiv_no(!.VarInfo ^ mc_varset, Context), NonlocalsAtCond,
!Constraints),
% In case the program variable appears in the nonlocal set
% of the condition, but its value is not used, we do not
% simply constrain LocalAtCond = yes and LocalAtThen = no.
% Instead we constrain exactly one of them to be yes.
list.foldl_corresponding(xor(!.VarInfo ^ mc_varset, Context),
LocalAndSharedAtCond, LocalAndSharedAtThen, !Constraints)
;
GoalExpr = call_foreign_proc(_, CalledPred, ProcId, ForeignArgs,
_, _, _),
CallArgs = list.map(foreign_arg_var, ForeignArgs),
module_info_pred_proc_info(ModuleInfo, CalledPred, ProcId, _,
ProcInfo),
( proc_info_get_maybe_declared_argmodes(ProcInfo, yes(_OrigDecl)) ->
proc_info_get_argmodes(ProcInfo, Decl),
% This pred should strip the disj(conj()) for the single
% declaration.
add_call_mode_decls_constraints(ModuleInfo, ProgVarset, Context,
PredId, [Decl], GoalId, CallArgs, !VarInfo, !Constraints)
;
unexpected($module, $pred, "no mode declaration for foreign proc")
)
;
GoalExpr = shorthand(Shorthand),
(
Shorthand = atomic_goal(_, _, _, _, _, _, _),
% Should record that
% - OuterDI is definitely not produced inside this goal
% - InnerDI is definitely produced by this goal
% - InnerUO should definitely be produced inside this goal,
% by the main goal and each orelse goal
% - OuterUO is definitely produced by this goal
sorry($module, $pred, "NYI: atomic_goal")
;
Shorthand = bi_implication(_, _),
% These should have been expanded out by now.
unexpected($module, $pred, "shorthand goal")
;
Shorthand = try_goal(_, _, _),
sorry($module, $pred, "NYI: try_goal")
)
).
%-----------------------------------------------------------------------------%
mode_decls_constraints(ModuleInfo, VarMap, PredId, Decls, HeadVarsList,
Constraints) :-
% Transform each headvar into the constraint variable representing
% the proposition that it is produced at the empty goal path (ie
% that it is produced by a call to the predicate).
HeadVarsMCVars =
list.map(list.map(
lookup_prog_var_at_path(VarMap, PredId, goal_id(0))),
HeadVarsList),
% Make the constraints for each declaration.
ConstraintsList = list.map_corresponding(
mode_decl_constraints(ModuleInfo), HeadVarsMCVars, Decls),
Constraints0 = list.condense(ConstraintsList),
( Constraints0 = [mc_conj(OneModeOnlyConstraints)] ->
Constraints = OneModeOnlyConstraints
;
Constraints = [mc_disj(Constraints0)]
).
add_mode_decl_constraints(ModuleInfo, PredId, ProcId, Decl, Args,
!VarInfo, !Constraints) :-
module_info_proc_info(ModuleInfo, PredId, ProcId, ProcInfo),
proc_info_get_varset(ProcInfo, ProgVarset),
proc_info_get_context(ProcInfo, Context),
prog_vars_at_path(ProgVarset, PredId, Args, goal_id(0), ArgsAtHead,
!VarInfo),
DeclConstraints = mode_decl_constraints(ModuleInfo, ArgsAtHead, Decl),
list.foldl(
add_proc_specific_constraint(!.VarInfo ^ mc_varset, Context, ProcId),
DeclConstraints, !Constraints).
% add_call_mode_decls_constraints(ModuleInfo, ProgVarset, Context,
% CallingPred, Decls, GoalId, CallArgs, !VarInfo, !Constraints)
%
% Adds the following to the mode_constraints:
% disj([ConstraintsForMode1, ..., ConstraintsForModeN])
% with context Context, and goal path GoalId (of the position of
% the call). ConstraintsForModei are the constraints for when the
% callee is called in the ith mode of Decls.
% Assumes CallArgs to be the program variables from CallingPred
% that are to be used as arguments for the called predicate.
% Uses and stores constraint variables in VarInfo, finding
% the string names for program variables in ProgVarset.
%
% Note that if Decls is an empty list, this is interpreted as
% there being no modes for which the predicate can be executed
% and the returned constraints are [disj([])] which cannot be
% satisfied. Predicate calls for callee predicates with no declared
% modes should not be handled by this.
%
% The constraints for a (call to a) predicate with declared
% modes is the disjunction of the constraints for each declared
% mode. If, according to the mode declaration, a variable is not
% initially free then it cannot be produced by a call to this
% goal; If a variable is initially free and becomes not free
% then it is produced by the predicate. Otherwise it is free ->
% free and is not produced.
%
:- pred add_call_mode_decls_constraints(module_info::in,
prog_varset::in, prog_context::in, pred_id::in, list(list(mer_mode))::in,
goal_id::in, args::in, mc_var_info::in, mc_var_info::out,
mode_constraints::in, mode_constraints::out) is det.
add_call_mode_decls_constraints(ModuleInfo, ProgVarset, CallContext,
CallingPred, Decls, GoalId, CallArgs, !VarInfo, !Constraints) :-
prog_vars_at_path(ProgVarset, CallingPred, CallArgs, GoalId,
CallArgsHere, !VarInfo),
ModeSpecificConstraints = list.condense(list.map(
mode_decl_constraints(ModuleInfo, CallArgsHere),
Decls)),
( ModeSpecificConstraints = [mc_conj(OneModeOnlyConstraints)] ->
list.foldl(add_constraint(!.VarInfo ^ mc_varset, CallContext),
OneModeOnlyConstraints, !Constraints)
;
add_constraint(!.VarInfo ^ mc_varset, CallContext,
mc_disj(ModeSpecificConstraints), !Constraints)
).
% add_call_headvar_constraints(ProgVarset, Context, GoalId, Caller,
% CallArgs, Callee, HeadVars, CallArgs, !VarInfo, !Constraints):
%
% Forms constraints that, when satisfied, mean any call arg will be
% produced if the corresponding headvar is produced by the main goal
% of the called predicate.
% The constraints are annotated with Context, which should be the
% context of the call goal in the original program.
%
% This should not be used if a mode declaration is supplied, as
% it means a predicate can only be used in a single mode
% throughout the whole body of the calling predicate.
%
:- pred add_call_headvar_constraints(prog_varset::in, prog_context::in,
goal_id::in, pred_id::in, args::in, pred_id::in, args::in,
mc_var_info::in, mc_var_info::out, mode_constraints::in,
mode_constraints::out) is det.
add_call_headvar_constraints(ProgVarset, Context, GoalId, CallerPredId,
CallArgs, CalleePredId, CalleeHeadVars, !VarInfo, !Constraints) :-
prog_vars_at_path(ProgVarset, CalleePredId, CalleeHeadVars, goal_id(0),
HeadVarsAtHead, !VarInfo),
prog_vars_at_path(ProgVarset, CallerPredId, CallArgs, GoalId,
CallArgsHere, !VarInfo),
% If the head var is produced by the body of the callee, then the
% corresponding argument variable is produced at the goal path
% of the call.
EquivVarss = list.map_corresponding(func(A, B) = [A, B],
HeadVarsAtHead, CallArgsHere),
list.foldl(equivalent(!.VarInfo ^ mc_varset, Context), EquivVarss,
!Constraints).
% mode_decl_constraints(ModuleInfo, ConstraintVars, ArgModes)
% looks at each mode in ArgModes to see if its variable is produced,
% and creates constraints for the corresponding constraint variable
% in ConstraintVars accordingly.
%
:- func mode_decl_constraints(module_info, list(mc_var), list(mer_mode)) =
list(mc_constraint).
mode_decl_constraints(ModuleInfo, ConstraintVars, ArgModes) =
[mc_conj(list.map_corresponding(
single_mode_constraints(ModuleInfo), ConstraintVars, ArgModes))].
% single_mode_constraints(ModuleInfo, MCVar, Mode) returns a
% constraint on MCVar such that the program variable it
% refers to should change instantiation according to Mode
% at the goal path it refers to.
%
:- func single_mode_constraints(module_info, mc_var, mer_mode) =
mc_constraint.
single_mode_constraints(ModuleInfo, MCVar, Mode) = Constraint :-
mode_util.mode_get_insts(ModuleInfo, Mode, InitialInst, FinalInst),
(
% Already produced?
not inst_is_free(ModuleInfo, InitialInst)
->
IsProduced = no % Not produced here.
;
% free -> non-free
not inst_is_free(ModuleInfo, FinalInst)
->
IsProduced = yes % Produced here.
;
% free -> free
IsProduced = no % Not produced here.
),
Constraint = mc_atomic(equiv_bool(MCVar, IsProduced)).
%-----------------------------------------------------------------------------%
% add_goal_nonlocals_to_conjunct_production_maps(VarMap, PredId,
% Nonlocals, Goal, !ConjConstraintsInfo)
%
% Assumes Goal is a conjunct of a conjunction with nonlocal
% variables Nonlocals, in predicate PredId. Adds the nonlocal
% variables from Goal to ConjConstraintsInfo according to whether
% they are local or nonlocal to the conjunction as a whole. The
% constraint variable propositions that the nonlocals of Goal are
% produced at Goal are associated with their corresponding program
% variable in ConjConstraintsInfo. These constraint variables are
% found in VarMap.
%
:- pred add_goal_nonlocals_to_conjunct_production_maps(
mc_var_map::in, pred_id::in, nonlocals::in, hlds_goal::in,
conj_constraints_info::in, conj_constraints_info::out) is det.
add_goal_nonlocals_to_conjunct_production_maps(VarMap, PredId, Nonlocals,
hlds_goal(_SubGoalExpr, SubGoalInfo), !ConjConstraintsInfo) :-
SubGoalNonlocals = goal_info_get_nonlocals(SubGoalInfo),
SubGoalId = goal_info_get_goal_id(SubGoalInfo),
% These are variables nonlocal to the conjunction that
% appear in this particular conjunct.
Nonlocal = set_of_var.intersect(SubGoalNonlocals, Nonlocals),
% These are variables local to the conjunction that
% are non-local to this particular conjunct.
Local = set_of_var.difference(SubGoalNonlocals, Nonlocals),
some [!LocalsMap, !NonlocalsMap] (
!:LocalsMap = !.ConjConstraintsInfo ^ locals_positions,
!:NonlocalsMap = !.ConjConstraintsInfo ^ nonlocals_positions,
set_of_var.fold(
add_variable_to_conjunct_production_map(VarMap, PredId, SubGoalId),
Local, !LocalsMap),
set_of_var.fold(
add_variable_to_conjunct_production_map(VarMap, PredId, SubGoalId),
Nonlocal, !NonlocalsMap),
!ConjConstraintsInfo ^ locals_positions := !.LocalsMap,
!ConjConstraintsInfo ^ nonlocals_positions := !.NonlocalsMap
).
% add_variable_to_conjunct_production_map(VarMap, PredId, GoalId
% ProgVar, !ConjunctProductionMap)
%
% A subsection of add_goal_nonlocals_to_conjunct_production_maps,
% puts into the ConjunctProductionMap the key ProgVar with the
% constraint variable found in VarMap, that represents the
% proposition that ProgVar is bound at GoalId in predicate PredId.
%
:- pred add_variable_to_conjunct_production_map(
mc_var_map::in, pred_id::in, goal_id::in, prog_var::in,
conjunct_production_map::in, conjunct_production_map::out) is det.
add_variable_to_conjunct_production_map(VarMap, PredId, GoalId, ProgVar,
!ConjunctProductionMap) :-
MCVar = lookup_prog_var_at_path(VarMap, PredId, GoalId, ProgVar),
multi_map.set(ProgVar, MCVar, !ConjunctProductionMap).
% add_nonlocal_var_conj_constraints(ProgVarset, PredId, Context, GoalId,
% ProgVar, ProgVarAtConjuncts, !VarInfo, !Constraints)
%
% Adds conjunction constraints concerning ProgVar, a variable
% in predicate PredID, to Constraints. The conjunction should
% exist at the position indicated by GoalId in the predicate,
% and have context Context in the source file. ProgVarset should
% contain a string name for ProgVar.
% ProgVar should be a variable in the nonlocal set of the conjunction,
% and as such may be produced outside it. Therefore the constraints
% will be that ProgVar is produced at "at most one" of the conjuncts.
% ProgVarAtConjuncts should contain the constraint variable
% propositions that ProgVar is produced at a conjunct for each
% conjunct in which it appears.
%
:- pred add_nonlocal_var_conj_constraints(prog_varset::in, pred_id::in,
prog_context::in, goal_id::in, prog_var::in, list(mc_var)::in,
mc_var_info::in, mc_var_info::out, mode_constraints::in,
mode_constraints::out) is det.
add_nonlocal_var_conj_constraints(ProgVarset, PredId, Context, GoalId,
ProgVar, ProgVarAtConjuncts, !VarInfo, !Constraints) :-
equiv_disj(!.VarInfo ^ mc_varset, Context,
ProgVarAtGoalId, ProgVarAtConjuncts, !Constraints),
at_most_one(!.VarInfo ^ mc_varset, Context,
ProgVarAtConjuncts, !Constraints),
prog_var_at_path(ProgVarset, PredId, ProgVar, GoalId, ProgVarAtGoalId,
!VarInfo).
% add_local_var_conj_constraints(MCVarSet, Context, ProgVar, MCVars,
% !Constraints)
%
% Adds conjunct constraints relating to ProgVar, to Constraints.
% ProgVar should be local to the conjunction, and MCVars should
% represent propositions that ProgVar is produced at conjuncts, for
% each conjunct that ProgVar is nonlocal to. Context should be
% the location in the source file that the conjunction in question
% came from.
%
% The constraints are: the program variable must be produced at
% exactly one conjunct.
%
:- pred add_local_var_conj_constraints(mc_varset::in, prog_context::in,
prog_var::in, list(mc_var)::in,
mode_constraints::in, mode_constraints::out) is det.
add_local_var_conj_constraints(MCVarSet, Context, _ProgVar, ProgVarAtConjuncts,
!Constraints) :-
exactly_one(MCVarSet, Context, ProgVarAtConjuncts, !Constraints).
% Initialises the conj_constraints_info_structure.
%
:- func conj_constraints_info_init = conj_constraints_info.
conj_constraints_info_init =
conj_constraints_info(multi_map.init, multi_map.init).
%-----------------------------------------------------------------------------%
% prog_var_at_path(ProgVarset, PredId, ProgVar, GoalId, MCVar, !VarInfo)
%
% MCVar is the constraint variable corresponding to the proposition
% that ProgVar is created at GoalId in predicate PredId,
% it is created if necessary and stored in VarInfo (in which
% case we need a string name of ProgVar in ProgVarset).
%
:- pred prog_var_at_path(prog_varset::in, pred_id::in, prog_var::in,
goal_id::in, mc_var::out, mc_var_info::in, mc_var_info::out) is det.
prog_var_at_path(ProgVarset, PredId, ProgVar, GoalId, MCVar, !VarInfo) :-
!.VarInfo = mc_var_info(MCVarset0, VarMap0),
ensure_prog_var_at_path(ProgVarset, PredId, GoalId, ProgVar,
MCVarset0, MCVarset, VarMap0, VarMap),
MCVar = lookup_prog_var_at_path(VarMap, PredId, GoalId, ProgVar),
!:VarInfo = mc_var_info(MCVarset, VarMap).
% prog_var_at_paths(ProgVarset, PredId, ProgVar, GoalIds, MCVars,
% !VarInfo)
%
% MCVars are the constraint variables corresponding to propositions
% that ProgVar is created at GoalId in predicate PredId, for each
% GoalId in GoalIds. The constraint variables are created if necessary
% and stored in VarInfo (in which case we need a string name of ProgVar
% in ProgVarset).
%
:- pred prog_var_at_paths(prog_varset::in, pred_id::in, prog_var::in,
list(goal_id)::in, list(mc_var)::out, mc_var_info::in,
mc_var_info::out) is det.
prog_var_at_paths(ProgVarset, PredID, ProgVar, GoalIds, MCVars, !VarInfo) :-
list.map_foldl(prog_var_at_path(ProgVarset, PredID, ProgVar),
GoalIds, MCVars, !VarInfo).
% prog_vars_at_path(ProgVarset, PredId, ProgVars, GoalId, MCVars,
% !VarInfo)
%
% MCVars are the constraint variables corresponding to propositions
% that ProgVar is created at GoalId in predicate PredId, for each
% ProgVar in ProgVars. They are created if necessary and stored in
% VarInfo (in which case we will need a string name for ProgVar from
% ProgVarset).
%
:- pred prog_vars_at_path(prog_varset::in, pred_id::in, list(prog_var)::in,
goal_id::in, list(mc_var)::out, mc_var_info::in, mc_var_info::out)
is det.
prog_vars_at_path(ProgVarset, PredId, ProgVars, GoalId, MCVars, !VarInfo) :-
list.map_foldl(
(pred(ProgVar::in, MCVar::out, !.VarInfo::in, !:VarInfo::out) is det :-
prog_var_at_path(ProgVarset, PredId, ProgVar, GoalId, MCVar,
!VarInfo)
), ProgVars, MCVars, !VarInfo).
% ensure_prog_var_at_path(ProgVarset, PredId, GoalId, ProgVar,
% !Varset, !VarMap)
%
% Adds, if necessary, to Varset and VarMap the constraint variable
% and its abstract representation that represents the proposition
% that ProgVar is produced at GoalId in PredId (in some mode of execution).
% ProgVarset should contain a string name for ProgVar.
%
:- pred ensure_prog_var_at_path(prog_varset::in, pred_id::in, goal_id::in,
prog_var::in, mc_varset::in, mc_varset::out,
mc_var_map::in, mc_var_map::out) is det.
ensure_prog_var_at_path(ProgVarset, PredId, GoalId, ProgVar,
!Varset, !VarMap) :-
RepVar = (ProgVar `in` PredId) `at` GoalId,
( bimap.search(!.VarMap, RepVar, _) ->
true
;
MCVarName = rep_var_to_string(ProgVarset, RepVar),
varset.new_named_var(MCVarName, NewMCVar, !Varset),
bimap.det_insert(RepVar, NewMCVar, !VarMap)
).
% lookup_prog_var_at_path(VarMap, PredId, GoalId, ProgVar) = ConstraintVar:
%
% Consults the VarMap to get the constraint variable ConstraintVar
% that represents the proposition that ProgVar is produced at GoalId
% in predicate PredId. The lookup function will report an error
% if the key (ProgVar `in` PredId) `at` GoalId does not exist in the map.
%
:- func lookup_prog_var_at_path(mc_var_map, pred_id, goal_id, prog_var)
= mc_var.
lookup_prog_var_at_path(VarMap, PredId, GoalId, ProgVar) =
bimap.lookup(VarMap, ((ProgVar `in` PredId) `at` GoalId)).
% Retrieves the mode constraint var as per prog_var_at_path, but
% attaches it to the list supplied rather than return it directly.
%
:- pred cons_prog_var_at_path(mc_var_map::in, pred_id::in, goal_id::in,
prog_var::in, list(mc_var)::in, list(mc_var)::out) is det.
cons_prog_var_at_path(VarMap, PredId, GoalId, ProgVar, MCVars0, MCVars) :-
MCVar = lookup_prog_var_at_path(VarMap, PredId, GoalId, ProgVar),
MCVars = [MCVar | MCVars0].
% nonlocals_at_path_and_subpaths(ProgVarset, GoalId, SubIds,
% Nonlocals, NonlocalsAtId, NonlocalsAtSubIds, !VarInfo)
%
% consults the VarInfo to find constraint variables associated with
% each of the program variables in the Nonlocals set for a GoalId,
% e.g. a conjunction and its SubIds (i.e. the individual conjuncts),
% although it doesn't check that the SubIds are indeed subpaths of
% GoalId. Nonlocals are converted to a sorted list so the Nth entry
% of NonlocalsAtId and the Nth entry of NonlocalsAtSubIds are
% respectively the constraint variable at the goal and a list of the
% %constraint variables for the subgoals, for the same program variable.
%
:- pred nonlocals_at_path_and_subpaths(prog_varset::in, pred_id::in,
goal_id::in, list(goal_id)::in, nonlocals::in, list(mc_var)::out,
list(list(mc_var))::out, mc_var_info::in, mc_var_info::out) is det.
nonlocals_at_path_and_subpaths(ProgVarset, PredId, GoalId, SubIds,
Nonlocals, NonlocalsAtId, NonlocalsAtSubIds, !VarInfo) :-
prog_vars_at_path(ProgVarset, PredId, NonlocalsList, GoalId,
NonlocalsAtId, !VarInfo),
list.map_foldl(
(pred(Nl::in, NlAtSubIds::out, !.VInfo::in, !:VInfo::out) is det :-
prog_var_at_paths(ProgVarset, PredId, Nl, SubIds, NlAtSubIds,
!VInfo)
), NonlocalsList, NonlocalsAtSubIds, !VarInfo),
NonlocalsList = set_of_var.to_sorted_list(Nonlocals).
%----------------------------------------------------------------------------%
var_info_init = mc_var_info(varset.init, bimap.init).
rep_var_to_string(ProgVarset, (ProgVar `in` _) `at` GoalId) = RepString :-
GoalId = goal_id(GoalIdNum),
GoalIdString = string.int_to_string(GoalIdNum),
varset.lookup_name(ProgVarset, ProgVar, ProgVarString),
RepString = ProgVarString ++ "." ++ GoalIdString.
%-----------------------------------------------------------------------------%
:- end_module check_hlds.build_mode_constraints.
%-----------------------------------------------------------------------------%