[m-dev.] Re: Undetected switches
Fergus Henderson
fjh at cs.mu.OZ.AU
Wed Feb 7 20:01:58 AEDT 2001
In mail to mercury-bugs, On 01-Feb-2001,
Nicholas James NETHERCOTE <njn at students.cs.mu.OZ.AU> wrote:
> I've discovered that there are certain kinds of switches that Mercury is
> unable to detect, eg:
>
> :- pred p3(list(T)::in) is det.
> p3(X) :-
> ( A = [], X = A % indirect switched-on unification not found
> ; X = [_|_]
> ).
>
> p2/1 and p3/1 are both clearly `det', but p3 is not found to be det.
Right. The reason for this is that the unification `X = A'
is a "simple_test" unification rather than a deconstruction.
Currently switch detection only looks for switches at deconstruction
unifications.
> Would it be possible for this kind of switch to be detected?
Yes.
> It shouldn't be very hard -
> I've done it myself for the switch detection in HAL, like this: if the
> switching var (X) is not found to be unified with a ground term, but is unified
> with another variable (A), then pass over the conjunct again looking for
> unifications of the form A=ground_term.
That sounds like an awful algorithm, if you iterate it, since it does
up to O(N) passes, where N is the length of the conjunction. Consider
for example what happens for the following case,
p(...) :-
(
A0=A1,
A1=A2,
A2=A3,
A3=A4,
A4=A5
;
...
).
when searching for a switch on A5.
And if you don't iterate it, then it doesn't solve the problem.
The Mercury compiler uses a nicer algorithm: do abstract interpretation
on the conjunction, interpreting all the unifications (and skipping
all the procedure calls). In other words, we keep track of a current
substitution and update it as we go along. This lets you do it in one
pass over the conjunction.
However, that's not enough; you also need to detect switches that
occur at simple_test unifications, not just at deconstruction
unifications.
Now, as it happens I have a patch sitting around from 1997 which does
this. All I have to do is to merge it in with the last three and a
half years worth of changes!
Here's the old patch from 1997. I'll post it as is, followed by some
commentary on what has changed since then.
----------
Estimated hours: 30
Two changes:
1. When flattening goals to superhomogeneous form, make sure that
we order unifications bottom-up, rather than top-down.
2. Convert in-in unifications against variables whose
top-level inst is known into deconstructions, so that they
are candidates for switch detection. (Necessary to prevent
change #1 from breaking existing code.)
make_hlds.m:
When flattening goals to superhomogeneous form, make sure that
we order unifications bottom-up, rather than top-down. This is
necessary for several reasons:
- The ordering affects the operational semantics,
if using the strict-sequential operational semantics.
The Semantics chapter of the Mercury Language
Reference Manual says that function calls are
executed "depth-first left-to-right", which (although
a bit unclear) is supposed to imply bottom-up
rather than top-down.
- When doing mode inference for functions,
the bottom-up ordering is almost always right, and
the top-down ordering is almost always wrong.
Since mode inference doesn't do reordering,
it is important to pick the right ordering in the
first place.
modecheck_unify.m:
modes.m:
For in-in unifications against variables whose top-level
inst is known, split such unifications into deconstructions and
argument unifications, so that the the deconstruction parts
become candidates for switch detection. This is necessary
because otherwise the above ordering change would break deep
indexing. For example, code such as `p(f(a)).' becomes
`p(H1) :- V1 = a, H1 = f(V1)', and then in `H1 = f(V1)', the
argument unification against `V1' is a complicated sub-unify
that will get split out as a complicated unify; the unification
`V1 = a' becomes a construction, not a deconstruction, and so
there would be no deconstruction for switch detection to find.
(Note that the alternative of making switch detection/cse also
check for these sort of complicated unifies is unappealing,
since it is easy enough for those passes to hoist a
deconstruction out, but it would be quite difficult for them to
hoist just the deconstruction part of a complicated unify out.)
Index: make_hlds.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/make_hlds.m,v
retrieving revision 1.239
diff -u -u -r1.239 make_hlds.m
--- make_hlds.m 1997/09/01 14:03:20 1.239
+++ make_hlds.m 1997/09/05 12:16:47
@@ -2893,6 +2893,9 @@
MainContext, SubContext, Goal0) },
{ ArgContext = functor(ConsId,
MainContext, SubContext) },
+ % Should this be insert_... rather than append_...?
+ % No, because that causes efficiency problems
+ % with type-checking :-(
append_arg_unifications(HeadVars, FunctorArgs,
FunctorContext, ArgContext, Goal0,
VarSet1, Goal, VarSet, Info0, Info)
Index: modecheck_unify.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/modecheck_unify.m,v
retrieving revision 1.23
diff -u -u -r1.23 modecheck_unify.m
--- modecheck_unify.m 1997/09/15 21:11:56 1.23
+++ modecheck_unify.m 1997/09/15 21:25:01
@@ -31,11 +31,13 @@
mode_info_di, mode_info_uo) is det.
% Work out what kind of unification a var-var unification is.
+ % Also, split complicated unifies into deconstructions,
+ % if the top-level functor is known.
:- pred categorize_unify_var_var(mode, mode, is_live, is_live, var, var,
determinism, unify_context, map(var, type), mode_info,
- hlds_goal_expr, mode_info).
+ hlds_goal_expr, extra_goals, mode_info).
:- mode categorize_unify_var_var(in, in, in, in, in, in, in, in, in,
- mode_info_di, out, mode_info_uo) is det.
+ mode_info_di, out, out, mode_info_uo) is det.
% Given a unification goal, which must be something that can be
% returned from categorize_unify_var_var (i.e. a unification
@@ -59,8 +61,8 @@
%-----------------------------------------------------------------------------%
-modecheck_unification(X, var(Y), _Unification0, UnifyContext, _GoalInfo, _,
- Unify, ModeInfo0, ModeInfo) :-
+modecheck_unification(X, var(Y), _Unification0, UnifyContext, GoalInfo0, _,
+ Goal, ModeInfo0, ModeInfo) :-
mode_info_get_module_info(ModeInfo0, ModuleInfo0),
mode_info_get_instmap(ModeInfo0, InstMap0),
instmap__lookup_var(InstMap0, X, InstOfX),
@@ -79,13 +81,18 @@
Inst = UnifyInst,
Det = Det1,
mode_info_set_module_info(ModeInfo0, ModuleInfo1, ModeInfo1),
- modecheck_set_var_inst(X, Inst, ModeInfo1, ModeInfo2),
- modecheck_set_var_inst(Y, Inst, ModeInfo2, ModeInfo3),
ModeOfX = (InstOfX -> Inst),
ModeOfY = (InstOfY -> Inst),
- mode_info_get_var_types(ModeInfo3, VarTypes),
+ mode_info_get_var_types(ModeInfo1, VarTypes),
categorize_unify_var_var(ModeOfX, ModeOfY, LiveX, LiveY, X, Y,
- Det, UnifyContext, VarTypes, ModeInfo3, Unify, ModeInfo)
+ Det, UnifyContext, VarTypes, ModeInfo1,
+ Unify, ExtraGoals, ModeInfo2),
+ modecheck_set_var_inst(X, Inst, ModeInfo2, ModeInfo3),
+ modecheck_set_var_inst(Y, Inst, ModeInfo3, ModeInfo),
+ unify_vars(Unify, UnifyVars),
+ handle_extra_goals(Unify, ExtraGoals, GoalInfo0,
+ [X, Y], UnifyVars,
+ InstMap0, ModeInfo, Goal)
;
set__list_to_set([X, Y], WaitingVars),
mode_info_error(WaitingVars, mode_error_unify_var_var(X, Y,
@@ -103,7 +110,7 @@
ModeOfX = (InstOfX -> Inst),
ModeOfY = (InstOfY -> Inst),
Modes = ModeOfX - ModeOfY,
- Unify = unify(X, var(Y), Modes, Unification, UnifyContext)
+ Goal = unify(X, var(Y), Modes, Unification, UnifyContext)
).
modecheck_unification(X0, functor(ConsId0, ArgVars0), Unification0,
@@ -735,7 +742,8 @@
UnifyContext),
categorize_unify_var_var(ModeVar0, ModeVar,
live, dead, Var0, Var, Det, UnifyContext,
- VarTypes, ModeInfo4, NewUnifyGoal, ModeInfo5),
+ VarTypes, ModeInfo4,
+ NewUnifyGoal, NewUnifyExtraGoals, ModeInfo5),
unify_vars(NewUnifyGoal, NewUnifyGoalVars),
% compute the goal_info nonlocal vars & instmap delta
@@ -743,21 +751,29 @@
% N.B. This may overestimate the set of non-locals,
% but that shouldn't cause any problems.
set__list_to_set(NewUnifyGoalVars, NonLocals),
- ( InitialInstY = FinalInstY ->
- InstMapDeltaAL0 = []
+ ( NewUnifyExtraGoals = extra_goals(InstMapAfterNewUnify, _) ->
+ compute_instmap_delta(InstMapAfterMain,
+ InstMapAfterNewUnify, NonLocals, InstMapDelta)
;
- InstMapDeltaAL0 = [Var0 - FinalInstY]
+ ( InitialInstY = FinalInstY ->
+ InstMapDeltaAL0 = []
+ ;
+ InstMapDeltaAL0 = [Var0 - FinalInstY]
+ ),
+ InstMapDeltaAL = [Var - FinalInstX | InstMapDeltaAL0],
+ instmap_delta_from_assoc_list(InstMapDeltaAL,
+ InstMapDelta)
),
- InstMapDeltaAL = [Var - FinalInstX | InstMapDeltaAL0],
- instmap_delta_from_assoc_list(InstMapDeltaAL, InstMapDelta),
goal_info_init(GoalInfo0),
goal_info_set_nonlocals(GoalInfo0, NonLocals, GoalInfo1),
goal_info_set_instmap_delta(GoalInfo1, InstMapDelta, GoalInfo),
% insert the new unification at
% the start of the extra goals
- ExtraGoals0 = extra_goals(InstMapAfterMain,
+ NewUnifyExtraGoal = extra_goals(InstMapAfterMain,
[NewUnifyGoal - GoalInfo]),
+ append_extra_goals(NewUnifyExtraGoal, NewUnifyExtraGoals,
+ ExtraGoals0),
% recursive call to handle the remaining variables...
split_complicated_subunifies_2(Vars0, UniModes0,
@@ -779,19 +795,28 @@
% categorize_unify_var_var works out which category a unification
% between a variable and another variable expression is - whether it is
% an assignment, a simple test or a complicated unify.
+% It also splits complicated unifies and simple tests
+% in the case where the top-level functor is bound to a known functor.
categorize_unify_var_var(ModeOfX, ModeOfY, LiveX, LiveY, X, Y, Det,
- UnifyContext, VarTypes, ModeInfo0, Unify, ModeInfo) :-
+ UnifyContext, VarTypes, ModeInfo0,
+ Unify, ExtraGoals, ModeInfo) :-
mode_info_get_module_info(ModeInfo0, ModuleInfo0),
(
mode_is_output(ModuleInfo0, ModeOfX)
->
Unification = assign(X, Y),
+ optimize_unify(Unification, X, Y, ModeOfX, ModeOfY,
+ Det, UnifyContext, ModeInfo0, Unify),
+ ExtraGoals = no_extra_goals,
ModeInfo = ModeInfo0
;
mode_is_output(ModuleInfo0, ModeOfY)
->
Unification = assign(Y, X),
+ optimize_unify(Unification, X, Y, ModeOfX, ModeOfY, Det,
+ UnifyContext, ModeInfo0, Unify),
+ ExtraGoals = no_extra_goals,
ModeInfo = ModeInfo0
;
mode_is_unused(ModuleInfo0, ModeOfX),
@@ -807,52 +832,82 @@
;
error("categorize_unify_var_var: free-free unify!")
),
+ optimize_unify(Unification, X, Y, ModeOfX, ModeOfY,
+ Det, UnifyContext, ModeInfo0, Unify),
+ ExtraGoals = no_extra_goals,
ModeInfo = ModeInfo0
;
+ %
+ % Check for in-in unifications that are unifying
+ % with a variable whose top-level functor is known.
+ % For such unifications, split out the functor check
+ % as a separate deconstruction unification, so that
+ % it will be a candidate for switch detection.
+ %
+ % For example, given X::ground and Y::bound(f(ground, ground)),
+ % we replace
+ % X = Y % complicated unification
+ % with
+ % X = f(T1, T2), % can_fail deconstruction
+ % Y = f(T1, T2) % deconstruction, possibly with
+ % % complicated argument sub-unifies
+ % where T1 and T2 are fresh variables.
+ %
+ % XXX due to the current lack of support for
+ % aliasing, this is done only if X is ground.
+ %
map__lookup(VarTypes, X, Type),
+ mode_get_insts(ModuleInfo0, ModeOfX, InitialInstX, _),
+ mode_get_insts(ModuleInfo0, ModeOfY, InitialInstY, _),
(
- type_is_atomic(Type, ModuleInfo0)
+ % check for known functor on the right hand side
+ InitialInstY = bound(_Uniq,
+ [functor(ConsId, ArgInsts)]),
+ inst_is_ground(ModuleInfo0, InitialInstX) % see above
->
- Unification = simple_test(X, Y),
- ModeInfo = ModeInfo0
+ split_complicated_unify(X, Y, ConsId,
+ Type, ArgInsts, UnifyContext, ModeInfo0,
+ Unify, ExtraGoals, ModeInfo)
+ ;
+ % check for known functor on the left hand side
+ InitialInstX = bound(_Uniq,
+ [functor(ConsId, ArgInsts)]),
+ inst_is_ground(ModuleInfo0, InitialInstY) % see above
+ ->
+ split_complicated_unify(Y, X, ConsId,
+ Type, ArgInsts, UnifyContext, ModeInfo0,
+ Unify, ExtraGoals, ModeInfo)
;
- mode_get_insts(ModuleInfo0, ModeOfX, IX, FX),
- mode_get_insts(ModuleInfo0, ModeOfY, IY, FY),
- determinism_components(Det, CanFail, _),
- UniMode = ((IX - IY) -> (FX - FY)),
- Unification = complicated_unify(UniMode, CanFail),
+ %
+ % This is the ordinary case of an in-in unification.
+ % Check whether it is a simple_test or a
+ % complicated_unify.
+ %
(
- type_is_higher_order(Type, PredOrFunc, _)
+ type_is_atomic(Type, ModuleInfo0)
->
- % we do not want to report this as an error
- % if it occurs in a compiler-generated
- % predicate - instead, we delay the error
- % until runtime so that it only occurs if
- % the compiler-generated predicate gets called
- mode_info_get_predid(ModeInfo0, PredId),
- module_info_pred_info(ModuleInfo0, PredId,
- PredInfo),
- ( code_util__compiler_generated(PredInfo) ->
- ModeInfo = ModeInfo0
- ;
- set__init(WaitingVars),
- mode_info_error(WaitingVars,
- mode_error_unify_pred(X, error_at_var(Y), Type, PredOrFunc),
- ModeInfo0, ModeInfo)
- )
- ;
- type_to_type_id(Type, TypeId, _)
- ->
- mode_info_get_context(ModeInfo0, Context),
- unify_proc__request_unify(TypeId - UniMode,
- Det, Context, ModuleInfo0, ModuleInfo),
- mode_info_set_module_info(ModeInfo0, ModuleInfo,
- ModeInfo)
- ;
+ Unification = simple_test(X, Y),
+ optimize_unify(Unification, X, Y,
+ ModeOfX, ModeOfY,
+ Det, UnifyContext, ModeInfo0,
+ Unify),
+ ExtraGoals = no_extra_goals,
ModeInfo = ModeInfo0
+ ;
+ handle_complicated_unify(X, Y,
+ ModeOfX, ModeOfY,
+ Det, UnifyContext, ModeInfo0,
+ Unify, ExtraGoals, ModeInfo)
)
)
- ),
+ ).
+
+:- pred optimize_unify(unification, var, var, mode, mode, determinism,
+ unify_context, mode_info, hlds_goal_expr).
+:- mode optimize_unify(in, in, in, in, in, in, in, mode_info_ui, out) is det.
+
+optimize_unify(Unification, X, Y, ModeOfX, ModeOfY, Det, UnifyContext, ModeInfo,
+ Unify) :-
%
% Optimize away unifications with dead variables
% and simple tests that cannot fail
@@ -889,6 +944,135 @@
Unify = unify(X, var(Y), ModeOfX - ModeOfY, Unification,
UnifyContext)
).
+
+:- pred handle_complicated_unify(var, var, mode, mode,
+ determinism, unify_context, mode_info,
+ hlds_goal_expr, extra_goals, mode_info).
+:- mode handle_complicated_unify(in, in, in, in, in, in, mode_info_di,
+ out, out, mode_info_uo) is det.
+
+handle_complicated_unify(X, Y, ModeOfX, ModeOfY, Det, UnifyContext, ModeInfo0,
+ Unify, ExtraGoals, ModeInfo) :-
+ mode_get_insts(ModuleInfo0, ModeOfX, InitialInstX, FinalInstX),
+ mode_get_insts(ModuleInfo0, ModeOfY, InitialInstY, FinalInstY),
+ UniMode = ((InitialInstX - InitialInstY) -> (FinalInstX - FinalInstY)),
+ determinism_components(Det, CanFail, _),
+
+ %
+ % report an error for unification of higher-order types
+ %
+ mode_info_get_module_info(ModeInfo0, ModuleInfo0),
+ mode_info_get_var_types(ModeInfo0, VarTypes),
+ map__lookup(VarTypes, X, Type),
+ (
+ type_is_higher_order(Type, PredOrFunc, _)
+ ->
+ % we do not want to report this as an error
+ % if it occurs in a compiler-generated
+ % predicate - instead, we delay the error
+ % until runtime so that it only occurs if
+ % the compiler-generated predicate gets called
+ mode_info_get_predid(ModeInfo0, PredId),
+ module_info_pred_info(ModuleInfo0, PredId, PredInfo),
+ ( code_util__compiler_generated(PredInfo) ->
+ ModeInfo = ModeInfo0
+ ;
+ set__init(WaitingVars),
+ mode_info_error(WaitingVars,
+ mode_error_unify_pred(X, error_at_var(Y),
+ Type, PredOrFunc),
+ ModeInfo0, ModeInfo)
+ ),
+ Unification = complicated_unify(UniMode, CanFail),
+ Unify = unify(X, var(Y), ModeOfX - ModeOfY,
+ Unification, UnifyContext),
+ ExtraGoals = no_extra_goals
+ ;
+ %
+ % It's a genuine complicated unification --
+ % we need to insert it into the unify_proc request queue.
+ %
+ (
+ type_to_type_id(Type, TypeId, _)
+ ->
+ mode_info_get_context(ModeInfo0, Context),
+ unify_proc__request_unify(TypeId - UniMode,
+ Det, Context, ModuleInfo0, ModuleInfo),
+ mode_info_set_module_info(ModeInfo0, ModuleInfo,
+ ModeInfo)
+ ;
+ ModeInfo = ModeInfo0
+ ),
+ Unification = complicated_unify(UniMode, CanFail),
+ Unify = unify(X, var(Y), ModeOfX - ModeOfY,
+ Unification, UnifyContext),
+ ExtraGoals = no_extra_goals
+ ).
+
+:- pred split_complicated_unify(var, var, cons_id,
+ type, list(inst), unify_context, mode_info,
+ hlds_goal_expr, extra_goals, mode_info).
+:- mode split_complicated_unify(in, in, in, in, in, in, mode_info_di,
+ out, out, mode_info_uo) is det.
+
+split_complicated_unify(X, Y, ConsId, Type, ArgInsts, UnifyContext,
+ ModeInfo0, MainGoal, ExtraGoals, ModeInfo) :-
+ % Hmm... would it be worthwhile to special-case things
+ % for the case where ArgInsts = []?
+
+ %
+ % figure out the types of the functor's arguments
+ %
+ mode_info_get_module_info(ModeInfo0, ModuleInfo0),
+ type_util__get_cons_id_arg_types(ModuleInfo0, Type, ConsId, ArgTypes),
+ ( list__same_length(ArgInsts, ArgTypes) ->
+ true
+ ;
+ error("split_complicated_unify: length mismatch")
+ ),
+
+ %
+ % construct the list of fresh argument variables ArgVars
+ % whose types we computed above
+ %
+ mode_info_get_var_types(ModeInfo0, VarTypes0),
+ mode_info_get_varset(ModeInfo0, VarSet0),
+ make_fresh_vars(ArgTypes, VarSet0, VarTypes0,
+ ArgVars, VarSet, VarTypes),
+ mode_info_set_varset(VarSet, ModeInfo0, ModeInfo1),
+ mode_info_set_var_types(VarTypes, ModeInfo1, ModeInfo2),
+
+ %
+ % modecheck the unifications `X = Functor(ArgVars)'
+ % and `Y = Functor(ArgVars)'.
+ %
+ DummyMode = ((free - free) -> (free - free)),
+ DummyUnify = complicated_unify(DummyMode, can_fail),
+ goal_info_init(GoalInfo0),
+ set__list_to_set([X|ArgVars], NonLocalsX),
+ goal_info_set_nonlocals(GoalInfo0, NonLocalsX, GoalInfoX0),
+ HowToCheckGoal = check_modes, % this is not really used
+ modecheck_unify_functor(X, Type, ConsId, ArgVars, DummyUnify,
+ UnifyContext, HowToCheckGoal, GoalInfoX0,
+ DeconstructX, ModeInfo2, ModeInfo3),
+ mode_info_get_instmap(ModeInfo3, InstMapBeforeY),
+ set__list_to_set([Y|ArgVars], NonLocalsY),
+ goal_info_set_nonlocals(GoalInfo0, NonLocalsY, GoalInfoY0),
+ modecheck_unify_functor(Y, Type, ConsId, ArgVars, DummyUnify,
+ UnifyContext, HowToCheckGoal, GoalInfoY0,
+ DeconstructY, ModeInfo3, ModeInfo),
+ mode_info_get_instmap(ModeInfo3, InstMapAfterY),
+ compute_instmap_delta(InstMapBeforeY, InstMapAfterY, NonLocalsY,
+ InstMapDeltaY),
+ goal_info_set_instmap_delta(GoalInfoY0, InstMapDeltaY, GoalInfoY),
+
+ %
+ % Now return the unifications we've constructed.
+ % We treat `DeconstructX' as the main unification,
+ % and put `DeconstructY' into the `AfterGoals' list.
+ %
+ MainGoal = DeconstructX,
+ ExtraGoals = extra_goals(InstMapBeforeY, [DeconstructY - GoalInfoY]).
% categorize_unify_var_lambda works out which category a unification
% between a variable and a lambda expression is - whether it is a construction
Index: modes.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/modes.m,v
retrieving revision 1.205
diff -u -u -r1.205 modes.m
--- modes.m 1997/09/15 21:12:24 1.205
+++ modes.m 1997/09/15 21:25:37
@@ -1366,21 +1366,32 @@
categorize_unify_var_var(ModeVar0, ModeVar,
live, dead, Var0, Var, Det, UnifyContext,
- VarTypes, ModeInfo3, NewUnifyGoal, ModeInfo),
+ VarTypes, ModeInfo3,
+ NewUnifyGoal, NewUnifyExtraGoals, ModeInfo),
unify_vars(NewUnifyGoal, NewUnifyGoalVars),
% compute the goal_info nonlocal vars & instmap delta
% N.B. This may overestimate the set of nonlocal vars,
% but that should not cause any problems.
set__list_to_set(NewUnifyGoalVars, NonLocals),
- ( VarInst = VarInst0 ->
- InstMapDeltaAL0 = []
+ (
+ NewUnifyExtraGoals = extra_goals(
+ InstMapAfterNewUnify, _)
+ ->
+ compute_instmap_delta(OrigInstMap,
+ InstMapAfterNewUnify, NonLocals,
+ InstMapDelta)
;
- InstMapDeltaAL0 = [Var0 - VarInst]
+ ( VarInst = VarInst0 ->
+ InstMapDeltaAL0 = []
+ ;
+ InstMapDeltaAL0 = [Var0 - VarInst]
+ ),
+ InstMapDeltaAL = [Var - VarInst |
+ InstMapDeltaAL0],
+ instmap_delta_from_assoc_list(InstMapDeltaAL,
+ InstMapDelta)
),
- InstMapDeltaAL = [Var - VarInst | InstMapDeltaAL0],
- instmap_delta_from_assoc_list(InstMapDeltaAL,
- InstMapDelta),
goal_info_init(GoalInfo0),
goal_info_set_nonlocals(GoalInfo0, NonLocals,
GoalInfo1),
@@ -1388,10 +1399,12 @@
GoalInfo),
% append the goals together in the appropriate order:
- % ExtraGoals0, then NewUnify
+ % ExtraGoals0, then NewUnify, then NewUnifyExtraGoals
NewUnifyExtraGoal = extra_goals(InstMapAfterMain,
[NewUnifyGoal - GoalInfo]),
append_extra_goals(ExtraGoals0, NewUnifyExtraGoal,
+ ExtraGoals1),
+ append_extra_goals(ExtraGoals1, NewUnifyExtraGoals,
ExtraGoals)
)
).
cvs diff: Diffing notes
--
Fergus Henderson <fjh at cs.mu.oz.au> | "I have always known that the pursuit
| of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh> | -- the last words of T. S. Garp.
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to: mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions: mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------
More information about the developers
mailing list