mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-18 02:43:40 +00:00
compiler/error_spec.m:
This new module contains the part of the old error_util.m that defines
the error_spec type, and some functions that can help construct pieces
of error_specs. Most modules of the compiler that deal with errors
will need to import only this part of the old error_util.m.
This change also renames the format_component type to format_piece,
which matches our long-standing naming convention for variables containing
(lists of) values of this type.
compiler/write_error_spec.m:
This new module contains the part of the old error_util.m that
writes out error specs, and converts them to strings.
This diff marks as obsolete the versions of predicates that
write out error specs to the current output stream, without
*explicitly* specifying the intended stream.
compiler/error_sort.m:
This new module contains the part of the old error_util.m that
sorts lists of error specs and error msgs.
compiler/error_type_util.m:
This new module contains the part of the old error_util.m that
convert types to format_pieces that generate readable output.
compiler/parse_tree.m:
compiler/notes/compiler_design.html:
Include and document the new modules.
compiler/error_util.m:
The code remaining in the original error_util.m consists of
general utility predicates and functions that don't fit into
any of the modules above.
Delete an unneeded pair of I/O states from the argument list
of a predicate.
compiler/file_util.m:
Move the unable_to_open_file predicate here from error_util.m,
since it belongs here. Mark another predicate that writes
to the current output stream as obsolete.
compiler/hlds_error_util.m:
Mark two predicates that wrote out error_spec to the current output
stream as obsolete, and add versions that take an explicit output stream.
compiler/Mercury.options:
Compile the modules that call the newly obsoleted predicates
with --no-warn-obsolete, for the time being.
compiler/*.m:
Conform to the changes above, mostly by updating import_module
declarations, and renaming format_component to format_piece.
1179 lines
47 KiB
Mathematica
1179 lines
47 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2001, 2003-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: mode_info.m.
|
|
% Main author: fjh.
|
|
%
|
|
% This file defines the mode_info data structure, which is used to hold
|
|
% the information we need during mode analysis.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module check_hlds.mode_info.
|
|
:- interface.
|
|
|
|
:- import_module check_hlds.delay_info.
|
|
:- import_module check_hlds.mode_errors.
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_inst_mode.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.instmap.
|
|
:- import_module hlds.pred_table.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.set_of_var.
|
|
:- import_module parse_tree.var_table.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bag.
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% The mode_info data structure and access predicates.
|
|
|
|
% XXX `side' is not used
|
|
:- type mode_context
|
|
---> mode_context_call(
|
|
mode_call_id,
|
|
int % Argument number (offset so that the real
|
|
% arguments start at number 1 whereas the
|
|
% type_info arguments have numbers <= 0).
|
|
)
|
|
; mode_context_unify(
|
|
unify_context, % original source of the unification
|
|
side % LHS or RHS
|
|
)
|
|
; mode_context_not_call_or_unify.
|
|
|
|
% Initialize a mode_context.
|
|
%
|
|
:- pred mode_context_init(mode_context::out) is det.
|
|
|
|
:- type side
|
|
---> left
|
|
; right.
|
|
|
|
:- type call_context
|
|
---> call_context_unify(unify_context)
|
|
; call_context_call(mode_call_id).
|
|
|
|
:- type mode_call_id
|
|
---> mode_call_plain(pred_id)
|
|
; mode_call_generic(generic_call_id).
|
|
|
|
:- type var_lock_reason
|
|
---> var_lock_negation
|
|
; var_lock_if_then_else
|
|
; var_lock_lambda(pred_or_func)
|
|
; var_lock_trace_goal
|
|
; var_lock_atomic_goal
|
|
; var_lock_par_conj.
|
|
|
|
% Specify how to process goals - using either modes.m or unique_modes.m.
|
|
:- type how_to_check_goal
|
|
---> check_modes
|
|
; check_unique_modes.
|
|
|
|
% Is mode analysis allowed to change which procedure of a predicate
|
|
% is called. It may not change the called procedure after deforestation
|
|
% has performed a generalisation step, since that could result
|
|
% in selecting a less efficient mode, or one which doesn't even
|
|
% have the same termination behaviour.
|
|
% Also, when rechecking a goal after adding extra goals, it is
|
|
% not necessary to choose again which procedure is to be called.
|
|
:- type may_change_called_proc
|
|
---> may_change_called_proc
|
|
; may_not_change_called_proc.
|
|
|
|
:- type locked_vars == assoc_list(var_lock_reason, set_of_progvar).
|
|
|
|
:- type mode_info.
|
|
|
|
:- type debug_flags
|
|
---> debug_flags(
|
|
% The empty string when mode checking, and a prefix to put
|
|
% in front of goal kind names when unique mode checking.
|
|
unique_prefix :: string,
|
|
|
|
% The value --debug-modes-verbose.
|
|
verbose :: bool,
|
|
|
|
% The value --debug-modes-minimal.
|
|
minimal :: bool,
|
|
|
|
% The value --debug-modes-statistics.
|
|
statistics :: bool
|
|
).
|
|
|
|
% Initialize the mode_info.
|
|
%
|
|
:- pred mode_info_init(module_info::in, pred_id::in, proc_id::in,
|
|
prog_context::in, set_of_progvar::in, head_inst_vars::in, instmap::in,
|
|
how_to_check_goal::in, may_change_called_proc::in, mode_info::out) is det.
|
|
|
|
:- type in_promise_purity_scope
|
|
---> in_promise_purity_scope
|
|
; not_in_promise_purity_scope.
|
|
|
|
:- type in_from_ground_term_scope
|
|
---> in_from_ground_term_scope
|
|
; not_in_from_ground_term_scope.
|
|
|
|
:- type had_from_ground_term_scope
|
|
---> had_from_ground_term_scope
|
|
; did_not_have_from_ground_term_scope.
|
|
|
|
:- type make_ground_terms_unique
|
|
---> make_ground_terms_unique
|
|
; do_not_make_ground_terms_unique.
|
|
|
|
:- type in_dupl_for_switch
|
|
---> in_dupl_for_switch
|
|
; not_in_dupl_for_switch.
|
|
|
|
% We use a stack of pairs of sets of variables used to mode-check
|
|
% parallel conjunctions. The first set is the nonlocals of the
|
|
% parallel conjunction. The second set is a subset of the
|
|
% first, and is the set of variables that have been [further]
|
|
% bound inside the current parallel conjunct. The stack
|
|
% is for the correct handling of nested parallel conjunctions.
|
|
:- type par_conj_mode_check_stack == list(par_conj_mode_check).
|
|
:- type par_conj_mode_check
|
|
---> par_conj_mode_check(
|
|
par_conj_nonlocals :: set_of_progvar,
|
|
par_conj_bound :: set_of_progvar
|
|
).
|
|
|
|
:- type pred_var_multimode_error_map
|
|
== map(prog_var, pred_id_var_multimode_error).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Please keep these getter and setter predicates in the same order
|
|
% as the fields in mode_info/mode_sub_info.
|
|
%
|
|
|
|
% Getters of the fields of mode_info.
|
|
:- pred mode_info_get_module_info(mode_info::in, module_info::out) is det.
|
|
:- pred mode_info_get_instmap(mode_info::in, instmap::out) is det.
|
|
:- pred mode_info_get_delay_info(mode_info::in, delay_info::out) is det.
|
|
:- pred mode_info_get_errors(mode_info::in, list(mode_error_info)::out) is det.
|
|
:- pred mode_info_get_mode_context(mode_info::in, mode_context::out) is det.
|
|
:- pred mode_info_get_context(mode_info::in, prog_context::out) is det.
|
|
:- pred mode_info_get_nondet_live_vars(mode_info::in, bag(prog_var)::out)
|
|
is det.
|
|
|
|
% Getters of the fields of mode_sub_info.
|
|
:- pred mode_info_get_pred_id(mode_info::in, pred_id::out) is det.
|
|
:- pred mode_info_get_proc_id(mode_info::in, proc_id::out) is det.
|
|
:- pred mode_info_get_var_table(mode_info::in, var_table::out) is det.
|
|
:- pred mode_info_get_debug_modes(mode_info::in, maybe(debug_flags)::out)
|
|
is det.
|
|
:- pred mode_info_get_locked_vars(mode_info::in, locked_vars::out) is det.
|
|
:- pred mode_info_get_live_vars(mode_info::in, bag(prog_var)::out) is det.
|
|
:- pred mode_info_get_instvarset(mode_info::in, inst_varset::out) is det.
|
|
:- pred mode_info_get_parallel_vars(mode_info::in,
|
|
par_conj_mode_check_stack::out) is det.
|
|
:- pred mode_info_get_last_checkpoint_insts(mode_info::in, instmap::out)
|
|
is det.
|
|
:- pred mode_info_get_initial_instmap(mode_info::in, instmap::out) is det.
|
|
:- pred mode_info_get_head_inst_vars(mode_info::in, head_inst_vars::out)
|
|
is det.
|
|
:- pred mode_info_get_warnings(mode_info::in, list(mode_warning_info)::out)
|
|
is det.
|
|
:- pred mode_info_get_pred_var_multimode_error_map(mode_info::in,
|
|
pred_var_multimode_error_map::out) is det.
|
|
:- pred mode_info_get_how_to_check(mode_info::in,
|
|
how_to_check_goal::out) is det.
|
|
:- pred mode_info_get_may_change_called_proc(mode_info::in,
|
|
may_change_called_proc::out) is det.
|
|
:- pred mode_info_get_changed_flag(mode_info::in, bool::out) is det.
|
|
:- pred mode_info_get_checking_extra_goals(mode_info::in, bool::out) is det.
|
|
:- pred mode_info_get_need_to_requantify(mode_info::in,
|
|
need_to_requantify::out) is det.
|
|
:- pred mode_info_get_in_promise_purity_scope(mode_info::in,
|
|
in_promise_purity_scope::out) is det.
|
|
:- pred mode_info_get_in_from_ground_term(mode_info::in,
|
|
in_from_ground_term_scope::out) is det.
|
|
:- pred mode_info_get_had_from_ground_term(mode_info::in,
|
|
had_from_ground_term_scope::out) is det.
|
|
:- pred mode_info_get_make_ground_terms_unique(mode_info::in,
|
|
make_ground_terms_unique::out) is det.
|
|
:- pred mode_info_get_in_dupl_for_switch(mode_info::in,
|
|
in_dupl_for_switch::out) is det.
|
|
|
|
%---------------------%
|
|
|
|
% Setters of the fields of mode_info.
|
|
:- pred mode_info_set_module_info(module_info::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
% There is a mode_info_set_instmap, but it is not JUST a setter.
|
|
:- pred mode_info_set_delay_info(delay_info::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_errors(list(mode_error_info)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_mode_context(mode_context::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_context(prog_context::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_nondet_live_vars(bag(prog_var)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
% Setters of the fields of mode_sub_info.
|
|
:- pred mode_info_set_pred_id(pred_id::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_proc_id(proc_id::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_var_table(var_table::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
% There is no mode_info_set_debug_modes; this field is read-only.
|
|
:- pred mode_info_set_locked_vars(locked_vars::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_live_vars(bag(prog_var)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_instvarset(inst_varset::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_parallel_vars(par_conj_mode_check_stack::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_last_checkpoint_insts(instmap::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
% There is no mode_info_set_initial_instmap; this field is read-only.
|
|
% There is no mode_info_set_head_var_insts; this field is read-only.
|
|
:- pred mode_info_set_warnings(list(mode_warning_info)::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_pred_var_multimode_error_map(
|
|
pred_var_multimode_error_map::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
% There is no mode_info_set_how_to_check; this field is read-only.
|
|
:- pred mode_info_set_may_change_called_proc(may_change_called_proc::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_changed_flag(bool::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
% There is a mode_info_set_checking_extra_goals, but it is not JUST a setter.
|
|
:- pred mode_info_set_need_to_requantify(need_to_requantify::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_in_promise_purity_scope(in_promise_purity_scope::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_in_from_ground_term(in_from_ground_term_scope::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_had_from_ground_term(had_from_ground_term_scope::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_make_ground_terms_unique(make_ground_terms_unique::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_in_dupl_for_switch(in_dupl_for_switch::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Nontrivial getter predicates.
|
|
%
|
|
|
|
:- pred mode_info_get_num_errors(mode_info::in, int::out) is det.
|
|
:- pred mode_info_get_liveness(mode_info::in, set_of_progvar::out) is det.
|
|
|
|
:- pred mode_info_get_pred_id_table(mode_info::in, pred_id_table::out) is det.
|
|
:- pred mode_info_get_insts(mode_info::in, inst_table::out) is det.
|
|
:- pred mode_info_get_modes(mode_info::in, mode_table::out) is det.
|
|
|
|
:- pred mode_info_get_types_of_vars(mode_info::in,
|
|
list(prog_var)::in, list(mer_type)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Nontrivial setter predicates.
|
|
%
|
|
|
|
:- pred mode_info_set_instmap(instmap::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_checking_extra_goals(bool::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_set_call_context(call_context::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_unset_call_context(
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_set_call_arg_context(int::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_need_to_requantify(mode_info::in, mode_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Predicates related to liveness.
|
|
%
|
|
|
|
% Check whether a variable or a list of variables are live or not.
|
|
%
|
|
:- pred mode_info_var_is_live(mode_info::in, prog_var::in,
|
|
is_live::out) is det.
|
|
:- pred mode_info_var_list_is_live(mode_info::in, list(prog_var)::in,
|
|
list(is_live)::out) is det.
|
|
|
|
% Check whether a variable is nondet_live or not.
|
|
%
|
|
:- pred mode_info_var_is_nondet_live(mode_info::in, prog_var::in,
|
|
is_live::out) is det.
|
|
|
|
% We keep track of the live variables and the nondet-live variables
|
|
% as two bags. This allows us to easily add and remove sets of variables.
|
|
% It is probably not maximally efficient.
|
|
%
|
|
% mode_info_add_live_vars adds the given set of vars
|
|
% to the bag of live vars and the bag of nondet-live vars.
|
|
%
|
|
% mode_info_remove_live_vars removes the given set of vars
|
|
% from the bag of live vars and the bag of nondet-live vars.
|
|
%
|
|
:- pred mode_info_add_live_vars(set_of_progvar::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
:- pred mode_info_remove_live_vars(set_of_progvar::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Predicates related to locked variables.
|
|
%
|
|
|
|
:- pred mode_info_var_is_locked(mode_info::in, prog_var::in,
|
|
var_lock_reason::out) is semidet.
|
|
|
|
:- pred mode_info_lock_vars(var_lock_reason::in, set_of_progvar::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_unlock_vars(var_lock_reason::in, set_of_progvar::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Predicates related to errors and warnings.
|
|
%
|
|
|
|
% Record a mode error (and associated context info) in the mode_info.
|
|
%
|
|
:- pred mode_info_error(set_of_progvar::in, mode_error::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_add_error(mode_error_info::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_warning(mode_warning::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
:- pred mode_info_add_warning(mode_warning_info::in,
|
|
mode_info::in, mode_info::out) is det.
|
|
|
|
% Find the pf_sym_name_arity to use in error messages
|
|
% for the given pred_id.
|
|
%
|
|
:- pred mode_info_get_pf_sym_name_arity(mode_info::in, pred_id::in,
|
|
pf_sym_name_arity::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module parse_tree.write_error_spec.
|
|
|
|
:- import_module getopt.
|
|
:- import_module int.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
:- import_module varset.
|
|
|
|
% The information we need while we modecheck a procedure.
|
|
%
|
|
% The mode_info and mode_sub_info types constitute a single logical
|
|
% data structure split into two parts for efficiency purposes.
|
|
%
|
|
% The most frequently used fields are in the mode_info type,
|
|
% while all the other fields are in the mode_sub_info type.
|
|
%
|
|
% The sub-word-sized arguments of the mode_sub_info type
|
|
% are all at the end of the structure, to allow them to be packed together.
|
|
:- type mode_info
|
|
---> mode_info(
|
|
% The Boehm collector allocates blocks whose sizes are
|
|
% multiples of 2, so we should keep the number of fields
|
|
% in a mode_info to be a multiple of 2 as well.
|
|
|
|
/* 1 */ mi_module_info :: module_info,
|
|
|
|
% The current instantiatedness of the variables.
|
|
/* 2 */ mi_instmap :: instmap,
|
|
|
|
% Info about delayed goals.
|
|
/* 3 */ mi_delay_info :: delay_info,
|
|
|
|
% The mode errors found.
|
|
/* 4 */ mi_errors :: list(mode_error_info),
|
|
|
|
% Almost all mode errors are diagnosed when mode analysis
|
|
% processes a unification or a call. To prepape for
|
|
% the possibility of an error, before mode analysis
|
|
% starts processing one side of a unification, it records
|
|
% the identity of the side and of the unification in the
|
|
% mode context, and likewise before it starts processing
|
|
% the Nth argument of a call, we record N and the identity
|
|
% of the call. If an error occurs, the code in mode_errors.m
|
|
% that constructs the error message will pick up this context
|
|
% from here.
|
|
%
|
|
% A few kinds of mode errors occur outside both unifications
|
|
% and calls, and when processing HLDS constructs in which
|
|
% such errors can occur, this field should contain
|
|
% "mode_context_not_call_or_unify".
|
|
%
|
|
% When not processing a HLDS construct that is guaranteed
|
|
% not to generate a mode error, the contents of this field
|
|
% does not matter; it may be anything.
|
|
%
|
|
% When an error is discovered by code that is specific
|
|
% to e.g. unifications, this field should not be needed;
|
|
% the code should know in which part of which construct
|
|
% it found the error. On the other hand, some errors
|
|
% are caught by predicates that help process many kinds
|
|
% of HLDS goals, and they need their caller to tell them
|
|
% e.g. where the insts they handle come from.
|
|
%
|
|
% XXX The reason this field exists is probably because
|
|
% Fergus thought that it is easier to stuff this informatio
|
|
% into the mode_info, which those general-purpose predicates
|
|
% have already got, than to pass them in separate arguments,
|
|
% even though the latter would be semantically cleaner.
|
|
/* 5 */ mi_mode_context :: mode_context,
|
|
|
|
% The line number of the subgoal we are currently checking.
|
|
/* 6 */ mi_context :: prog_context,
|
|
|
|
% The nondet-live variables, i.e. those variables which may be
|
|
% referenced again after deep backtracking TO THE CURRENT
|
|
% EXECUTION POINT. These are the variables which need to be
|
|
% made mostly_unique rather than unique when we get to a
|
|
% nondet disjunction or a nondet call. We do not include
|
|
% variables which may be referenced again after backtracking
|
|
% to a point EARLIER THAN the current execution point, since
|
|
% those variables will *already* have been marked as
|
|
% mostly_unique rather than unique.)
|
|
/* 7 */ mi_nondet_live_vars :: bag(prog_var),
|
|
|
|
/* 8 */ mi_sub_info :: mode_sub_info
|
|
).
|
|
|
|
:- type mode_sub_info
|
|
---> mode_sub_info(
|
|
% The pred we are checking.
|
|
msi_pred_id :: pred_id,
|
|
|
|
% The mode which we are checking.
|
|
msi_proc_id :: proc_id,
|
|
|
|
% The names and types of the variables.
|
|
msi_var_table :: var_table,
|
|
|
|
% Is mode debugging of this procedure enabled? If yes,
|
|
% is verbose mode debugging enabled, is minimal mode debugging
|
|
% enabled, and is statistics printing enabled?
|
|
msi_debug :: maybe(debug_flags),
|
|
|
|
% The "locked" variables, i.e. variables which cannot be
|
|
% further instantiated inside a negated context.
|
|
msi_locked_vars :: locked_vars,
|
|
|
|
% The live variables, i.e. those variables which may be
|
|
% referenced again on forward execution or after shallow
|
|
% backtracking. (By shallow backtracking, I mean semidet
|
|
% backtracking in a negation, if-then-else, or semidet
|
|
% disjunction within the current predicate.)
|
|
msi_live_vars :: bag(prog_var),
|
|
|
|
% This starts out as the inst_varset taken from the proc_info
|
|
% of the procedure we are doing mode analysis on. As we make
|
|
% calls, we add to it the renamed-apart inst variables from
|
|
% the proc_infos of the procedures we call.
|
|
%
|
|
% I (zs) don't know the reason why these additions are needed.
|
|
%
|
|
% XXX Actually, we add to it the renamed-apart inst variables
|
|
% from the proc_infos of every procedure we *have considered*
|
|
% calling, because if a called predicate has two or more modes,
|
|
% we add the insts from *all* the inst_varsets of those
|
|
% procedures. Interestingly, if we throw away the additions
|
|
% to the inst_varset in the mode_info after we have finished
|
|
% considering making the call to a particular procedure,
|
|
% a bootcheck completes with just one test case failure,
|
|
% and that failure consists of the compiler generating
|
|
% an error message for invalid/constrained_poly_insts2
|
|
% that replaces a constrained inst variable with the
|
|
% constraining inst.
|
|
msi_instvarset :: inst_varset,
|
|
|
|
% A stack of pairs of sets of variables used to mode-check
|
|
% parallel conjunctions. The first set is the nonlocals of the
|
|
% parallel conjunction. The second set is a subset of the
|
|
% first, and is the set of variables that have been [further]
|
|
% bound inside the current parallel conjunct - the stack
|
|
% is for the correct handling of nested parallel conjunctions.
|
|
msi_par_conj :: par_conj_mode_check_stack,
|
|
|
|
% This field is used by the checkpoint code when debug_modes
|
|
% is on. It has the instmap that was current at the last mode
|
|
% checkpoint, so that checkpoints do not print out the insts
|
|
% of variables whose insts have not changed since the last
|
|
% checkpoint. This field will always contain an unreachable
|
|
% instmap if debug_modes is off, since its information is not
|
|
% needed then.
|
|
msi_last_checkpoint_insts :: instmap,
|
|
|
|
% The initial instmap of the procedure body. Used to decide
|
|
% whether a unification that cannot fail could be influenced
|
|
% by an argument mode that enforces a subtype.
|
|
msi_initial_instmap :: instmap,
|
|
|
|
% The inst vars that appear in the procedure head and their
|
|
% constraints.
|
|
msi_head_inst_vars :: head_inst_vars,
|
|
|
|
% The mode warnings found.
|
|
msi_warnings :: list(mode_warning_info),
|
|
|
|
% This field maps variables for which we have generated
|
|
% a mode_error_unify_var_multimode_pred to the rest of the
|
|
% fields in that mode error. If this variable is later found
|
|
% to be insufficiently instantiated, we will want to print
|
|
% the mode_error_unify_var_multimode_pred as a possible
|
|
% explanation of that error.
|
|
%
|
|
% Any code that creates a mode_error_unify_var_multimode_pred
|
|
% should include the information in that error in this map.
|
|
%
|
|
% Since this information is valid only in straight-line
|
|
% conjunctions, entries added to this map must be thrown away
|
|
% when the code that added them is backtracked over.
|
|
% The simplest way to make this happen is to take a snapshot
|
|
% of the value of this map at the start of each branched
|
|
% control structure, and to reset it to that snapshot
|
|
% at the start of each branch.
|
|
msi_pred_var_multimode_error_map
|
|
:: pred_var_multimode_error_map,
|
|
|
|
% All the arguments from here on are sub-word-sized,
|
|
% which should allow the compiler to pack them together.
|
|
|
|
msi_how_to_check :: how_to_check_goal,
|
|
|
|
% Is mode analysis allowed to change which procedure is called?
|
|
msi_may_change_called_proc :: may_change_called_proc,
|
|
|
|
% Changed flag: if `yes', then we may need to repeat
|
|
% mode inference.
|
|
msi_changed_flag :: bool,
|
|
|
|
% Are we rechecking a goal after introducing unifications
|
|
% for complicated sub-unifications or an implied mode?
|
|
% If so, redoing the mode check should not introduce more
|
|
% extra unifications.
|
|
msi_checking_extra_goals :: bool,
|
|
|
|
% Says whether we need to requantify the procedure body
|
|
% after mode analysis finishes.
|
|
msi_need_to_requantify :: need_to_requantify,
|
|
|
|
% Says whether we are in a promise_<purity> scope.
|
|
% This information is needed to check that potentially impure
|
|
% uses of inst any non-locals in negated contexts are properly
|
|
% acknowledged by the programmer.
|
|
msi_in_promise_purity_scope :: in_promise_purity_scope,
|
|
|
|
% Says whether we are in a from_ground_term scope.
|
|
% This information allows us to optimize some aspects of
|
|
% mode analysis.
|
|
msi_in_from_ground_term :: in_from_ground_term_scope,
|
|
|
|
% Says whether we have ever come across in a from_ground_term
|
|
% scope.
|
|
msi_had_from_ground_term :: had_from_ground_term_scope,
|
|
|
|
% Says whether we should copy the ground terms created by
|
|
% from_ground_term scopes, making them unique.
|
|
msi_make_ground_terms_unique :: make_ground_terms_unique,
|
|
|
|
% Set to `yes' if we are inside a goal with a
|
|
% duplicate_for_switch feature.
|
|
msi_in_dupl_for_switch :: in_dupl_for_switch
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mode_context_init(mode_context_not_call_or_unify).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mode_info_init(ModuleInfo, PredId, ProcId, Context, LiveVars, HeadInstVars,
|
|
InstMap0, HowToCheck, MayChangeProc, ModeInfo) :-
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, debug_modes, DebugModes),
|
|
globals.lookup_int_option(Globals, debug_modes_pred_id, DebugModesPredId),
|
|
pred_id_to_int(PredId, PredIdInt),
|
|
( if
|
|
DebugModes = yes,
|
|
( DebugModesPredId >= 0 => DebugModesPredId = PredIdInt )
|
|
then
|
|
(
|
|
HowToCheck = check_modes,
|
|
UniquePrefix = ""
|
|
;
|
|
HowToCheck = check_unique_modes,
|
|
UniquePrefix = "unique "
|
|
),
|
|
globals.lookup_bool_option(Globals, debug_modes_verbose, DebugVerbose),
|
|
globals.lookup_bool_option(Globals, debug_modes_minimal, DebugMinimal),
|
|
globals.lookup_bool_option(Globals, debug_modes_statistics,
|
|
Statistics),
|
|
Flags = debug_flags(UniquePrefix, DebugVerbose, DebugMinimal,
|
|
Statistics),
|
|
MaybeDebug = yes(Flags)
|
|
else
|
|
MaybeDebug = no
|
|
),
|
|
|
|
module_info_proc_info(ModuleInfo, PredId, ProcId, ProcInfo),
|
|
proc_info_get_var_table(ProcInfo, VarTable),
|
|
proc_info_get_inst_varset(ProcInfo, InstVarSet),
|
|
|
|
bag.from_sorted_list(set_of_var.to_sorted_list(LiveVars), LiveVarsBag),
|
|
instmap.init_unreachable(LastCheckpointInstMap),
|
|
LockedVars = [],
|
|
ParallelVars = [],
|
|
Warnings = [],
|
|
|
|
Changed = no,
|
|
CheckingExtraGoals = no,
|
|
NeedToRequantify = do_not_need_to_requantify,
|
|
InPromisePurityScope = not_in_promise_purity_scope,
|
|
InFromGroundTerm = not_in_from_ground_term_scope,
|
|
HadFromGroundTerm = did_not_have_from_ground_term_scope,
|
|
MakeGroundTermsUnique = do_not_make_ground_terms_unique,
|
|
InDuplForSwitch = not_in_dupl_for_switch,
|
|
map.init(PredVarMultiModeMap),
|
|
|
|
ModeSubInfo = mode_sub_info(PredId, ProcId, VarTable,
|
|
MaybeDebug, LockedVars, LiveVarsBag, InstVarSet, ParallelVars,
|
|
LastCheckpointInstMap, InstMap0, HeadInstVars, Warnings,
|
|
PredVarMultiModeMap,
|
|
HowToCheck, MayChangeProc, Changed, CheckingExtraGoals,
|
|
NeedToRequantify, InPromisePurityScope, InFromGroundTerm,
|
|
HadFromGroundTerm, MakeGroundTermsUnique, InDuplForSwitch),
|
|
|
|
mode_context_init(ModeContext),
|
|
delay_info_init(DelayInfo),
|
|
ErrorList = [],
|
|
bag.from_sorted_list(set_of_var.to_sorted_list(LiveVars),
|
|
NondetLiveVarsBag),
|
|
|
|
ModeInfo = mode_info(ModuleInfo, InstMap0, DelayInfo, ErrorList,
|
|
ModeContext, Context, NondetLiveVarsBag, ModeSubInfo).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Getter and setter predicates for mode_info.
|
|
% The setter predicates where the new value of the field is often
|
|
% the same as its old value check whether the new value may differ
|
|
% from the old one, and allocate a new mode_info structure (and maybe
|
|
% a new mode_sub_info structure) only if the old and new values are
|
|
% *not* guaranteed to be the same.
|
|
%
|
|
% I (zs) used profiling data derived from a bootcheck to establish
|
|
% which mode_info fields fall into this category. See below for further info.
|
|
%
|
|
|
|
% Getters of the fields of mode_info.
|
|
mode_info_get_module_info(MI, X) :-
|
|
X = MI ^ mi_module_info.
|
|
mode_info_get_instmap(MI, X) :-
|
|
X = MI ^ mi_instmap.
|
|
mode_info_get_delay_info(MI, X) :-
|
|
X = MI ^ mi_delay_info.
|
|
mode_info_get_errors(MI, X) :-
|
|
X = MI ^ mi_errors.
|
|
mode_info_get_mode_context(MI, X) :-
|
|
X = MI ^ mi_mode_context.
|
|
mode_info_get_context(MI, X) :-
|
|
X = MI ^ mi_context.
|
|
mode_info_get_nondet_live_vars(MI, X) :-
|
|
X = MI ^ mi_nondet_live_vars.
|
|
|
|
% Getters of the fields of mode_sub_info.
|
|
mode_info_get_pred_id(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_pred_id.
|
|
mode_info_get_proc_id(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_proc_id.
|
|
mode_info_get_var_table(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_var_table.
|
|
mode_info_get_debug_modes(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_debug.
|
|
mode_info_get_locked_vars(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_locked_vars.
|
|
mode_info_get_live_vars(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_live_vars.
|
|
mode_info_get_instvarset(ModeInfo, X) :-
|
|
X = ModeInfo ^ mi_sub_info ^ msi_instvarset.
|
|
mode_info_get_parallel_vars(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_par_conj.
|
|
mode_info_get_last_checkpoint_insts(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_last_checkpoint_insts.
|
|
mode_info_get_initial_instmap(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_initial_instmap.
|
|
mode_info_get_head_inst_vars(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_head_inst_vars.
|
|
mode_info_get_warnings(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_warnings.
|
|
mode_info_get_pred_var_multimode_error_map(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_pred_var_multimode_error_map.
|
|
mode_info_get_how_to_check(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_how_to_check.
|
|
mode_info_get_may_change_called_proc(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_may_change_called_proc.
|
|
mode_info_get_changed_flag(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_changed_flag.
|
|
mode_info_get_checking_extra_goals(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_checking_extra_goals.
|
|
mode_info_get_need_to_requantify(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_need_to_requantify.
|
|
mode_info_get_in_promise_purity_scope(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_in_promise_purity_scope.
|
|
mode_info_get_in_from_ground_term(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_in_from_ground_term.
|
|
mode_info_get_had_from_ground_term(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_had_from_ground_term.
|
|
mode_info_get_make_ground_terms_unique(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_make_ground_terms_unique.
|
|
mode_info_get_in_dupl_for_switch(MI, X) :-
|
|
X = MI ^ mi_sub_info ^ msi_in_dupl_for_switch.
|
|
|
|
%---------------------%
|
|
|
|
% Setters of the fields of mode_info.
|
|
mode_info_set_module_info(X, !MI) :-
|
|
( if private_builtin.pointer_equal(X, !.MI ^ mi_module_info) then
|
|
true
|
|
else
|
|
!MI ^ mi_module_info := X
|
|
).
|
|
mode_info_set_delay_info(X, !MI) :-
|
|
( if private_builtin.pointer_equal(X, !.MI ^ mi_delay_info) then
|
|
true
|
|
else
|
|
!MI ^ mi_delay_info := X
|
|
).
|
|
mode_info_set_errors(X, !MI) :-
|
|
( if private_builtin.pointer_equal(X, !.MI ^ mi_errors) then
|
|
true
|
|
else
|
|
!MI ^ mi_errors := X
|
|
).
|
|
mode_info_set_mode_context(X, !MI) :-
|
|
!MI ^ mi_mode_context := X.
|
|
mode_info_set_context(X, !MI) :-
|
|
( if private_builtin.pointer_equal(X, !.MI ^ mi_context) then
|
|
true
|
|
else
|
|
!MI ^ mi_context := X
|
|
).
|
|
mode_info_set_nondet_live_vars(X, !MI) :-
|
|
( if private_builtin.pointer_equal(X, !.MI ^ mi_nondet_live_vars) then
|
|
true
|
|
else
|
|
!MI ^ mi_nondet_live_vars := X
|
|
).
|
|
|
|
% Setters of the fields of mode_sub_info.
|
|
mode_info_set_pred_id(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_pred_id := X.
|
|
mode_info_set_proc_id(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_proc_id := X.
|
|
mode_info_set_var_table(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_var_table := X.
|
|
mode_info_set_locked_vars(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_locked_vars := X.
|
|
mode_info_set_live_vars(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_live_vars := X.
|
|
mode_info_set_instvarset(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_instvarset := X.
|
|
mode_info_set_parallel_vars(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_par_conj := X.
|
|
mode_info_set_last_checkpoint_insts(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_last_checkpoint_insts := X.
|
|
mode_info_set_warnings(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_warnings := X.
|
|
mode_info_set_pred_var_multimode_error_map(X, !MI) :-
|
|
( if
|
|
private_builtin.pointer_equal(X,
|
|
!.MI ^ mi_sub_info ^ msi_pred_var_multimode_error_map)
|
|
then
|
|
true
|
|
else
|
|
!MI ^ mi_sub_info ^ msi_pred_var_multimode_error_map := X
|
|
).
|
|
mode_info_set_may_change_called_proc(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_may_change_called_proc := X.
|
|
mode_info_set_changed_flag(X, !MI) :-
|
|
( if
|
|
private_builtin.pointer_equal(X, !.MI ^ mi_sub_info ^ msi_changed_flag)
|
|
then
|
|
true
|
|
else
|
|
!MI ^ mi_sub_info ^ msi_changed_flag := X
|
|
).
|
|
mode_info_set_need_to_requantify(X, !MI) :-
|
|
( if
|
|
private_builtin.pointer_equal(X,
|
|
!.MI ^ mi_sub_info ^ msi_need_to_requantify)
|
|
then
|
|
true
|
|
else
|
|
!MI ^ mi_sub_info ^ msi_need_to_requantify := X
|
|
).
|
|
mode_info_set_in_promise_purity_scope(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_in_promise_purity_scope := X.
|
|
mode_info_set_in_from_ground_term(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_in_from_ground_term := X.
|
|
mode_info_set_had_from_ground_term(X, !MI) :-
|
|
( if
|
|
private_builtin.pointer_equal(X,
|
|
!.MI ^ mi_sub_info ^ msi_had_from_ground_term)
|
|
then
|
|
true
|
|
else
|
|
!MI ^ mi_sub_info ^ msi_had_from_ground_term := X
|
|
).
|
|
mode_info_set_make_ground_terms_unique(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_make_ground_terms_unique := X.
|
|
mode_info_set_in_dupl_for_switch(X, !MI) :-
|
|
!MI ^ mi_sub_info ^ msi_in_dupl_for_switch := X.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% I (zs) decided which mode_info fields to call pointer_equal for
|
|
% based on this profiling information derived from a bootcheck. During this
|
|
% bootcheck, each of the setter predicates was given a number from 0 to 25,
|
|
% and called gather_mode_info_stats below to record whether the new value
|
|
% was the same bit pattern as the old one.
|
|
%
|
|
% The profiling code follows the data derived from it.
|
|
|
|
% same diff same%
|
|
% 0: 22869923 651583 97.230% module_info
|
|
% 1: 0 1 0.000% pred_id
|
|
% 2: 1 0 100.000% proc_id
|
|
% 3: 4 8815 0.045% varset
|
|
% 4: 4 8815 0.045% vartypes
|
|
% 5: 1824389 3523646 34.113% context
|
|
% 6: 1314611 32769008 3.857% mode_context
|
|
% 7: 0 438852 0.000% locked_vars
|
|
% 8: 0 1283911 0.000% instvarset
|
|
% 9: 3234680 1901125 62.983% errors
|
|
% 10: 0 892 0.000% warnings
|
|
% 11: 57326 19964 74.170% need_to_requantify
|
|
% 12: 0 868 0.000% promise_purity_scope
|
|
% 13: 12223802 2022001 85.806% delay_info
|
|
% 14: 58338 9161895 0.633% live_vars
|
|
% 15: 1707132 12480878 12.032% nondet_live_vars
|
|
% 16: 0 0 last_checkpoint
|
|
% 17: 0 0 parallel vars
|
|
% 18: 604507 562 99.907% changed_flag
|
|
% 19: 0 0 how_to_check
|
|
% 20: 0 16620 0.000% may_change_called_proc
|
|
% 21: 1156755 684924 62.810% may_init_solver_vars
|
|
% 22: 0 0 in_from_ground_term
|
|
% 23: 2355 1775 57.022% had_from_ground_term
|
|
% 24: 0 1 0.000% make_ground_term_unique
|
|
% 25: 212 16930 1.237% in_dupl_for_switch
|
|
|
|
% :- import_module io.
|
|
% :- pred write_mode_info_stats(io::di, io::uo) is det.
|
|
%
|
|
% :- pragma foreign_decl("C", local,
|
|
% "
|
|
% #define MR_NUM_MODE_INFO_STATS 26
|
|
% unsigned long MR_stats_same[MR_NUM_MODE_INFO_STATS];
|
|
% unsigned long MR_stats_diff[MR_NUM_MODE_INFO_STATS];
|
|
% ").
|
|
%
|
|
% :- pred gather_mode_info_stats(int::in, T::in, T::in,
|
|
% mode_info::in, mode_info::out) is det.
|
|
%
|
|
% :- pragma foreign_proc("C",
|
|
% gather_mode_info_stats(N::in, Old::in, New::in, MI0::in, MI::out),
|
|
% [will_not_call_mercury, promise_pure],
|
|
% "
|
|
% if (((MR_Unsigned) Old) == ((MR_Unsigned) New)) {
|
|
% ++MR_stats_same[N];
|
|
% } else {
|
|
% ++MR_stats_diff[N];
|
|
% }
|
|
%
|
|
% MI = MI0;
|
|
% ").
|
|
%
|
|
% :- pragma foreign_proc("C",
|
|
% write_mode_info_stats(IO0::di, IO::uo),
|
|
% [will_not_call_mercury, promise_pure],
|
|
% "
|
|
% FILE *fp;
|
|
%
|
|
% fp = fopen(""/tmp/MODE_INFO_STATS"", ""a"");
|
|
% if (fp != NULL) {
|
|
% int i;
|
|
% for (i = 0; i < MR_NUM_MODE_INFO_STATS; i++) {
|
|
% fprintf(fp, ""%d: %lu %lu\\n"",
|
|
% i, MR_stats_same[i], MR_stats_diff[i]);
|
|
% }
|
|
% }
|
|
%
|
|
% IO = IO0;
|
|
% ").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mode_info_get_num_errors(ModeInfo, NumErrors) :-
|
|
mode_info_get_errors(ModeInfo, Errors),
|
|
list.length(Errors, NumErrors).
|
|
|
|
mode_info_get_liveness(ModeInfo, LiveVars) :-
|
|
mode_info_get_live_vars(ModeInfo, LiveVarsBag),
|
|
bag.to_list_without_duplicates(LiveVarsBag, SortedList),
|
|
set_of_var.sorted_list_to_set(SortedList, LiveVars).
|
|
|
|
mode_info_get_pred_id_table(ModeInfo, PredIdTable) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
module_info_get_pred_id_table(ModuleInfo, PredIdTable).
|
|
|
|
mode_info_get_insts(ModeInfo, Insts) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
module_info_get_inst_table(ModuleInfo, Insts).
|
|
|
|
mode_info_get_modes(ModeInfo, Modes) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
module_info_get_mode_table(ModuleInfo, Modes).
|
|
|
|
mode_info_get_types_of_vars(ModeInfo, Vars, TypesOfVars) :-
|
|
mode_info_get_var_table(ModeInfo, VarTable),
|
|
lookup_var_types(VarTable, Vars, TypesOfVars).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mode_info_set_instmap(InstMap, !MI) :-
|
|
mode_info_get_instmap(!.MI, InstMap0),
|
|
!MI ^ mi_instmap := InstMap,
|
|
( if
|
|
instmap_is_unreachable(InstMap),
|
|
instmap_is_reachable(InstMap0)
|
|
then
|
|
mode_info_get_delay_info(!.MI, DelayInfo0),
|
|
delay_info_bind_all_vars(DelayInfo0, DelayInfo),
|
|
mode_info_set_delay_info(DelayInfo, !MI)
|
|
else
|
|
true
|
|
).
|
|
|
|
mode_info_set_checking_extra_goals(Checking, !MI) :-
|
|
mode_info_get_checking_extra_goals(!.MI, Checking0),
|
|
( if
|
|
Checking0 = yes,
|
|
Checking = yes
|
|
then
|
|
% This should never happen - once the extra goals are introduced,
|
|
% rechecking the goal should not introduce more extra goals.
|
|
unexpected($pred, "rechecking extra goals adds more extra goals")
|
|
else
|
|
!MI ^ mi_sub_info ^ msi_checking_extra_goals := Checking
|
|
).
|
|
|
|
mode_info_set_call_context(CallContext, !MI) :-
|
|
(
|
|
CallContext = call_context_unify(UnifyContext),
|
|
mode_info_set_mode_context(mode_context_unify(UnifyContext, left), !MI)
|
|
;
|
|
CallContext = call_context_call(CallId),
|
|
mode_info_set_mode_context(mode_context_call(CallId, 0), !MI)
|
|
).
|
|
|
|
mode_info_unset_call_context(!MI) :-
|
|
mode_info_set_mode_context(mode_context_not_call_or_unify, !MI).
|
|
|
|
mode_info_set_call_arg_context(ArgNum, !ModeInfo) :-
|
|
mode_info_get_mode_context(!.ModeInfo, ModeContext0),
|
|
(
|
|
ModeContext0 = mode_context_call(CallId, _),
|
|
mode_info_set_mode_context(mode_context_call(CallId, ArgNum),
|
|
!ModeInfo)
|
|
;
|
|
ModeContext0 = mode_context_unify(_UnifyContext, _Side)
|
|
% This only happens when checking that the typeinfo variables
|
|
% for polymorphic complicated unifications are ground.
|
|
% For that case, we don't care about the ArgNum.
|
|
;
|
|
ModeContext0 = mode_context_not_call_or_unify,
|
|
unexpected($pred, "not_call_or_unify")
|
|
).
|
|
|
|
mode_info_need_to_requantify(!ModeInfo) :-
|
|
mode_info_set_need_to_requantify(need_to_requantify, !ModeInfo).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mode_info_var_is_live(ModeInfo, Var, Result) :-
|
|
mode_info_get_live_vars(ModeInfo, LiveVars0),
|
|
( if bag.contains(LiveVars0, Var) then
|
|
Result = is_live
|
|
else
|
|
Result = is_dead
|
|
).
|
|
|
|
mode_info_var_list_is_live(_, [], []).
|
|
mode_info_var_list_is_live(ModeInfo, [Var | Vars], [Live | Lives]) :-
|
|
mode_info_var_is_live(ModeInfo, Var, Live),
|
|
mode_info_var_list_is_live(ModeInfo, Vars, Lives).
|
|
|
|
mode_info_var_is_nondet_live(ModeInfo, Var, Result) :-
|
|
mode_info_get_nondet_live_vars(ModeInfo, NondetLiveVars0),
|
|
( if bag.contains(NondetLiveVars0, Var) then
|
|
Result = is_live
|
|
else
|
|
Result = is_dead
|
|
).
|
|
|
|
mode_info_add_live_vars(NewLiveVars, !MI) :-
|
|
set_of_var.to_sorted_list(NewLiveVars, NewLiveVarsList),
|
|
mode_info_get_live_vars(!.MI, LiveVars0),
|
|
mode_info_get_nondet_live_vars(!.MI, NondetLiveVars0),
|
|
bag.insert_list(NewLiveVarsList, LiveVars0, LiveVars),
|
|
bag.insert_list(NewLiveVarsList, NondetLiveVars0, NondetLiveVars),
|
|
mode_info_set_live_vars(LiveVars, !MI),
|
|
mode_info_set_nondet_live_vars(NondetLiveVars, !MI).
|
|
|
|
mode_info_remove_live_vars(OldLiveVars, !MI) :-
|
|
set_of_var.to_sorted_list(OldLiveVars, OldLiveVarsList),
|
|
mode_info_get_live_vars(!.MI, LiveVars0),
|
|
mode_info_get_nondet_live_vars(!.MI, NondetLiveVars0),
|
|
bag.det_remove_list(OldLiveVarsList, LiveVars0, LiveVars),
|
|
bag.det_remove_list(OldLiveVarsList, NondetLiveVars0, NondetLiveVars),
|
|
mode_info_set_live_vars(LiveVars, !MI),
|
|
mode_info_set_nondet_live_vars(NondetLiveVars, !MI),
|
|
|
|
% When a variable becomes dead, we may be able to wake up a goal
|
|
% which is waiting on that variable.
|
|
mode_info_get_delay_info(!.MI, DelayInfo0),
|
|
delay_info_bind_var_list(OldLiveVarsList, DelayInfo0, DelayInfo),
|
|
mode_info_set_delay_info(DelayInfo, !MI).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% The locked variables are stored as a stack of sets of variables.
|
|
% A variable is locked if it is a member of any of the sets.
|
|
% To lock a set of vars, we just push them on the stack,
|
|
% and to unlock a set of vars, we just pop them off the stack.
|
|
% The stack is implemented as a list.
|
|
|
|
mode_info_var_is_locked(ModeInfo, Var, Reason) :-
|
|
mode_info_get_locked_vars(ModeInfo, LockedVarsList),
|
|
mode_info_var_is_locked_loop(LockedVarsList, Var, Reason).
|
|
|
|
:- pred mode_info_var_is_locked_loop(locked_vars::in, prog_var::in,
|
|
var_lock_reason::out) is semidet.
|
|
|
|
mode_info_var_is_locked_loop([ThisReason - Set | Sets], Var, Reason) :-
|
|
( if set_of_var.member(Set, Var) then
|
|
Reason = ThisReason
|
|
else
|
|
mode_info_var_is_locked_loop(Sets, Var, Reason)
|
|
).
|
|
|
|
mode_info_lock_vars(Reason, Vars, !ModeInfo) :-
|
|
mode_info_get_locked_vars(!.ModeInfo, LockedVars),
|
|
mode_info_set_locked_vars([Reason - Vars | LockedVars], !ModeInfo).
|
|
|
|
mode_info_unlock_vars(Reason, Vars, !ModeInfo) :-
|
|
mode_info_get_locked_vars(!.ModeInfo, LockedVars0),
|
|
( if
|
|
LockedVars0 = [Reason - TheseVars | LockedVars1],
|
|
set_of_var.equal(TheseVars, Vars)
|
|
then
|
|
LockedVars = LockedVars1
|
|
else
|
|
unexpected($pred, "some kind of nesting error")
|
|
),
|
|
mode_info_set_locked_vars(LockedVars, !ModeInfo).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
mode_info_error(Vars, ModeError, !ModeInfo) :-
|
|
mode_info_get_context(!.ModeInfo, Context),
|
|
mode_info_get_mode_context(!.ModeInfo, ModeContext),
|
|
ModeErrorInfo = mode_error_info(Vars, ModeError, Context, ModeContext),
|
|
mode_info_add_error(ModeErrorInfo, !ModeInfo).
|
|
|
|
mode_info_add_error(ModeErrorInfo, !ModeInfo) :-
|
|
mode_info_get_errors(!.ModeInfo, Errors0),
|
|
list.append(Errors0, [ModeErrorInfo], Errors),
|
|
mode_info_set_errors(Errors, !ModeInfo),
|
|
mode_info_get_debug_modes(!.ModeInfo, DebugModes),
|
|
(
|
|
DebugModes = no
|
|
;
|
|
DebugModes = yes(_DebugFlags),
|
|
trace [io(!IO)] (
|
|
mode_info_get_module_info(!.ModeInfo, ModuleInfo),
|
|
module_info_get_globals(ModuleInfo, Globals0),
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
get_debug_output_stream(Globals0, ModuleName, DebugStream, !IO),
|
|
list.length(Errors, ErrorNum),
|
|
io.format(DebugStream, "Adding error_spec %d\n",
|
|
[i(ErrorNum)], !IO),
|
|
Spec = mode_error_info_to_spec(!.ModeInfo, ModeErrorInfo),
|
|
globals.set_option(print_error_spec_id, bool(yes),
|
|
Globals0, Globals),
|
|
write_error_spec(DebugStream, Globals, Spec, !IO),
|
|
io.flush_output(DebugStream, !IO)
|
|
)
|
|
).
|
|
|
|
mode_info_warning(ModeWarning, !ModeInfo) :-
|
|
mode_info_get_context(!.ModeInfo, Context),
|
|
mode_info_get_mode_context(!.ModeInfo, ModeContext),
|
|
ModeWarningInfo = mode_warning_info(ModeWarning, Context, ModeContext),
|
|
mode_info_add_warning(ModeWarningInfo, !ModeInfo).
|
|
|
|
mode_info_add_warning(ModeWarningInfo, !ModeInfo) :-
|
|
mode_info_get_warnings(!.ModeInfo, Warnings0),
|
|
list.append(Warnings0, [ModeWarningInfo], Warnings),
|
|
mode_info_set_warnings(Warnings, !ModeInfo).
|
|
|
|
mode_info_get_pf_sym_name_arity(ModeInfo, PredId, CallId) :-
|
|
mode_info_get_module_info(ModeInfo, ModuleInfo),
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
pred_info_get_pf_sym_name_arity(PredInfo, CallId).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module check_hlds.mode_info.
|
|
%---------------------------------------------------------------------------%
|