mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-16 14:25:56 +00:00
compiler/hlds_goal.m:
Change the names of the fields of the hlds_goal structure to avoid
conflicts with the names of the hlds_goal_expr and hlds_goal_info types.
compiler/coverage_profiling.m:
compiler/deep_profiling.m:
compiler/dep_par_conj.m:
compiler/goal_util.m:
compiler/higher_order.m:
compiler/introduce_parallelism.m:
compiler/middle_rec.m:
compiler/mode_ordering.m:
compiler/modecheck_conj.m:
compiler/modecheck_goal.m:
compiler/ordering_mode_constraints.m:
compiler/par_conj_gen.m:
compiler/par_loop_control.m:
compiler/prog_rep.m:
compiler/stm_expand.m:
compiler/term_constr_build.m:
compiler/tupling.m:
compiler/untupling.m:
Conform to the change above.
387 lines
16 KiB
Mathematica
387 lines
16 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2009-2012 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: modecheck_conj.m.
|
|
% Main author: fjh.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module check_hlds.modecheck_conj.
|
|
:- interface.
|
|
|
|
:- import_module check_hlds.mode_info.
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_goal.
|
|
|
|
:- import_module list.
|
|
|
|
:- pred modecheck_conj_list(conj_type::in,
|
|
list(hlds_goal)::in, list(hlds_goal)::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.delay_info.
|
|
:- import_module check_hlds.mode_debug.
|
|
:- import_module check_hlds.mode_errors.
|
|
:- import_module check_hlds.modecheck_goal.
|
|
:- import_module check_hlds.modecheck_util.
|
|
:- import_module hlds.hlds_clauses.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.instmap.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.set_of_var.
|
|
|
|
:- import_module bool.
|
|
:- import_module cord.
|
|
:- import_module int.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
modecheck_conj_list(ConjType, Goals0, Goals, !ModeInfo) :-
|
|
mode_info_get_errors(!.ModeInfo, OldErrors),
|
|
mode_info_set_errors([], !ModeInfo),
|
|
|
|
mode_info_get_delay_info(!.ModeInfo, DelayInfo0),
|
|
delay_info_enter_conj(DelayInfo0, DelayInfo1),
|
|
mode_info_set_delay_info(DelayInfo1, !ModeInfo),
|
|
|
|
mode_info_get_live_vars(!.ModeInfo, LiveVars1),
|
|
mode_info_add_goals_live_vars(ConjType, Goals0, !ModeInfo),
|
|
|
|
% Try to schedule the goals of the conjunction.
|
|
modecheck_conj_list_flatten_and_schedule(ConjType, Goals0, Goals1,
|
|
[], RevImpurityErrors0, !ModeInfo),
|
|
|
|
mode_info_get_delay_info(!.ModeInfo, DelayInfo2),
|
|
delay_info_leave_conj(DelayInfo2, DelayedGoals0, DelayInfo3),
|
|
mode_info_set_delay_info(DelayInfo3, !ModeInfo),
|
|
|
|
% Try to schedule the goals that our earlier scheduling attempt delayed.
|
|
modecheck_delayed_goals(ConjType, DelayedGoals0, DelayedGoals, Goals2,
|
|
RevImpurityErrors0, RevImpurityErrors, !ModeInfo),
|
|
Goals = Goals1 ++ Goals2,
|
|
|
|
mode_info_get_errors(!.ModeInfo, NewErrors),
|
|
Errors = OldErrors ++ NewErrors,
|
|
mode_info_set_errors(Errors, !ModeInfo),
|
|
|
|
% We only report impurity errors if there were no other errors.
|
|
(
|
|
DelayedGoals = [],
|
|
|
|
% Report all the impurity errors
|
|
% (making sure we report the errors in the correct order).
|
|
list.reverse(RevImpurityErrors, ImpurityErrors),
|
|
mode_info_get_errors(!.ModeInfo, Errors5),
|
|
Errors6 = Errors5 ++ ImpurityErrors,
|
|
mode_info_set_errors(Errors6, !ModeInfo)
|
|
;
|
|
DelayedGoals = [FirstDelayedGoal | MoreDelayedGoals],
|
|
% The variables in the delayed goals should no longer be considered
|
|
% live (the conjunction itself will delay, and its nonlocals will be
|
|
% made live).
|
|
mode_info_set_live_vars(LiveVars1, !ModeInfo),
|
|
(
|
|
MoreDelayedGoals = [],
|
|
FirstDelayedGoal = delayed_goal(_DVars, Error, _DGoal),
|
|
mode_info_add_error(Error, !ModeInfo)
|
|
;
|
|
MoreDelayedGoals = [_ | _],
|
|
get_all_waiting_vars(DelayedGoals, Vars),
|
|
ModeError = mode_error_conj(DelayedGoals, conj_floundered),
|
|
mode_info_error(Vars, ModeError, !ModeInfo)
|
|
)
|
|
).
|
|
|
|
:- type impurity_errors == list(mode_error_info).
|
|
|
|
% Schedule goals, and also flatten conjunctions of the same type as we go.
|
|
%
|
|
:- pred modecheck_conj_list_flatten_and_schedule(conj_type::in,
|
|
list(hlds_goal)::in, list(hlds_goal)::out,
|
|
impurity_errors::in, impurity_errors::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
modecheck_conj_list_flatten_and_schedule(ConjType, Goals0, Goals,
|
|
!ImpurityErrors, !ModeInfo) :-
|
|
modecheck_conj_list_flatten_and_schedule_acc(ConjType, Goals0,
|
|
cord.init, GoalsCord, !ImpurityErrors, !ModeInfo),
|
|
Goals = cord.list(GoalsCord).
|
|
|
|
:- pred modecheck_conj_list_flatten_and_schedule_acc(conj_type::in,
|
|
list(hlds_goal)::in, cord(hlds_goal)::in, cord(hlds_goal)::out,
|
|
impurity_errors::in, impurity_errors::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
modecheck_conj_list_flatten_and_schedule_acc(_ConjType, [], !Goals,
|
|
!ImpurityErrors, !ModeInfo).
|
|
modecheck_conj_list_flatten_and_schedule_acc(ConjType, [Goal0 | Goals0],
|
|
!Goals, !ImpurityErrors, !ModeInfo) :-
|
|
( if
|
|
Goal0 = hlds_goal(conj(plain_conj, ConjGoals), _),
|
|
ConjType = plain_conj
|
|
then
|
|
Goals1 = ConjGoals ++ Goals0,
|
|
modecheck_conj_list_flatten_and_schedule_acc(ConjType, Goals1,
|
|
!Goals, !ImpurityErrors, !ModeInfo)
|
|
else
|
|
% We attempt to schedule the first goal in the conjunction.
|
|
% If successful, we try to wake up pending goals (if any), and if not,
|
|
% we delay the goal. Then we continue attempting to schedule
|
|
% all the rest of the conjuncts.
|
|
|
|
Purity = goal_get_purity(Goal0),
|
|
(
|
|
Purity = purity_impure,
|
|
Impure = yes,
|
|
check_for_impurity_error(Goal0, ScheduledSolverGoals,
|
|
!ImpurityErrors, !ModeInfo),
|
|
!:Goals = !.Goals ++ cord.from_list(ScheduledSolverGoals)
|
|
;
|
|
( Purity = purity_pure
|
|
; Purity = purity_semipure
|
|
),
|
|
Impure = no
|
|
),
|
|
|
|
% Hang onto the original instmap, delay_info, and live_vars.
|
|
mode_info_get_instmap(!.ModeInfo, InstMap0),
|
|
mode_info_get_delay_info(!.ModeInfo, DelayInfo0),
|
|
|
|
% Modecheck the goal, noting first that the non-locals
|
|
% which occur in the goal might not be live anymore.
|
|
NonLocalVars = goal_get_nonlocals(Goal0),
|
|
mode_info_remove_live_vars(NonLocalVars, !ModeInfo),
|
|
modecheck_goal(Goal0, Goal, !ModeInfo),
|
|
|
|
% Now see whether the goal was successfully scheduled. If we didn't
|
|
% manage to schedule the goal, then we restore the original instmap,
|
|
% delay_info and livevars here, and delay the goal.
|
|
mode_info_get_errors(!.ModeInfo, Errors),
|
|
(
|
|
Errors = [FirstErrorInfo | _],
|
|
mode_info_set_errors([], !ModeInfo),
|
|
mode_info_set_instmap(InstMap0, !ModeInfo),
|
|
mode_info_add_live_vars(NonLocalVars, !ModeInfo),
|
|
delay_info_delay_goal(FirstErrorInfo, Goal0,
|
|
DelayInfo0, DelayInfo1),
|
|
% Delaying an impure goal is an impurity error.
|
|
(
|
|
Impure = yes,
|
|
FirstErrorInfo = mode_error_info(Vars, _, _, _),
|
|
ImpureError = mode_error_conj(
|
|
[delayed_goal(Vars, FirstErrorInfo, Goal0)],
|
|
goal_itself_was_impure),
|
|
mode_info_get_context(!.ModeInfo, Context),
|
|
mode_info_get_mode_context(!.ModeInfo, ModeContext),
|
|
ImpureErrorInfo = mode_error_info(Vars, ImpureError,
|
|
Context, ModeContext),
|
|
!:ImpurityErrors = [ImpureErrorInfo | !.ImpurityErrors]
|
|
;
|
|
Impure = no
|
|
)
|
|
;
|
|
Errors = [],
|
|
mode_info_get_delay_info(!.ModeInfo, DelayInfo1),
|
|
% We successfully scheduled this goal, so insert it
|
|
% in the list of successfully scheduled goals.
|
|
% We flatten out conjunctions if we can. They can arise
|
|
% when Goal0 was a scope(from_ground_term, _) goal.
|
|
( if Goal = hlds_goal(conj(ConjType, SubGoals), _) then
|
|
!:Goals = !.Goals ++ from_list(SubGoals)
|
|
else
|
|
!:Goals = snoc(!.Goals, Goal)
|
|
)
|
|
),
|
|
|
|
% Next, we attempt to wake up any pending goals, and then continue
|
|
% scheduling the rest of the goal.
|
|
delay_info_wakeup_goals(WokenGoals, DelayInfo1, DelayInfo),
|
|
Goals1 = WokenGoals ++ Goals0,
|
|
(
|
|
WokenGoals = []
|
|
;
|
|
WokenGoals = [_],
|
|
mode_checkpoint(wakeup, "goal", !ModeInfo)
|
|
;
|
|
WokenGoals = [_, _ | _],
|
|
mode_checkpoint(wakeup, "goals", !ModeInfo)
|
|
),
|
|
mode_info_set_delay_info(DelayInfo, !ModeInfo),
|
|
mode_info_get_instmap(!.ModeInfo, InstMap),
|
|
( if instmap_is_unreachable(InstMap) then
|
|
% We should not mode-analyse the remaining goals, since they are
|
|
% unreachable. Instead we optimize them away, so that later passes
|
|
% won't complain about them not having mode information.
|
|
mode_info_remove_goals_live_vars(Goals1, !ModeInfo)
|
|
else
|
|
% The remaining goals may still need to be flattened.
|
|
modecheck_conj_list_flatten_and_schedule_acc(ConjType, Goals1,
|
|
!Goals, !ImpurityErrors, !ModeInfo)
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% The attempt to schedule the goals of a conjunction by our caller
|
|
% may have delayed some goals. Try to schedule those goals now.
|
|
%
|
|
% If we succeed in scheduling some delayed goal, this may wake up other
|
|
% delayed goals, so recurse in order to try to schedule them.
|
|
% Keep recursing until we can schedule no more delayed goal,
|
|
% whether that happens because there are no more left, or not.
|
|
%
|
|
:- pred modecheck_delayed_goals(conj_type::in, list(delayed_goal)::in,
|
|
list(delayed_goal)::out, list(hlds_goal)::out,
|
|
impurity_errors::in, impurity_errors::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
modecheck_delayed_goals(ConjType, DelayedGoals0, DelayedGoals, Goals,
|
|
!ImpurityErrors, !ModeInfo) :-
|
|
(
|
|
% There are no unscheduled goals, so we don't need to do anything.
|
|
DelayedGoals0 = [],
|
|
DelayedGoals = [],
|
|
Goals = []
|
|
;
|
|
% There are some unscheduled goals.
|
|
DelayedGoals0 = [_ | _],
|
|
|
|
Goals0 = list.map(hlds_goal_from_delayed_goal, DelayedGoals0),
|
|
|
|
mode_info_get_delay_info(!.ModeInfo, DelayInfo0),
|
|
delay_info_enter_conj(DelayInfo0, DelayInfo1),
|
|
mode_info_set_delay_info(DelayInfo1, !ModeInfo),
|
|
|
|
modecheck_conj_list_flatten_and_schedule(ConjType, Goals0, Goals1,
|
|
!ImpurityErrors, !ModeInfo),
|
|
|
|
mode_info_get_delay_info(!.ModeInfo, DelayInfo2),
|
|
delay_info_leave_conj(DelayInfo2, DelayedGoals1, DelayInfo3),
|
|
mode_info_set_delay_info(DelayInfo3, !ModeInfo),
|
|
|
|
% See if we scheduled any goals.
|
|
( if list.length(DelayedGoals1) < list.length(DelayedGoals0) then
|
|
% We scheduled some goals. Keep going until we either
|
|
% flounder or succeed.
|
|
modecheck_delayed_goals(ConjType, DelayedGoals1, DelayedGoals,
|
|
Goals2, !ImpurityErrors, !ModeInfo),
|
|
Goals = Goals1 ++ Goals2
|
|
else
|
|
DelayedGoals = DelayedGoals1,
|
|
Goals = Goals1
|
|
)
|
|
).
|
|
|
|
:- func hlds_goal_from_delayed_goal(delayed_goal) = hlds_goal.
|
|
|
|
hlds_goal_from_delayed_goal(delayed_goal(_WaitingVars, _ModeError, Goal)) =
|
|
Goal.
|
|
|
|
% Check whether there are any delayed goals (other than unifications)
|
|
% at the point where we are about to schedule an impure goal. If so,
|
|
% that is an error. Headvar unifications are allowed to be delayed
|
|
% because in the case of output arguments, they cannot be scheduled until
|
|
% the variable value is known. If headvar unifications couldn't be delayed
|
|
% past impure goals, impure predicates wouldn't be able to have outputs!
|
|
% (Note that we first try to schedule any delayed solver goals waiting
|
|
% for initialisation.)
|
|
%
|
|
:- pred check_for_impurity_error(hlds_goal::in, list(hlds_goal)::out,
|
|
impurity_errors::in, impurity_errors::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
check_for_impurity_error(Goal, Goals, !ImpurityErrors, !ModeInfo) :-
|
|
mode_info_get_delay_info(!.ModeInfo, DelayInfo0),
|
|
delay_info_leave_conj(DelayInfo0, DelayedGoals0, DelayInfo1),
|
|
mode_info_set_delay_info(DelayInfo1, !ModeInfo),
|
|
mode_info_get_module_info(!.ModeInfo, ModuleInfo),
|
|
mode_info_get_pred_id(!.ModeInfo, PredId),
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
pred_info_get_clauses_info(PredInfo, ClausesInfo),
|
|
clauses_info_get_headvar_list(ClausesInfo, HeadVars),
|
|
filter_headvar_unification_goals(HeadVars, DelayedGoals0,
|
|
HeadVarUnificationGoals, NonHeadVarUnificationGoals0),
|
|
modecheck_delayed_goals(plain_conj,
|
|
NonHeadVarUnificationGoals0, NonHeadVarUnificationGoals, Goals,
|
|
!ImpurityErrors, !ModeInfo),
|
|
mode_info_get_delay_info(!.ModeInfo, DelayInfo2),
|
|
delay_info_enter_conj(DelayInfo2, DelayInfo3),
|
|
redelay_goals(HeadVarUnificationGoals, DelayInfo3, DelayInfo),
|
|
mode_info_set_delay_info(DelayInfo, !ModeInfo),
|
|
(
|
|
NonHeadVarUnificationGoals = []
|
|
;
|
|
NonHeadVarUnificationGoals = [_ | _],
|
|
get_all_waiting_vars(NonHeadVarUnificationGoals, Vars),
|
|
ModeError = mode_error_conj(NonHeadVarUnificationGoals,
|
|
goals_followed_by_impure_goal(Goal)),
|
|
mode_info_get_context(!.ModeInfo, Context),
|
|
mode_info_get_mode_context(!.ModeInfo, ModeContext),
|
|
ImpurityError = mode_error_info(Vars, ModeError, Context, ModeContext),
|
|
!:ImpurityErrors = [ImpurityError | !.ImpurityErrors]
|
|
).
|
|
|
|
:- pred filter_headvar_unification_goals(list(prog_var)::in,
|
|
list(delayed_goal)::in, list(delayed_goal)::out, list(delayed_goal)::out)
|
|
is det.
|
|
|
|
filter_headvar_unification_goals(HeadVars, DelayedGoals,
|
|
HeadVarUnificationGoals, NonHeadVarUnificationGoals) :-
|
|
list.filter(is_headvar_unification_goal(HeadVars), DelayedGoals,
|
|
HeadVarUnificationGoals, NonHeadVarUnificationGoals).
|
|
|
|
:- pred is_headvar_unification_goal(list(prog_var)::in, delayed_goal::in)
|
|
is semidet.
|
|
|
|
is_headvar_unification_goal(HeadVars, delayed_goal(_, _, Goal)) :-
|
|
Goal ^ hg_expr = unify(Var, RHS, _, _, _),
|
|
(
|
|
list.member(Var, HeadVars)
|
|
;
|
|
RHS = rhs_var(OtherVar),
|
|
list.member(OtherVar, HeadVars)
|
|
).
|
|
|
|
% Given an association list of Vars - Goals,
|
|
% combine all the Vars together into a single set.
|
|
%
|
|
:- pred get_all_waiting_vars(list(delayed_goal)::in, set_of_progvar::out)
|
|
is det.
|
|
|
|
get_all_waiting_vars(DelayedGoals, Vars) :-
|
|
get_all_waiting_vars_2(DelayedGoals, set_of_var.init, Vars).
|
|
|
|
:- pred get_all_waiting_vars_2(list(delayed_goal)::in,
|
|
set_of_progvar::in, set_of_progvar::out) is det.
|
|
|
|
get_all_waiting_vars_2([], Vars, Vars).
|
|
get_all_waiting_vars_2([delayed_goal(Vars1, _, _) | Rest], Vars0, Vars) :-
|
|
set_of_var.union(Vars0, Vars1, Vars2),
|
|
get_all_waiting_vars_2(Rest, Vars2, Vars).
|
|
|
|
:- pred redelay_goals(list(delayed_goal)::in, delay_info::in, delay_info::out)
|
|
is det.
|
|
|
|
redelay_goals([], !DelayInfo).
|
|
redelay_goals([DelayedGoal | DelayedGoals], !DelayInfo) :-
|
|
DelayedGoal = delayed_goal(_WaitingVars, ModeErrorInfo, Goal),
|
|
delay_info_delay_goal(ModeErrorInfo, Goal, !DelayInfo),
|
|
redelay_goals(DelayedGoals, !DelayInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module check_hlds.modecheck_conj.
|
|
%-----------------------------------------------------------------------------%
|