mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-16 06:14:59 +00:00
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.
677 lines
26 KiB
Mathematica
677 lines
26 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2007-2012 The University of Melbourne.
|
|
% Copyright (C) 2014-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: delay_partial_inst.m.
|
|
% Author: wangp.
|
|
%
|
|
% This module runs just after mode analysis on mode-correct procedures and
|
|
% tries to transform procedures to avoid intermediate partially instantiated
|
|
% data structures. The Erlang backend in particular cannot handle partially
|
|
% instantiated data structures (we cannot use destructive update to further
|
|
% instantiate data structures since all values are immutable).
|
|
%
|
|
% There are two situations. An implied mode call, e.g.
|
|
%
|
|
% p(f(_, _))
|
|
%
|
|
% looks like this after mode checking:
|
|
%
|
|
% X := f(V_1, V_2), % partially instantiated
|
|
% p(Y),
|
|
% X ?= Y
|
|
%
|
|
% We transform it to this more obvious sequence which doesn't need the
|
|
% partially instantiated data structure:
|
|
%
|
|
% p(Y),
|
|
% Y ?= f(_, _)
|
|
%
|
|
% The other situation is if the user writes code that constructs data
|
|
% structures with free variables, e.g.
|
|
%
|
|
% :- type t
|
|
% ---> t(
|
|
% a :: int,
|
|
% b :: int
|
|
% ).
|
|
%
|
|
% F ^ a = 1,
|
|
% F ^ b = 2
|
|
%
|
|
% After mode checking we get:
|
|
%
|
|
% V_1 = 1,
|
|
% F := t(V_1, V_2), % ground, free
|
|
% V_3 = 2,
|
|
% F => t(V_4, V_3) % ground, ground
|
|
%
|
|
% Whereas we would like to see this:
|
|
%
|
|
% V_1 = 1,
|
|
% V_2 = 2,
|
|
% F := t(V_1, V_2)
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% ALGORITHM
|
|
%
|
|
% The idea is to remove unifications that produce partially instantiated data
|
|
% structures (as the mode checker can't be counted on to move these), and keep
|
|
% track of variables which are bound to top-level functors with free arguments.
|
|
% In place of the unifications we remove, we insert the unifications for the
|
|
% sub-components which are ground. Only once the variable is ground, because
|
|
% all its sub-components are ground, do we create the top-level memory cell.
|
|
%
|
|
% The algorithm makes a single forward pass over each procedure. When we see
|
|
% a unification that binds a variable V to a functor f/n with at least one
|
|
% free argument, we add an entry to the "construction map" and delete the
|
|
% unification. The construction map records that V was bound to f/n.
|
|
% We also create new "canonical" variables for each of the arguments.
|
|
%
|
|
% When we later see a deconstruction unification of V we first unify each
|
|
% argument in the deconstruction with its corresponding "canonical" variable.
|
|
% This way we can always use the canonical variables when it comes time to
|
|
% reconstruct V, so we don't need to keep track of aliases. If the mode of the
|
|
% deconstruction unification indicates that V should be ground at the end
|
|
% of the deconstruction, we insert a construction unification using the
|
|
% canonical variables, in place of the deconstruction, and delete V's entry
|
|
% from the construction map now. Otherwise, if V is not ground, we just delete
|
|
% the deconstruction unification.
|
|
%
|
|
% To handle the problem with implied mode calls, we look for complicated
|
|
% `can_fail' unifications that have V on the left-hand side. We transform them
|
|
% as in the example above, i.e. instead of unifying a ground variable G with a
|
|
% partially instantiated V, we unify G with the functor that V is bound to.
|
|
%
|
|
% After transforming all the procedures, we requantify and rerun mode analysis,
|
|
% which should do the rest.
|
|
%
|
|
% This algorithm can't handle everything that the mode checker allows, however
|
|
% most code written in practice should be okay. Here is an example of code we
|
|
% cannot handle:
|
|
%
|
|
% foo(Xs) :-
|
|
% ( Xs = []
|
|
% ; Xs = [1 | _]
|
|
% ),
|
|
% ( Xs = []
|
|
% ; Xs = [_ | []]
|
|
% ).
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module check_hlds.delay_partial_inst.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
|
|
:- import_module list.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred delay_partial_inst_preds(list(pred_id)::in, list(pred_id)::out,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.inst_test.
|
|
:- import_module check_hlds.mode_util.
|
|
:- import_module hlds.
|
|
:- import_module hlds.goal_util.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_out.
|
|
:- import_module hlds.hlds_out.hlds_out_goal.
|
|
:- import_module hlds.instmap.
|
|
:- import_module hlds.make_goal.
|
|
:- import_module hlds.passes_aux.
|
|
:- import_module hlds.quantification.
|
|
:- import_module hlds.vartypes.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_rename.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module io.
|
|
:- import_module map.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type delay_partial_inst_info
|
|
---> delay_partial_inst_info(
|
|
% Read-only.
|
|
dpi_module_info :: module_info,
|
|
|
|
% Read-write.
|
|
dpi_varset :: prog_varset,
|
|
dpi_vartypes :: vartypes,
|
|
dpi_changed :: bool
|
|
).
|
|
|
|
% A map from the variable to the functor to which it is bound, which maps
|
|
% to the canonical variables assigned for that functor.
|
|
%
|
|
% We can actually only handle the case when a variable is definitely bound
|
|
% to a single functor. If different disjuncts bind a variable to different
|
|
% functors, then our algorithm won't work. So why do we use a single map
|
|
% from the variable to (cons_id, canon_vars)? To handle code like this,
|
|
% which can result from a reasonable predicate definition.
|
|
%
|
|
% ( X := f
|
|
% ; X := g
|
|
% ; X := h(_), fail
|
|
% ; X := i(_), fail
|
|
% )
|
|
%
|
|
% We don't want to abort as soon as we see that "X := i(_)" is incompatible
|
|
% with "X := h(_)". We *will* abort later if we need to look up the sole
|
|
% functor that X could be bound to, and find that there are multiple
|
|
% choices.
|
|
%
|
|
:- type construct_map == map(prog_var, canon_vars_map).
|
|
|
|
:- type canon_vars_map == map(cons_id, prog_vars).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
delay_partial_inst_preds(PredIds, ChangedPredIds, !ModuleInfo) :-
|
|
delay_partial_inst_preds_acc(PredIds, [], RevChangedPredIds, !ModuleInfo),
|
|
list.reverse(RevChangedPredIds, ChangedPredIds).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred delay_partial_inst_preds_acc(list(pred_id)::in,
|
|
list(pred_id)::in, list(pred_id)::out,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
delay_partial_inst_preds_acc([], !RevChangedPredIds, !ModuleInfo).
|
|
delay_partial_inst_preds_acc([PredId | PredIds], !RevChangedPredIds,
|
|
!ModuleInfo) :-
|
|
module_info_pred_info(!.ModuleInfo, PredId, PredInfo0),
|
|
pred_info_get_proc_table(PredInfo0, ProcTable0),
|
|
ProcIds = pred_info_non_imported_procids(PredInfo0),
|
|
list.foldl(delay_partial_inst_proc(!.ModuleInfo, PredId, ProcTable0),
|
|
ProcIds, [], ChangedProcs),
|
|
(
|
|
ChangedProcs = [_ | _],
|
|
map.set_from_assoc_list(ChangedProcs, ProcTable0, ProcTable),
|
|
pred_info_set_proc_table(ProcTable, PredInfo0, PredInfo),
|
|
module_info_set_pred_info(PredId, PredInfo, !ModuleInfo),
|
|
!:RevChangedPredIds = [PredId | !.RevChangedPredIds]
|
|
;
|
|
ChangedProcs = []
|
|
),
|
|
delay_partial_inst_preds_acc(PredIds, !RevChangedPredIds, !ModuleInfo).
|
|
|
|
:- pred delay_partial_inst_proc(module_info::in, pred_id::in,
|
|
proc_table::in, proc_id::in,
|
|
assoc_list(proc_id, proc_info)::in, assoc_list(proc_id, proc_info)::out)
|
|
is det.
|
|
|
|
delay_partial_inst_proc(ModuleInfo, PredId, ProcTable, ProcId,
|
|
!ChangedProcs) :-
|
|
trace [io(!IO)] (
|
|
write_proc_progress_message("% Delaying partial instantiations in ",
|
|
PredId, ProcId, ModuleInfo, !IO)
|
|
),
|
|
some [!ProcInfo] (
|
|
map.lookup(ProcTable, ProcId, !:ProcInfo),
|
|
proc_info_get_varset(!.ProcInfo, VarSet0),
|
|
proc_info_get_vartypes(!.ProcInfo, VarTypes0),
|
|
proc_info_get_initial_instmap(!.ProcInfo, ModuleInfo, InstMap0),
|
|
proc_info_get_goal(!.ProcInfo, Goal0),
|
|
|
|
Changed0 = no,
|
|
DelayInfo0 = delay_partial_inst_info(ModuleInfo,
|
|
VarSet0, VarTypes0, Changed0),
|
|
delay_partial_inst_in_goal(InstMap0, Goal0, Goal,
|
|
map.init, _ConstructMap, DelayInfo0, DelayInfo),
|
|
DelayInfo = delay_partial_inst_info(_,
|
|
VarSet, VarTypes, Changed),
|
|
|
|
(
|
|
Changed = yes,
|
|
proc_info_set_goal(Goal, !ProcInfo),
|
|
proc_info_set_varset(VarSet, !ProcInfo),
|
|
proc_info_set_vartypes(VarTypes, !ProcInfo),
|
|
requantify_proc_general(ordinary_nonlocals_maybe_lambda,
|
|
!ProcInfo),
|
|
!:ChangedProcs = [ProcId - !.ProcInfo | !.ChangedProcs],
|
|
|
|
trace [compiletime(flag("debug_delay_partial_inst")), io(!IO)] (
|
|
io.write_string("predicate body BEFORE delay_partial_inst:\n",
|
|
!IO),
|
|
dump_goal(ModuleInfo, VarSet0, Goal0, !IO),
|
|
io.nl(!IO),
|
|
io.write_string("predicate body AFTER delay_partial_inst:\n",
|
|
!IO),
|
|
dump_goal(ModuleInfo, VarSet, Goal, !IO),
|
|
io.nl(!IO)
|
|
)
|
|
;
|
|
Changed = no
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred delay_partial_inst_in_goal(instmap::in, hlds_goal::in, hlds_goal::out,
|
|
construct_map::in, construct_map::out,
|
|
delay_partial_inst_info::in, delay_partial_inst_info::out) is det.
|
|
|
|
delay_partial_inst_in_goal(InstMap0, Goal0, Goal, !ConstructMap, !DelayInfo) :-
|
|
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
|
|
(
|
|
GoalExpr0 = conj(ConjType, Goals0),
|
|
delay_partial_inst_in_conj(InstMap0, Goals0, Goals, !ConstructMap,
|
|
!DelayInfo),
|
|
Goal = hlds_goal(conj(ConjType, Goals), GoalInfo0)
|
|
;
|
|
GoalExpr0 = disj(Goals0),
|
|
delay_partial_inst_in_disj(InstMap0, Goals0, Goals, !ConstructMap,
|
|
!DelayInfo),
|
|
Goal = hlds_goal(disj(Goals), GoalInfo0)
|
|
;
|
|
GoalExpr0 = negation(NegGoal0),
|
|
delay_partial_inst_in_goal(InstMap0, NegGoal0, NegGoal,
|
|
!.ConstructMap, _, !DelayInfo),
|
|
Goal = hlds_goal(negation(NegGoal), GoalInfo0)
|
|
;
|
|
GoalExpr0 = switch(Var, CanFail, Cases0),
|
|
delay_partial_inst_in_cases(InstMap0, Cases0, Cases, !ConstructMap,
|
|
!DelayInfo),
|
|
Goal = hlds_goal(switch(Var, CanFail, Cases), GoalInfo0)
|
|
;
|
|
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
|
|
update_instmap(Cond0, InstMap0, InstMapThen),
|
|
delay_partial_inst_in_goal(InstMap0, Cond0, Cond, !ConstructMap,
|
|
!DelayInfo),
|
|
delay_partial_inst_in_goal(InstMapThen, Then0, Then, !ConstructMap,
|
|
!DelayInfo),
|
|
delay_partial_inst_in_goal(InstMap0, Else0, Else, !ConstructMap,
|
|
!DelayInfo),
|
|
Goal = hlds_goal(if_then_else(Vars, Cond, Then, Else), GoalInfo0)
|
|
;
|
|
GoalExpr0 = scope(Reason, SubGoal0),
|
|
( if
|
|
Reason = from_ground_term(_, FGT),
|
|
( FGT = from_ground_term_construct
|
|
; FGT = from_ground_term_deconstruct
|
|
)
|
|
then
|
|
Goal = Goal0
|
|
else
|
|
delay_partial_inst_in_goal(InstMap0, SubGoal0, SubGoal,
|
|
!.ConstructMap, _, !DelayInfo),
|
|
Goal = hlds_goal(scope(Reason, SubGoal), GoalInfo0)
|
|
)
|
|
;
|
|
GoalExpr0 = unify(LHS, RHS0, Mode, Unify, Context),
|
|
(
|
|
Unify = construct(_Var, ConsId, _Args, UniModes, _, _, _),
|
|
( if
|
|
% Is this construction of the form
|
|
% V = f(A1, A2, A3, ...)
|
|
% and at least one of the arguments is free?
|
|
%
|
|
( ConsId = cons(_, _, _)
|
|
; ConsId = tuple_cons(_)
|
|
),
|
|
ModuleInfo = !.DelayInfo ^ dpi_module_info,
|
|
some [RhsAfter] (
|
|
list.member(_ -> _ - RhsAfter, UniModes),
|
|
inst_is_free(ModuleInfo, RhsAfter)
|
|
)
|
|
then
|
|
delay_partial_inst_in_partial_construct(GoalInfo0, Unify, Goal,
|
|
!ConstructMap, !DelayInfo)
|
|
else
|
|
(
|
|
% Tranform lambda goals as well. Non-local variables in
|
|
% lambda goals must be any or ground so we don't carry the
|
|
% construct map into the lambda goal.
|
|
RHS0 = rhs_lambda_goal(Purity, Groundness, PredOrFunc,
|
|
EvalMethod, NonLocals, LambdaQuantVars, Modues, Detism,
|
|
LambdaGoal0),
|
|
delay_partial_inst_in_goal(InstMap0,
|
|
LambdaGoal0, LambdaGoal, map.init, _ConstructMap,
|
|
!DelayInfo),
|
|
RHS = rhs_lambda_goal(Purity, Groundness, PredOrFunc,
|
|
EvalMethod, NonLocals, LambdaQuantVars, Modues, Detism,
|
|
LambdaGoal),
|
|
GoalExpr = unify(LHS, RHS, Mode, Unify, Context),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
( RHS0 = rhs_var(_)
|
|
; RHS0 = rhs_functor(_, _, _)
|
|
),
|
|
Goal = Goal0
|
|
)
|
|
)
|
|
;
|
|
Unify = deconstruct(_Var, _ConsId, _Args, _UniModes,
|
|
_CanFail, _CanCGC),
|
|
delay_partial_inst_in_deconstruct(Goal0, Mode, Unify, Goal,
|
|
!ConstructMap, !DelayInfo)
|
|
;
|
|
Unify = complicated_unify(_UniMode, _CanFail, _TypeInfos),
|
|
delay_partial_inst_in_complicated_unify(Goal0, LHS, RHS0,
|
|
Unify, Goal, !ConstructMap, !DelayInfo)
|
|
;
|
|
( Unify = assign(_, _)
|
|
; Unify = simple_test(_, _)
|
|
),
|
|
Goal = Goal0
|
|
)
|
|
;
|
|
( GoalExpr0 = generic_call(_, _, _, _, _)
|
|
; GoalExpr0 = plain_call(_, _, _, _, _, _)
|
|
; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
|
|
),
|
|
Goal = Goal0
|
|
;
|
|
GoalExpr0 = shorthand(ShortHand0),
|
|
(
|
|
ShortHand0 = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
|
|
MainGoal0, OrElseGoals0, OrElseInners),
|
|
% XXX Is it ok to ignore the updated ConstructMaps,
|
|
% and if yes, why? This should be documented.
|
|
delay_partial_inst_in_goal(InstMap0, MainGoal0, MainGoal,
|
|
!.ConstructMap, _, !DelayInfo),
|
|
delay_partial_inst_in_disj(InstMap0, OrElseGoals0, OrElseGoals,
|
|
!.ConstructMap, _, !DelayInfo),
|
|
ShortHand = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
|
|
MainGoal, OrElseGoals, OrElseInners),
|
|
GoalExpr = shorthand(ShortHand),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
|
|
delay_partial_inst_in_goal(InstMap0, SubGoal0, SubGoal,
|
|
!ConstructMap, !DelayInfo),
|
|
ShortHand = try_goal(MaybeIO, ResultVar, SubGoal),
|
|
GoalExpr = shorthand(ShortHand),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
ShortHand0 = bi_implication(_, _),
|
|
% These should have been expanded out by now.
|
|
unexpected($module, $pred, "bi_implication")
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Handle compound goals.
|
|
%
|
|
|
|
:- pred delay_partial_inst_in_conj(instmap::in,
|
|
list(hlds_goal)::in, list(hlds_goal)::out,
|
|
construct_map::in, construct_map::out,
|
|
delay_partial_inst_info::in, delay_partial_inst_info::out) is det.
|
|
|
|
delay_partial_inst_in_conj(_, [], [], !ConstructMap, !DelayInfo).
|
|
delay_partial_inst_in_conj(InstMap0, [HeadGoal0 | TailGoals0], Goals,
|
|
!ConstructMap, !DelayInfo) :-
|
|
delay_partial_inst_in_goal(InstMap0, HeadGoal0, HeadGoal, !ConstructMap,
|
|
!DelayInfo),
|
|
update_instmap(HeadGoal0, InstMap0, InstMap1),
|
|
delay_partial_inst_in_conj(InstMap1, TailGoals0, TailGoals, !ConstructMap,
|
|
!DelayInfo),
|
|
goal_to_conj_list(HeadGoal, HeadGoals),
|
|
Goals = HeadGoals ++ TailGoals.
|
|
|
|
:- pred delay_partial_inst_in_disj(instmap::in,
|
|
list(hlds_goal)::in, list(hlds_goal)::out,
|
|
construct_map::in, construct_map::out,
|
|
delay_partial_inst_info::in, delay_partial_inst_info::out) is det.
|
|
|
|
delay_partial_inst_in_disj(_, [], [], !ConstructMap, !DelayInfo).
|
|
delay_partial_inst_in_disj(InstMap0, [Goal0 | Goals0], [Goal | Goals],
|
|
!ConstructMap, !DelayInfo) :-
|
|
% Each time that a variable X is bound to a partially instantiated term
|
|
% with functor f/n somewhere in the disjunction, we want the same set of
|
|
% "canonical" variables to name the individual arguments of f/n.
|
|
% That is why we thread the construct map through the disjunctions,
|
|
% so we don't end up with different canonical variables per disjunct.
|
|
%
|
|
% XXX we depend on the fact that (it seems) after mode checking a
|
|
% variable won't become ground in each of the disjuncts, but rather
|
|
% will become ground after the disjunction as a whole. Otherwise
|
|
% entries could be removed from the construct map in earlier disjuncts
|
|
% that should be visible in later disjuncts. To lift this assumption we
|
|
% would need to use separate construct maps per disjunct, merge them
|
|
% afterwards and renaming variables so that there is only one set of
|
|
% canonical variables.
|
|
%
|
|
delay_partial_inst_in_goal(InstMap0, Goal0, Goal, !ConstructMap,
|
|
!DelayInfo),
|
|
delay_partial_inst_in_disj(InstMap0, Goals0, Goals, !ConstructMap,
|
|
!DelayInfo).
|
|
|
|
:- pred delay_partial_inst_in_cases(instmap::in,
|
|
list(case)::in, list(case)::out, construct_map::in, construct_map::out,
|
|
delay_partial_inst_info::in, delay_partial_inst_info::out) is det.
|
|
|
|
delay_partial_inst_in_cases(_, [], [], !ConstructMap, !DelayInfo).
|
|
delay_partial_inst_in_cases(InstMap0, [Case0 | Cases0], [Case | Cases],
|
|
!ConstructMap, !DelayInfo) :-
|
|
% See comment in delay_partial_inst_in_goals.
|
|
Case0 = case(MainConsId, OtherConsIds, Goal0),
|
|
delay_partial_inst_in_goal(InstMap0, Goal0, Goal, !ConstructMap,
|
|
!DelayInfo),
|
|
Case = case(MainConsId, OtherConsIds, Goal),
|
|
delay_partial_inst_in_cases(InstMap0, Cases0, Cases, !ConstructMap,
|
|
!DelayInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Handle unifications that construct partially instantated terms.
|
|
%
|
|
|
|
:- pred delay_partial_inst_in_partial_construct(hlds_goal_info::in,
|
|
unification::in(unification_construct), hlds_goal::out,
|
|
construct_map::in, construct_map::out,
|
|
delay_partial_inst_info::in, delay_partial_inst_info::out) is det.
|
|
|
|
delay_partial_inst_in_partial_construct(GoalInfo0, Unify, Goal,
|
|
!ConstructMap, !DelayInfo) :-
|
|
Unify = construct(Var, ConsId, Args, UniModes, _, _, _),
|
|
% Add an entry for Var to the construct map if it doesn't exist
|
|
% already, otherwise look up the canonical variables.
|
|
( if
|
|
map.search(!.ConstructMap, Var, CanonVarsMap0),
|
|
map.search(CanonVarsMap0, ConsId, CanonVars0)
|
|
then
|
|
CanonVars = CanonVars0
|
|
else
|
|
create_canonical_variables(Args, CanonVars, !DelayInfo),
|
|
add_to_construct_map(Var, ConsId, CanonVars, !ConstructMap)
|
|
),
|
|
|
|
% Unify the canonical variables and corresponding ground
|
|
% arguments (if any).
|
|
ModuleInfo = !.DelayInfo ^ dpi_module_info,
|
|
ProgContext = goal_info_get_context(GoalInfo0),
|
|
SubUnifyGoals = list.filter_map_corresponding3(
|
|
maybe_unify_var_with_ground_var(ModuleInfo, ProgContext),
|
|
CanonVars, Args, UniModes),
|
|
conj_list_to_goal(SubUnifyGoals, GoalInfo0, Goal),
|
|
|
|
% Mark the procedure as changed.
|
|
!DelayInfo ^ dpi_changed := yes.
|
|
|
|
:- pred create_canonical_variables(prog_vars::in, prog_vars::out,
|
|
delay_partial_inst_info::in, delay_partial_inst_info::out) is det.
|
|
|
|
create_canonical_variables(OrigVars, CanonVars, !DelayInfo) :-
|
|
VarSet0 = !.DelayInfo ^ dpi_varset,
|
|
VarTypes0 = !.DelayInfo ^ dpi_vartypes,
|
|
clone_variables(OrigVars, VarSet0, VarTypes0,
|
|
VarSet0, VarSet, VarTypes0, VarTypes, map.init, Renaming),
|
|
rename_var_list(must_rename, Renaming, OrigVars, CanonVars),
|
|
!DelayInfo ^ dpi_varset := VarSet,
|
|
!DelayInfo ^ dpi_vartypes := VarTypes.
|
|
|
|
:- pred add_to_construct_map(prog_var::in, cons_id::in, prog_vars::in,
|
|
construct_map::in, construct_map::out) is det.
|
|
|
|
add_to_construct_map(Var, ConsId, CanonVars, !ConstructMap) :-
|
|
( if map.search(!.ConstructMap, Var, ConsIdMap0) then
|
|
ConsIdMap1 = ConsIdMap0
|
|
else
|
|
ConsIdMap1 = map.init
|
|
),
|
|
map.det_insert(ConsId, CanonVars, ConsIdMap1, ConsIdMap),
|
|
map.set(Var, ConsIdMap, !ConstructMap).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Handle deconstructions. In some of these, information flows from right to
|
|
% left, i.e. from some of the function symbol's argument variables to the
|
|
% LHS variable, which previously must have been bound to a partially
|
|
% instantiated term.
|
|
%
|
|
|
|
:- pred delay_partial_inst_in_deconstruct(hlds_goal::in,
|
|
unify_mode::in, unification::in(unification_deconstruct), hlds_goal::out,
|
|
construct_map::in, construct_map::out,
|
|
delay_partial_inst_info::in, delay_partial_inst_info::out) is det.
|
|
|
|
delay_partial_inst_in_deconstruct(Goal0, UniMode, Unify, Goal,
|
|
!ConstructMap, !DelayInfo) :-
|
|
Unify = deconstruct(Var, ConsId, Args, UniModes, _CanFail, _CanCGC),
|
|
( if
|
|
map.search(!.ConstructMap, Var, CanonVarsMap0),
|
|
map.search(CanonVarsMap0, ConsId, CanonArgs)
|
|
then
|
|
% Unify each ground argument with the corresponding canonical
|
|
% variable.
|
|
ModuleInfo = !.DelayInfo ^ dpi_module_info,
|
|
ProgContext = goal_info_get_context(GoalInfo0),
|
|
SubUnifyGoals = list.filter_map_corresponding3(
|
|
maybe_unify_var_with_ground_var(ModuleInfo, ProgContext),
|
|
CanonArgs, Args, UniModes),
|
|
|
|
% Construct Var if it should be ground now.
|
|
UniMode = LHS_Mode - _RHS_Mode,
|
|
FinalInst = mode_get_final_inst(ModuleInfo, LHS_Mode),
|
|
( if inst_is_ground(ModuleInfo, FinalInst) then
|
|
construct_functor(Var, ConsId, CanonArgs, ConstructGoal),
|
|
|
|
% Delete the variable on the LHS from the construct map
|
|
% since it has been constructed.
|
|
map.delete(ConsId, CanonVarsMap0, CanonVarsMap),
|
|
map.det_update(Var, CanonVarsMap, !ConstructMap),
|
|
|
|
ConjList = SubUnifyGoals ++ [ConstructGoal]
|
|
else
|
|
ConjList = SubUnifyGoals
|
|
),
|
|
Goal0 = hlds_goal(_, GoalInfo0),
|
|
conj_list_to_goal(ConjList, GoalInfo0, Goal)
|
|
else
|
|
Goal = Goal0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Utility predicate used by handling of both partial constructions and
|
|
% deconstructions.
|
|
%
|
|
|
|
:- func maybe_unify_var_with_ground_var(module_info::in, prog_context::in,
|
|
prog_var::in, prog_var::in, uni_mode::in) = (hlds_goal::out) is semidet.
|
|
|
|
maybe_unify_var_with_ground_var(ModuleInfo, Context, LhsVar, RhsVar, ArgMode)
|
|
= Goal :-
|
|
ArgMode = (_ - _ -> Inst - _),
|
|
inst_is_ground(ModuleInfo, Inst),
|
|
create_pure_atomic_complicated_unification(LhsVar, rhs_var(RhsVar),
|
|
Context, umc_implicit("delay_partial_inst"), [], Goal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Handle complicated test unifications.
|
|
%
|
|
|
|
:- pred delay_partial_inst_in_complicated_unify(hlds_goal::in,
|
|
prog_var::in, unify_rhs::in,
|
|
unification::in(unification_complicated_unify), hlds_goal::out,
|
|
construct_map::in, construct_map::out,
|
|
delay_partial_inst_info::in, delay_partial_inst_info::out) is det.
|
|
|
|
delay_partial_inst_in_complicated_unify(Goal0, LHS, RHS0, Unify, Goal,
|
|
!ConstructMap, !DelayInfo) :-
|
|
Unify = complicated_unify(_UniMode, CanFail, _TypeInfos),
|
|
% Deal with tests generated for calls to implied modes.
|
|
%
|
|
% LHS := f(_),
|
|
% p(RHS),
|
|
% LHS ?= RHS
|
|
% ===>
|
|
% p(RHS),
|
|
% RHS ?= f(_),
|
|
% LHS := RHS
|
|
%
|
|
% XXX I have not seen a case where the LHS and RHS are swapped
|
|
% but we should handle that if it comes up.
|
|
( if
|
|
CanFail = can_fail,
|
|
RHS0 = rhs_var(RHSVar),
|
|
get_sole_cons_id_and_canon_vars(!.ConstructMap, LHS, ConsId,
|
|
CanonArgs)
|
|
then
|
|
Goal0 = hlds_goal(_, GoalInfo0),
|
|
ProgContext = goal_info_get_context(GoalInfo0),
|
|
create_pure_atomic_complicated_unification(RHSVar,
|
|
rhs_functor(ConsId, is_not_exist_constr, CanonArgs),
|
|
ProgContext, umc_explicit, [], TestGoal),
|
|
create_pure_atomic_complicated_unification(LHS, RHS0,
|
|
ProgContext, umc_implicit("delay_partial_inst"), [],
|
|
AssignGoal),
|
|
conjoin_goals(TestGoal, AssignGoal, Goal)
|
|
else
|
|
Goal = Goal0
|
|
).
|
|
|
|
:- pred get_sole_cons_id_and_canon_vars(construct_map::in, prog_var::in,
|
|
cons_id::out, prog_vars::out) is semidet.
|
|
|
|
get_sole_cons_id_and_canon_vars(ConstructMap, Var, ConsId, CanonVars) :-
|
|
map.search(ConstructMap, Var, CanonVarsMap),
|
|
List = map.to_assoc_list(CanonVarsMap),
|
|
(
|
|
List = [],
|
|
fail
|
|
;
|
|
List = [ConsId - CanonVars | Rest],
|
|
(
|
|
Rest = []
|
|
;
|
|
Rest = [_ | _],
|
|
% This algorithm does not work if a variable could be bound to
|
|
% multiple functors when we try to do a tag test against it.
|
|
% XXX report a nicer error message
|
|
sorry($module, $pred,
|
|
"delaying partial instantiations when variable could be " ++
|
|
"bound to multiple functors")
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module check_hlds.delay_partial_inst.
|
|
%-----------------------------------------------------------------------------%
|