Files
mercury/compiler/build_mode_constraints.m
Zoltan Somogyi b2012c0c0e Rename the types 'type', 'inst' and 'mode' to 'mer_type', 'mer_inst'
Estimated hours taken: 8
Branches: main

compiler/*.m:
	Rename the types 'type', 'inst' and 'mode' to 'mer_type', 'mer_inst'
	and 'mer_mode'. This is to avoid the need to parenthesize these type
	names in some contexts, and to prepare for the possibility of a parser
	that considers those words to be reserved words.

	Rename some other uses of those names (e.g. as item types in
	recompilation.m).

	Delete some redundant synonyms (prog_type, mercury_type) for mer_type.

	Change some type names (e.g. mlds__type) and predicate names (e.g.
	deforest__goal) to make them unique even without module qualification.

	Rename the function symbols (e.g. pure, &) that need to be renamed
	to avoid the need to parenthesize them. Make their replacement names
	more expressive.

	Convert some more modules to four space indentation.

	Avoid excessively long lines, such as those resulting from the
	automatic substitution of 'mer_type' for 'type'.
2005-10-24 04:14:34 +00:00

889 lines
36 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2004-2005 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: 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.hlds_goal.
:- import_module hlds.hlds_pred.
:- import_module hlds.hlds_module.
% :- import_module hlds.inst_graph.
% % Needed if converting for partial instantiation.
:- import_module parse_tree__prog_data.
:- import_module bimap.
:- import_module list.
:- import_module set.
%-----------------------------------------------------------------------------%
% XXX Change to include more information? This will just be a
% list of constraints representing the conjunction of them. When
% a solver is written, a more elaborate data structure with
% update functions will no doubt be needed - at that time
% constraints should be passed as state variables.
%
% This represents the conjunction of the constraints it
% contains.
%
:- type mode_constraints == constraint_formulae.
% A map between the constraint variables (mc_var) and what they
% represent ie that some program variable is produced at some
% goal path (for 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(prog_var).
% In order to uniquely distinguish 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.
%
% It represents the constraint variable that determines whether
% the program variable specified is produced at the goal path
% specified.
%
:- type mc_rep_var ---> mc_prog_var `at` goal_path.
% Provides a nice way to display constraint variables - just
% convert to the relevant mc_rep_var using the mc_var_map and
% then convert to a string using this. It comes out in format
% "ProgVarName.GoalPath"
%
:- func rep_var_to_string(prog_varset, mc_rep_var) = (string).
% For each head variable of each predicate in the supplied SCC,
% this predicate adds to the varset 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. At the same
% time it records in the mc_var_map the position and program
% variable to which the new constraint variable corresponds.
%
:- pred add_mc_vars_for_scc_heads(module_info::in, list(pred_id)::in,
mc_varset::in, mc_varset::out, mc_var_map::in, mc_var_map::out) is det.
% Makes sure that the necessary constraint variables exist to
% create goal constraints for all goal types except predicate
% calls.
%
% At this stage, constraint variables are created at a goal for
% each variable in the nonlocal set of 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.
%
% Note that in order to produce constraints for a goal,
% constraint variables for the head variables of called
% predicates with no declared modes are needed as well as these
% goal constraint variables, so before constraints are built for
% any predicate in an SCC add_mc_vars_for_scc_heads should be
% called for that whole SCC.
%
:- pred add_mc_vars_for_goal(pred_id::in, prog_varset::in, hlds_goal::in,
mc_varset::in, mc_varset::out, mc_var_map::in, mc_var_map::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. ie
% disj([ConstraintsForMode1, ..., ConstraintsForModen])
%
% 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. Mode declarations for predicates with no declared
% modes should not be handled by this - they must be handled
% elsewhere.
%
% 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 cannot be produced by a call to this predicate;
% 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 mode_decls_constraints(module_info::in, mc_var_map::in,
pred_id::in, list(list(mer_mode))::in, list(args)::in,
mode_constraints::out) is det.
% In the event that type_info arguments have been added to a
% predicate call's arguments/headvars, but the modes have not
% been edited to reflect this, extra in modes need to be added
% to the front of the mode declaration to account for this.
%
% XXX This predicate shouldn't be needed, but if you're getting
% map_corresponding errors for the use of lists of different
% lengths it would be worthwhile modifying any calls to
% mode_decl predicates with this so that the headvars list and
% mode declarations are the same length.
% Unfortunately this predicate relies the type_info arguments
% being added to the front of the arg list, and assumes that
% they are all specifically of mode in as given by function
% prog_tree.prog_mode.in_mode
:- pred add_sufficient_in_modes_for_type_info_args(args::in,
list(mer_mode)::in, list(mer_mode)::out) is det.
% goal_expr_constraints generates the constraints that apply to
% a given goal_expr at a given goal path.
%
:- pred goal_expr_constraints( module_info::in, mc_var_map::in,
pred_id::in, hlds_goal_expr::in, goal_path::in, nonlocals::in,
mode_constraints::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module check_hlds.goal_path.
:- import_module check_hlds.mode_util.
:- import_module check_hlds.inst_match.
:- import_module hlds.hlds_data.
:- import_module hlds.passes_aux.
:- import_module parse_tree.error_util.
:- import_module parse_tree.prog_mode.
:- import_module transform_hlds.dependency_graph.
:- import_module bool.
:- import_module int.
:- import_module map.
:- import_module multi_map.
:- import_module sparse_bitset.
:- import_module std_util.
:- import_module string.
:- import_module svbimap.
:- import_module svmulti_map.
:- import_module svvarset.
:- import_module term.
:- import_module varset.
%-----------------------------------------------------------------------------%
add_mc_vars_for_scc_heads(_ModuleInfo, [], !Varset, !VarMap).
add_mc_vars_for_scc_heads(ModuleInfo, [PredId | PredIds], !Varset, !VarMap) :-
module_info_pred_info(ModuleInfo, PredId, PredInfo),
pred_info_clauses_info(PredInfo, ClausesInfo),
clauses_info_headvars(ClausesInfo, Headvars),
clauses_info_varset(ClausesInfo, ProgVarset),
list.foldl2(add_mc_var_for_pred_head(PredId, ProgVarset), Headvars,
!Varset, !VarMap),
add_mc_vars_for_scc_heads(ModuleInfo, PredIds, !Varset, !VarMap).
% XXX This potentially needs to be performed for the
% headvars in the proc_infos associated with a predicate
% as well, since the mode declaration constraints refer
% to them, however at this point in time they should be
% the same as the headvars in the clauses_info
% Makes sure a constraint variable exists to say that the
% supplied program variable is produced by the predicate in
% which it exists.
%
% Just checks that the constraint variable in question doesn't
% already exist, and then gets a new one from the varset and
% adds it to the varmap if it doesn't.
%
:- pred add_mc_var_for_pred_head(pred_id::in, prog_varset::in, prog_var::in,
mc_varset::in, mc_varset::out, mc_var_map::in, mc_var_map::out) is det.
add_mc_var_for_pred_head(PredId, ProgVarset, HeadVar, !Varset, !VarMap) :-
RepVar = (HeadVar `in` PredId) `at` [],
( bimap.search(!.VarMap, RepVar, _MCVar) ->
true
;
svvarset.new_named_var(rep_var_to_string(ProgVarset, RepVar),
NewMCvar, !Varset),
svbimap.det_insert(RepVar, NewMCvar, !VarMap)
).
add_mc_vars_for_goal(PredId, ProgVarset, GoalExpr - GoalInfo,
!Varset, !VarMap) :-
goal_info_get_nonlocals(GoalInfo, Nonlocals),
goal_info_get_goal_path(GoalInfo, GoalPath),
{!:Varset, !:VarMap} = set.fold(
(func(Nonlocal, {Vset0, Vmap0}) = {Vset, Vmap} :-
RepVar = (Nonlocal `in` PredId) `at` GoalPath,
( bimap.search(Vmap0, RepVar, _) ->
Vset = Vset0,
Vmap = Vmap0
;
varset.new_named_var(Vset0,
rep_var_to_string(ProgVarset, RepVar), NewMCvar, Vset),
bimap.det_insert(Vmap0, RepVar, NewMCvar, Vmap)
)
),
Nonlocals,
{!.Varset, !.VarMap}
),
% Switch on GoalExpr for recursion
(
GoalExpr = conj(Goals),
list.foldl2(add_mc_vars_for_goal(PredId, ProgVarset), Goals,
!Varset, !VarMap)
;
GoalExpr = call(_, _, _, _, _, _)
;
GoalExpr = generic_call(_, _, _, _)
;
GoalExpr = switch(_, _, Cases),
Goals = list.map(func(case(_, Goal)) = Goal, Cases),
list.foldl2(add_mc_vars_for_goal(PredId, ProgVarset), Goals,
!Varset, !VarMap)
;
GoalExpr = unify(_, _, _, _, _)
;
GoalExpr = disj(Goals),
list.foldl2(add_mc_vars_for_goal(PredId, ProgVarset), Goals,
!Varset, !VarMap)
;
GoalExpr = not(Goal),
add_mc_vars_for_goal(PredId, ProgVarset, Goal, !Varset, !VarMap)
; GoalExpr = scope(_, Goal),
add_mc_vars_for_goal(PredId, ProgVarset, Goal, !Varset, !VarMap)
;
GoalExpr = if_then_else(_, Cond, Then, Else),
Goals = [Cond, Then, Else],
list.foldl2(add_mc_vars_for_goal(PredId, ProgVarset), Goals,
!Varset, !VarMap)
;
GoalExpr = foreign_proc(_, _, _, _, _, _)
;
GoalExpr = par_conj(_Goals)
;
GoalExpr = shorthand(_ShorthandGoalExpr)
).
rep_var_to_string(ProgVarset, (ProgVar `in` _) `at` GoalPath) = RepString :-
goal_path_to_string(GoalPath, GoalPathString),
varset.lookup_name(ProgVarset, ProgVar, ProgVarString),
( GoalPathString = "" ->
RepString = ProgVarString
;
RepString = ProgVarString ++ "." ++ GoalPathString
).
%-----------------------------------------------------------------------------%
% Goal_constraints gives the mode constraints for the supplied hlds_goal.
%
:- pred goal_constraints(module_info::in, mc_var_map::in, pred_id::in,
hlds_goal::in, mode_constraints::out) is det.
goal_constraints(ModuleInfo, VarMap, PredId, GoalExpr - GoalInfo,
Constraints) :-
goal_info_get_nonlocals(GoalInfo, Nonlocals),
goal_info_get_goal_path(GoalInfo, GoalPath),
goal_expr_constraints(ModuleInfo, VarMap, PredId, GoalExpr, GoalPath,
Nonlocals, Constraints).
goal_expr_constraints(ModuleInfo, VarMap, PredId,
conj(Goals), GoalPath, Nonlocals, Constraints) :-
list.map(
goal_constraints(ModuleInfo, VarMap, PredId),
Goals, ConjunctConstraints
),
Constraints0 = list.condense(ConjunctConstraints),
list.foldl(
fold_local_var_into_conj_constraints(VarMap, LocalsPositions),
multi_map.keys(LocalsPositions),
Constraints0, Constraints1
),
list.foldl(
fold_nonlocal_var_into_conj_constraints(VarMap, PredId,
NonlocalsPositions, GoalPath),
multi_map.keys(NonlocalsPositions),
% Nonlocal variables that are nonlocals to subgoals.
Constraints1, Constraints
),
EmptyMultiMap = multi_map.init,
list.foldl2(fold_goal_into_var_position_maps(VarMap, PredId, Nonlocals),
Goals, EmptyMultiMap, LocalsPositions,
% A map from each local variable to
% its corresponding constraint
% variables for the paths at each of
% the conjuncts it is non-local to,
% if such conjuncts exist. (If a
% variable is local to one conjunct we
% need not deal with it here).
EmptyMultiMap, NonlocalsPositions
% A map from each non-local variable
% to its corresponding constraint
% variables at the paths of each of the
% conjuncts it is non-local to. Note
% that all non-local variables should
% appear in this map, since if a
% variable is in the non-local set of a
% conjunction it ought to be in the
% non-local set of at least one of the
% conjuncts.
).
goal_expr_constraints(ModuleInfo, VarMap, PredId,
call(CalledPred, _ProcID, Args, _Builtin, _UnifyContext, _Name),
GoalPath, _Nonlocals, Constraints) :-
module_info_pred_info(ModuleInfo, CalledPred, PredInfo),
pred_info_procedures(PredInfo, ProcTable),
map.values(ProcTable, ProcInfos),
list.filter_map(
(pred(PInfo::in, MDecl::out) is semidet :-
proc_info_maybe_declared_argmodes(PInfo, yes(_)),
proc_info_argmodes(PInfo, MDecl)
),
ProcInfos,
ArgModeDecls
),
(
% No modes declared, must be in the same SCC as
% the calling predicate.
ArgModeDecls = [],
% Get the head variables of the called pred.
pred_info_clauses_info(PredInfo, ClausesInfo),
clauses_info_headvars(ClausesInfo, HeadVars),
call_headvar_constraints( VarMap, GoalPath, PredId, Args, CalledPred,
HeadVars, Constraints)
;
% At least one declared mode
ArgModeDecls = [_| _],
% list.map(
% add_sufficient_in_modes_for_type_info_args(Args),
% ArgModeDecls,
% FullArgModeDecls
% ), % XXX type_info args should be at the start and
% % should be 'in' so that is what this predicate
% % adds however, I am not happy with it.
call_mode_decls_constraints(ModuleInfo, VarMap, PredId, ArgModeDecls,
GoalPath, Args, Constraints)
).
goal_expr_constraints(_ModuleInfo, _VarMap, _PredId,
generic_call(_, _, _, _), _GoalPath, _Nonlocals, _Constraints) :-
% XXX Need to do something here.
sorry(this_file, "generic_call NYI.").
goal_expr_constraints(_ModuleInfo, _VarMap, _PredId,
switch(_, _, _), _GoalPath, _Nonlocals, _Constraints) :-
% XXX Need to do something here.
sorry(this_file, "switch NYI.").
goal_expr_constraints(_ModuleInfo, VarMap, PredId,
unify(LHSvar, RHS, _Mode, _Kind, _Context),
GoalPath, _Nonlocals, Constraints) :-
(
RHS = var(RHSvar),
% Goal: LHSvar = RHSvar
% At most one of the left and right hand sides of a unification
% is produced at the unification.
Constraints = [
atomic_constraint(at_most_one([
prog_var_at_path(VarMap, PredId, GoalPath, LHSvar),
prog_var_at_path(VarMap, PredId, GoalPath, RHSvar)
]))
]
;
RHS = functor(_Functor, _IsExistConstr, Args),
LHSproducedHere = prog_var_at_path(
VarMap, PredId, GoalPath, LHSvar
),
ArgsProducedHere =
list.map(prog_var_at_path(VarMap, PredId, GoalPath), Args),
(
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.
Constraints = [
atomic_constraint(equivalent(ArgsProducedHere)),
atomic_constraint(
at_most_one([LHSproducedHere, OneArgProducedHere]))
]
;
ArgsProducedHere = [OneArgProducedHere],
% Goal: LHSvar = functor(Arg)
% At most one side of the unification is produced.
Constraints = [
atomic_constraint(
at_most_one([LHSproducedHere, OneArgProducedHere]))
]
;
ArgsProducedHere = [],
% Goal: LHSvar = functor
% In this case, LHSvar need not be produced
% - it could be a test, so no constraints.
Constraints = []
)
;
RHS = lambda_goal(_, _, _, _, _, _, _, _, _),
sorry(this_file, "unify with lambda goal NYI")
).
goal_expr_constraints(ModuleInfo, VarMap, PredId,
disj(Goals), GoalPath, Nonlocals, Constraints) :-
nonlocals_at_path_and_subpaths(
VarMap, PredId, GoalPath, DisjunctGoalPaths,
Nonlocals, NonlocalsHere, NonlocalsAtDisjuncts
),
list.map(
pred(_GExpr - GInfo::in, GPath::out) is det :-
goal_info_get_goal_path(GInfo, GPath),
Goals,
DisjunctGoalPaths
),
list.map(goal_constraints(ModuleInfo, VarMap, PredId),
Goals, DisjunctConstraints),
Constraints = list.condense([
list.map_corresponding(
func(X, Ys) = atomic_constraint(equivalent([X | Ys])),
% A variable bound at any disjunct is bound for the disjunct
% as a whole. If a variable can be bound at one conjunct
% it must be able to be bound at any.
NonlocalsHere, NonlocalsAtDisjuncts
) |
DisjunctConstraints
]).
goal_expr_constraints(ModuleInfo, VarMap, PredId,
not(Goal), GoalPath, Nonlocals, Constraints) :-
Goal = _ - NegatedGoalInfo,
goal_info_get_goal_path(NegatedGoalInfo, NegatedGoalPath),
NonlocalsConstraintVars = set.fold(
func(Nonlocal, MCVars) = [
prog_var_at_path(VarMap, PredId, GoalPath, Nonlocal),
prog_var_at_path(VarMap, PredId, NegatedGoalPath, Nonlocal)
|
MCVars
],
Nonlocals,
[]
),
goal_constraints(
ModuleInfo, VarMap, PredId, Goal, NegatedGoalConstraints
),
Constraints = list.foldl(
func(MCVar, Cnstrnts) = [
atomic_constraint(equiv_bool(MCVar, no))|
% The variables non-local to the negation are not to be
% produced at the negation or any deeper.
Cnstrnts
],
NonlocalsConstraintVars,
NegatedGoalConstraints
).
goal_expr_constraints(ModuleInfo, VarMap, PredId,
scope(_Reason, Goal),
GoalPath, Nonlocals, Constraints) :-
Goal = _ - SomeGoalInfo,
goal_info_get_goal_path(SomeGoalInfo, SomeGoalPath),
Constraints = set.fold(
func(NL, NLConstraints) = [
% If a program variable is produced by the sub-goal of the some
% statement, it is produced at the main goal as well.
atomic_constraint(equivalent([
prog_var_at_path(VarMap, PredId, GoalPath, NL),
prog_var_at_path(VarMap, PredId, SomeGoalPath, NL)
])) | NLConstraints],
Nonlocals, SomeGoalConstraints),
goal_constraints(ModuleInfo, VarMap, PredId, Goal, SomeGoalConstraints).
goal_expr_constraints(ModuleInfo, VarMap, PredId,
if_then_else(ExistVars, If, Then, Else),
GoalPath, Nonlocals, Constraints) :-
If = _ - IfInfo, Then = _ - ThenInfo, Else = _ - ElseInfo,
goal_info_get_goal_path(IfInfo, CondPath),
goal_info_get_goal_path(ThenInfo, ThenPath),
goal_info_get_goal_path(ElseInfo, ElsePath),
NonlocalsHere = list.map(prog_var_at_path(VarMap, PredId, GoalPath),
NonlocalsList),
NonlocalsAtCond = list.map(prog_var_at_path(VarMap, PredId, CondPath),
NonlocalsList),
NonlocalsAtThen = list.map(prog_var_at_path(VarMap, PredId, ThenPath),
NonlocalsList),
NonlocalsAtElse = list.map(prog_var_at_path(VarMap, PredId, ElsePath),
NonlocalsList),
NonlocalsList = set.to_sorted_list(Nonlocals),
% The existentially quantified variables shared between the condition
% and the then-part have special constraints.
LocalAndSharedAtCond = list.map(prog_var_at_path(VarMap, PredId, CondPath),
ExistVars),
LocalAndSharedAtThen = list.map(prog_var_at_path(VarMap, PredId, ThenPath),
ExistVars),
goal_constraints(ModuleInfo, VarMap, PredId, If, IfConstraints),
goal_constraints(ModuleInfo, VarMap, PredId, Then, ThenConstraints),
goal_constraints(ModuleInfo, VarMap, PredId, Else, ElseConstraints),
Constraints = list.condense([
list.map_corresponding3(
func(NLHere, NLAtThen, NLAtElse) =
% If a variable is to be produced at this path,
% the then and else parts must be able to produce it.
atomic_constraint(equivalent([NLHere, NLAtThen, NLAtElse])),
NonlocalsHere, NonlocalsAtThen, NonlocalsAtElse),
% No nonlocal is produced in the condition.
list.map(func(Cond) = atomic_constraint(equiv_bool(Cond, no)),
NonlocalsAtCond),
% XXX Do we want this, or do we just constrain 'Cond true and 'Then
% false? Maybe a local variable shared between the condition and
% then-part should always be bound in the condition, but I'm not sure
% about the possibility of checking the variable's type in the Cond
% and then binding it in the Then...
list.map_corresponding(
(func(LocalAtCond, LocalAtThen) =
atomic_constraint(exactly_one([LocalAtCond, LocalAtThen]))),
LocalAndSharedAtCond, LocalAndSharedAtThen),
IfConstraints, ThenConstraints, ElseConstraints]).
goal_expr_constraints(ModuleInfo, VarMap, PredId,
foreign_proc(_, CalledPred, ProcID, ForeignArgs, _, _),
GoalPath, _Nonlocals, Constraints) :-
CallArgs = list.map(foreign_arg_var, ForeignArgs),
module_info_pred_proc_info(ModuleInfo, CalledPred, ProcID, _, ProcInfo),
( proc_info_maybe_declared_argmodes(ProcInfo, yes(_OrigDecl)) ->
proc_info_argmodes(ProcInfo, Decl),
% add_sufficient_in_modes_for_type_info_args(
% CallArgs,
% Decl,
% FullDecl
% ), % XXX type_info args should be at the start and
% % should be 'in' so that is what this predicate
% % adds however, I am not happy with it.
% This pred should strip the disj(conj()) for the single declaration.
call_mode_decls_constraints(ModuleInfo, VarMap, PredId, [Decl],
GoalPath, CallArgs, Constraints)
;
unexpected(this_file, "no mode declaration for foreign proc")
).
goal_expr_constraints(_ModuleInfo, _VarMap, _PredId,
par_conj(_Goals), _GoalPath, _Nonlocals, _Constraints) :-
% XXX What to do here?
sorry(this_file, "NYI par_conj").
goal_expr_constraints(_ModuleInfo, _VarMap, _PredId,
shorthand(_ShorthandGoalExpr), _GoalPath, _Nonlocals, _Constraints) :-
% Shorthand goals not exist at this point in compilation.
unexpected(this_file, "shorthand goal").
%-----------------------------------------------------------------------------%
% prog_var_at_path(VarMap, PredId, GoalPath, ProgVar) = ConstraintVar:
%
% consults the map to get the constraint variable ConstraintVar that says
% that ProgVar is produced at GoalPath. The lookup function will report
% an error if the key (ProgVar `in` PredId) `at` GoalPath does not exist
% in the map.
%
:- func prog_var_at_path(mc_var_map, pred_id, goal_path, prog_var) = (mc_var).
prog_var_at_path(VarMap, PredId, GoalPath, ProgVar) =
bimap.lookup(VarMap, ((ProgVar `in` PredId) `at` GoalPath)).
% prog_var_at_paths(VarMap, GoalPaths, ProgVar) = ConstraintVars
% consults the map to form a list of the constraint variables
% that say that ProgVar is produced at each of the paths in
% GoalPaths respectively. The lookup function will report an
% error if the key (ProgVar `in` PredId) `at` GoalPath does not
% exist in the map for any of the 'GoalPath's in GoalPaths.
%
:- func prog_var_at_paths(mc_var_map, pred_id, list(goal_path), prog_var) =
list(mc_var).
prog_var_at_paths(VarMap, PredId, GoalPaths, ProgVar) =
list.map(
func(GoalPath) = bimap.lookup(VarMap,
(ProgVar `in` PredId) `at` GoalPath),
GoalPaths
).
% nonlocals_at_path_and_subpaths(VarMap, GoalPath,
% SubPaths, Nonlocals, NonlocalsAtPath, NonlocalsAtSubPaths)
% consults the VarMap to find constraint variables associated
% with each of the program variables in the Nonlocals set for a
% GoalPath eg a conjunction and its SubPaths (ie the individual
% conjuncts), although it doesn't check that the SubPaths are
% indeed subpaths of GoalPath. Nonlocals are converted to a
% sorted set, so the Nth entry of NonlocalsAtPath and the Nth
% entry of NonlocalsAtSubPaths 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(
mc_var_map::in, pred_id::in, goal_path::in, list(goal_path)::in,
nonlocals::in, list(mc_var)::out, list(list(mc_var))::out) is det.
nonlocals_at_path_and_subpaths(VarMap, PredId, GoalPath, SubPaths, Nonlocals,
NonlocalsAtPath, NonlocalsAtSubPaths) :-
NonlocalsAtPath = list.map(prog_var_at_path(VarMap, PredId, GoalPath),
NonlocalsList),
NonlocalsAtSubPaths = list.map(prog_var_at_paths(VarMap, PredId, SubPaths),
NonlocalsList),
NonlocalsList = set.to_sorted_list(Nonlocals).
%----------------------------------------------------------------------------%
mode_decls_constraints(
ModuleInfo, VarMap, PredId, Decls, HeadVarsList, Constraints) :-
ConstraintsList = list.map_corresponding(
mode_decl_constraints(ModuleInfo),
list.map(list.map(prog_var_at_path(VarMap, PredId, [])), HeadVarsList),
Decls
),
Constraints0 = list.condense(ConstraintsList),
( Constraints0 = [conj(OneModeOnlyConstraints)] ->
Constraints = OneModeOnlyConstraints
;
Constraints = [disj(Constraints0)]
).
% call_mode_decls_constraints(ModuleInfo, VarMap, CallingPred,
% Decls, GoalPath, CallArgs, Constraints)
%
% Returns
% disj([ConstraintsForMode1, ..., ConstraintsForModen])
%
% 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 predicates with no declared
% modes should not be handled by this - they must be handled
% elsewhere.
%
% 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 call_mode_decls_constraints(module_info::in,
mc_var_map::in, pred_id::in, list(list(mer_mode))::in,
goal_path::in, args::in, mode_constraints::out) is det.
call_mode_decls_constraints(ModuleInfo, VarMap, CallingPred, Decls, GoalPath,
CallArgs, Constraints) :-
CallArgsHere = list.map(prog_var_at_path(VarMap, CallingPred, GoalPath),
CallArgs),
Constraints0 =
list.condense(list.map(mode_decl_constraints(ModuleInfo, CallArgsHere),
Decls)),
( Constraints0 = [conj(OneModeOnlyConstraints)] ->
Constraints = OneModeOnlyConstraints
;
Constraints = [disj(Constraints0)]
).
% mode_decl_constraints(ModuleInfo, ConstraintVars, ArgModes)
% looks at each mode to see if its variable is produced, and
% creates constraints for the corresponding constraint variables
% accordingly.
%
:- func mode_decl_constraints(module_info, list(mc_var), list(mer_mode)) =
mode_constraints.
mode_decl_constraints(ModuleInfo, ConstraintVars, ArgModes) =
[conj(list.map_corresponding(
(func(CVar, Mode) =
atomic_constraint(equiv_bool(CVar, IsProduced)) :-
mode_util.mode_get_insts(ModuleInfo, Mode, InitialInst, FinalInst),
(
% Already produced.
not inst_match.inst_is_free(ModuleInfo, InitialInst)
->
IsProduced = no % Not produced here.
;
% free -> non-free
not inst_match.inst_is_free( ModuleInfo, FinalInst)
->
IsProduced = yes % Produced here.
;
IsProduced = no % free -> free
% Not produced here.
)
),
ConstraintVars,
ArgModes
))].
add_sufficient_in_modes_for_type_info_args(Args, Decl, FullDecl) :-
NumArgs = list.length(Args),
NumArgModes = list.length(Decl),
Diff = NumArgs - NumArgModes,
( Diff = 0 ->
FullDecl = Decl
; Diff > 0 ->
FullDecl = list.append(list.duplicate(Diff, prog_mode.in_mode), Decl)
;
unexpected(this_file, "Too many mode declared args.m")
).
% call_headvar_constraints(HeadVars, VarMap, GoalPath, CallArgs,
% Constraints)
% Forms constraints that mean any call arg will be produced if
% the corresponding headvar is produced by the main goal of the
% called predicate.
%
% 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 a whole SCC.
%
:- pred call_headvar_constraints(mc_var_map::in, goal_path::in,
pred_id::in, args::in, pred_id::in, args::in,
mode_constraints::out) is det.
call_headvar_constraints(VarMap, GoalPath,
CallingPred, CallArgs, CalledPred, HeadVars, Constraints) :-
HeadVarsAtHome = list.map(prog_var_at_path(VarMap, CalledPred, []),
HeadVars),
CallArgsHere = list.map( prog_var_at_path(VarMap, CallingPred, GoalPath),
CallArgs),
Constraints = list.map_corresponding(
(func(HeadVarThere, CallArgHere) =
atomic_constraint(equivalent([HeadVarThere, CallArgHere]))
),
HeadVarsAtHome, CallArgsHere
).
%-----------------------------------------------------------------------------%
% At times we want constraint variables at the sub-paths of a
% goal (eg conjunction/disjunction) but only at the paths in
% which the corresponding program variable occurs and is
% non-local. This predicate builds two maps - one for variables
% non-local to the parent goal and one for those local to it.
% They map program variables that are non-local to the sub-goals
% to the constraint variables associated with them being
% produced at each of the goal paths in which they appear.
%
:- pred fold_goal_into_var_position_maps(
mc_var_map::in, pred_id::in, nonlocals::in, hlds_goal::in,
multi_map(prog_var, mc_var)::in, multi_map(prog_var, mc_var)::out,
multi_map(prog_var, mc_var)::in, multi_map(prog_var, mc_var)::out) is det.
fold_goal_into_var_position_maps(VarMap, PredId, Nonlocals,
_SubGoalExpr - SubGoalInfo, !LocalsMap, !NonlocalsMap) :-
goal_info_get_nonlocals(SubGoalInfo, SubGoalNonlocals),
goal_info_get_goal_path(SubGoalInfo, SubGoalPath),
Nonlocal = set.intersect(SubGoalNonlocals, Nonlocals),
% Note that if any variable in Nonlocals does not
% appear in the nonlocal set of any conjunct then
% this is simply due a conservative estimate on the
% non-locals of the conjunction and we can't form
% reliable constraints for it anyway (XXX although we
% might want to consider constraining it to false
% considering as it can't be produced at this goal path)
Local = set.difference(SubGoalNonlocals, Nonlocals),
% Note this is the local variables that
% are non-local to this particular sub-goal
set.fold(fold_variable_into_var_position_map(VarMap, PredId, SubGoalPath),
Local, !LocalsMap),
set.fold(fold_variable_into_var_position_map(VarMap, PredId, SubGoalPath),
Nonlocal, !NonlocalsMap).
% A subsection of fold_goal_into_var_position_maps, puts into
% the map the key ProgVar with the constraint variable that says
% ProgVar is bound at GoalPath.
%
:- pred fold_variable_into_var_position_map(
mc_var_map::in, pred_id::in, goal_path::in, prog_var::in,
multi_map(prog_var, mc_var)::in, multi_map(prog_var, mc_var)::out) is det.
fold_variable_into_var_position_map(VarMap, PredId, GoalPath, ProgVar, !Map) :-
MCVar = prog_var_at_path(VarMap, PredId, GoalPath, ProgVar),
svmulti_map.set(ProgVar, MCVar, !Map).
% This predicate adds the constraints for a variable in the
% non-local set of a conjunction (to other previously
% constructed constraints). The NonLocalsMap should provide the
% list of constraint variables representing the program variable
% being produced at each conjunct it appears in respectively.
%
:- pred fold_nonlocal_var_into_conj_constraints(mc_var_map::in,
pred_id::in, multi_map(prog_var, mc_var)::in, goal_path::in,
prog_var::in, mode_constraints::in, mode_constraints::out) is det.
fold_nonlocal_var_into_conj_constraints(VarMap, PredId, NonlocalsMap,
GoalPath, ProgVar, !Constraints) :-
NewConstraints = [atomic_constraint(equiv_disj(ProgVarAtGoalPath, Xs)),
atomic_constraint(at_most_one(Xs))],
list.append(NewConstraints, !Constraints),
ProgVarAtGoalPath = prog_var_at_path(VarMap, PredId, GoalPath, ProgVar),
Xs = multi_map.lookup(NonlocalsMap, ProgVar).
% This predicate adds the constraints for a variable in the
% non-local set of a conjunct but not nonlocal to the
% conjunction as a whole, to the constraints supplied. The
% NonLocalsMap should provide the list of constraint variables
% representing the program variable being produced at each
% conjunct it appears in respectively.
%
:- pred fold_local_var_into_conj_constraints(
mc_var_map::in, multi_map(prog_var, mc_var)::in,
prog_var::in, mode_constraints::in, mode_constraints::out) is det.
fold_local_var_into_conj_constraints(_VarMap, LocalsMap, ProgVar,
!Constraints) :-
list.cons(atomic_constraint(exactly_one(Xs)), !Constraints),
Xs = multi_map.lookup(LocalsMap, ProgVar).
%-----------------------------------------------------------------------------%
:- func this_file = string.
this_file = "build_mode_constraints.m".
%-----------------------------------------------------------------------------%
:- end_module build_mode_constraints.
%-----------------------------------------------------------------------------%