mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-20 11:54:02 +00:00
compiler/mode_errors.m:
Give the function symbols representing the various kinds of mode errors
more meaningful names. (Some fix misleading implications, such as when
a function symbol name includes "pred", but the error applies to functions
as well.) Do the same for other function symbols in this module.
Where arguments are lists that should never be empty, change their type
to one_or_more to encode this invariant.
In one case (var_multimode_pred_error), the same data items were packaged
in two different ways in two different places. Remove this unnecessary
difference.
In some cases, put arguments in a more logical order.
Improve the comments on most of these function symbols, fixing errors,
fixing omissions, documenting arguments. Add XXXs where warranted.
Put the types used in the various kinds of mode errors below the
mode_error type itself. Put them into meaningful groups.
The current order of the mode_error function symbols is totally
haphazard. Propose a new order for them, but do not implement it yet,
in order to make this diff easier to review.
Make the representation of the error that says "calling an implied mode
for this predicate or function is not implemented" say *why* it is
not implemented. Include this info in the error message we generate.
This error message is not exercised by any existing test case, which is
why there are no changes to test cases in this diff.
Improve the explanation for why you cannot unify two functions or
two predicates. Since this explanation occurs only in verbose output,
no test case is affected by this change either.
Improve variable names.
compiler/mode_info.m:
Conform to the changes in mode_errors.m.
If --debug-modes is specified, print each error message as it is added
to the mode_info. This helped me track down a bug in this change.
compiler/instmap.m:
compiler/modecheck_call.m:
compiler/modecheck_conj.m:
compiler/modecheck_goal.m:
compiler/modecheck_unify.m:
compiler/modecheck_util.m:
compiler/modes.m:
Conform to the changes in mode_errors.m and/or mode_info.m.
Add XXXs in some places. One marks one possible cause of Mantis bug #529.
1060 lines
43 KiB
Mathematica
1060 lines
43 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2009-2012 The University of Melbourne.
|
|
% Copyright (C) 2015 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: modecheck_util.m.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module check_hlds.modecheck_util.
|
|
:- interface.
|
|
|
|
:- import_module check_hlds.mode_info.
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_goal.
|
|
:- 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 list.
|
|
:- import_module maybe.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type extra_goals
|
|
---> no_extra_goals
|
|
; extra_goals(
|
|
% Goals to insert before the main goal.
|
|
extra_before_main :: list(hlds_goal),
|
|
|
|
% Goals to append after the main goal.
|
|
extra_after_main :: list(hlds_goal)
|
|
).
|
|
|
|
:- type after_goals
|
|
---> no_after_goals
|
|
; after_goals(
|
|
% Instmap at end of main goal.
|
|
after_instmap :: instmap,
|
|
|
|
% Goals to append after the main goal.
|
|
after_goals :: list(hlds_goal)
|
|
).
|
|
|
|
% Append_extra_goals inserts adds some goals to the
|
|
% list of goals to insert before/after the main goal.
|
|
%
|
|
:- pred append_extra_goals(extra_goals::in, extra_goals::in,
|
|
extra_goals::out) is det.
|
|
|
|
% Handle_extra_goals combines MainGoal and ExtraGoals into a single
|
|
% hlds_goal_expr, rerunning mode analysis on the entire conjunction
|
|
% if ExtraGoals is not empty.
|
|
%
|
|
:- pred handle_extra_goals(hlds_goal_expr::in, extra_goals::in,
|
|
hlds_goal_info::in, list(prog_var)::in, list(prog_var)::in,
|
|
instmap::in, hlds_goal_expr::out, mode_info::in, mode_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Calculate the argument number offset that needs to be passed to
|
|
% modecheck_var_list_is_live, modecheck_var_has_inst_list, and
|
|
% modecheck_set_var_inst_list. This offset number is calculated so that
|
|
% real arguments get positive argument numbers and type_info arguments
|
|
% get argument numbers less than or equal to 0.
|
|
%
|
|
:- pred compute_arg_offset(pred_info::in, int::out) is det.
|
|
|
|
% Given a list of variables and a list of expected liveness, ensure
|
|
% that the inst of each variable satisfies the corresponding expected
|
|
% liveness. See below for the difference between the two variants.
|
|
%
|
|
:- pred modecheck_var_list_is_live_exact_match(list(prog_var)::in,
|
|
list(is_live)::in, int::in, mode_info::in, mode_info::out) is det.
|
|
:- pred modecheck_var_list_is_live_no_exact_match(list(prog_var)::in,
|
|
list(is_live)::in, int::in, mode_info::in, mode_info::out) is det.
|
|
|
|
% Given a list of variables and a list of initial insts, ensure that
|
|
% the inst of each variable matches the corresponding initial inst.
|
|
% The first variant requires an exact match (using inst_matches_final),
|
|
% while the second we allow the var to be more instantiated than the inst
|
|
% (using inst_matches_initial).
|
|
%
|
|
:- pred modecheck_var_has_inst_list_exact_match(list(prog_var)::in,
|
|
list(mer_inst)::in, int::in, inst_var_sub::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred modecheck_var_has_inst_list_no_exact_match(list(prog_var)::in,
|
|
list(mer_inst)::in, int::in, inst_var_sub::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
% This is a special-cased, cut-down version of
|
|
% modecheck_var_has_inst_list_no_exact_match for use specifically
|
|
% on introduced type_info_type variables.
|
|
%
|
|
:- pred modecheck_introduced_type_info_var_has_inst_no_exact_match(
|
|
prog_var::in, mer_type::in, mer_inst::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred get_var_inst(mode_info::in, prog_var::in, mer_inst::out) is det.
|
|
|
|
% modecheck_set_var_inst(Var, Inst, MaybeUInst, !ModeInfo):
|
|
%
|
|
% Assign the given Inst to the given Var, after checking that it is
|
|
% okay to do so. If the inst to be assigned is the result of an
|
|
% abstract unification then the MaybeUInst argument should be the
|
|
% initial inst of the _other_ side of the unification. This allows
|
|
% more precise (i.e. less conservative) checking in the case that
|
|
% Inst contains `any' components and Var is locked (i.e. is a
|
|
% nonlocal variable in a negated context). Where the inst is not
|
|
% the result of an abstract unification then MaybeUInst should be `no'.
|
|
%
|
|
:- pred modecheck_set_var_inst(prog_var::in, mer_inst::in, maybe(mer_inst)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred modecheck_set_var_inst_list(list(prog_var)::in, list(mer_inst)::in,
|
|
list(mer_inst)::in, int::in, list(prog_var)::out, extra_goals::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_add_goals_live_vars(conj_type::in, list(hlds_goal)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_remove_goals_live_vars(list(hlds_goal)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% modecheck_functor_test(Var, ConsId, !ModeInfo):
|
|
%
|
|
% Update the instmap to reflect the fact that Var was bound to ConsId.
|
|
% This is used for the functor tests in `switch' statements.
|
|
%
|
|
:- pred modecheck_functor_test(prog_var::in, cons_id::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
% modecheck_functors_test(Var, MainConsId, OtherConsIds, !ModeInfo):
|
|
%
|
|
% Update the instmap to reflect the fact that Var was bound to either
|
|
% MainConsId or one of the OtherConsIds.
|
|
% This is used for the functor tests in `switch' statements.
|
|
%
|
|
:- pred modecheck_functors_test(prog_var::in, cons_id::in, list(cons_id)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% compute_goal_instmap_delta(InstMap0, GoalExpr, !GoalInfo, !ModeInfo):
|
|
%
|
|
% Work out the instmap_delta for a goal from the instmaps before and after
|
|
% the goal. The instmap before the goal is given by InstMap0; the instmap
|
|
% after the goal is given by !.ModeInfo.
|
|
%
|
|
:- pred compute_goal_instmap_delta(instmap::in, hlds_goal_expr::in,
|
|
hlds_goal_info::in, hlds_goal_info::out, mode_info::in, mode_info::out)
|
|
is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred mode_context_to_unify_context(mode_info::in, mode_context::in,
|
|
unify_context::out) is det.
|
|
|
|
% Given a list of variables, and a list of livenesses,
|
|
% select the live variables.
|
|
%
|
|
:- pred get_live_vars(list(prog_var)::in, list(is_live)::in,
|
|
list(prog_var)::out) is det.
|
|
|
|
% Return a map of all the inst variables in the given modes, and the
|
|
% sub-insts to which they are constrained.
|
|
%
|
|
:- pred get_constrained_inst_vars(module_info::in, list(mer_mode)::in,
|
|
head_inst_vars::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.delay_info.
|
|
:- import_module check_hlds.inst_match.
|
|
:- import_module check_hlds.inst_test.
|
|
:- import_module check_hlds.inst_util.
|
|
:- import_module check_hlds.mode_errors.
|
|
:- import_module check_hlds.mode_util.
|
|
:- import_module check_hlds.modecheck_goal.
|
|
:- import_module check_hlds.modecheck_unify.
|
|
:- import_module check_hlds.type_util.
|
|
:- import_module hlds.vartypes.
|
|
:- import_module parse_tree.prog_mode.
|
|
:- import_module parse_tree.set_of_var.
|
|
|
|
:- import_module bool.
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module set_tree234.
|
|
:- import_module term.
|
|
:- import_module unit.
|
|
:- import_module varset.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
append_extra_goals(no_extra_goals, ExtraGoals, ExtraGoals).
|
|
append_extra_goals(extra_goals(BeforeGoals, AfterGoals),
|
|
no_extra_goals, extra_goals(BeforeGoals, AfterGoals)).
|
|
append_extra_goals(extra_goals(BeforeGoals0, AfterGoals0),
|
|
extra_goals(BeforeGoals1, AfterGoals1),
|
|
extra_goals(BeforeGoals, AfterGoals)) :-
|
|
BeforeGoals = BeforeGoals0 ++ BeforeGoals1,
|
|
AfterGoals = AfterGoals0 ++ AfterGoals1.
|
|
|
|
handle_extra_goals(MainGoal, no_extra_goals, _GoalInfo0, _Args0, _Args,
|
|
_InstMap0, MainGoal, !ModeInfo).
|
|
handle_extra_goals(MainGoal, extra_goals(BeforeGoals0, AfterGoals0),
|
|
GoalInfo0, Args0, Args, InstMap0, Goal, !ModeInfo) :-
|
|
mode_info_get_errors(!.ModeInfo, Errors),
|
|
( if
|
|
% There is no point adding extra goals if the code is unreachable
|
|
% anyway.
|
|
instmap_is_reachable(InstMap0),
|
|
|
|
% If we recorded errors processing the goal, it will have to be
|
|
% reprocessed anyway, so don't add the extra goals now.
|
|
Errors = []
|
|
then
|
|
% We need to be careful to update the delta-instmaps
|
|
% correctly, using the appropriate instmaps:
|
|
%
|
|
% % InstMapAtStart is here
|
|
% BeforeGoals,
|
|
% % we don't know the instmap here,
|
|
% % but as it happens we don't need it
|
|
% main goal,
|
|
% % InstMapAfterMain is here
|
|
% AfterGoals
|
|
% % InstMapAtEnd (from the ModeInfo) is here
|
|
|
|
% Recompute the new set of non-local variables for the main goal.
|
|
NonLocals0 = goal_info_get_nonlocals(GoalInfo0),
|
|
set_of_var.list_to_set(Args0, OldArgVars),
|
|
set_of_var.list_to_set(Args, NewArgVars),
|
|
set_of_var.difference(NewArgVars, OldArgVars, IntroducedVars),
|
|
set_of_var.union(NonLocals0, IntroducedVars, OutsideVars),
|
|
set_of_var.intersect(OutsideVars, NewArgVars, NonLocals),
|
|
goal_info_set_nonlocals(NonLocals, GoalInfo0, GoalInfo),
|
|
|
|
% Combine the main goal and the extra goals into a conjunction.
|
|
Goal0 = hlds_goal(MainGoal, GoalInfo),
|
|
Context = goal_info_get_context(GoalInfo0),
|
|
handle_extra_goals_contexts(BeforeGoals0, Context, BeforeGoals),
|
|
handle_extra_goals_contexts(AfterGoals0, Context, AfterGoals),
|
|
GoalList0 = BeforeGoals ++ [Goal0 | AfterGoals],
|
|
|
|
mode_info_get_may_change_called_proc(!.ModeInfo, MayChangeCalledProc0),
|
|
|
|
% Make sure we don't go into an infinite loop if
|
|
% there is a bug in the code to add extra goals.
|
|
mode_info_set_checking_extra_goals(yes, !ModeInfo),
|
|
|
|
% We have already worked out which procedure should be called,
|
|
% we don't need to do it again.
|
|
mode_info_set_may_change_called_proc(may_not_change_called_proc,
|
|
!ModeInfo),
|
|
|
|
mode_info_set_instmap(InstMap0, !ModeInfo),
|
|
|
|
% Recheck the goals to compute the instmap_deltas.
|
|
%
|
|
% This can fail even if the original check on the goal
|
|
% succeeded in the case of a unification procedure which
|
|
% binds a partially instantiated variable, because adding
|
|
% the extra goals can make the partially instantiated
|
|
% variables `live' after the main goal.
|
|
% The other thing to beware of in this case is that delaying
|
|
% must be disabled while processing the extra goals. If it
|
|
% is not, the main unification will be delayed until after the
|
|
% argument unifications, which turns them into assignments,
|
|
% and we end up repeating the process forever.
|
|
mode_info_add_goals_live_vars(plain_conj, GoalList0, !ModeInfo),
|
|
modecheck_conj_list_no_delay(GoalList0, GoalList, !ModeInfo),
|
|
Goal = conj(plain_conj, GoalList),
|
|
mode_info_set_checking_extra_goals(no, !ModeInfo),
|
|
mode_info_set_may_change_called_proc(MayChangeCalledProc0, !ModeInfo)
|
|
else
|
|
Goal = MainGoal
|
|
).
|
|
|
|
% Modecheck a conjunction without doing any reordering.
|
|
% This is used by handle_extra_goals above.
|
|
%
|
|
:- pred modecheck_conj_list_no_delay(list(hlds_goal)::in, list(hlds_goal)::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
modecheck_conj_list_no_delay([], [], !ModeInfo).
|
|
modecheck_conj_list_no_delay([Goal0 | Goals0], [Goal | Goals], !ModeInfo) :-
|
|
NonLocals = goal_get_nonlocals(Goal0),
|
|
mode_info_remove_live_vars(NonLocals, !ModeInfo),
|
|
modecheck_goal(Goal0, Goal, !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(Goals0, !ModeInfo),
|
|
Goals = []
|
|
else
|
|
modecheck_conj_list_no_delay(Goals0, Goals, !ModeInfo)
|
|
).
|
|
|
|
:- pred handle_extra_goals_contexts(list(hlds_goal)::in, prog_context::in,
|
|
list(hlds_goal)::out) is det.
|
|
|
|
handle_extra_goals_contexts([], _Context, []).
|
|
handle_extra_goals_contexts([Goal0 | Goals0], Context, [Goal | Goals]) :-
|
|
Goal0 = hlds_goal(GoalExpr, GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo),
|
|
handle_extra_goals_contexts(Goals0, Context, Goals).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
compute_arg_offset(PredInfo, ArgOffset) :-
|
|
OrigArity = pred_info_orig_arity(PredInfo),
|
|
pred_info_get_arg_types(PredInfo, ArgTypes),
|
|
list.length(ArgTypes, CurrentArity),
|
|
ArgOffset = OrigArity - CurrentArity.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
modecheck_var_list_is_live_exact_match([], [], _ArgNum, !ModeInfo).
|
|
modecheck_var_list_is_live_exact_match([_ | _], [], _, !ModeInfo) :-
|
|
unexpected($pred, "length mismatch").
|
|
modecheck_var_list_is_live_exact_match([], [_ | _], _, !ModeInfo) :-
|
|
unexpected($pred, "length mismatch").
|
|
modecheck_var_list_is_live_exact_match([Var | Vars], [IsLive | IsLives],
|
|
ArgNum0, !ModeInfo) :-
|
|
ArgNum = ArgNum0 + 1,
|
|
mode_info_set_call_arg_context(ArgNum, !ModeInfo),
|
|
modecheck_var_is_live_exact_match(Var, IsLive, !ModeInfo),
|
|
modecheck_var_list_is_live_exact_match(Vars, IsLives, ArgNum, !ModeInfo).
|
|
|
|
modecheck_var_list_is_live_no_exact_match([], [], _ArgNum, !ModeInfo).
|
|
modecheck_var_list_is_live_no_exact_match([_ | _], [], _, !ModeInfo) :-
|
|
unexpected($pred, "length mismatch").
|
|
modecheck_var_list_is_live_no_exact_match([], [_ | _], _, !ModeInfo) :-
|
|
unexpected($pred, "length mismatch").
|
|
modecheck_var_list_is_live_no_exact_match([Var | Vars], [IsLive | IsLives],
|
|
ArgNum0, !ModeInfo) :-
|
|
ArgNum = ArgNum0 + 1,
|
|
mode_info_set_call_arg_context(ArgNum, !ModeInfo),
|
|
modecheck_var_is_live_no_exact_match(Var, IsLive, !ModeInfo),
|
|
modecheck_var_list_is_live_no_exact_match(Vars, IsLives, ArgNum,
|
|
!ModeInfo).
|
|
|
|
% `live' means possibly used later on, and `dead' means definitely not used
|
|
% later on. If you don't need an exact match, then the only time you get
|
|
% an error is if you pass a variable which is live to a predicate
|
|
% that expects the variable to be dead; the predicate may use destructive
|
|
% update to clobber the variable, so we must be sure that it is dead
|
|
% after the call.
|
|
%
|
|
|
|
% A version of modecheck_var_is_live specialized for NeedExactMatch = no.
|
|
%
|
|
:- pred modecheck_var_is_live_no_exact_match(prog_var::in, is_live::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
modecheck_var_is_live_no_exact_match(Var, ExpectedIsLive, !ModeInfo) :-
|
|
mode_info_var_is_live(!.ModeInfo, Var, VarIsLive),
|
|
( if
|
|
ExpectedIsLive = is_dead,
|
|
VarIsLive = is_live
|
|
then
|
|
WaitingVars = set_of_var.make_singleton(Var),
|
|
ModeError = mode_error_clobbered_var_is_live(Var),
|
|
mode_info_error(WaitingVars, ModeError, !ModeInfo)
|
|
else
|
|
true
|
|
).
|
|
|
|
% A version of modecheck_var_is_live specialized for NeedExactMatch = yes.
|
|
%
|
|
:- pred modecheck_var_is_live_exact_match(prog_var::in, is_live::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
modecheck_var_is_live_exact_match(Var, ExpectedIsLive, !ModeInfo) :-
|
|
mode_info_var_is_live(!.ModeInfo, Var, VarIsLive),
|
|
( if VarIsLive = ExpectedIsLive then
|
|
true
|
|
else
|
|
WaitingVars = set_of_var.make_singleton(Var),
|
|
ModeError = mode_error_clobbered_var_is_live(Var),
|
|
mode_info_error(WaitingVars, ModeError, !ModeInfo)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
modecheck_var_has_inst_list_exact_match(Vars, Insts, ArgNum, Subst,
|
|
!ModeInfo) :-
|
|
modecheck_var_has_inst_list_exact_match_2(Vars, Insts, ArgNum,
|
|
map.init, Subst, !ModeInfo),
|
|
modecheck_head_inst_vars(Vars, Subst, !ModeInfo).
|
|
|
|
modecheck_var_has_inst_list_no_exact_match(Vars, Insts, ArgNum, Subst,
|
|
!ModeInfo) :-
|
|
modecheck_var_has_inst_list_no_exact_match_2(Vars, Insts, ArgNum,
|
|
map.init, Subst, !ModeInfo),
|
|
modecheck_head_inst_vars(Vars, Subst, !ModeInfo).
|
|
|
|
:- pred modecheck_var_has_inst_list_exact_match_2(list(prog_var)::in,
|
|
list(mer_inst)::in, int::in, inst_var_sub::in, inst_var_sub::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
modecheck_var_has_inst_list_exact_match_2([], [], _ArgNum, !Subst, !ModeInfo).
|
|
modecheck_var_has_inst_list_exact_match_2([_ | _], [], _, !Subst, !ModeInfo) :-
|
|
unexpected($pred, "length mismatch").
|
|
modecheck_var_has_inst_list_exact_match_2([], [_ | _], _, !Subst, !ModeInfo) :-
|
|
unexpected($pred, "length mismatch").
|
|
modecheck_var_has_inst_list_exact_match_2([Var | Vars], [Inst | Insts],
|
|
ArgNum0, !Subst, !ModeInfo) :-
|
|
ArgNum = ArgNum0 + 1,
|
|
mode_info_set_call_arg_context(ArgNum, !ModeInfo),
|
|
modecheck_var_has_inst_exact_match(Var, Inst, !Subst, !ModeInfo),
|
|
modecheck_var_has_inst_list_exact_match_2(Vars, Insts, ArgNum,
|
|
!Subst, !ModeInfo).
|
|
|
|
:- pred modecheck_var_has_inst_list_no_exact_match_2(list(prog_var)::in,
|
|
list(mer_inst)::in, int::in, inst_var_sub::in, inst_var_sub::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
modecheck_var_has_inst_list_no_exact_match_2([], [], _ArgNum,
|
|
!Subst, !ModeInfo).
|
|
modecheck_var_has_inst_list_no_exact_match_2([_ | _], [], _,
|
|
!Subst, !ModeInfo) :-
|
|
unexpected($pred, "length mismatch").
|
|
modecheck_var_has_inst_list_no_exact_match_2([], [_ | _], _,
|
|
!Subst, !ModeInfo) :-
|
|
unexpected($pred, "length mismatch").
|
|
modecheck_var_has_inst_list_no_exact_match_2([Var | Vars], [Inst | Insts],
|
|
ArgNum0, !Subst, !ModeInfo) :-
|
|
ArgNum = ArgNum0 + 1,
|
|
mode_info_set_call_arg_context(ArgNum, !ModeInfo),
|
|
modecheck_var_has_inst_no_exact_match(Var, Inst, !Subst, !ModeInfo),
|
|
modecheck_var_has_inst_list_no_exact_match_2(Vars, Insts, ArgNum,
|
|
!Subst, !ModeInfo).
|
|
|
|
:- pred modecheck_var_has_inst_exact_match(prog_var::in, mer_inst::in,
|
|
inst_var_sub::in, inst_var_sub::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
modecheck_var_has_inst_exact_match(Var, Inst0, !Subst, !ModeInfo) :-
|
|
% Apply the substitution computed while matching earlier arguments.
|
|
inst_apply_substitution(!.Subst, Inst0, Inst),
|
|
mode_info_get_instmap(!.ModeInfo, InstMap),
|
|
instmap_lookup_var(InstMap, Var, VarInst),
|
|
mode_info_get_var_types(!.ModeInfo, VarTypes),
|
|
lookup_var_type(VarTypes, Var, Type),
|
|
mode_info_get_module_info(!.ModeInfo, ModuleInfo0),
|
|
( if
|
|
inst_matches_initial_no_implied_modes_sub(VarInst, Inst, Type,
|
|
ModuleInfo0, ModuleInfo, !Subst)
|
|
then
|
|
mode_info_set_module_info(ModuleInfo, !ModeInfo)
|
|
else
|
|
mode_info_get_pred_var_multimode_error_map(!.ModeInfo,
|
|
MultiModeErrorMap),
|
|
( if map.search(MultiModeErrorMap, Var, MultiModeError) then
|
|
MaybeMultiModeError = yes(MultiModeError)
|
|
else
|
|
MaybeMultiModeError = no
|
|
),
|
|
WaitingVars = set_of_var.make_singleton(Var),
|
|
ModeError = mode_error_var_is_not_sufficiently_instantiated(Var,
|
|
VarInst, Inst, MaybeMultiModeError),
|
|
mode_info_error(WaitingVars, ModeError, !ModeInfo)
|
|
).
|
|
|
|
:- pred modecheck_var_has_inst_no_exact_match(prog_var::in, mer_inst::in,
|
|
inst_var_sub::in, inst_var_sub::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
modecheck_var_has_inst_no_exact_match(Var, Inst0, !Subst, !ModeInfo) :-
|
|
% Apply the substitution computed while matching earlier arguments.
|
|
inst_apply_substitution(!.Subst, Inst0, Inst),
|
|
mode_info_get_instmap(!.ModeInfo, InstMap),
|
|
instmap_lookup_var(InstMap, Var, VarInst),
|
|
mode_info_get_var_types(!.ModeInfo, VarTypes),
|
|
lookup_var_type(VarTypes, Var, Type),
|
|
mode_info_get_module_info(!.ModeInfo, ModuleInfo0),
|
|
( if
|
|
inst_matches_initial_sub(VarInst, Inst, Type, ModuleInfo0, ModuleInfo,
|
|
!Subst)
|
|
then
|
|
mode_info_set_module_info(ModuleInfo, !ModeInfo)
|
|
else
|
|
mode_info_get_pred_var_multimode_error_map(!.ModeInfo,
|
|
MultiModeErrorMap),
|
|
( if map.search(MultiModeErrorMap, Var, MultiModeError) then
|
|
MaybeMultiModeError = yes(MultiModeError)
|
|
else
|
|
MaybeMultiModeError = no
|
|
),
|
|
WaitingVars = set_of_var.make_singleton(Var),
|
|
ModeError = mode_error_var_is_not_sufficiently_instantiated(Var,
|
|
VarInst, Inst, MaybeMultiModeError),
|
|
mode_info_error(WaitingVars, ModeError, !ModeInfo)
|
|
).
|
|
|
|
modecheck_introduced_type_info_var_has_inst_no_exact_match(Var, Type, Inst,
|
|
!ModeInfo) :-
|
|
mode_info_get_instmap(!.ModeInfo, InstMap),
|
|
instmap_lookup_var(InstMap, Var, VarInst),
|
|
mode_info_get_module_info(!.ModeInfo, ModuleInfo0),
|
|
( if
|
|
inst_matches_initial_sub(VarInst, Inst, Type, ModuleInfo0, ModuleInfo,
|
|
map.init, _Subst)
|
|
then
|
|
mode_info_set_module_info(ModuleInfo, !ModeInfo)
|
|
else
|
|
WaitingVars = set_of_var.make_singleton(Var),
|
|
ModeError = mode_error_var_is_not_sufficiently_instantiated(Var,
|
|
VarInst, Inst, no),
|
|
mode_info_error(WaitingVars, ModeError, !ModeInfo)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred modecheck_head_inst_vars(list(prog_var)::in, inst_var_sub::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
modecheck_head_inst_vars(Vars, InstVarSub, !ModeInfo) :-
|
|
mode_info_get_head_inst_vars(!.ModeInfo, HeadInstVars),
|
|
( if
|
|
map.foldl(modecheck_head_inst_var(HeadInstVars), InstVarSub, unit, _)
|
|
then
|
|
true
|
|
else
|
|
mode_info_get_instmap(!.ModeInfo, InstMap),
|
|
instmap_lookup_vars(InstMap, Vars, VarInsts),
|
|
WaitingVars = set_of_var.list_to_set(Vars),
|
|
ModeError = mode_error_no_matching_mode(Vars, VarInsts, []),
|
|
mode_info_error(WaitingVars, ModeError, !ModeInfo)
|
|
).
|
|
|
|
:- pred modecheck_head_inst_var(inst_var_sub::in, inst_var::in, mer_inst::in,
|
|
unit::in, unit::out) is semidet.
|
|
|
|
modecheck_head_inst_var(HeadInstVars, InstVar, Subst, !Acc) :-
|
|
( if map.search(HeadInstVars, InstVar, Inst) then
|
|
% Subst should not change the constraint. However, the two insts
|
|
% may have different information about inst test results.
|
|
Subst = constrained_inst_vars(SubstInstVars, SubstInst),
|
|
set.member(InstVar, SubstInstVars),
|
|
( if
|
|
Inst = bound(Uniq, _, BoundInsts),
|
|
SubstInst = bound(SubstUniq, _, SubstBoundInsts)
|
|
then
|
|
Uniq = SubstUniq,
|
|
BoundInsts = SubstBoundInsts
|
|
else
|
|
Inst = SubstInst
|
|
)
|
|
else
|
|
true
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
get_var_inst(ModeInfo, Var, Inst) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
mode_info_get_instmap(ModeInfo, InstMap),
|
|
mode_info_get_var_types(ModeInfo, VarTypes),
|
|
instmap_lookup_var(InstMap, Var, Inst0),
|
|
lookup_var_type(VarTypes, Var, Type),
|
|
normalise_inst(ModuleInfo, Type, Inst0, Inst).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
modecheck_set_var_inst(Var0, NewInst0, MaybeUInst, !ModeInfo) :-
|
|
% Note that there are two versions of modecheck_set_var_inst,
|
|
% one with arity 8 (suffixed with _call) and one with arity 5.
|
|
% The former is used for predicate calls, where we may need
|
|
% to introduce unifications to handle calls to implied modes.
|
|
%
|
|
mode_info_get_parallel_vars(!.ModeInfo, PVars0),
|
|
mode_info_get_instmap(!.ModeInfo, InstMap0),
|
|
( if instmap_is_reachable(InstMap0) then
|
|
instmap_lookup_var(InstMap0, Var0, OldInst),
|
|
mode_info_get_module_info(!.ModeInfo, ModuleInfo0),
|
|
% The final new inst must be computed by unifying the old inst
|
|
% and the tentative new inst. However, abstractly unifying
|
|
% a large inst with itself can be VERY expensive; it can be worse
|
|
% than quadratic. The OldInst = NewInst test here may increase
|
|
% execution time slightly in normal cases, but should reduce it
|
|
% greatly in the worst cases.
|
|
( if
|
|
OldInst = NewInst0
|
|
then
|
|
ModuleInfo = ModuleInfo0,
|
|
NewInst = NewInst0
|
|
else if
|
|
abstractly_unify_inst(is_dead, OldInst, NewInst0,
|
|
fake_unify, UnifyInst, _Det, ModuleInfo0, ModuleInfo1)
|
|
then
|
|
ModuleInfo = ModuleInfo1,
|
|
NewInst = UnifyInst
|
|
else
|
|
unexpected($pred, "unify_inst failed")
|
|
),
|
|
mode_info_set_module_info(ModuleInfo, !ModeInfo),
|
|
mode_info_get_var_types(!.ModeInfo, VarTypes),
|
|
lookup_var_type(VarTypes, Var0, Type),
|
|
( if
|
|
% If the top-level inst of the variable is not_reached,
|
|
% then the instmap as a whole must be unreachable.
|
|
inst_expand(ModuleInfo, NewInst, not_reached)
|
|
then
|
|
instmap.init_unreachable(InstMap),
|
|
mode_info_set_instmap(InstMap, !ModeInfo)
|
|
else if
|
|
% If we haven't added any information and
|
|
% we haven't bound any part of the var, then
|
|
% the only thing we can have done is lose uniqueness.
|
|
inst_matches_initial(OldInst, NewInst, Type, ModuleInfo)
|
|
then
|
|
instmap_set_var(Var0, NewInst, InstMap0, InstMap),
|
|
mode_info_set_instmap(InstMap, !ModeInfo)
|
|
else if
|
|
% We must have either added some information,
|
|
% lost some uniqueness, or bound part of the var.
|
|
% The call to inst_matches_binding will succeed
|
|
% only if we haven't bound any part of the var.
|
|
not inst_matches_binding(NewInst, OldInst, Type, ModuleInfo),
|
|
|
|
% We have bound part of the var. If the var was locked,
|
|
% then we need to report an error ...
|
|
mode_info_var_is_locked(!.ModeInfo, Var0, Reason0),
|
|
not (
|
|
% ... unless the goal is a unification and the var was unified
|
|
% with something no more instantiated than itself. This allows
|
|
% for the case of `any = free', for example. The call to
|
|
% inst_matches_binding above will fail for the var with
|
|
% mode `any >> any', however it should be allowed because
|
|
% it has only been unified with a free variable.
|
|
MaybeUInst = yes(UInst),
|
|
inst_is_at_least_as_instantiated(NewInst, UInst, Type,
|
|
ModuleInfo),
|
|
inst_matches_binding_allow_any_any(NewInst0, OldInst, Type,
|
|
ModuleInfo)
|
|
)
|
|
then
|
|
WaitingVars = set_of_var.make_singleton(Var0),
|
|
ModeError = mode_error_bind_locked_var(Reason0, Var0,
|
|
OldInst, NewInst),
|
|
mode_info_error(WaitingVars, ModeError, !ModeInfo)
|
|
else
|
|
instmap_set_var(Var0, NewInst, InstMap0, InstMap),
|
|
mode_info_set_instmap(InstMap, !ModeInfo),
|
|
mode_info_get_delay_info(!.ModeInfo, DelayInfo0),
|
|
delay_info_bind_var(Var0, DelayInfo0, DelayInfo),
|
|
mode_info_set_delay_info(DelayInfo, !ModeInfo)
|
|
)
|
|
else
|
|
true
|
|
),
|
|
(
|
|
PVars0 = []
|
|
;
|
|
PVars0 = [par_conj_mode_check(NonLocals, Bound0) | PVars1],
|
|
( if set_of_var.member(NonLocals, Var0) then
|
|
set_of_var.insert(Var0, Bound0, Bound),
|
|
PVars = [par_conj_mode_check(NonLocals, Bound) | PVars1]
|
|
else
|
|
PVars = PVars0
|
|
),
|
|
mode_info_set_parallel_vars(PVars, !ModeInfo)
|
|
).
|
|
|
|
modecheck_set_var_inst_list(Vars0, InitialInsts, FinalInsts, ArgOffset,
|
|
Vars, Goals, !ModeInfo) :-
|
|
( if
|
|
modecheck_set_var_inst_list_2(Vars0, InitialInsts, FinalInsts,
|
|
ArgOffset, Vars1, no_extra_goals, Goals1, !ModeInfo)
|
|
then
|
|
Vars = Vars1,
|
|
Goals = Goals1
|
|
else
|
|
unexpected($pred, "length mismatch")
|
|
).
|
|
|
|
:- pred modecheck_set_var_inst_list_2(list(prog_var)::in, list(mer_inst)::in,
|
|
list(mer_inst)::in, int::in, list(prog_var)::out,
|
|
extra_goals::in, extra_goals::out, mode_info::in, mode_info::out)
|
|
is semidet.
|
|
|
|
modecheck_set_var_inst_list_2([], [], [], _, [], !ExtraGoals, !ModeInfo).
|
|
modecheck_set_var_inst_list_2([Var0 | Vars0], [InitialInst | InitialInsts],
|
|
[FinalInst | FinalInsts], ArgNum0, [Var | Vars],
|
|
!ExtraGoals, !ModeInfo) :-
|
|
ArgNum = ArgNum0 + 1,
|
|
mode_info_set_call_arg_context(ArgNum, !ModeInfo),
|
|
modecheck_set_var_inst_call(Var0, InitialInst, FinalInst,
|
|
Var, !ExtraGoals, !ModeInfo),
|
|
modecheck_set_var_inst_list_2(Vars0, InitialInsts, FinalInsts, ArgNum,
|
|
Vars, !ExtraGoals, !ModeInfo).
|
|
|
|
:- pred modecheck_set_var_inst_call(prog_var::in, mer_inst::in, mer_inst::in,
|
|
prog_var::out, extra_goals::in, extra_goals::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
modecheck_set_var_inst_call(Var0, InitialInst, FinalInst, Var, !ExtraGoals,
|
|
!ModeInfo) :-
|
|
mode_info_get_instmap(!.ModeInfo, InstMap0),
|
|
( if instmap_is_reachable(InstMap0) then
|
|
instmap_lookup_var(InstMap0, Var0, VarInst0),
|
|
handle_implied_mode(Var0, VarInst0, InitialInst, Var, !ExtraGoals,
|
|
!ModeInfo),
|
|
% The new inst must be computed by unifying the old inst
|
|
% and the proc's final inst; modecheck_set_var_inst will do this.
|
|
modecheck_set_var_inst(Var0, FinalInst, no, !ModeInfo),
|
|
( if Var = Var0 then
|
|
true
|
|
else
|
|
modecheck_set_var_inst(Var, FinalInst, no, !ModeInfo)
|
|
)
|
|
else
|
|
Var = Var0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% If this was a call to an implied mode for that variable, then we need to
|
|
% introduce a fresh variable.
|
|
%
|
|
:- pred handle_implied_mode(prog_var::in, mer_inst::in, mer_inst::in,
|
|
prog_var::out, extra_goals::in, extra_goals::out,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
handle_implied_mode(Var0, VarInst0, InitialInst0, Var,
|
|
!ExtraGoals, !ModeInfo) :-
|
|
mode_info_get_module_info(!.ModeInfo, ModuleInfo0),
|
|
inst_expand(ModuleInfo0, InitialInst0, InitialInst),
|
|
inst_expand(ModuleInfo0, VarInst0, VarInst1),
|
|
|
|
mode_info_get_var_types(!.ModeInfo, VarTypes0),
|
|
lookup_var_type(VarTypes0, Var0, VarType),
|
|
( if
|
|
% If the initial inst of the variable matches_final the initial inst
|
|
% specified in the pred's mode declaration, then it is not a call
|
|
% to an implied mode, it is an exact match with a genuine mode.
|
|
inst_matches_initial_no_implied_modes_sub(VarInst1, InitialInst,
|
|
VarType, ModuleInfo0, _ModuleInfo, map.init, _Sub)
|
|
then
|
|
Var = Var0
|
|
else
|
|
% This is the implied mode case.
|
|
( if
|
|
InitialInst = any(_, _),
|
|
inst_is_free(ModuleInfo0, VarInst1)
|
|
then
|
|
% This is the simple case of implied `any' modes, where
|
|
% the declared mode was `any -> ...' and the argument passed
|
|
% was `free'.
|
|
Var = Var0,
|
|
|
|
% If the variable's type is not a solver type (in which case
|
|
% inst `any' means the same as inst `ground') then this is
|
|
% an implied mode that we don't yet know how to handle.
|
|
% XXX I (zs) see no test for solver types here.
|
|
WaitingVars = set_of_var.make_singleton(Var0),
|
|
ModeError = mode_error_cannot_create_implied_mode(cannot_init_any,
|
|
Var0, VarInst0, InitialInst),
|
|
mode_info_error(WaitingVars, ModeError, !ModeInfo)
|
|
else if
|
|
inst_is_bound(ModuleInfo0, InitialInst)
|
|
then
|
|
% We do not yet handle implied modes for partially instantiated
|
|
% vars, since that would require doing a partially instantiated
|
|
% deep copy, and we don't know how to do that yet.
|
|
Var = Var0,
|
|
WaitingVars = set_of_var.make_singleton(Var0),
|
|
Reason = cannot_deep_copy_partial_term,
|
|
ModeError = mode_error_cannot_create_implied_mode(Reason,
|
|
Var0, VarInst0, InitialInst),
|
|
mode_info_error(WaitingVars, ModeError, !ModeInfo)
|
|
else
|
|
% This is the simple case of implied modes,
|
|
% where the declared mode was free -> ...
|
|
|
|
% Introduce a new variable.
|
|
mode_info_get_varset(!.ModeInfo, VarSet0),
|
|
varset.new_var(Var, VarSet0, VarSet),
|
|
add_var_type(Var, VarType, VarTypes0, VarTypes),
|
|
mode_info_set_varset(VarSet, !ModeInfo),
|
|
mode_info_set_var_types(VarTypes, !ModeInfo),
|
|
|
|
% Construct the code to do the unification.
|
|
create_var_var_unification(Var0, Var, VarType, !.ModeInfo,
|
|
ExtraGoal),
|
|
|
|
% Append the goals together in the appropriate order:
|
|
% ExtraGoals0, then NewUnify.
|
|
NewUnifyExtraGoal = extra_goals([], [ExtraGoal]),
|
|
append_extra_goals(!.ExtraGoals, NewUnifyExtraGoal, !:ExtraGoals)
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
mode_info_add_goals_live_vars(_ConjType, [], !ModeInfo).
|
|
mode_info_add_goals_live_vars(ConjType, [Goal | Goals], !ModeInfo) :-
|
|
% We add the live vars for the goals in the goal list in reverse order,
|
|
% because this ensures that in the common case (where there is no
|
|
% delaying), when we come to remove the live vars for the first goal
|
|
% they will have been added last and will thus be at the start of the list
|
|
% of live vars sets, which makes them cheaper to remove.
|
|
mode_info_add_goals_live_vars(ConjType, Goals, !ModeInfo),
|
|
( if
|
|
% Recurse into conjunctions, in case there are any conjunctions
|
|
% that have not been flattened.
|
|
Goal = hlds_goal(conj(ConjType, ConjGoals), _)
|
|
then
|
|
mode_info_add_goals_live_vars(ConjType, ConjGoals, !ModeInfo)
|
|
else
|
|
NonLocals = goal_get_nonlocals(Goal),
|
|
mode_info_add_live_vars(NonLocals, !ModeInfo)
|
|
).
|
|
|
|
mode_info_remove_goals_live_vars([], !ModeInfo).
|
|
mode_info_remove_goals_live_vars([Goal | Goals], !ModeInfo) :-
|
|
( if
|
|
% Recurse into conjunctions, in case there are any conjunctions
|
|
% that have not been flattened.
|
|
Goal = hlds_goal(conj(plain_conj, ConjGoals), _)
|
|
then
|
|
mode_info_remove_goals_live_vars(ConjGoals, !ModeInfo)
|
|
else
|
|
NonLocals = goal_get_nonlocals(Goal),
|
|
mode_info_remove_live_vars(NonLocals, !ModeInfo)
|
|
),
|
|
mode_info_remove_goals_live_vars(Goals, !ModeInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
modecheck_functor_test(Var, ConsId, !ModeInfo) :-
|
|
% Figure out the arity of this constructor, _including_ any type-infos
|
|
% or typeclass-infos inserted for existential data types.
|
|
mode_info_get_module_info(!.ModeInfo, ModuleInfo),
|
|
mode_info_get_var_types(!.ModeInfo, VarTypes),
|
|
lookup_var_type(VarTypes, Var, Type),
|
|
BoundInst = cons_id_to_bound_inst(ModuleInfo, Type, ConsId),
|
|
|
|
% Record the fact that Var was bound to ConsId.
|
|
Inst = bound(unique, inst_test_no_results, [BoundInst]),
|
|
modecheck_set_var_inst(Var, Inst, no, !ModeInfo).
|
|
|
|
modecheck_functors_test(Var, MainConsId, OtherConsIds, !ModeInfo) :-
|
|
% Figure out the arity of this constructor, _including_ any type-infos
|
|
% or typeclass-infos inserted for existential data types.
|
|
mode_info_get_module_info(!.ModeInfo, ModuleInfo),
|
|
mode_info_get_var_types(!.ModeInfo, VarTypes),
|
|
lookup_var_type(VarTypes, Var, Type),
|
|
BoundInsts = list.map(cons_id_to_bound_inst(ModuleInfo, Type),
|
|
[MainConsId | OtherConsIds]),
|
|
|
|
% Record the fact that Var was bound to MainConsId or one of the
|
|
% OtherConsIds.
|
|
Inst = bound(unique, inst_test_no_results, BoundInsts),
|
|
modecheck_set_var_inst(Var, Inst, no, !ModeInfo).
|
|
|
|
:- func cons_id_to_bound_inst(module_info, mer_type, cons_id) = bound_inst.
|
|
|
|
cons_id_to_bound_inst(ModuleInfo, Type, ConsId) = BoundInst :-
|
|
ConsIdAdjustedArity = cons_id_adjusted_arity(ModuleInfo, Type, ConsId),
|
|
list.duplicate(ConsIdAdjustedArity, free, ArgInsts),
|
|
BoundInst = bound_functor(ConsId, ArgInsts).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
compute_goal_instmap_delta(InstMap0, GoalExpr, !GoalInfo, !ModeInfo) :-
|
|
( if GoalExpr = conj(_, []) then
|
|
% When modecheck_unify.m replaces a unification with a dead variable
|
|
% with `true', make sure the instmap_delta of the goal is empty.
|
|
% The code generator and mode_util.recompute_instmap_delta can be
|
|
% confused by references to the dead variable in the instmap_delta,
|
|
% resulting in calls to error/1.
|
|
instmap_delta_init_reachable(InstMapDelta),
|
|
mode_info_set_instmap(InstMap0, !ModeInfo)
|
|
else
|
|
NonLocals = goal_info_get_nonlocals(!.GoalInfo),
|
|
mode_info_get_instmap(!.ModeInfo, InstMap),
|
|
compute_instmap_delta(InstMap0, InstMap, NonLocals, InstMapDelta)
|
|
),
|
|
goal_info_set_instmap_delta(InstMapDelta, !GoalInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
mode_context_to_unify_context(ModeInfo, ModeContext, UnifyContext) :-
|
|
(
|
|
ModeContext = mode_context_unify(UnifyContext, _)
|
|
;
|
|
ModeContext = mode_context_call(ModeCallId, Arg),
|
|
(
|
|
ModeCallId = mode_call_plain(PredId),
|
|
mode_info_get_pf_sym_name_arity(ModeInfo, PredId, PFSymNameArity),
|
|
CallId = plain_call_id(PFSymNameArity)
|
|
;
|
|
ModeCallId = mode_call_generic(GenericCallId),
|
|
CallId = generic_call_id(GenericCallId)
|
|
),
|
|
UnifyContext = unify_context(umc_call(CallId, Arg), [])
|
|
;
|
|
ModeContext = mode_context_uninitialized,
|
|
unexpected($pred, "uninitialized context")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
get_live_vars([], [], []).
|
|
get_live_vars([_ | _], [], _) :-
|
|
unexpected($pred, "length mismatch").
|
|
get_live_vars([], [_ | _], _) :-
|
|
unexpected($pred, "length mismatch").
|
|
get_live_vars([Var | Vars], [IsLive | IsLives], LiveVars) :-
|
|
(
|
|
IsLive = is_live,
|
|
LiveVars = [Var | LiveVars0]
|
|
;
|
|
IsLive = is_dead,
|
|
LiveVars = LiveVars0
|
|
),
|
|
get_live_vars(Vars, IsLives, LiveVars0).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type inst_expansions == set_tree234(inst_name).
|
|
|
|
get_constrained_inst_vars(ModuleInfo, Modes, Map) :-
|
|
list.foldl2(get_constrained_insts_in_mode(ModuleInfo), Modes,
|
|
map.init, Map, set_tree234.init, _Expansions).
|
|
|
|
:- pred get_constrained_insts_in_mode(module_info::in, mer_mode::in,
|
|
head_inst_vars::in, head_inst_vars::out,
|
|
inst_expansions::in, inst_expansions::out) is det.
|
|
|
|
get_constrained_insts_in_mode(ModuleInfo, Mode, !Map, !Expansions) :-
|
|
mode_get_insts(ModuleInfo, Mode, InitialInst, FinalInst),
|
|
get_constrained_insts_in_inst(ModuleInfo, InitialInst, !Map, !Expansions),
|
|
get_constrained_insts_in_inst(ModuleInfo, FinalInst, !Map, !Expansions).
|
|
|
|
:- pred get_constrained_insts_in_inst(module_info::in, mer_inst::in,
|
|
head_inst_vars::in, head_inst_vars::out,
|
|
inst_expansions::in, inst_expansions::out) is det.
|
|
|
|
get_constrained_insts_in_inst(ModuleInfo, Inst, !Map, !Expansions) :-
|
|
(
|
|
( Inst = free
|
|
; Inst = free(_)
|
|
; Inst = not_reached
|
|
)
|
|
;
|
|
Inst = bound(_, InstResults, BoundInsts),
|
|
(
|
|
InstResults = inst_test_results_fgtc
|
|
;
|
|
InstResults = inst_test_results(_, _, _, InstVarsResult, _, _),
|
|
( if
|
|
InstVarsResult =
|
|
inst_result_contains_inst_vars_known(InstVars),
|
|
set.is_empty(InstVars)
|
|
then
|
|
true
|
|
else
|
|
list.foldl2(get_constrained_insts_in_bound_inst(ModuleInfo),
|
|
BoundInsts, !Map, !Expansions)
|
|
)
|
|
;
|
|
InstResults = inst_test_no_results,
|
|
list.foldl2(get_constrained_insts_in_bound_inst(ModuleInfo),
|
|
BoundInsts, !Map, !Expansions)
|
|
)
|
|
;
|
|
( Inst = any(_, HOInstInfo)
|
|
; Inst = ground(_, HOInstInfo)
|
|
),
|
|
(
|
|
HOInstInfo = none_or_default_func
|
|
;
|
|
HOInstInfo = higher_order(PredInstInfo),
|
|
get_constrained_insts_in_ho_inst(ModuleInfo, PredInstInfo,
|
|
!Map, !Expansions)
|
|
)
|
|
;
|
|
Inst = constrained_inst_vars(InstVars, _),
|
|
inst_expand_and_remove_constrained_inst_vars(ModuleInfo,
|
|
Inst, SubInst),
|
|
set.fold(add_constrained_inst(SubInst), InstVars, !Map)
|
|
;
|
|
Inst = defined_inst(InstName),
|
|
( if insert_new(InstName, !Expansions) then
|
|
inst_lookup(ModuleInfo, InstName, ExpandedInst),
|
|
get_constrained_insts_in_inst(ModuleInfo, ExpandedInst,
|
|
!Map, !Expansions)
|
|
else
|
|
true
|
|
)
|
|
;
|
|
Inst = inst_var(_),
|
|
unexpected($pred, "inst_var")
|
|
;
|
|
Inst = abstract_inst(_, _),
|
|
sorry($pred, "abstract_inst")
|
|
).
|
|
|
|
:- pred get_constrained_insts_in_bound_inst(module_info::in, bound_inst::in,
|
|
head_inst_vars::in, head_inst_vars::out,
|
|
inst_expansions::in, inst_expansions::out) is det.
|
|
|
|
get_constrained_insts_in_bound_inst(ModuleInfo, BoundInst,
|
|
!Map, !Expansions) :-
|
|
BoundInst = bound_functor(_ConsId, Insts),
|
|
list.foldl2(get_constrained_insts_in_inst(ModuleInfo), Insts,
|
|
!Map, !Expansions).
|
|
|
|
:- pred get_constrained_insts_in_ho_inst(module_info::in, pred_inst_info::in,
|
|
head_inst_vars::in, head_inst_vars::out,
|
|
inst_expansions::in, inst_expansions::out) is det.
|
|
|
|
get_constrained_insts_in_ho_inst(ModuleInfo, PredInstInfo,
|
|
!Map, !Expansions) :-
|
|
PredInstInfo = pred_inst_info(_, Modes, _, _),
|
|
list.foldl2(get_constrained_insts_in_mode(ModuleInfo), Modes,
|
|
!Map, !Expansions).
|
|
|
|
:- pred add_constrained_inst(mer_inst::in, inst_var::in,
|
|
head_inst_vars::in, head_inst_vars::out) is det.
|
|
|
|
add_constrained_inst(SubInst, InstVar, !Map) :-
|
|
( if map.search(!.Map, InstVar, SubInst0) then
|
|
( if SubInst0 = SubInst then
|
|
true
|
|
else
|
|
unexpected($pred, "SubInst differs")
|
|
)
|
|
else
|
|
map.det_insert(InstVar, SubInst, !Map)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module check_hlds.modecheck_util.
|
|
%-----------------------------------------------------------------------------%
|