%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1996-2012 The University of Melbourne.
% Copyright (C) 2014-2024 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: hlds_module.m.
% Main authors: fjh, conway.
%
% This module defines the main part of the High Level Data Structure or HLDS,
% the module_info type, which deals with issues that concern the module
% as a whole.
%
%---------------------------------------------------------------------------%

:- module hlds.hlds_module.
:- interface.

:- import_module analysis.
:- import_module analysis.operations.
:- import_module check_hlds.
:- import_module check_hlds.proc_requests.
:- import_module hlds.const_struct.
:- import_module hlds.hlds_class.
:- import_module hlds.hlds_cons.
:- import_module hlds.hlds_data.
:- import_module hlds.hlds_dependency_graph.
:- import_module hlds.hlds_inst_mode.
:- import_module hlds.hlds_pred.
:- import_module hlds.hlds_promise.
:- import_module hlds.pred_table.
:- import_module hlds.special_pred.
:- import_module hlds.status.
:- import_module libs.
:- import_module libs.globals.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.
:- import_module parse_tree.module_qual.
:- import_module parse_tree.module_qual.mq_info.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_data_event.
:- import_module parse_tree.prog_data_foreign.
:- import_module parse_tree.prog_data_pragma.
:- import_module parse_tree.prog_data_used_modules.
:- import_module parse_tree.prog_foreign.
:- import_module parse_tree.prog_item.
:- import_module parse_tree.prog_parse_tree.
:- import_module recompilation.
:- import_module recompilation.record_uses.

:- import_module cord.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module multi_map.
:- import_module one_or_more.
:- import_module one_or_more_map.
:- import_module pair.
:- import_module set.
:- import_module set_tree234.

%---------------------------------------------------------------------------%
%
% The module_info contains many fields of many types. These are the types
% that are not defined elsewhere.
%

:- type module_info.

:- type pragma_exported_proc
    --->    pragma_exported_proc(
                foreign_language,       % The language we are exporting to.
                pred_id,
                proc_id,
                string,                 % The exported name of the procedure.
                                        % i.e. the function name in C, method
                                        % name in Java etc.
                prog_context
            ).

    % This structure contains the information we need to generate
    % a type_ctor_info structure for a type defined in this module.

:- type type_ctor_gen_info
    --->    type_ctor_gen_info(
                type_ctor,
                module_name,            % module name
                string,                 % type name
                int,                    % type arity
                type_status,            % status of the type
                hlds_type_defn,         % defn of type
                pred_proc_id,           % unify procedure
                pred_proc_id            % compare procedure
                % maybe(pred_proc_id)   % prettyprinter, if relevant
            ).

    % Map from proc to a list of unused argument numbers.
    %
:- type unused_arg_info == map(pred_proc_id, list(int)).

    % For every procedure that requires its own tabling structure,
    % this field records the information needed to define that structure.
:- type table_struct_map == map(pred_proc_id, table_struct_info).

:- type table_struct_info
    --->    table_struct_info(
                table_struct_proc                   :: proc_table_struct_info,
                table_struct_attrs                  :: table_attributes
            ).

:- type type_spec_pragma_map ==
    one_or_more_map(pred_id, decl_pragma_type_spec_info).

    % List of procedures for which there are user-requested type
    % specializations, and a list of predicates which should be processed
    % by higher_order.m to ensure the production of those versions.
:- type type_spec_tables
    --->    type_spec_tables(
                % Procedures for which there are user-requested type
                % specializations.
                user_req_procs      :: set(pred_proc_id),

                % Set of predicates which need to be processed by
                % higher_order.m to produce those specialized versions.
                must_process_preds  :: set(pred_id),

                % Map from predicates for which the user requested a type
                % specialization to the list of predicates which must be
                % processed by higher_order.m to force the production of those
                % versions. This is used by dead_proc_elim.m to avoid creating
                % versions unnecessarily for versions in imported modules.
                user_to_process_map :: multi_map(pred_id, pred_id),

                % Type spec pragmas to be placed in the `.opt' file if a
                % predicate becomes exported.
                pragma_map          :: type_spec_pragma_map
            ).

    % Once filled in by simplify_proc.m (for all non-lambda procedures)
    % and by lambda.m (for procedures created to implement lambda expressions),
    % this map should have an entry for every procedure that is of interest to
    % direct_arg_in_out.m. A procedure may be of interest to that module
    % because it has one or more arguments that it needs to clone,
    % or because it has one or more arguments whose modes do not specify
    % whether they need to be cloned, and for which therefore it should
    % generate a "sorry, not implemented" message. In both cases, the
    % one_or_more(int) specify the argument positions involved; in the latter
    % case, we also record the list of arguments for which we *could* tell
    % they need to be cloned.
    %
:- type direct_arg_proc_map == map(pred_proc_id, direct_arg_proc).
:- type direct_arg_proc
    --->    direct_arg_clone_proc(
                clone_daio_args     :: one_or_more(int)
            )
    ;       direct_arg_problem_proc(
                problem_args        :: one_or_more(int),
                no_problem_args     :: list(int)
            ).

    % Maps the full names of procedures (in the sense of complexity_proc_name
    % in complexity.m) to the number of their slot in MR_complexity_proc_table.
:- type complexity_proc_map == map(string, int).

:- type complexity_proc_info
    --->    complexity_proc_info(
                % The index of the procedure in the runtime system's
                % MR_complexity_procs array.
                complexity_proc_num     :: int,

                % The full name of the procedure, in the form
                % fqn/arity-modenum, where fqn is the predicate or function's
                % fully qualified name.
                complexity_proc_name    :: string,

                complexity_proc_args    :: list(complexity_arg_info)
            ).

:- type complexity_arg_info
    --->    complexity_arg_info(
                complexity_arg_name :: maybe(string),
                complexity_arg_kind :: complexity_arg_kind
            ).

:- type complexity_arg_kind
    --->    complexity_input_variable_size
    ;       complexity_input_fixed_size
    ;       complexity_output.

:- type exported_enum_info
    --->    exported_enum_info(
                % The type whose constants we are exporting to a foreign
                % language.
                eei_type_ctor               :: type_ctor,

                % The constants we are exporting.
                eei_constants               :: list(constructor_repn),

                % The language we are exporting to.
                eei_language                :: foreign_language,

                % This maps the names of the constructors in the Mercury type
                % to their names in the foreign language.
                eei_name_map                :: map(string, string),

                % The context of the foreign_export_enum pragma
                % that asked for all this.
                eei_context                 :: prog_context
            ).

:- type proc_analysis_kind
    --->    pak_exception
    ;       pak_trailing
    ;       pak_mm_tabling
    ;       pak_termination
    ;       pak_termination2
    ;       pak_structure_sharing
    ;       pak_structure_reuse.

    % Types for order-independent state update (oisu).
    %
:- type oisu_map == map(type_ctor, oisu_preds).
:- type oisu_preds
    --->    oisu_preds(
                op_creators     :: list(pred_id),
                op_mutators     :: list(pred_id),
                op_destructors  :: list(pred_id)
            ).

%---------------------------------------------------------------------------%
%
% The initialization predicate for the module_info data structure,
% and its raw getter and setter predicates.
%

    % Create an empty module_info for the module named in the given
    % compilation unit.
    %
:- pred module_info_init(globals::in, module_name::in, prog_context::in,
    string::in, include_module_map::in, used_modules::in, set(module_name)::in,
    partial_qualifier_info::in, maybe(recompilation_info)::in,
    type_repn_decision_data::in, module_info::out) is det.

    % Once the module_info has been built, we call module_info_optimize
    % to attempt to optimize the data structures for lots of accesses
    % and relatively few insertion/deletions. This was useful when
    % we stored maps using not-necessarily-balanced binary trees;
    % this was when we balanced them. Now that we store maps in 234-trees,
    % it is a no-op. However, we keep this predicate around, since we may
    % yet switch to newer data structures that could also benefit from
    % knowing when their access patterns change from mostly-write to
    % mostly read. For example, we could switch some tables from
    % 234-trees to arrays.
    %
:- pred module_info_optimize(module_info::in, module_info::out) is det.

:- type avail_module_map == map(module_name, avail_module_entry).
:- type avail_module_entry
    --->    avail_module_entry(
                % ms_interface iff any avail_module has ms_interface.
                module_section,

                % import_decl iff any avail_module has import_decl.
                % XXX CLEANUP This is wrong. A module may have
                % a ":- use_module" declaration in the interface and
                % an ":- import_module" declaration in the implementation,
                % and this design cannot express that.
                import_or_use,

                % The locations of the *explicit* import_module and
                % use_import declarations, in the *source* of the module.
                % The implicit ones aren't in the list, and neither
                % are the imports and uses read in from interface
                % and optimization files, so it is possible for the list
                % to be empty.
                list(avail_module)
            ).

:- type avail_module
    --->    avail_module(
                module_section,
                import_or_use,
                prog_context
            ).

    % The getter predicates. Please keep the order of declarations here
    % and the order of the clauses below in sync with the order of the
    % fields in the module_info and module_sub_info types.

:- pred module_info_get_globals(module_info::in, globals::out) is det.
:- pred module_info_get_predicate_table(module_info::in,
    predicate_table::out) is det.
:- pred module_info_get_type_table(module_info::in, type_table::out) is det.
:- pred module_info_get_no_tag_types(module_info::in,
    no_tag_type_table::out) is det.
:- pred module_info_get_inst_table(module_info::in, inst_table::out) is det.
:- pred module_info_get_mode_table(module_info::in, mode_table::out) is det.
:- pred module_info_get_cons_table(module_info::in, cons_table::out) is det.
:- pred module_info_get_ctor_field_table(module_info::in,
    ctor_field_table::out) is det.

:- pred module_info_get_special_pred_maps(module_info::in,
    special_pred_maps::out) is det.
:- pred module_info_get_class_table(module_info::in, class_table::out) is det.
:- pred module_info_get_instance_table(module_info::in,
    instance_table::out) is det.
:- pred module_info_get_type_spec_tables(module_info::in,
    type_spec_tables::out) is det.
:- pred module_info_get_const_struct_db(module_info::in,
    const_struct_db::out) is det.
:- pred module_info_get_c_j_cs_fims(module_info::in,
    c_j_cs_fims::out) is det.
:- pred module_info_get_pragma_exported_procs(module_info::in,
    cord(pragma_exported_proc)::out) is det.

:- pred module_info_get_name(module_info::in, module_name::out) is det.
:- pred module_info_get_name_context(module_info::in,
    prog_context::out) is det.
:- pred module_info_get_dump_hlds_base_file_name(module_info::in,
    string::out) is det.
:- pred module_info_get_include_module_map(module_info::in,
    include_module_map::out) is det.
:- pred module_info_get_partial_qualifier_info(module_info::in,
    partial_qualifier_info::out) is det.
:- pred module_info_get_maybe_recompilation_info(module_info::in,
    maybe(recompilation_info)::out) is det.
:- pred module_info_get_proc_requests(module_info::in,
    proc_requests::out) is det.
:- pred module_info_get_assertion_table(module_info::in,
    assertion_table::out) is det.
:- pred module_info_get_exclusive_table(module_info::in,
    exclusive_table::out) is det.
:- pred module_info_get_has_parallel_conj(module_info::in,
    has_parallel_conj::out) is det.
:- pred module_info_get_has_user_event(module_info::in,
    has_user_event::out) is det.
:- pred module_info_get_direct_arg_proc_map(module_info::in,
    direct_arg_proc_map::out) is det.
:- pred module_info_get_foreign_decl_codes_user(module_info::in,
    cord(foreign_decl_code)::out) is det.
:- pred module_info_get_foreign_decl_codes_aux(module_info::in,
    cord(foreign_decl_code)::out) is det.
:- pred module_info_get_foreign_body_codes(module_info::in,
    cord(foreign_body_code)::out) is det.
:- pred module_info_get_fact_table_file_names(module_info::in,
    list(string)::out) is det.
:- pred module_info_get_int_bad_clauses(module_info::in,
    set(pred_pf_name_arity)::out) is det.
:- pred module_info_get_maybe_dependency_info(module_info::in,
    maybe(hlds_dependency_info)::out) is det.
:- pred module_info_get_type_ctor_gen_infos(module_info::in,
    list(type_ctor_gen_info)::out) is det.
:- pred module_info_get_must_be_stratified_preds(module_info::in,
    set(pred_id)::out) is det.
:- pred module_info_get_unused_arg_info(module_info::in,
    unused_arg_info::out) is det.
:- pred module_info_get_table_struct_map(module_info::in,
    table_struct_map::out) is det.
:- pred module_info_get_avail_module_map(module_info::in,
    avail_module_map::out) is det.
:- pred module_info_get_used_modules(module_info::in,
    used_modules::out) is det.
:- pred module_info_get_ancestor_avail_modules(module_info::in,
    set(module_name)::out) is det.
:- pred module_info_get_maybe_complexity_proc_map(module_info::in,
    maybe(pair(int, complexity_proc_map))::out) is det.
:- pred module_info_get_complexity_proc_infos(module_info::in,
    list(complexity_proc_info)::out) is det.
:- pred module_info_get_proc_analysis_kinds(module_info::in,
    set(proc_analysis_kind)::out) is det.
:- pred module_info_get_analysis_info(module_info::in,
    analysis_info::out) is det.
:- pred module_info_get_user_init_pred_target_names(module_info::in,
    pred_target_names::out) is det.
:- pred module_info_get_user_final_pred_target_names(module_info::in,
    pred_target_names::out) is det.
:- pred module_info_get_structure_reuse_preds(module_info::in,
    set(pred_id)::out) is det.
:- pred module_info_get_format_call_pragma_preds(module_info::in,
    set(pred_id)::out) is det.
:- pred module_info_get_exported_enums(module_info::in,
    list(exported_enum_info)::out) is det.
:- pred module_info_get_event_set(module_info::in, event_set::out) is det.
:- pred module_info_get_oisu_map(module_info::in, oisu_map::out) is det.
:- pred module_info_get_oisu_procs(module_info::in,
    set(pred_proc_id)::out) is det.
:- pred module_info_get_ts_rev_string_table(module_info::in, int::out,
    list(string)::out) is det.
:- pred module_info_get_type_repn_dec(module_info::in,
    type_repn_decision_data::out) is det.

    % The setter predicates. Please keep the order of declarations here
    % and the order of the clauses below in sync with the order of the
    % fields in the module_info and module_sub_info types.

:- pred module_info_set_globals(globals::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_predicate_table(predicate_table::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_type_table(type_table::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_no_tag_types(no_tag_type_table::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_inst_table(inst_table::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_mode_table(mode_table::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_cons_table(cons_table::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_ctor_field_table(ctor_field_table::in,
    module_info::in, module_info::out) is det.

:- pred module_info_set_special_pred_maps(special_pred_maps::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_class_table(class_table::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_instance_table(instance_table::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_type_spec_tables(type_spec_tables::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_const_struct_db(const_struct_db::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_c_j_cs_fims(c_j_cs_fims::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_pragma_exported_procs(cord(pragma_exported_proc)::in,
    module_info::in, module_info::out) is det.

:- pred module_info_set_maybe_recompilation_info(maybe(recompilation_info)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_proc_requests(proc_requests::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_assertion_table(assertion_table::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_exclusive_table(exclusive_table::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_has_parallel_conj(
    module_info::in, module_info::out) is det.
:- pred module_info_set_has_user_event(
    module_info::in, module_info::out) is det.
:- pred module_info_set_direct_arg_proc_map(direct_arg_proc_map::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_foreign_decl_codes_user(cord(foreign_decl_code)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_foreign_decl_codes_aux(cord(foreign_decl_code)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_foreign_body_codes(cord(foreign_body_code)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_int_bad_clauses(set(pred_pf_name_arity)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_type_ctor_gen_infos(list(type_ctor_gen_info)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_must_be_stratified_preds(set(pred_id)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_unused_arg_info(unused_arg_info::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_table_struct_map(table_struct_map::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_used_modules(used_modules::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_ancestor_avail_modules(set(module_name)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_maybe_complexity_proc_map(
    maybe(pair(int, complexity_proc_map))::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_complexity_proc_infos(list(complexity_proc_info)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_proc_analysis_kinds(set(proc_analysis_kind)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_analysis_info(analysis_info::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_user_init_pred_target_names(pred_target_names::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_user_final_pred_target_names(pred_target_names::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_structure_reuse_preds(set(pred_id)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_format_call_pragma_preds(set(pred_id)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_exported_enums(list(exported_enum_info)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_event_set(event_set::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_oisu_map(oisu_map::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_oisu_procs(set(pred_proc_id)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_ts_rev_string_table(int::in, list(string)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_type_repn_dec(type_repn_decision_data::in,
    module_info::in, module_info::out) is det.

%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
%
% Utility predicates that are a bit more complicated than
% a simple getter or setter predicates.
%

:- pred module_info_get_pred_id_table(module_info::in,
    pred_id_table::out) is det.
:- pred module_info_set_pred_id_table(pred_id_table::in,
    module_info::in, module_info::out) is det.

    % Given a pred_id, return the pred_info of the specified pred.
    %
:- pred module_info_pred_info(module_info::in, pred_id::in, pred_info::out)
    is det.

    % Given a pred_proc_id, return the proc_info of the specified procedure.
    %
:- pred module_info_proc_info(module_info::in, pred_proc_id::in,
    proc_info::out) is det.
:- pred module_info_proc_info(module_info::in, pred_id::in, proc_id::in,
    proc_info::out) is det.

    % Given a pred_id and a proc_id, get the pred_info of that predicate
    % and the proc_info for that mode of that predicate.
    %
:- pred module_info_pred_proc_info(module_info::in, pred_id::in, proc_id::in,
    pred_info::out, proc_info::out) is det.
:- pred module_info_pred_proc_info(module_info::in, pred_proc_id::in,
    pred_info::out, proc_info::out) is det.

    % Return a set of the pred_ids of all the valid predicates.
    % Predicates whose definition contains a type error, etc.
    % get removed from this set, so that later passes can rely
    % on the predicates in this set being type-correct, etc.
    %
:- pred module_info_get_valid_pred_id_set(module_info::in,
    set_tree234(pred_id)::out) is det.

    % Return the same pred_ids as module_info_get_valid_pred_id_set,
    % but in the form of a sorted list.
    %
:- pred module_info_get_valid_pred_ids(module_info::in,
    list(pred_id)::out) is det.

    % Remove one or more predicates from the set of valid pred_ids,
    % to prevent further processing of those predicates after errors
    % have been encountered in them.
    %
:- pred module_info_make_pred_id_invalid(pred_id::in,
    module_info::in, module_info::out) is det.
:- pred module_info_make_pred_ids_invalid(list(pred_id)::in,
    module_info::in, module_info::out) is det.

    % Completely remove a predicate from a module.
    %
:- pred module_info_remove_predicate(pred_id::in,
    module_info::in, module_info::out) is det.

:- pred module_info_set_pred_info(pred_id::in, pred_info::in,
    module_info::in, module_info::out) is det.

:- pred module_info_set_pred_proc_info(pred_proc_id::in,
    pred_info::in, proc_info::in, module_info::in, module_info::out) is det.
:- pred module_info_set_pred_proc_info(pred_id::in, proc_id::in,
    pred_info::in, proc_info::in, module_info::in, module_info::out) is det.

%---------------------%

:- pred predicate_id(module_info::in, pred_id::in, module_name::out,
    string::out, pred_form_arity::out) is det.

:- func predicate_module(module_info, pred_id) = module_name.
:- func predicate_name(module_info, pred_id) = string.
:- func predicate_arity(module_info, pred_id) = pred_form_arity.

%---------------------%

:- pred module_add_foreign_decl_code_user(foreign_decl_code::in,
    module_info::in, module_info::out) is det.
:- pred module_add_foreign_decl_code_aux(foreign_decl_code::in,
    module_info::in, module_info::out) is det.

:- pred module_add_foreign_body_code(foreign_body_code::in,
    module_info::in, module_info::out) is det.

:- pred module_add_item_fim(item_fim::in,
    module_info::in, module_info::out) is det.

:- pred module_add_fact_table_file(string::in,
    module_info::in, module_info::out) is det.

%---------------------%

    % Please see module_info_ensure_dependency_info for the constraints
    % on the returned dependency_info.
    %
:- pred module_info_dependency_info(module_info::in,
    hlds_dependency_info::out) is det.

:- pred module_info_set_dependency_info(hlds_dependency_info::in,
    module_info::in, module_info::out) is det.

:- pred module_info_clobber_dependency_info(
    module_info::in, module_info::out) is det.

%---------------------%

    % The module_info stores a counter which is used to distinguish
    % lambda predicates which appear on the same line in the same file.
    % This predicate returns the next number for the given context
    % and increments the counter for that context.
    %
:- pred module_info_next_lambda_count(prog_context::in, int::out,
    module_info::in, module_info::out) is det.

:- pred module_info_next_atomic_count(prog_context::in, int::out,
    module_info::in, module_info::out) is det.

:- pred module_info_next_loop_inv_count(prog_context::in, int::out,
    module_info::in, module_info::out) is det.

%---------------------%

:- pred module_add_avail_module(module_name::in,
    module_section::in, import_or_use::in, maybe(prog_context)::in,
    module_info::in, module_info::out) is det.

:- pred module_add_indirectly_imported_module(module_name::in,
    module_info::in, module_info::out) is det.

    % Return the set of the visible modules. These are
    %
    % (1) the current module,
    % (2) any imported modules,
    % (3) any ancestor modules, and
    % (4) any modules imported by ancestor modules.
    %
    % It excludes transitively imported modules (those for which we read
    % `.int2' files).
    %
:- pred module_info_get_visible_modules(module_info::in,
    set(module_name)::out) is det.

    % This returns all the modules that this module's code depends on,
    % i.e. all modules that have been used or imported by this module,
    % directly or indirectly, including parent modules.
    %
:- pred module_info_get_all_deps(module_info::in,
    set(module_name)::out) is det.

:- pred module_info_add_module_to_public_used_modules(module_name::in,
    module_info::in, module_info::out) is det.

%---------------------%

:- pred module_info_user_init_pred_target_names(module_info::in,
    list(string)::out) is det.
:- pred module_info_user_final_pred_target_names(module_info::in,
    list(string)::out) is det.

:- pred module_info_user_init_pred_procs(module_info::in,
    list(pred_proc_id)::out) is det.
:- pred module_info_user_final_pred_procs(module_info::in,
    list(pred_proc_id)::out) is det.

%---------------------------------------------------------------------------%

    % The information needed by the pass that decides type representations.
    % The first three fields contain information that it uses make its
    % decisions; the fourth contains information that says what it should *do*
    % with the results of its decisions.
    %
:- type type_repn_decision_data
    --->    type_repn_decision_data(
                % The contents of type_repn items read in from the interface
                % files of other modules, containing information about the
                % representations of the types defined in those modules.
                trdd_type_repns         :: type_ctor_repn_map,

                % The contents of direct_arg clauses in type definitions
                % read in from the interface files of other modules,
                % containing information about the direct_arg part of the
                % representations of those types.
                %
                % XXX TYPE_REPN In the future, this information should *also*
                % be stored in type_repn items.
                trdd_direct_arg_map     :: direct_arg_map,

                % The contents of foreign_enum pragmas read in either
                % from interface files of other modules, or the source file
                % of the module being compiled, giving the foreign language
                % definition of a type.
                trdd_foreign_enums      :: list({item_mercury_status,
                                                item_foreign_enum_info}),

                % The contents of foreign_export_enum pragmas read in
                % from the source file of the module being compiled,
                % asking the compiler to generate definitions
                % of the named types in the named foreign languages.
                trdd_foreign_exports    :: list(item_foreign_export_enum_info)
            ).

:- type direct_arg_map == map(type_ctor, list(sym_name_arity)).

%---------------------------------------------------------------------------%

:- implementation.

:- import_module libs.op_mode.
:- import_module mdbcomp.builtin_modules.
:- import_module transform_hlds.
:- import_module transform_hlds.mmc_analysis.

:- import_module assoc_list.
:- import_module bool.
:- import_module counter.
:- import_module require.
:- import_module string.

    % The module_info, module_sub_info and module_rare_info types
    % constitute a single logical data structure that has been split
    % into three parts for efficiency purposes.
    %
    % The module_info type itself contains the most frequently accessed
    % and updated pieces of information about the module.
    %
    % We keep the other pieces of information we need about the module
    % in the module_sub_info or the module_rare_info types. Those that are
    % reasonably frequently updated are in the module_sub_info; those that
    % are rarely or never updated are in the module_rare_info.
    %
    % This arrangement minimizes the amount of memory that needs to be
    % allocated, and filled in, when a field is updated.
    %
    % Note that a field may be rarely read or written for two main reasons.
    %
    % - One reason is that the compiler simply does not need to read or write
    %   the field very often. This may be e.g. because it is used only
    %   when processing a language construct that is rare, or because
    %   its uses are concentrated in a few pieces of code that read it
    %   only when they start and write it only when they finish.
    %
    % - Another reason is that the field is used only when a compiler option
    %   is given, and it is rarely given.

    % Please keep the order of the fields in module_info, module_sub_info
    % and module_rare_info in sync with the order of the both the
    % declarations and definitions of both the getter and setter predicates.

:- type module_info
    --->    module_info(
                % The Boehm collector allocates blocks whose sizes are
                % multiples of 2. Please keep the number of fields here
                % to a multiple of 2 as well.

                % Note that the no_tag_type_table contains information
                % about notag types that is also available in the type_table,
                % but in a format that allows faster access.

/* 01 */        mi_sub_info                     :: module_sub_info,
/* 02 */        mi_rare_info                    :: module_rare_info,
/* 03 */        mi_globals                      :: globals,
/* 04 */        mi_predicate_table              :: predicate_table,
/* 05 */        mi_type_table                   :: type_table,
/* 06 */        mi_no_tag_type_table            :: no_tag_type_table,
/* 07 */        mi_inst_table                   :: inst_table,
/* 08 */        mi_mode_table                   :: mode_table,
/* 09 */        mi_cons_table                   :: cons_table,
/* 10 */        mi_ctor_field_table             :: ctor_field_table
            ).

:- type module_sub_info
    --->    module_sub_info(
                msi_special_pred_maps           :: special_pred_maps,
                msi_class_table                 :: class_table,
                msi_instance_table              :: instance_table,

                % Data used for user-guided type specialization.
                msi_type_spec_tables            :: type_spec_tables,

                % The database of constant structures the code generator
                % will generate independently, outside all the procedures
                % of the program.
                msi_const_struct_db             :: const_struct_db,

                msi_c_j_cs_fims                 :: c_j_cs_fims,

                % List of the procs for which there is a
                % pragma foreign_export(...) declaration.
                msi_pragma_exported_procs       :: cord(pragma_exported_proc)
            ).

:- type module_rare_info
    --->    module_rare_info(
                mri_module_name                 :: module_name,
                mri_module_name_context         :: prog_context,
                mri_dump_base_file_name         :: string,

                mri_include_module_map          :: include_module_map,

                mri_partial_qualifier_info      :: partial_qualifier_info,
                mri_maybe_recompilation_info    :: maybe(recompilation_info),

                mri_proc_requests               :: proc_requests,
                mri_assertion_table             :: assertion_table,
                mri_exclusive_table             :: exclusive_table,

                mri_direct_arg_proc_map         :: direct_arg_proc_map,

                mri_has_parallel_conj           :: has_parallel_conj,
                mri_has_user_event              :: has_user_event,

                % We classify foreign code fragments bodily included in the
                % generated target language file into three categories,
                % based on two criteria.
                %
                % The first criterion is declarations (decl_codes) vs
                % non-declarations (body codes). We separate these because
                % we have to put declarations before code that may use those
                % declarations.
                %
                % We would prefer the second criterion to be declarations
                % that define types vs declarations that define other entities
                % that may refer to those types, such as global variables
                % or function, again so that we can emit the former before
                % the latter, Unfortunately, foreign_decl pragmas do not
                % specify what they define. Instead, our second criterion is
                % user-provided declarations vs aux declarations added
                % by the Mercury compiler itself to implement either
                % (a) mutables, or (b) fact tables. Neither of the latter
                % define types, so putting these after user-provided
                % declarations will work, as long as in the user-provided
                % declarations, definitions of types precede definitions
                % of other entities that refer to those types. Ensuring that
                % is the programmer's responsibility.
                mri_foreign_decl_codes_user     :: cord(foreign_decl_code),
                mri_foreign_decl_codes_aux      :: cord(foreign_decl_code),
                mri_foreign_body_codes          :: cord(foreign_body_code),

                % The names of the files containing fact tables implementing
                % predicates defined in this module.
                mri_fact_table_file_names       :: list(string),

                % The set of predicates and functions for which there was
                % an attempt to define them in the interface (by clause,
                % foreign_proc, or external_proc pragma), which means that
                % if we find no definition for them in the implementation
                % section either, we should NOT generate an error message
                % complaining about the definition being missing. Such a
                % message would be misleading, since the definition is not
                % missing, it was just misplaced, and we have already
                % generated an error message about that misplaced attempt
                % at definition.
                mri_int_bad_clauses             :: set(pred_pf_name_arity),

                % Please see module_info_ensure_dependency_info for the
                % meaning of this dependency_info, and the constraints on it.
                mri_maybe_dependency_info       :: maybe(hlds_dependency_info),

                mri_type_ctor_gen_infos         :: list(type_ctor_gen_info),
                mri_must_be_stratified_preds    :: set(pred_id),

                % Unused argument info about predicates in the current module
                % which has been exported in .opt files.
                mri_unused_arg_info             :: unused_arg_info,

                % For every procedure that requires its own tabling structure,
                % this field records the information needed to define that
                % structure.
                mri_table_struct_map            :: table_struct_map,

                % How many lambda expressions there are at different contexts
                % in the module. This is used to uniquely identify lambda
                % expressions that appear on the same line of the same file.
                mri_lambdas_per_context         :: map(prog_context, counter),

                % How many STM atomic expressions there are at different
                % contexts in the module. This is used to uniquely identify
                % STM atomic expressions that appear on the same line of
                % the same file.
                mri_atomics_per_context         :: map(prog_context, counter),

                % How many loop invariant optimizations we have done at
                % different contexts in the module. This is used to provide
                % a uniquely identify the predicates that we create
                % using the loop invariant optimization, in the rare case
                % that one line contains the definition of more than one
                % predicate.
                mri_loop_invs_per_context       :: map(prog_context, counter),

                % The add_item_avails predicate in make_hlds_passes.m fills
                % this field with information about all the import- and
                % use_module declarations both in the module being compiled,
                % and in the .int0 interface files of its ancestors.
                %
                % Each entry in the avail_module_map will specify
                %
                % - whether the module is imported or used in the interface
                %   of either the module or its ancestors, and
                %
                % - whether the module is imported (as opposed to used)
                %   in either the module or its ancestors.
                %
                % Each entry will also contain a list of avail_modules
                % *for import/use_module declarations in this module only*;
                % there won't be any entries for imports/uses in ancestors.
                %
                % This field is used by:
                %
                % - intermod.m to (over-)estimate the set of use_module decls
                %   needed by the code put into a .opt file;
                %
                % - try_expand.m to see whether exception is imported
                %   and hence whether it has anything to do at all
                %   (since we import exception.m implicitly if some code
                %   contains a try goal);
                %
                % - by unused_imports.m to decide what import_module and/or
                %   use_module declarations to warn about;
                %
                % - by xml_documentation to prettyprint a module as XML;
                %
                % and possibly more.
                %
                % XXX ITEM_LIST The import_and_or_use_map type in prog_item.m
                % would be an improvement over this type, for several reasons.
                %
                % - It contains more detailed information. It may be possible
                %   to use the extra detail to eliminate or at least reduce
                %   some overapproximations we now do.
                %
                % - Copying the import_and_or_use_map from the parse tree
                %   of the module would be simpler than building this data
                %   structure from it. It would probably allow us to delete
                %   the code that now does that building, and it would
                %   eliminate a possible source of unwanted differences
                %   between the parse tree and HLDS representations
                %   of the module.
                mri_avail_module_map            :: avail_module_map,

                % The names of all the indirectly imported/used modules.
                % This field never used on its own; it is always used together
                % with the previous one, which records info about the
                % directly imported/used modules, to compute the set of modules
                % that are imported or used either directly or indirectly.
                %
                % This is used for purposes such as:
                %
                % - including references to those modules in .d files;
                % - reading and writing the .analysis files of those modules;
                % - #including the .mh and .mih files of those modules.
                mri_indirectly_imported_modules :: set(module_name),

                % The modules which have already been calculated as being used.
                % This slot is initialized to the set of modules that have
                % been seen to be used during the expansion of equivalence
                % types and insts.
                mri_used_modules                :: used_modules,

                % The set of modules imported by ancestor modules.
                %
                % We used to add these to mri_used_modules, but that prevented
                % the compiler from generating useful warnings about unused
                % local imports/uses of those modules. We now keep this info
                % on a "may be useful later" basis; it is currently unused.
                mri_ancestor_avail_modules      :: set(module_name),

                % Information about the procedures we are performing
                % complexity experiments on.
                mri_maybe_complexity_proc_map   :: maybe(pair(int,
                                                complexity_proc_map)),
                mri_complexity_proc_infos       :: list(complexity_proc_info),

                % Records the set of analyses whose results are now available
                % in the proc_infos. As each analysis puts its results
                % in the proc_infos, it should add its id to this set.
                mri_proc_analysis_kinds         :: set(proc_analysis_kind),

                % Information for the inter-module analysis framework.
                mri_analysis_info               :: analysis_info,

                % Exported target language names for preds appearing in
                % `:- initialise' directives, and implicitly in
                % `:- mutable' directives, in this module.
                mri_user_init_pred_target_names :: pred_target_names,

                % Exported target names for preds appearing in
                % `:- finalise' directives in this module.
                mri_user_final_pred_target_names :: pred_target_names,

                % Predicates which were created as reuse versions of other
                % procedures. Its only use is to avoid writing out pragmas
                % for structure reuse predicates to `.trans_opt' files.
                mri_structure_reuse_preds       :: set(pred_id),

                % The set of predicates in the HLDS that have
                % a format_call pragma.
                %
                % This set is filled in by add_pragma.m when the HLDS
                % is first constructed. Usually, this set will be empty,
                % but if it is not, the check_pragma_format_call_preds pass
                % will check all these pragmas for errors.
                %
                % This pass must be after type and mode analysis (since it
                % needs to know the types and modes of procedure arguments),
                % and must be before the simplification pass at the end of
                % semantic analysis (since that is when format_call.m's code
                % uses this info, relying on it having being checked).
                %
                % The presence of a pred_id in this field does NOT keep
                % the predicate it refers to alive. Any code handling these
                % pred_ids must be prepared for the possibility that dead
                % predicate elimination has deleted the pred_info it originally
                % referred to.
                mri_format_call_pragma_preds    :: set(pred_id),

                % Enumeration types that have been exported to a foreign
                % language.
                mri_exported_enums              :: list(exported_enum_info),

                mri_event_set                   :: event_set,

                % The set of visible declarations about order-independent
                % state update.
                mri_oisu_map                    :: oisu_map,

                % The set of procedures defined in this module that
                % have OISU arguments.
                mri_oisu_procs                  :: set(pred_proc_id),

                % A table of strings used by some threadscope events.
                % Currently threadscope events are introduced for each future
                % in dep_par_conj.m which is why we need to record the table
                % within the HLDS. The LLDS also uses threadscope string
                % tables, see global_data.m, the LLDS introduces strings during
                % the HLDS->LLDS transformation of parallel conjunctions.
                mri_ts_string_table_size        :: int,
                mri_ts_rev_string_table         :: list(string),

                % Information needed to decide type representations.
                mri_type_repn_dec               :: type_repn_decision_data
            ).

% Access stats for the module_info structure on 30 december 2014.
%
%  i      read      same      diff   same%
%  0 188233540        17  38369323   0.00%  predicate_table
%  1      7933         0       480   0.00%  proc_requests
%  2    261171         0    103230   0.00%  special_pred_maps
%  3   4576898         0         0          partial_qualifier_info
%  4  21758620      2908   1589788   0.18%  type_table
%  5  22754063         0   2360725   0.00%  inst_table
%  6 145877431     10501    149637   6.56%  mode_table
%  7  16110150      3767    803152   0.47%  cons_table
%  8   7125765       353     65777   0.53%  class_table
%  9   2543131       353    206012   0.17%  instance_table
% 10      7935         0      3798   0.00%  assertion_table
% 11         0         0         0          exclusive_table
% 12   4552293    620283    180042  77.50%  ctor_field_table
% 13   2256146   2219707       235  99.99%  maybe_recompilation_info
% 14  14893776         0         0          name
% 15         0         0         0          dump_hlds_base_file_name
% 16  39144524         0     16950   0.00%  globals
% 17      8481        17        64  20.99%  has_parallel_conj
% 18       253         4         5  44.44%  has_user_event
% 19         0    223574      3371  98.51%  contains_foreign_type
% 20      9180         0       710   0.00%  foreign_decl_codes
% 21      3683         0       856   0.00%  foreign_body_codes
% 22    966008         0    963108   0.00%  foreign_import_modules
% 23    331870    281405     35898  88.69%  pragma_exported_procs
% 24         1         0         0          fact_table_file_names
% 25     10630     11345      8490  57.20%  maybe_dependency_info
% 26     11342      4885       128  97.45%  num_errors
% 27     35292      2442      3209  43.21%  type_ctor_gen_infos
% 28      3506         0         2   0.00%  must_be_stratified_preds
% 29         1         0         1   0.00%  unused_arg_info
% 30   3972001         0   3445467   0.00%  exception_info
% 31       296         0         0          trailing_info
% 32      4071         0        75   0.00%  table_struct_map
% 33       296         0         0          mm_tabling_info
% 34      4019         0      4019   0.00%  lambdas_per_context
% 35         0         0         0          atomics_per_context
% 36      7053         0         0          imported_module_names
% 37      3135         0         0          indirectly_imported_mod_specs
% 38         1         0         0          interface_module_names
% 39      6568         0      3767   0.00%  used_modules
% 40   1656135         0    126058   0.00%  type_spec_tables
% 41  22588003         0     87106   0.00%  no_tag_types
% 42    171993         0         0          complexity_proc_map
% 43      2821         0         0          complexity_proc_infos
% 44       763       336         2  99.41%  analysis_info
% 45       316         0        20   0.00%  structure_reuse_preds
% 46      5703         0        55   0.00%  exported_enums
% 47        48         0      3767   0.00%  event_set
% 48      3510         0         6   0.00%  oisu_map
% 49         0         0         2   0.00%  oisu_procs
% 50   2684695   2592456     10222  99.61%  const_struct_db
% 51      2821         0         0          threadscope_string_table

%---------------------------------------------------------------------------%

module_info_init(Globals, ModuleName, ModuleNameContext, DumpBaseFileName,
        InclMap, UsedModules, ImplicitlyUsedModules,
        QualifierInfo, MaybeRecompInfo, TypeRepnDec, ModuleInfo) :-
    SpecialPredMaps = special_pred_maps(map.init, map.init, map.init),
    map.init(ClassTable),
    map.init(InstanceTable),

    set.init(TypeSpecPreds),
    set.init(TypeSpecForcePreds),
    map.init(SpecMap),
    map.init(PragmaMap),
    TypeSpecInfo = type_spec_tables(TypeSpecPreds, TypeSpecForcePreds,
        SpecMap, PragmaMap),

    const_struct_db_init(Globals, ConstStructDb),
    ForeignImportModules = init_foreign_import_modules,
    PragmaExportedProcs = cord.init,

    ModuleSubInfo = module_sub_info(
        SpecialPredMaps,
        ClassTable,
        InstanceTable,
        TypeSpecInfo,
        ConstStructDb,
        ForeignImportModules,
        PragmaExportedProcs),

    init_requests(ProcRequests),
    assertion_table_init(AssertionTable),
    exclusive_table_init(ExclusiveTable),
    map.init(DirectArgInOutMap),
    HasParallelConj = has_no_parallel_conj,
    HasUserEvent = has_no_user_event,
    ForeignDeclsUser = cord.init,
    ForeignDeclsAux = cord.init,
    ForeignBodies = cord.init,
    FactTableFiles = [],
    set.init(IntBadPreds),
    MaybeDependencyInfo = maybe.no,
    MustBeStratifiedPreds = [],
    set.init(StratPreds),
    map.init(UnusedArgInfo),
    map.init(TablingStructMap),
    map.init(LambdasPerContext),
    map.init(AtomicsPerContext),
    map.init(LoopInvsPerContext),

    % XXX ITEM_LIST Given that we start with an aug_compilation_unit,
    % shouldn't the work of finding implicit dependencies have already
    % been done?
    % XXX ITEM_LIST Should a tabled predicate declared in a .int* or .*opt
    % file generate an implicit dependency?

    map.init(AvailModuleMap0),
    add_implicit_avail_module(import_decl, mercury_public_builtin_module,
        AvailModuleMap0, AvailModuleMap1),
    set.fold(add_implicit_avail_module(use_decl), ImplicitlyUsedModules,
        AvailModuleMap1, AvailModuleMap),

    set.init(IndirectlyImportedModules),

    set.init(AncestorAvailModules),
    MaybeComplexityMap = no,
    ComplexityProcInfos = [],
    set.init(ProcAnalysisKinds),

    globals.get_op_mode(Globals, OpMode),
    ( if
        OpMode = opm_top_args(opma_augment(opmau_make_analysis_registry), _)
    then
        MakeAnalysisReg = yes
    else
        MakeAnalysisReg = no
    ),
    AnalysisInfo = init_analysis_info(mmc, ModuleName, MakeAnalysisReg),

    UserInitPredTargetNames = pred_target_names(map.init),
    UserFinalPredTargetNames = pred_target_names(map.init),
    set.init(StructureReusePredIds),
    set.init(FormatCallPragmaPredIds),
    ExportedEnums = [],
    EventSet = event_set("", map.init),
    map.init(OISUMap),
    set.init(OISUProcs),
    TSStringTableSize = 0,
    TSRevStringTable = [],

    ModuleRareInfo = module_rare_info(
        ModuleName,
        ModuleNameContext,
        DumpBaseFileName,
        InclMap,
        QualifierInfo,
        MaybeRecompInfo,
        ProcRequests,
        AssertionTable,
        ExclusiveTable,
        DirectArgInOutMap,
        HasParallelConj,
        HasUserEvent,
        ForeignDeclsUser,
        ForeignDeclsAux,
        ForeignBodies,
        FactTableFiles,
        IntBadPreds,
        MaybeDependencyInfo,
        MustBeStratifiedPreds,
        StratPreds,
        UnusedArgInfo,
        TablingStructMap,
        LambdasPerContext,
        AtomicsPerContext,
        LoopInvsPerContext,
        AvailModuleMap,
        IndirectlyImportedModules,
        UsedModules,
        AncestorAvailModules,
        MaybeComplexityMap,
        ComplexityProcInfos,
        ProcAnalysisKinds,
        AnalysisInfo,
        UserInitPredTargetNames,
        UserFinalPredTargetNames,
        StructureReusePredIds,
        FormatCallPragmaPredIds,
        ExportedEnums,
        EventSet,
        OISUMap,
        OISUProcs,
        TSStringTableSize,
        TSRevStringTable,
        TypeRepnDec),

    predicate_table_init(PredicateTable),
    TypeTable = init_type_table,
    map.init(NoTagTypes),
    inst_table_init(InstTable),
    mode_table_init(ModeTable),
    CtorTable = init_cons_table,
    map.init(FieldNameTable),

    ModuleInfo = module_info(
        ModuleSubInfo,
        ModuleRareInfo,
        Globals,
        PredicateTable,
        TypeTable,
        NoTagTypes,
        InstTable,
        ModeTable,
        CtorTable,
        FieldNameTable).

:- pred add_implicit_avail_module(import_or_use::in, module_name::in,
    avail_module_map::in, avail_module_map::out) is det.

add_implicit_avail_module(ImportOrUse, ModuleName, !AvailModuleMap) :-
    Entry = avail_module_entry(ms_implementation, ImportOrUse, []),
    map.det_insert(ModuleName, Entry, !AvailModuleMap).

module_info_optimize(!ModuleInfo) :-
    % Currently, all the calls to *_table_optimize are no-ops.
    % We keep them, and this predicate, in case that changes in the future.

    module_info_get_predicate_table(!.ModuleInfo, Preds0),
    predicate_table_optimize(Preds0, Preds),
    module_info_set_predicate_table(Preds, !ModuleInfo),

    module_info_get_inst_table(!.ModuleInfo, InstTable0),
    inst_table_get_user_insts(InstTable0, UserInstTable0),
    map.optimize(UserInstTable0, UserInstTable),
    inst_table_set_user_insts(UserInstTable, InstTable0, InstTable),
    module_info_set_inst_table(InstTable, !ModuleInfo),

    module_info_get_mode_table(!.ModuleInfo, Modes0),
    mode_table_optimize(Modes0, Modes),
    module_info_set_mode_table(Modes, !ModuleInfo),

    module_info_get_cons_table(!.ModuleInfo, Ctors0),
    cons_table_optimize(Ctors0, Ctors),
    module_info_set_cons_table(Ctors, !ModuleInfo).

%---------------------------------------------------------------------------%
%
% Getter and setter predicates on the module_info that are local to this
% module.
%

:- pred module_info_get_lambdas_per_context(module_info::in,
    map(prog_context, counter)::out) is det.
:- pred module_info_get_atomics_per_context(module_info::in,
    map(prog_context, counter)::out) is det.
:- pred module_info_get_loop_invs_per_context(module_info::in,
    map(prog_context, counter)::out) is det.
:- pred module_info_get_indirectly_imported_modules(module_info::in,
    set(module_name)::out) is det.

:- pred module_info_set_maybe_dependency_info(maybe(hlds_dependency_info)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_lambdas_per_context(map(prog_context, counter)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_atomics_per_context(map(prog_context, counter)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_loop_invs_per_context(map(prog_context, counter)::in,
    module_info::in, module_info::out) is det.
:- pred module_info_set_indirectly_imported_modules(set(module_name)::in,
    module_info::in, module_info::out) is det.

%---------------------------------------------------------------------------%
%
% Getter predicates for the module_info.
%

module_info_get_globals(MI, X) :-
    X = MI ^ mi_globals.
module_info_get_predicate_table(MI, X) :-
    X = MI ^ mi_predicate_table.
module_info_get_type_table(MI, X) :-
    X = MI ^ mi_type_table.
module_info_get_no_tag_types(MI, X) :-
    X = MI ^ mi_no_tag_type_table.
module_info_get_inst_table(MI, X) :-
    X = MI ^ mi_inst_table.
module_info_get_mode_table(MI, X) :-
    X = MI ^ mi_mode_table.
module_info_get_cons_table(MI, X) :-
    X = MI ^ mi_cons_table.
module_info_get_ctor_field_table(MI, X) :-
    X = MI ^ mi_ctor_field_table.

module_info_get_special_pred_maps(MI, X) :-
    X = MI ^ mi_sub_info ^ msi_special_pred_maps.
module_info_get_class_table(MI, X) :-
    X = MI ^ mi_sub_info ^ msi_class_table.
module_info_get_instance_table(MI, X) :-
    X = MI ^ mi_sub_info ^ msi_instance_table.
module_info_get_type_spec_tables(MI, X) :-
    X = MI ^ mi_sub_info ^ msi_type_spec_tables.
module_info_get_const_struct_db(MI, X) :-
    X = MI ^ mi_sub_info ^ msi_const_struct_db.
module_info_get_c_j_cs_fims(MI, X) :-
    X = MI ^ mi_sub_info ^ msi_c_j_cs_fims.
module_info_get_pragma_exported_procs(MI, X) :-
    X = MI ^ mi_sub_info ^ msi_pragma_exported_procs.

module_info_get_name(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_module_name.
module_info_get_name_context(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_module_name_context.
module_info_get_dump_hlds_base_file_name(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_dump_base_file_name.
module_info_get_include_module_map(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_include_module_map.
module_info_get_partial_qualifier_info(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_partial_qualifier_info.
module_info_get_maybe_recompilation_info(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_maybe_recompilation_info.
module_info_get_proc_requests(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_proc_requests.
module_info_get_assertion_table(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_assertion_table.
module_info_get_exclusive_table(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_exclusive_table.
module_info_get_has_parallel_conj(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_has_parallel_conj.
module_info_get_has_user_event(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_has_user_event.
module_info_get_direct_arg_proc_map(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_direct_arg_proc_map.
module_info_get_foreign_decl_codes_user(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_foreign_decl_codes_user.
module_info_get_foreign_decl_codes_aux(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_foreign_decl_codes_aux.
module_info_get_foreign_body_codes(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_foreign_body_codes.
module_info_get_fact_table_file_names(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_fact_table_file_names.
module_info_get_int_bad_clauses(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_int_bad_clauses.
module_info_get_maybe_dependency_info(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_maybe_dependency_info.
module_info_get_type_ctor_gen_infos(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_type_ctor_gen_infos.
module_info_get_must_be_stratified_preds(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_must_be_stratified_preds.
module_info_get_unused_arg_info(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_unused_arg_info.
module_info_get_table_struct_map(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_table_struct_map.
module_info_get_lambdas_per_context(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_lambdas_per_context.
module_info_get_atomics_per_context(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_atomics_per_context.
module_info_get_loop_invs_per_context(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_loop_invs_per_context.
module_info_get_avail_module_map(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_avail_module_map.
module_info_get_indirectly_imported_modules(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_indirectly_imported_modules.
module_info_get_used_modules(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_used_modules.
module_info_get_ancestor_avail_modules(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_ancestor_avail_modules.
module_info_get_maybe_complexity_proc_map(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_maybe_complexity_proc_map.
module_info_get_complexity_proc_infos(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_complexity_proc_infos.
module_info_get_proc_analysis_kinds(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_proc_analysis_kinds.
module_info_get_analysis_info(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_analysis_info.
module_info_get_user_init_pred_target_names(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_user_init_pred_target_names.
module_info_get_user_final_pred_target_names(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_user_final_pred_target_names.
module_info_get_structure_reuse_preds(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_structure_reuse_preds.
module_info_get_format_call_pragma_preds(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_format_call_pragma_preds.
module_info_get_exported_enums(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_exported_enums.
module_info_get_event_set(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_event_set.
module_info_get_oisu_map(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_oisu_map.
module_info_get_oisu_procs(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_oisu_procs.
module_info_get_ts_rev_string_table(MI, X, Y) :-
    X = MI ^ mi_rare_info ^ mri_ts_string_table_size,
    Y = MI ^ mi_rare_info ^ mri_ts_rev_string_table.
module_info_get_type_repn_dec(MI, X) :-
    X = MI ^ mi_rare_info ^ mri_type_repn_dec.

%---------------------------------------------------------------------------%
%
% Setter predicates for the module_info.
%

module_info_set_globals(X, !MI) :-
    !MI ^ mi_globals := X.
module_info_set_predicate_table(X, !MI) :-
    !MI ^ mi_predicate_table := X.
module_info_set_type_table(X, !MI) :-
    !MI ^ mi_type_table := X.
module_info_set_no_tag_types(X, !MI) :-
    !MI ^ mi_no_tag_type_table := X.
module_info_set_inst_table(X, !MI) :-
    !MI ^ mi_inst_table := X.
module_info_set_mode_table(X, !MI) :-
    !MI ^ mi_mode_table := X.
module_info_set_cons_table(X, !MI) :-
    !MI ^ mi_cons_table := X.
module_info_set_ctor_field_table(X, !MI) :-
    !MI ^ mi_ctor_field_table := X.

module_info_set_special_pred_maps(X, !MI) :-
    !MI ^ mi_sub_info ^ msi_special_pred_maps := X.
module_info_set_class_table(X, !MI) :-
    !MI ^ mi_sub_info ^ msi_class_table := X.
module_info_set_instance_table(X, !MI) :-
    !MI ^ mi_sub_info ^ msi_instance_table := X.
module_info_set_type_spec_tables(X, !MI) :-
    !MI ^ mi_sub_info ^ msi_type_spec_tables := X.
module_info_set_const_struct_db(X, !MI) :-
    ( if
        private_builtin.pointer_equal(X,
            !.MI ^ mi_sub_info ^ msi_const_struct_db)
    then
        true
    else
        !MI ^ mi_sub_info ^ msi_const_struct_db := X
    ).
module_info_set_c_j_cs_fims(X, !MI) :-
    !MI ^ mi_sub_info ^ msi_c_j_cs_fims := X.
module_info_set_pragma_exported_procs(X, !MI) :-
    ( if
        private_builtin.pointer_equal(X,
            !.MI ^ mi_sub_info ^ msi_pragma_exported_procs)
    then
        true
    else
        !MI ^ mi_sub_info ^ msi_pragma_exported_procs := X
    ).

module_info_set_maybe_recompilation_info(X, !MI) :-
    ( if
        private_builtin.pointer_equal(X,
            !.MI ^ mi_rare_info ^ mri_maybe_recompilation_info)
    then
        true
    else
        !MI ^ mi_rare_info ^ mri_maybe_recompilation_info := X
    ).
module_info_set_proc_requests(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_proc_requests := X.
module_info_set_assertion_table(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_assertion_table := X.
module_info_set_exclusive_table(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_exclusive_table := X.
module_info_set_has_parallel_conj(!MI) :-
    X = has_parallel_conj,
    !MI ^ mi_rare_info ^ mri_has_parallel_conj := X.
module_info_set_has_user_event(!MI) :-
    X = has_user_event,
    !MI ^ mi_rare_info ^ mri_has_user_event := X.
module_info_set_direct_arg_proc_map(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_direct_arg_proc_map := X.
module_info_set_foreign_decl_codes_user(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_foreign_decl_codes_user := X.
module_info_set_foreign_decl_codes_aux(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_foreign_decl_codes_aux := X.
module_info_set_foreign_body_codes(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_foreign_body_codes := X.
module_info_set_int_bad_clauses(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_int_bad_clauses := X.
module_info_set_maybe_dependency_info(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_maybe_dependency_info := X.
module_info_set_type_ctor_gen_infos(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_type_ctor_gen_infos := X.
module_info_set_must_be_stratified_preds(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_must_be_stratified_preds := X.
module_info_set_unused_arg_info(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_unused_arg_info := X.
module_info_set_table_struct_map(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_table_struct_map := X.
module_info_set_lambdas_per_context(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_lambdas_per_context := X.
module_info_set_atomics_per_context(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_atomics_per_context := X.
module_info_set_loop_invs_per_context(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_loop_invs_per_context := X.
module_info_set_indirectly_imported_modules(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_indirectly_imported_modules := X.
module_info_set_used_modules(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_used_modules := X.
module_info_set_ancestor_avail_modules(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_ancestor_avail_modules := X.
module_info_set_maybe_complexity_proc_map(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_maybe_complexity_proc_map := X.
module_info_set_complexity_proc_infos(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_complexity_proc_infos := X.
module_info_set_proc_analysis_kinds(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_proc_analysis_kinds := X.
module_info_set_analysis_info(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_analysis_info := X.
module_info_set_user_init_pred_target_names(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_user_init_pred_target_names := X.
module_info_set_user_final_pred_target_names(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_user_final_pred_target_names := X.
module_info_set_structure_reuse_preds(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_structure_reuse_preds := X.
module_info_set_format_call_pragma_preds(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_format_call_pragma_preds := X.
module_info_set_exported_enums(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_exported_enums := X.
module_info_set_event_set(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_event_set := X.
module_info_set_oisu_map(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_oisu_map := X.
module_info_set_oisu_procs(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_oisu_procs := X.
module_info_set_ts_rev_string_table(X, Y, !MI) :-
    !MI ^ mi_rare_info ^ mri_ts_string_table_size := X,
    !MI ^ mi_rare_info ^ mri_ts_rev_string_table := Y.
module_info_set_type_repn_dec(X, !MI) :-
    !MI ^ mi_rare_info ^ mri_type_repn_dec := X.

%---------------------------------------------------------------------------%
%
% Utility predicates that are a bit more complicated than
% a simple getter or setter predicates.
%

module_info_get_pred_id_table(MI, PredIdTable) :-
    module_info_get_predicate_table(MI, PredTable),
    predicate_table_get_pred_id_table(PredTable, PredIdTable).

module_info_set_pred_id_table(PredIdTable, !MI) :-
    module_info_get_predicate_table(!.MI, PredTable0),
    predicate_table_set_pred_id_table(PredIdTable, PredTable0, PredTable),
    module_info_set_predicate_table(PredTable, !MI).

module_info_pred_info(MI, PredId, PredInfo) :-
    module_info_get_pred_id_table(MI, PredIdTable),
    ( if map.search(PredIdTable, PredId, PredInfoPrime) then
        PredInfo = PredInfoPrime
    else
        pred_id_to_int(PredId, PredInt),
        string.int_to_string(PredInt, PredStr),
        unexpected($pred, "cannot find predicate number " ++ PredStr)
    ).

module_info_proc_info(MI, PPId, ProcInfo) :-
    module_info_pred_proc_info(MI, PPId, _, ProcInfo).

module_info_proc_info(MI, PredId, ProcId, ProcInfo) :-
    module_info_pred_proc_info(MI, PredId, ProcId, _, ProcInfo).

module_info_pred_proc_info(MI, PredId, ProcId, PredInfo, ProcInfo) :-
    module_info_pred_info(MI, PredId, PredInfo),
    pred_info_get_proc_table(PredInfo, Procs),
    map.lookup(Procs, ProcId, ProcInfo).

module_info_pred_proc_info(MI, proc(PredId, ProcId), PredInfo, ProcInfo) :-
    module_info_pred_proc_info(MI, PredId, ProcId, PredInfo, ProcInfo).

module_info_get_valid_pred_id_set(MI, PredIdSet) :-
    module_info_get_predicate_table(MI, PredTable),
    predicate_table_get_valid_pred_id_set(PredTable, PredIdSet).

module_info_get_valid_pred_ids(MI, PredIds) :-
    module_info_get_predicate_table(MI, PredTable),
    predicate_table_get_valid_pred_id_set(PredTable, PredIdSet),
    set_tree234.to_sorted_list(PredIdSet, PredIds).

module_info_make_pred_id_invalid(PredId, !MI) :-
    module_info_get_predicate_table(!.MI, PredTable0),
    predicate_table_make_pred_id_invalid(PredId, PredTable0, PredTable),
    module_info_set_predicate_table(PredTable, !MI).

module_info_make_pred_ids_invalid(PredIds, !MI) :-
    module_info_get_predicate_table(!.MI, PredTable0),
    predicate_table_make_pred_ids_invalid(PredIds, PredTable0, PredTable),
    module_info_set_predicate_table(PredTable, !MI).

module_info_remove_predicate(PredId, !MI) :-
    module_info_get_predicate_table(!.MI, PredTable0),
    predicate_table_remove_predicate(PredId, PredTable0, PredTable),
    module_info_set_predicate_table(PredTable, !MI).

module_info_set_pred_info(PredId, PredInfo, !MI) :-
    module_info_get_pred_id_table(!.MI, PredIdTable0),
    % XXX Should be map.det_update.
    map.set(PredId, PredInfo, PredIdTable0, PredIdTable),
    module_info_set_pred_id_table(PredIdTable, !MI).

module_info_set_pred_proc_info(proc(PredId, ProcId), PredInfo, ProcInfo,
        !MI) :-
    module_info_set_pred_proc_info(PredId, ProcId,
        PredInfo, ProcInfo, !MI).

module_info_set_pred_proc_info(PredId, ProcId, PredInfo0, ProcInfo, !MI) :-
    % XXX Should be pred_info_set_proc_info, which calls map.det_update.
    pred_info_get_proc_table(PredInfo0, Procs0),
    map.set(ProcId, ProcInfo, Procs0, Procs),
    pred_info_set_proc_table(Procs, PredInfo0, PredInfo),
    module_info_set_pred_info(PredId, PredInfo, !MI).

%---------------------%

predicate_id(ModuleInfo, PredId, ModuleName, PredName, PredFormArity) :-
    module_info_pred_info(ModuleInfo, PredId, PredInfo),
    ModuleName = pred_info_module(PredInfo),
    PredName = pred_info_name(PredInfo),
    pred_info_get_orig_arity(PredInfo, PredFormArity).

predicate_module(ModuleInfo, PredId) = ModuleName :-
    module_info_pred_info(ModuleInfo, PredId, PredInfo),
    ModuleName = pred_info_module(PredInfo).

predicate_name(ModuleInfo, PredId) = PredName :-
    module_info_pred_info(ModuleInfo, PredId, PredInfo),
    PredName = pred_info_name(PredInfo).

predicate_arity(ModuleInfo, PredId) = PredFormArity :-
    module_info_pred_info(ModuleInfo, PredId, PredInfo),
    pred_info_get_orig_arity(PredInfo, PredFormArity).

%---------------------%

module_add_foreign_decl_code_user(ForeignDeclCode, !Module) :-
    module_info_get_foreign_decl_codes_user(!.Module, ForeignDeclCodes0),
    ForeignDeclCodes = cord.snoc(ForeignDeclCodes0, ForeignDeclCode),
    module_info_set_foreign_decl_codes_user(ForeignDeclCodes, !Module).

module_add_foreign_decl_code_aux(ForeignDeclCode, !Module) :-
    module_info_get_foreign_decl_codes_aux(!.Module, ForeignDeclCodes0),
    ForeignDeclCodes = cord.snoc(ForeignDeclCodes0, ForeignDeclCode),
    module_info_set_foreign_decl_codes_aux(ForeignDeclCodes, !Module).

module_add_foreign_body_code(ForeignBodyCode, !Module) :-
    module_info_get_foreign_body_codes(!.Module, ForeignBodyCodes0),
    ForeignBodyCodes = cord.snoc(ForeignBodyCodes0, ForeignBodyCode),
    module_info_set_foreign_body_codes(ForeignBodyCodes, !Module).

module_add_item_fim(ItemFIM, !Module) :-
    module_info_get_c_j_cs_fims(!.Module, FIMs0),
    ItemFIM = item_fim(Lang, ModuleName, _Context, _SeqNum),
    add_fim(Lang, ModuleName, FIMs0, FIMs),
    module_info_set_c_j_cs_fims(FIMs, !Module).

module_add_fact_table_file(FileName, !Module) :-
    FileNames0 = !.Module ^ mi_rare_info ^ mri_fact_table_file_names,
    FileNames = [FileName | FileNames0],
    !Module ^ mi_rare_info ^ mri_fact_table_file_names := FileNames.

%---------------------%

module_info_dependency_info(MI, DepInfo) :-
    module_info_get_maybe_dependency_info(MI, MaybeDepInfo),
    (
        MaybeDepInfo = yes(DepInfoPrime),
        DepInfo = DepInfoPrime
    ;
        MaybeDepInfo = no,
        unexpected($pred, "attempted to access invalid dependency_info")
    ).

module_info_set_dependency_info(DependencyInfo, !MI) :-
    module_info_set_maybe_dependency_info(yes(DependencyInfo), !MI).

module_info_clobber_dependency_info(!MI) :-
    module_info_set_maybe_dependency_info(no, !MI).

%---------------------%

module_info_next_lambda_count(Context, Count, !MI) :-
    module_info_get_lambdas_per_context(!.MI, ContextCounter0),
    ( if
        map.insert(Context, counter.init(2),
            ContextCounter0, FoundContextCounter)
    then
        Count = 1,
        ContextCounter = FoundContextCounter
    else
        map.lookup(ContextCounter0, Context, Counter0),
        counter.allocate(Count, Counter0, Counter),
        map.det_update(Context, Counter, ContextCounter0, ContextCounter)
    ),
    module_info_set_lambdas_per_context(ContextCounter, !MI).

module_info_next_atomic_count(Context, Count, !MI) :-
    module_info_get_atomics_per_context(!.MI, ContextCounter0),
    ( if
        map.insert(Context, counter.init(2),
            ContextCounter0, FoundContextCounter)
    then
        Count = 1,
        ContextCounter = FoundContextCounter
    else
        map.lookup(ContextCounter0, Context, Counter0),
        counter.allocate(Count, Counter0, Counter),
        map.det_update(Context, Counter, ContextCounter0, ContextCounter)
    ),
    module_info_set_atomics_per_context(ContextCounter, !MI).

module_info_next_loop_inv_count(Context, Count, !MI) :-
    module_info_get_loop_invs_per_context(!.MI, ContextCounter0),
    ( if
        map.insert(Context, counter.init(2),
            ContextCounter0, FoundContextCounter)
    then
        Count = 1,
        ContextCounter = FoundContextCounter
    else
        map.lookup(ContextCounter0, Context, Counter0),
        counter.allocate(Count, Counter0, Counter),
        map.det_update(Context, Counter, ContextCounter0, ContextCounter)
    ),
    module_info_set_loop_invs_per_context(ContextCounter, !MI).

%---------------------%

module_add_avail_module(ModuleName, NewSection, NewImportOrUse,
        MaybeContext, !MI) :-
    (
        MaybeContext = no,
        NewAvails = []
    ;
        MaybeContext = yes(Context),
        NewAvails = [avail_module(NewSection, NewImportOrUse, Context)]
    ),
    AvailMap0 = !.MI ^ mi_rare_info ^ mri_avail_module_map,
    ( if map.search(AvailMap0, ModuleName, OldEntry) then
        OldEntry = avail_module_entry(OldSection, OldImportOrUse, OldAvails),
        % XXX: If one of the entries (new or old) is a use_module in the
        % interface section, while the other is an import_module in the
        % implementation section, the result *ought* to be something like
        % the int_use_imp_import alternative of the section_import_or_use type,
        % BUT the design of avail_module_entry has no way to express that.
        % The code of combine_old_new_avail_attrs returns "import_module in the
        % interface section" in such cases, which is almost certainly a bug.
        combine_old_new_avail_attrs(OldSection, NewSection,
            OldImportOrUse, NewImportOrUse, Section, ImportOrUse),
        Avails = NewAvails ++ OldAvails,
        NewEntry = avail_module_entry(Section, ImportOrUse, Avails),
        map.det_update(ModuleName, NewEntry, AvailMap0, AvailMap)
    else
        NewEntry = avail_module_entry(NewSection, NewImportOrUse, NewAvails),
        map.det_insert(ModuleName, NewEntry, AvailMap0, AvailMap)
    ),
    !MI ^ mi_rare_info ^ mri_avail_module_map := AvailMap.

:- pred combine_old_new_avail_attrs(module_section::in, module_section::in,
    import_or_use::in, import_or_use::in,
    module_section::out, import_or_use::out) is det.

combine_old_new_avail_attrs(OldSection, NewSection,
        OldImportOrUse, NewImportOrUse, Section, ImportOrUse) :-
    ( if
        ( OldSection = ms_interface
        ; NewSection = ms_interface
        )
    then
        Section = ms_interface
    else
        Section = ms_implementation
    ),
    ( if
        ( OldImportOrUse = import_decl
        ; NewImportOrUse = import_decl
        )
    then
        ImportOrUse = import_decl
    else
        ImportOrUse = use_decl
    ).

module_add_indirectly_imported_module(AddedModule, !MI) :-
    module_info_get_indirectly_imported_modules(!.MI, Modules0),
    set.insert(AddedModule, Modules0, Modules),
    module_info_set_indirectly_imported_modules(Modules, !MI).

module_info_get_visible_modules(ModuleInfo, !:VisibleModules) :-
    module_info_get_name(ModuleInfo, ThisModule),
    module_info_get_avail_module_map(ModuleInfo, AvailModuleMap),

    map.keys(AvailModuleMap, AvailModules),
    set.list_to_set(AvailModules, !:VisibleModules),
    set.insert(ThisModule, !VisibleModules),
    set.insert_list(get_ancestors(ThisModule), !VisibleModules).

module_info_get_all_deps(ModuleInfo, AllImports) :-
    module_info_get_name(ModuleInfo, ModuleName),
    Parents = get_ancestors(ModuleName),
    module_info_get_avail_module_map(ModuleInfo, AvailModuleMap),
    map.keys(AvailModuleMap, DirectImports),
    module_info_get_indirectly_imported_modules(ModuleInfo, IndirectImports),
    AllImports = set.union_list([IndirectImports,
        set.list_to_set(DirectImports), set.list_to_set(Parents)]).

module_info_add_module_to_public_used_modules(ModuleName, !MI) :-
    module_info_get_used_modules(!.MI, UsedModules0),
    record_module_and_ancestors_as_used(visibility_public, ModuleName,
        UsedModules0, UsedModules),
    module_info_set_used_modules(UsedModules, !MI).

%---------------------%

module_info_user_init_pred_target_names(MI, CNames) :-
    module_info_get_user_init_pred_target_names(MI, InitPredTargetNames),
    InitPredTargetNames = pred_target_names(SeqNumToPredTargetNamesMap),
    map.values(SeqNumToPredTargetNamesMap, PredTargetNamesLists),
    list.condense(PredTargetNamesLists, PredTargetNames),
    CNames = assoc_list.values(PredTargetNames).

module_info_user_final_pred_target_names(MI, CNames) :-
    module_info_get_user_final_pred_target_names(MI, FinalPredTargetNames),
    FinalPredTargetNames = pred_target_names(SeqNumToPredTargetNamesMap),
    map.values(SeqNumToPredTargetNamesMap, PredTargetNamesLists),
    list.condense(PredTargetNamesLists, PredTargetNames),
    CNames = assoc_list.values(PredTargetNames).

module_info_user_init_pred_procs(MI, PredProcIds) :-
    module_info_get_user_init_pred_target_names(MI, InitPredTargetNames),
    InitPredTargetNames = pred_target_names(SeqNumToPredTargetNamesMap),
    map.values(SeqNumToPredTargetNamesMap, PredTargetNamesLists),
    list.condense(PredTargetNamesLists, PredTargetNames),
    SymNamesArities = assoc_list.keys(PredTargetNames),
    list.map(get_unique_pred_proc_id_for_pred_sym_name_arity(MI),
        SymNamesArities, PredProcIds).

module_info_user_final_pred_procs(MI, PredProcIds) :-
    module_info_get_user_final_pred_target_names(MI, FinalPredTargetNames),
    FinalPredTargetNames = pred_target_names(SeqNumToPredTargetNamesMap),
    map.values(SeqNumToPredTargetNamesMap, PredTargetNamesLists),
    list.condense(PredTargetNamesLists, PredTargetNames),
    SymNamesArities = assoc_list.keys(PredTargetNames),
    list.map(get_unique_pred_proc_id_for_pred_sym_name_arity(MI),
        SymNamesArities, PredProcIds).

:- pred get_unique_pred_proc_id_for_pred_sym_name_arity(module_info::in,
    sym_name_arity::in, pred_proc_id::out) is det.

get_unique_pred_proc_id_for_pred_sym_name_arity(MI, SNA, PredProcId) :-
    module_info_get_predicate_table(MI, PredTable),
    SNA = sym_name_arity(SymName, Arity),
    UserArity = user_arity(Arity),
    % XXX If the names we are looking up may be only partially qualified,
    % then why are insisting on there being at most one match?
    predicate_table_lookup_pred_sym_arity(PredTable,
        may_be_partially_qualified, SymName, UserArity, PredIds),
    ( if PredIds = [PredId] then
        pred_table.get_single_proc_id(MI, PredId, ProcId),
        PredProcId = proc(PredId, ProcId)
    else
        unexpected($pred, "lookup failed")
    ).

%---------------------------------------------------------------------------%
:- end_module hlds.hlds_module.
%---------------------------------------------------------------------------%
