Files
mercury/compiler/modules.m
Zoltan Somogyi fb97df69ed Make "compute type representations" a separate pass.
The ultimate purpose of this diff is to prepare for future improvements
in type representations, allowing values of some data types to be represented
more compactly than up to now.

The main way this diff does that is by creating a separate pass for deciding
how values of each type should be represented. We have traditionally decided
data representations for each type as its type definition was processed
during the make_hlds pass, but these decisions were always tentative,
and could be overridden later, e.g. when we processed foreign_type or
foreign_enum pragmas for the type. This dispersed decision making algorithm
is hard to understand, and therefore to change.

This diff centralizes decisions about type representations in a separate
pass that does nothing else. It leaves the algorithm distributed among
several files (du_type_layout.m, make_tags.m, and add_foreign_enum.m) for now,
to make reviewing this diff easier, but soon after it is committed I intend
to move all the relevant code to du_type_layout.m, to centralize the decision
code in "space" as well as in time.

For the reason why this pass runs before any of the semantic analysis
passes, instead of after all of them as I originally intended and as we
discussed on m-dev in late october 2017, see the big comment at the start of
du_type_layout.m.

As per another part of that same discussion on m-dev, this diff
makes a start on implementing a new type of item, the type_repn item,
which is intended *only* to be used in compiler-generated interface files,
*not* in source files. It is only a start because we can use these items
only *after* the creation of a separate type representation decision pass,
and this diff is already very big. The code for making the compiler understand
these items will be added later. The code for generating them will be added
later still, once the code for understanding them has been installed on
all our systems.

Since I was going to be working on the affected code anyway, this diff
also carries out two other decisions that came out of that discussion:

- the deletion of the ability to reserve a tag in a type for HAL,
  either via a compiler option or via a pragma, and

- the deletion of the ability to represent a functor using the address
  of a statically allocated object (which we haven't used and won't use,
  because it slows down accesses to *all the other functors* of the type).

compiler/mercury_compile_front_end.m:
    Invoke the new pass for making decisions about type representations
    after the make_hlds pass. (We used to do only the final part of it then.)

    Fix a bad dump stage name.

    Add an extra check for what it means for a module to be error free.

    Make a sub-switch explicit.

compiler/hlds.m:
compiler/make_hlds.m:
    Move the modules that implement the new pass from the make_hlds package
    to the hlds package, to give the compiler's top level access to them.

    Make the same move for the modules that the new pass's modules need.
    Since they are now part of hlds, they cannot reach into make_hlds,
    and I think this is a cleaner solution than forwarding predicates.

    Delete some forwarding predicates that are no longer needed.

compiler/notes/compiler_design.html:
    Document the updated location of the moved modules.

    Add an XXX to note a place where the documentation has not been
    updated in the past.

compiler/du_type_layout.m:
    Add code to implement the new pass.

    Keep the algorithm for deciding type representations as close
    to the previously used algorithm as possible, since this diff
    is already big enough. (The previous algorithm was scattered across
    add_type.m, add_foreign_enum.m, and make_hlds_passes.m.)

    Simplifications and optimizations will come later, after this module
    is merged with make_tags.m and with (at least) the foreign_enum half of
    add_foreign_enum.m.

compiler/make_tags.m:
    Keep the functionality of this module, which does both the first part
    of deciding type representations (tentatively assigning tags to functors,
    an assignment that may be overridden later), and the last part (packing
    multiple adjacent less-than-word-sized enum args into a single word,
    if possible.), but simplify it where possible, and note possibilities
    for further improvements.

compiler/add_foreign_enum.m:
    This module has two halves, one dealing with foreign_enum pragmas
    and one dealing with foreign_export_enum pragmas.

    Change the half that deals with foreign_enum pragmas to just build
    a data structure that du_type_layout.m will need to make its decisions,
    this structure being a map from type_ctors to the foreign enum
    specification applicable to the current target language. Include
    in this structure a component that add_foreign_enum.m itself can use
    to report better error messages for duplicate foreign_enum pragmas;
    this component records, for each type_ctor and language, the context
    of the previous foreign_enum pragma for that combo.

    Change the input for the half that deals with foreign_export_enum pragmas
    to reflect the fact that it is invoked by du_type_layout.m after all
    decisions about type representations have already been made.

compiler/add_special_pred.m:
    Move this module from the make_hlds package to the hlds package,
    since the code that adds special preds for type is now called from
    du_type_layout.m.

    Change the names of predicates to make clear whether they add
    only the declaration of a predicate, only its definition, or both.

    Don't try to pre-guess whether the implementation of a type's
    compare predicate will need an index predicate. Let the code
    that generates calls to the index predicate both declare and define
    the index predicate. This change removes the potential for
    inconsistencies between the two pieces of code.

compiler/add_pred.m:
    Move this module from the make_hlds package to the hlds package,
    since add_special_pred.m needs access to it.

compiler/add_type.m:
    When adding a type definition to the HLDS, don't try to decide
    its representation. Any such decision was tentative anyway, due
    to the possibility of e.g. the later processing of foreign_type
    or foreign_enum pragmas for the type. Likewise, don't try to
    create the special (unify, compare) predicates for the type.
    Leave both tasks to the du_type_layout pass.

    Likewise, don't try to pack the representation of types, or record
    no_tag types in the table of no_tag types, during the post-processing
    pass either; leave both of these to du_type_layout as well.
    Rename the predicate that post_processes type definitions to reflect
    the two tasks left for it to do.

compiler/prog_data.m:
    Do not store width information about the arguments of those data
    constructors in the parse tree. That information is not computed
    until later; until then, it was always filled in with dummy values.
    (But see hlds_data.m below.)

    Use bespoke types to represent the presence or absence of user-specified
    unify and compare predicates.

    Change the representation of data constructors to use a single "maybe"
    type, not two lists, to denote the presence or absence of existentially
    typed arguments.

    Give the HLDS the ability to hold representation information about
    abstract types that in the future we will get from type_repn items
    in the defining modules' interface files.

    Delete the uses_reserved_tag type, since we never use reserved tags
    anymore.

compiler/prog_item.m:
    Add the new type_repn item type, which is not used yet.

    Delete the reserve_tag pragma.

    Fix an earlier mistake in the wording of a context message.

compiler/hlds_data.m:
    Put all the fields of hlds_du_type (the type definition variant dealing
    with discriminated union types) that deal with type representation
    issues in a single "maybe" field that is set to "no" before the
    type representation decision pass has been run.

    Add new type, constructor_repn, that stores the same information as the old
    constructor type (defined in prog_data.m), PLUS the information
    describing how terms with that data constructor are stored.

    Likewise, add a new type ctor_arg_rep, which likewise stores
    the widths of each constructor argument. When we implement
    argument reordering, we would store the offset of the arg as well.

    Since the parse tree representations of constructors and their arguments
    don't store representation information anymore, the cons_table they
    are stored in doesn't either. Make the lookup of representation information
    for a given constructor possible by adding a map to the new "maybe" field
    of hlds_du_type.

    Provide some utility predicates.

    Optimize some existing predicates.

    Rename some types to better reflect their meaning.

compiler/hlds_module.m:
    Provide a slot in the module_info for storing the information
    gathered by make_hlds.m that is needed by the new pass.

compiler/make_hlds_separate_items.m:
    When we see either a foreign_enum or a foreign_export_enum pragma,
    return values of a bespoke type for them (a type defined in
    hlds_module.m), instead of an item_pragma. This makes handling them
    considerably easier.

compiler/make_hlds_passes.m:
    With the changes in this diff, adding a type to the HLDS won't
    decide its representation. Therefore delete the code that used
    to loop over foreign_export_enum pragmas; in the absence of
    the final type representation information, it won't work right.

    Record the information that the du_type_layout pass will need
    in the module_info.

compiler/add_pragma.m:
    Delete the code for passing on foreign_enum and foreign_export_enum
    pragmas to add_foreign_enum.m; they are now passed to add_foreign_enum.m
    by du_type_layout.m.

    Move a utility predicate to make_hlds_error.m, to allow add_foreign_enum.m
    to call it.

compiler/make_hlds_error.m:
    Add the utility predicate moved from add_pragma.m.

    Move the module from the make_hlds to the hlds package.

compiler/module_qual.m:
    Provide a mechanism for recording error messages about e.g. undefined
    types without recording that we found an undefined type. This sounds
    strange, but there is a valid use case.

    When a type definition declares a functor's argument to be of an
    undefined type, that error is usually fatal; we stop the compiler
    from proceeding even to typechecking, since the typechecker will
    probably abort with a map lookup failure. Most other references
    to undefined types are similarly fatal for the same reason. However,
    if e.g. a foreign_export_enum pragma refers to an undefined type,
    that error *won't* be visible to the typechecker, and therefore
    won't crash it. The error will still cause the compiler to exit
    without generating any target language code, but at least it will be
    able to run the typechecker and other semantic analysis passes.

    Without this change, the compiler will report only one error in
    the ee_invalid.m test case; with it, it reports *every* error
    in the test case expected output.

compiler/module_qual.qualify_items.m:
    Use the capability describe above for undefined types in
    foreign_export_enum pragmas.

compiler/module_qual.qual_errors.m:
    Delete a (somewhat incorrect) copy of a predicate in prog_item.m,
    to reduce code duplication.

compiler/prog_type.m:
    Add ways to represent abstract types whose representations are nevertheless
    known (from type_repn items in the defining modules' interface files)
    to be notag or dummy types. This will be needed to fix Mantis bug #441,
    a fix that will probably be one of the first later changes to build
    on this diff.

    Delete a type moved to type_util.m.

compiler/type_util.m:
    Provide extra versions of some predicates, with the difference between
    the old and the new versions being that one requires type representations
    to have been decided already, and the other one does not.

    Move the definition of the ctor_defn type here from prog_type.m,
    since prog_type.m itself does not use it, but type_util.m does.

    Give some predicates more meaningful names.

compiler/parse_type_defn.m:
    Simplify the code for parsing type definitions, to make it easier
    to reuse to parse type_repn items.

    Add a sanity check that requires existential constraints to have
    *some* existential variables to apply to.

    Allow "type_is_representable_in_n_bits" as a synonym for
    "type_is_abstract_enum", since in the future we want to be able to pack
    e.g. multiple int8s, not just multiple enums, into a single word.

    Generate more specific error messages for some classes of malformed input.

compiler/parse_type_repn.m:
    New module to parse type_repn items.

compiler/polymorphism.m:
    Make some predicates that operate on type constructors take
    the type constructors themselves as input arguments, not a whole type
    *using* that type constructor. Put the arguments of those predicates
    in a more standard order.

    Note that some predicates don't belong in this module.

compiler/special_pred.m:
    Make the code that decides whether a special predicate for a type
    constructor can be defined lazily avoid using type representation
    information. (Actually, we now make decisions about lazy vs eager
    definitions after type representation is available, but that was
    not so in an earlier version of this change, and the new code
    is more robust.)

compiler/unify_proc.m:
    When we decide to generate code for a compare predicate that needs
    the type to have an index predicate, don't presume that the index
    predicate has already been declared and defined; instead, declare
    and define it then and there. (Index predicates are *never* called
    from anywhere else.)

    Pack the information needed to define a special predicate
    into a single structure, to simplify the above.

    Since the creation of a clause for a compare predicate may now require
    the declaration and definition of an index predicate, the module_info
    field of the unify_proc_info is now a writeable field.

    Give some predicates and function symbols more meaningful names.

    Note some problems with the existing code.

compiler/add_class.m:
compiler/add_clause.m:
compiler/add_foreign_proc.m:
compiler/add_mode.m:
compiler/add_mutable_aux_preds.m:
compiler/add_pragma_tabling.m:
compiler/add_pragma_type_spec.m:
compiler/add_solver.m:
compiler/check_typeclass.m:
compiler/code_info.m:
compiler/comp_unit_interface.m:
compiler/ctgc.selector.m:
compiler/ctgc.util.m:
compiler/default_func_mode.m:
compiler/det_report.m:
compiler/equiv_type.m:
compiler/equiv_type_hlds.m:
compiler/erl_code_gen.m:
compiler/export.m:
compiler/foreign.m:
compiler/get_dependencies.m:
compiler/goal_expr_to_goal.m:
compiler/hhf.m:
compiler/higher_order.m:
compiler/hlds_code_util.m:
compiler/hlds_out_module.m:
compiler/inst_check.m:
compiler/inst_test.m:
compiler/inst_util.m:
compiler/intermod.m:
compiler/item_util.m:
compiler/make_hlds_warn.m:
compiler/ml_accurate_gc.m:
compiler/ml_simplify_switch.m:
compiler/ml_type_gen.m:
compiler/ml_unify_gen.m:
compiler/mlds_to_cs.m:
compiler/mlds_to_java.m:
compiler/mode_util.m:
compiler/modecheck_goal.m:
compiler/module_qual.collect_mq_info.m:
compiler/modules.m:
compiler/parse_item.m:
compiler/parse_pragma.m:
compiler/parse_tree.m:
compiler/parse_tree_out.m:
compiler/parse_tree_out_pragma.m:
compiler/post_term_analysis.m:
compiler/proc_requests.m:
compiler/prog_item_stats.m:
compiler/qual_info.m:
compiler/recompilation.check.m:
compiler/recompilation.usage.m:
compiler/recompilation.version.m:
compiler/resolve_unify_functor.m:
compiler/rtti.m:
compiler/rtti_out.m:
compiler/rtti_to_mlds.m:
compiler/simplify_goal_ite.m:
compiler/stack_opt.m:
compiler/state_var.m:
compiler/structure_reuse.direct.choose_reuse.m:
compiler/superhomogeneous.m:
compiler/switch_gen.m:
compiler/switch_util.m:
compiler/table_gen.m:
compiler/term_constr_build.m:
compiler/term_norm.m:
compiler/trailing_analysis.m:
compiler/type_constraints.m:
compiler/type_ctor_info.m:
compiler/typecheck.m:
compiler/unify_gen.m:
compiler/untupling.m:
compiler/unused_imports.m:
compiler/write_module_interface_files.m:
compiler/xml_documentation.m:
    Conform to the changes above.

tests/invalid/Mmakefile:
    Disable the reserve_tag test case, as it is not applicable anymore.

tests/invalid/exported_foreign_enum.{m,err_exp}:
tests/invalid/pragma_qual_error.{m,err_exp}:
    Delete reserve_tag pragmas from these test cases, and its effects
    from the expected outputs.

tests/invalid/bad_foreign_type.err_exp:
tests/invalid/bigtest.err_exp:
tests/invalid/foreign_enum_invalid.err_exp:
tests/invalid/type_lhs_var.err_exp:
tests/invalid/uu_type.err_exp:
tests/invalid/where_abstract_enum.err_exp:
tests/invalid/where_direct_arg.err_exp:
    Expect the updated messages for some errors.

tests/valid/Mmake.valid.common:
tests/valid/Mmakefile:
    Disable any reserve_tag test cases, as they are not applicable anymore.
2018-01-31 17:54:40 +11:00

1908 lines
86 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1996-2011 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: modules.m.
% Main author: fjh.
%
% Given a module_and_imports structure initialized for a raw_comp_unit,
% this module has the task of figuring out which interface files the
% raw_comp_unit needs either directly or indirectly, and reading them in,
% adding them to the module_and_imports structure. If intermodule optimization
% is enabled, then calls to grab_opt_files and maybe grab_trans_optfiles
% will figure out what .opt and .trans_opt files the compilation unit can use,
% again either directly or indirectly, and add those to the module_and_imports
% structure. When all this is done, the module_and_imports structure
% will contain an augmented version of the original compilation unit.
%
% The roles of the interface files (.int0, .int3, .int2 and .int) that
% this module reads in are documented (to the extent that they are documented
% anywhere) in the module that creates them, which is
% write_module_interface_files.m.
%
%---------------------------------------------------------------------------%
:- module parse_tree.modules.
:- interface.
:- import_module libs.
:- import_module libs.file_util.
:- import_module libs.globals.
:- import_module libs.timestamp.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.module_imports.
:- import_module parse_tree.prog_item.
:- import_module parse_tree.read_modules.
:- import_module bool.
:- import_module io.
:- import_module list.
:- import_module maybe.
:- import_module set.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
% grab_imported_modules(Globals, SourceFileName, SourceFileModuleName,
% ModuleTimestamp, NestedSubModules, RawCompUnit, HaveReadModuleMaps,
% ModuleAndImports, !IO):
%
% Given the raw CompUnit, one of the modules stored in SourceFileName,
% read in the private interface files (.int0) for all the parent modules,
% the long interface files (.int) for all the imported modules, and the
% short interface files (.in2) for all the indirectly imported modules.
% Return the `module_and_imports' structure containing all the information
% gathered this way, from which we will compute the augmented version
% of RawCompUnit.
% XXX ITEM_LIST Move the actual compuation of the AugCompUnit together
% with this code, preferably in a new module, perhaps named something like
% "augment_comp_unit.m".
%
% SourceFileModuleName is the top-level module name in SourceFileName.
% ModuleTimestamp is the timestamp of the SourceFileName. NestedSubModules
% is the list of the names of the nested submodules in SourceFileName
% if RawCompUnit is the toplevel module in SourceFileName (i.e. if it
% the compilation unit of SourceFileModuleName). XXX ITEM_LIST document
% exactly what NestedSubModules is if RawCompUnit is NOT the toplevel
% module in SourceFileName. HaveReadModuleMaps contains the interface
% files read during recompilation checking.
%
:- pred grab_imported_modules(globals::in, file_name::in,
module_name::in, maybe(timestamp)::in, set(module_name)::in,
raw_compilation_unit::in, have_read_module_maps::in,
module_and_imports::out, io::di, io::uo) is det.
% grab_unqual_imported_modules(Globals, SourceFileName,
% SourceFileModuleName, CompUnit, ModuleAndImports, !IO):
%
% Similar to grab_imported_modules, but only reads in the unqualified
% short interfaces (.int3s), and the .int0 files for parent modules,
% instead of reading the long interfaces and qualified short interfaces
% (.int and int2s). Does not set the `PublicChildren', `FactDeps'
% `ForeignIncludeFiles' fields of the module_and_imports structure.
%
:- pred grab_unqual_imported_modules(globals::in, file_name::in,
module_name::in, raw_compilation_unit::in, module_and_imports::out,
io::di, io::uo) is det.
% Add the items from the .opt files of imported modules to
% the items for this module.
%
:- pred grab_opt_files(globals::in,
module_and_imports::in, module_and_imports::out, bool::out,
io::di, io::uo) is det.
% grab_trans_optfiles(Globals, ModuleList, !ModuleAndImports, Error, !IO):
%
% Add the items from each of the modules in ModuleList.trans_opt to
% the items in ModuleAndImports.
%
:- pred grab_trans_opt_files(globals::in, list(module_name)::in,
module_and_imports::in, module_and_imports::out, bool::out,
io::di, io::uo) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module libs.options.
:- import_module parse_tree.comp_unit_interface. % undesirable dependency
:- import_module parse_tree.error_util.
:- import_module parse_tree.file_kind.
:- import_module parse_tree.file_names.
:- import_module parse_tree.get_dependencies.
:- import_module parse_tree.parse_error.
:- import_module parse_tree.parse_module.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_data_foreign.
:- import_module cord.
:- import_module dir.
:- import_module map.
:- import_module multi_map.
:- import_module require.
:- import_module term.
:- import_module string.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- type which_grab
---> grab_imported(maybe(timestamp), set(module_name))
; grab_unqual_imported.
:- pred grab_maybe_qual_imported_modules(file_name::in, module_name::in,
which_grab::in, raw_compilation_unit::in,
list(src_item_block)::out, module_and_imports::out,
multi_map(module_name, prog_context)::out,
multi_map(module_name, prog_context)::out,
multi_map(module_name, prog_context)::out,
multi_map(module_name, prog_context)::out, io::di, io::uo) is det.
grab_maybe_qual_imported_modules(SourceFileName, SourceFileModuleName,
WhichGrab, RawCompUnit, SrcItemBlocks, !:ModuleAndImports,
!:IntImported, !:IntUsed, !:ImpImported, !:ImpUsed, !IO) :-
RawCompUnit = raw_compilation_unit(ModuleName, ModuleNameContext,
RawItemBlocks),
% XXX Why do we compute NestedChildren, FactDeps, ForeignIncludeFiles,
% SrcItemBlocks and PublicChildren differently in these two cases?
(
WhichGrab = grab_imported(MaybeTimestamp, NestedChildren),
(
MaybeTimestamp = yes(Timestamp),
MaybeTimestampMap = yes(map.singleton(ModuleName,
module_timestamp(fk_src, Timestamp, may_be_unqualified)))
;
MaybeTimestamp = no,
MaybeTimestampMap = no
),
get_src_item_blocks_public_children(RawCompUnit,
SrcItemBlocks, PublicChildren),
% XXX ITEM_LIST Store the FactDeps and ForeignIncludeFiles
% in the raw_comp_unit.
get_fact_table_dependencies_in_item_blocks(RawItemBlocks, FactDeps),
get_foreign_include_files_in_item_blocks(RawItemBlocks,
ForeignIncludeFiles)
;
WhichGrab = grab_unqual_imported,
set.init(NestedChildren),
MaybeTimestampMap = no,
raw_item_blocks_to_src(RawItemBlocks, SrcItemBlocks),
map.init(PublicChildren),
FactDeps = [],
ForeignIncludeFiles = cord.init
),
% Construct the initial module import structure.
InitSpecs = [],
make_module_and_imports(SourceFileName, SourceFileModuleName,
ModuleName, ModuleNameContext, SrcItemBlocks, InitSpecs,
PublicChildren, NestedChildren, FactDeps, ForeignIncludeFiles,
MaybeTimestampMap, !:ModuleAndImports),
% Find the modules named in import_module and use_module decls.
get_dependencies_int_imp_in_raw_item_blocks(RawItemBlocks,
!:IntImported, !:IntUsed, !:ImpImported, !:ImpUsed).
grab_imported_modules(Globals, SourceFileName, SourceFileModuleName,
MaybeTimestamp, NestedChildren, RawCompUnit, HaveReadModuleMaps,
!:ModuleAndImports, !IO) :-
% The predicates grab_imported_modules and grab_unqual_imported_modules
% have quite similar tasks. Please keep the corresponding parts of these
% two predicates in sync.
%
% XXX ITEM_LIST Why aren't we updating !HaveReadModuleMaps?
some [!Specs, !IntUsed, !IntImported, !ImpUsed, !ImpImported,
!IntIndirectImported, !ImpIndirectImported,
!IntImpIndirectImported, !ImpImpIndirectImported]
(
WhichGrab = grab_imported(MaybeTimestamp, NestedChildren),
grab_maybe_qual_imported_modules(SourceFileName, SourceFileModuleName,
WhichGrab, RawCompUnit, SrcItemBlocks, !:ModuleAndImports,
IntImportedMap, IntUsedMap, ImpImportedMap, ImpUsedMap, !IO),
set.sorted_list_to_set(map.keys(IntImportedMap), !:IntImported),
set.sorted_list_to_set(map.keys(IntUsedMap), !:IntUsed),
set.sorted_list_to_set(map.keys(ImpImportedMap), !:ImpImported),
set.sorted_list_to_set(map.keys(ImpUsedMap), !:ImpUsed),
RawCompUnit = raw_compilation_unit(ModuleName, ModuleNameContext,
RawItemBlocks),
HaveReadModuleMapInt = HaveReadModuleMaps ^ hrmm_int,
Ancestors = set.list_to_set(get_ancestors(ModuleName)),
!:Specs = [],
warn_if_import_for_self_or_ancestor(ModuleName, RawItemBlocks,
Ancestors, !.IntImported, !Specs),
warn_if_import_for_self_or_ancestor(ModuleName, RawItemBlocks,
Ancestors, !.IntUsed, !Specs),
warn_if_import_for_self_or_ancestor(ModuleName, RawItemBlocks,
Ancestors, !.ImpImported, !Specs),
warn_if_import_for_self_or_ancestor(ModuleName, RawItemBlocks,
Ancestors, !.ImpUsed, !Specs),
warn_if_duplicate_use_import_decls(ModuleName, ModuleNameContext,
!IntImported, !IntUsed, !ImpImported, !ImpUsed, IntUsedImpImported,
!Specs),
% Add `builtin' and `private_builtin', and any other builtin modules
% needed by any of the items, to the imported modules.
% XXX Why are these added to the interface, and not the implementation
% dependencies?
get_implicit_dependencies_in_item_blocks(Globals, SrcItemBlocks,
ImplicitIntImportedMap, ImplicitIntUsedMap),
set.sorted_list_to_set(map.keys(ImplicitIntImportedMap),
ImplicitIntImported),
set.sorted_list_to_set(map.keys(ImplicitIntUsedMap),
ImplicitIntUsed),
set.union(ImplicitIntImported, !IntImported),
set.union(ImplicitIntUsed, !IntUsed),
% Get the .int0 files of the ancestor modules.
%
% Uses of the items declared in ancestor modules do not need
% module qualifiers. Modules imported by ancestors are considered
% to be visible in the current module.
% XXX grab_unqual_imported_modules treats ParentImported and ParentUsed
% slightly differently from !.IntImported and !.IntUsed.
process_module_private_interfaces(Globals, HaveReadModuleMapInt,
"ancestors", Ancestors,
make_ims_imported(import_locn_interface),
make_ims_imported(import_locn_ancestor_private_interface_proper),
module_and_imports_add_direct_int_item_blocks,
!IntImported, !IntUsed, !ModuleAndImports, !IO),
% Get the .int files of the modules imported using `import_module'.
set.init(!:IntIndirectImported),
set.init(!:ImpIndirectImported),
set.init(!:IntImpIndirectImported),
set.init(!:ImpImpIndirectImported),
process_module_long_interfaces(Globals, HaveReadModuleMapInt,
"int_imported", may_be_unqualified, !.IntImported, ifk_int,
make_ims_imported(import_locn_interface),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
!IntIndirectImported, !IntImpIndirectImported,
!ModuleAndImports, !IO),
process_module_long_interfaces(Globals, HaveReadModuleMapInt,
"imp_imported", may_be_unqualified, !.ImpImported, ifk_int,
make_ims_imported(import_locn_implementation),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
!ImpIndirectImported, !ImpImpIndirectImported,
!ModuleAndImports, !IO),
% Get the .int files of the modules imported using `use_module'.
process_module_long_interfaces(Globals, HaveReadModuleMapInt,
"int_used", must_be_qualified, !.IntUsed, ifk_int,
make_ims_used(import_locn_interface),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
!IntIndirectImported, !IntImpIndirectImported,
!ModuleAndImports, !IO),
process_module_long_interfaces(Globals, HaveReadModuleMapInt,
"imp_used", must_be_qualified, !.ImpUsed, ifk_int,
make_ims_used(import_locn_implementation),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
!ImpIndirectImported, !ImpImpIndirectImported,
!ModuleAndImports, !IO),
% Get the .int files of the modules imported using `use_module'
% in the interface and `import_module' in the implementation.
process_module_long_interfaces(Globals, HaveReadModuleMapInt,
"int_used_imp_imported", may_be_unqualified,
IntUsedImpImported, ifk_int,
make_ims_used_and_imported(import_locn_interface),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
!IntIndirectImported, !IntImpIndirectImported,
!ModuleAndImports, !IO),
% Get the .int2 files of the modules imported in .int files.
process_module_short_interfaces_transitively(Globals,
HaveReadModuleMapInt, "int_indirect_imported",
!.IntIndirectImported, ifk_int2,
make_ims_used(import_locn_interface),
make_ims_abstract_imported,
module_and_imports_add_indirect_int_item_blocks,
!IntImpIndirectImported, !ModuleAndImports, !IO),
process_module_short_interfaces_transitively(Globals,
HaveReadModuleMapInt, "imp_indirect_imported",
!.ImpIndirectImported, ifk_int2,
make_ims_used(import_locn_implementation),
make_ims_abstract_imported,
module_and_imports_add_indirect_int_item_blocks,
!ImpImpIndirectImported, !ModuleAndImports, !IO),
% Get the .int2 files of the modules indirectly imported
% the implementation sections of .int/.int2 files.
% XXX Shouldn't these be .int3 files, as implied by the following
% old comment?
% Process the short interfaces for modules imported in the
% implementation of indirectly imported modules. The items in these
% modules shouldn't be visible to typechecking -- they are used for
% fully expanding equivalence types after the semantic checking passes.
process_module_short_interfaces_and_impls_transitively(Globals,
HaveReadModuleMapInt, "int_imp_indirect_imported",
!.IntImpIndirectImported, ifk_int2,
make_ims_abstract_imported, make_ims_abstract_imported,
module_and_imports_add_indirect_int_item_blocks,
!ModuleAndImports, !IO),
process_module_short_interfaces_and_impls_transitively(Globals,
HaveReadModuleMapInt, "imp_imp_indirect_imported",
!.ImpImpIndirectImported, ifk_int2,
make_ims_abstract_imported, make_ims_abstract_imported,
module_and_imports_add_indirect_int_item_blocks,
!ModuleAndImports, !IO),
module_and_imports_get_aug_comp_unit(!.ModuleAndImports, AugCompUnit,
_, _),
AllImportedOrUsed = set.union_list([!.IntImported, !.IntUsed,
!.ImpImported, !.ImpUsed]),
check_imports_accessibility(AugCompUnit, AllImportedOrUsed, !Specs),
module_and_imports_add_specs(!.Specs, !ModuleAndImports)
).
grab_unqual_imported_modules(Globals, SourceFileName, SourceFileModuleName,
RawCompUnit, !:ModuleAndImports, !IO) :-
% The predicates grab_imported_modules and grab_unqual_imported_modules
% have quite similar tasks. Please keep the corresponding parts of these
% two predicates in sync.
%
% XXX ITEM_LIST Why aren't we updating !HaveReadModuleMaps?
some [!IntUsed, !IntImported, !ImpUsed, !ImpImported,
!IntIndirectImported, !ImpIndirectImported]
(
WhichGrab = grab_unqual_imported,
% XXX _SrcItemBlocks
grab_maybe_qual_imported_modules(SourceFileName, SourceFileModuleName,
WhichGrab, RawCompUnit, _SrcItemBlocks, !:ModuleAndImports,
IntImportedMap, IntUsedMap, ImpImportedMap, ImpUsedMap, !IO),
set.sorted_list_to_set(map.keys(IntImportedMap), !:IntImported),
set.sorted_list_to_set(map.keys(IntUsedMap), !:IntUsed),
set.sorted_list_to_set(map.keys(ImpImportedMap), !:ImpImported),
set.sorted_list_to_set(map.keys(ImpUsedMap), !:ImpUsed),
RawCompUnit = raw_compilation_unit(ModuleName, _ModuleNameContext,
RawItemBlocks),
map.init(HaveReadModuleMapInt),
% Add `builtin' and `private_builtin', and any other builtin modules
% needed by any of the items, to the imported modules.
% XXX Why are these added to the interface, and not the implementation
% dependencies?
get_implicit_dependencies_in_item_blocks(Globals, RawItemBlocks,
ImplicitIntImportedMap, ImplicitIntUsedMap),
set.sorted_list_to_set(map.keys(ImplicitIntImportedMap),
ImplicitIntImported),
set.sorted_list_to_set(map.keys(ImplicitIntUsedMap),
ImplicitIntUsed),
set.union(ImplicitIntImported, !IntImported),
set.union(ImplicitIntUsed, !IntUsed),
Ancestors = set.list_to_set(get_ancestors(ModuleName)),
% Get the .int0 files of the ancestor modules.
process_module_private_interfaces(Globals, HaveReadModuleMapInt,
"unqual_ancestors", Ancestors,
make_ims_imported(import_locn_interface),
make_ims_imported(import_locn_ancestor_private_interface_proper),
module_and_imports_add_direct_int_item_blocks,
set.init, ParentImported, set.init, ParentUsed,
!ModuleAndImports, !IO),
% Get the .int3 files of the modules imported using `import_module'.
set.init(!:IntIndirectImported),
set.init(!:ImpIndirectImported),
process_module_long_interfaces(Globals, HaveReadModuleMapInt,
"unqual_parent_imported", may_be_unqualified,
ParentImported, ifk_int3,
make_ims_imported(import_locn_import_by_ancestor),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
!IntIndirectImported, set.init, _, !ModuleAndImports, !IO),
process_module_long_interfaces(Globals, HaveReadModuleMapInt,
"unqual_int_imported", may_be_unqualified,
!.IntImported, ifk_int3,
make_ims_imported(import_locn_interface),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
!IntIndirectImported, set.init, _, !ModuleAndImports, !IO),
process_module_long_interfaces(Globals, HaveReadModuleMapInt,
"unqual_imp_imported", may_be_unqualified,
!.ImpImported, ifk_int3,
make_ims_imported(import_locn_implementation),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
!ImpIndirectImported, set.init, _, !ModuleAndImports, !IO),
% Get the .int3 files of the modules imported using `use_module'.
process_module_long_interfaces(Globals, HaveReadModuleMapInt,
"unqual_parent_used", may_be_unqualified,
ParentUsed, ifk_int3,
make_ims_imported(import_locn_import_by_ancestor),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
!IntIndirectImported, set.init, _, !ModuleAndImports, !IO),
process_module_long_interfaces(Globals, HaveReadModuleMapInt,
"unqual_int_used", must_be_qualified,
!.IntUsed, ifk_int3,
make_ims_used(import_locn_interface), make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
!IntIndirectImported, set.init, _, !ModuleAndImports, !IO),
process_module_long_interfaces(Globals, HaveReadModuleMapInt,
"unqual_imp_used", must_be_qualified,
!.ImpUsed, ifk_int3,
make_ims_used(import_locn_implementation),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
!ImpIndirectImported, set.init, _, !ModuleAndImports, !IO),
% Get the .int3 files of the modules imported in .int3 files.
process_module_short_interfaces_transitively(Globals,
HaveReadModuleMapInt, "unqual_int_indirect_imported",
!.IntIndirectImported, ifk_int3,
make_ims_used(import_locn_interface), make_ims_abstract_imported,
module_and_imports_add_indirect_int_item_blocks,
set.init, _, !ModuleAndImports, !IO),
process_module_short_interfaces_transitively(Globals,
HaveReadModuleMapInt, "unqual_imp_indirect_imported",
!.ImpIndirectImported, ifk_int3,
make_ims_used(import_locn_implementation),
make_ims_abstract_imported,
module_and_imports_add_indirect_int_item_blocks,
set.init, _, !ModuleAndImports, !IO),
module_and_imports_get_aug_comp_unit(!.ModuleAndImports, AugCompUnit,
_, _),
AllImportedOrUsed = set.union_list([!.IntImported, !.IntUsed,
!.ImpImported, !.ImpUsed]),
check_imports_accessibility(AugCompUnit, AllImportedOrUsed,
[], ImportAccessSpecs),
module_and_imports_add_specs(ImportAccessSpecs, !ModuleAndImports)
).
%---------------------------------------------------------------------------%
:- pred get_src_item_blocks_public_children(raw_compilation_unit::in,
list(src_item_block)::out, multi_map(module_name, prog_context)::out)
is det.
get_src_item_blocks_public_children(RawCompUnit,
SrcItemBlocks, PublicChildren) :-
RawCompUnit = raw_compilation_unit(_, _, RawItemBlocks),
get_included_modules_in_item_blocks(RawItemBlocks, Children),
% If this module has any separately-compiled submodules, then we need
% to make everything in the implementation of this module exported to
% submodules. We do that by splitting out the implementation declarations
% and putting them in a special `sms_impl_but_exported_to_submodules'
% section.
( if map.is_empty(Children) then
raw_item_blocks_to_src(RawItemBlocks, SrcItemBlocks),
map.init(PublicChildren)
else
get_int_and_impl(dont_include_impl_types, RawCompUnit,
IFileItemBlocks, NoIFileItemBlocks),
raw_item_blocks_to_src(IFileItemBlocks, IFileSrcItemBlocks),
raw_item_blocks_to_split_src(NoIFileItemBlocks, NoIFileSrcItemBlocks),
SrcItemBlocks = IFileSrcItemBlocks ++ NoIFileSrcItemBlocks,
get_included_modules_in_item_blocks(IFileItemBlocks,
PublicChildren)
).
:- pred raw_item_blocks_to_src(list(item_block(module_section))::in,
list(item_block(src_module_section))::out) is det.
raw_item_blocks_to_src([], []).
raw_item_blocks_to_src([RawItemBlock | RawItemBlocks],
[SrcItemBlock | SrcItemBlocks]) :-
RawItemBlock = item_block(Section, SectionContext,
Incls, Avails, Items),
(
Section = ms_interface,
SrcSection = sms_interface
;
Section = ms_implementation,
SrcSection = sms_implementation
),
SrcItemBlock = item_block(SrcSection, SectionContext,
Incls, Avails, Items),
raw_item_blocks_to_src(RawItemBlocks, SrcItemBlocks).
:- pred raw_item_blocks_to_split_src(list(raw_item_block)::in,
list(src_item_block)::out) is det.
raw_item_blocks_to_split_src([], []).
raw_item_blocks_to_split_src([RawItemBlock | RawItemBlocks],
!:SrcItemBlocks) :-
raw_item_blocks_to_split_src(RawItemBlocks, !:SrcItemBlocks),
RawItemBlock = item_block(_Section, SectionContext, Incls, Avails, Items),
% _Section can sometimes (rarely) be ms_interface. This can happen
% when an instance declaration occurs in the interface section of a module.
% The abstract version of the declaration gets put into the interface,
% but the full version gets put into the noifile item blocks, with
% the original (i.e. ms_interface) section marker.
% XXX ITEM_LIST Fix that section marker.
split_items_into_clauses_and_decls(Items,
[], RevClauses, [], RevImpDecls),
( if
RevClauses = []
then
true
else
list.reverse(RevClauses, Clauses),
ClauseItemBlock = item_block(sms_implementation,
SectionContext, [], [], Clauses),
!:SrcItemBlocks = [ClauseItemBlock | !.SrcItemBlocks]
),
( if
Incls = [],
Avails = [],
RevImpDecls = []
then
true
else
list.reverse(RevImpDecls, ImpDecls),
ImpDeclItemBlock = item_block(sms_impl_but_exported_to_submodules,
SectionContext, Incls, Avails, ImpDecls),
!:SrcItemBlocks = [ImpDeclItemBlock | !.SrcItemBlocks]
).
:- pred split_items_into_clauses_and_decls(list(item)::in,
list(item)::in, list(item)::out, list(item)::in, list(item)::out) is det.
split_items_into_clauses_and_decls([], !RevClauses, !RevImpDecls).
split_items_into_clauses_and_decls([Item | Items],
!RevClauses, !RevImpDecls) :-
(
( Item = item_clause(_)
; Item = item_initialise(_)
; Item = item_finalise(_)
),
!:RevClauses = [Item | !.RevClauses]
;
Item = item_pragma(ItemPragma),
ItemPragma = item_pragma_info(Pragma, _, _, _),
AllowedInInterface = pragma_allowed_in_interface(Pragma),
(
AllowedInInterface = no,
!:RevClauses = [Item | !.RevClauses]
;
AllowedInInterface = yes,
!:RevImpDecls = [Item | !.RevImpDecls]
)
;
% XXX ITEM_LIST I (zs) think that item_nothings should not be put
% anywhere.
( Item = item_type_defn(_)
; Item = item_inst_defn(_)
; Item = item_mode_defn(_)
; Item = item_pred_decl(_)
; Item = item_mode_decl(_)
; Item = item_promise(_)
; Item = item_typeclass(_)
; Item = item_instance(_)
; Item = item_mutable(_)
; Item = item_type_repn(_)
; Item = item_nothing(_)
),
!:RevImpDecls = [Item | !.RevImpDecls]
),
split_items_into_clauses_and_decls(Items, !RevClauses, !RevImpDecls).
%---------------------------------------------------------------------------%
% Warn if a module imports itself, or an ancestor.
%
:- pred warn_if_import_for_self_or_ancestor(module_name::in,
list(raw_item_block)::in, set(module_name)::in, set(module_name)::in,
list(error_spec)::in, list(error_spec)::out) is det.
warn_if_import_for_self_or_ancestor(ModuleName, RawItemBlocks,
Ancestors, ImportedOrUsed, !Specs) :-
set.intersect(Ancestors, ImportedOrUsed, ImportedOrUsedAncestors),
set.fold(find_and_warn_import_for_ancestor(ModuleName, RawItemBlocks),
ImportedOrUsedAncestors, !Specs),
( if set.member(ModuleName, ImportedOrUsed) then
find_and_warn_import_for_self(ModuleName, RawItemBlocks, !Specs)
else
true
).
%---------------------%
:- pred find_and_warn_import_for_self(module_name::in,
list(raw_item_block)::in,
list(error_spec)::in, list(error_spec)::out) is det.
find_and_warn_import_for_self(ModuleName, RawItemBlocks, !Specs) :-
find_avail_contexts_for_module_in_item_blocks(RawItemBlocks,
ModuleName, [], AvailContexts),
list.foldl(warn_import_for_self(ModuleName), AvailContexts, !Specs).
:- pred find_and_warn_import_for_ancestor(module_name::in,
list(raw_item_block)::in, module_name::in,
list(error_spec)::in, list(error_spec)::out) is det.
find_and_warn_import_for_ancestor(ModuleName, RawItemBlocks,
AncestorModuleName, !Specs) :-
find_avail_contexts_for_module_in_item_blocks(RawItemBlocks,
AncestorModuleName, [], AvailContexts),
list.foldl(warn_import_for_ancestor(ModuleName, AncestorModuleName),
AvailContexts, !Specs).
%---------------------%
% Return the set of contexts in which the given item blocks import or use
% the named module.
%
% The order in which we return the contexts doesn't matter, because
% the error specs we generate for the returned contexts will be sorted,
% and any duplicates removed, before they are printed.
%
:- pred find_avail_contexts_for_module_in_item_blocks(list(raw_item_block)::in,
module_name::in, list(prog_context)::in, list(prog_context)::out) is det.
find_avail_contexts_for_module_in_item_blocks([], _, !AvailContexts).
find_avail_contexts_for_module_in_item_blocks([ItemBlock | ItemBlocks],
ModuleName, !AvailContexts) :-
ItemBlock = item_block(_SectionKind, _SectionContext,
_Includes, Avails, _Items),
find_avail_contexts_for_module_in_avails(Avails,
ModuleName, !AvailContexts),
find_avail_contexts_for_module_in_item_blocks(ItemBlocks,
ModuleName, !AvailContexts).
:- pred find_avail_contexts_for_module_in_avails(list(item_avail)::in,
module_name::in, list(prog_context)::in, list(prog_context)::out) is det.
find_avail_contexts_for_module_in_avails([], _, !AvailContexts).
find_avail_contexts_for_module_in_avails([Avail | Avails],
ModuleName, !AvailContexts) :-
(
Avail = avail_import(Import),
Import = avail_import_info(AvailModuleName, Context, _SeqNum)
;
Avail = avail_use(Use),
Use = avail_use_info(AvailModuleName, Context, _SeqNum)
),
( if ModuleName = AvailModuleName then
!:AvailContexts = [Context | !.AvailContexts]
else
true
),
find_avail_contexts_for_module_in_avails(Avails,
ModuleName, !AvailContexts).
%---------------------%
:- pred warn_import_for_self(module_name::in, prog_context::in,
list(error_spec)::in, list(error_spec)::out) is det.
warn_import_for_self(ModuleName, Context, !Specs) :-
Pieces = [words("Warning: module"), qual_sym_name(ModuleName),
words("imports itself!"), nl],
Msg = simple_msg(Context,
[option_is_set(warn_simple_code, yes, [always(Pieces)])]),
Severity = severity_conditional(warn_simple_code, yes,
severity_warning, no),
Spec = error_spec(Severity, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs].
:- pred warn_import_for_ancestor(module_name::in, module_name::in,
prog_context::in,list(error_spec)::in, list(error_spec)::out) is det.
warn_import_for_ancestor(ModuleName, AncestorName, Context, !Specs) :-
MainPieces = [words("Module"), qual_sym_name(ModuleName),
words("imports its own ancestor, module"),
qual_sym_name(AncestorName), words(".")],
VerbosePieces = [words("Every submodule"),
words("implicitly imports its ancestors."),
words("There is no need to explicitly import them.")],
Msg = simple_msg(Context,
[option_is_set(warn_simple_code, yes,
[always(MainPieces),
verbose_only(verbose_once, VerbosePieces)])]),
Severity = severity_conditional(warn_simple_code, yes,
severity_warning, no),
Spec = error_spec(Severity, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs].
%---------------------------------------------------------------------------%
% This predicate ensures that every import_module declaration is checked
% against every use_module declaration, except for the case where
% the interface has `:- use_module foo.' and the implementation
% `:- import_module foo.'. Return the set of modules that have a
% `:- use_module foo' in the interface and an `:- import_module foo'
% in the implementation.
%
:- pred warn_if_duplicate_use_import_decls(module_name::in, prog_context::in,
set(module_name)::in, set(module_name)::out,
set(module_name)::in, set(module_name)::out,
set(module_name)::in, set(module_name)::out,
set(module_name)::in, set(module_name)::out,
set(module_name)::out,
list(error_spec)::in, list(error_spec)::out) is det.
warn_if_duplicate_use_import_decls(ModuleName, Context,
!IntImported, !IntUsed, !ImpImported, !ImpUsed, IntUsedImpImported,
!Specs) :-
do_warn_if_duplicate_use_import_decls(ModuleName, Context,
!IntImported, !IntUsed, !Specs),
do_warn_if_duplicate_use_import_decls(ModuleName, Context,
!IntImported, !ImpUsed, !Specs),
do_warn_if_duplicate_use_import_decls(ModuleName, Context,
!ImpImported, !ImpUsed, !Specs),
IntUsedImpImported = set.intersect(!.ImpImported, !.IntUsed),
( if set.is_empty(IntUsedImpImported) then
% This is the usual case; optimize it.
true
else
!:IntUsed = set.difference(!.IntUsed, IntUsedImpImported),
!:ImpImported = set.difference(!.ImpImported, IntUsedImpImported)
).
% Report warnings for modules imported using both `:- use_module'
% and `:- import_module'. Remove the unnecessary `:- use_module'
% declarations.
%
:- pred do_warn_if_duplicate_use_import_decls(module_name::in,
prog_context::in,
set(module_name)::in, set(module_name)::out,
set(module_name)::in, set(module_name)::out,
list(error_spec)::in, list(error_spec)::out) is det.
do_warn_if_duplicate_use_import_decls(_ModuleName, Context,
!Imported, !Used, !Specs) :-
set.intersect(!.Imported, !.Used, ImportedAndUsed),
( if set.is_empty(ImportedAndUsed) then
true
else
set.to_sorted_list(ImportedAndUsed, ImportedAndUsedList),
Pieces = [words("Warning:"),
words(choose_number(ImportedAndUsedList, "module", "modules"))] ++
component_list_to_pieces("and",
list.map(wrap_symname, ImportedAndUsedList)) ++
[words(choose_number(ImportedAndUsedList, "is", "are")),
words("imported using both"), decl("import_module"),
words("and"), decl("use_module"), words("declarations."), nl],
Msg = simple_msg(Context,
[option_is_set(warn_simple_code, yes, [always(Pieces)])]),
Severity = severity_conditional(warn_simple_code, yes,
severity_warning, no),
Spec = error_spec(Severity, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs],
% Treat the modules with both types of import as if they
% were imported using `:- import_module.'
set.difference(!.Used, ImportedAndUsed, !:Used)
).
:- func wrap_symname(module_name) = format_component.
wrap_symname(ModuleName) = qual_sym_name(ModuleName).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
% XXX ITEM_LIST Document what the process_xxx_interface predicates do
% more precisely, and document exactly WHY they do each of their actions.
% I (zs) think it likely that some of the interface files we now read in
% are read in unnecessarily.
:- type int_section_maker(MS) ==
(func(module_name, int_file_kind) = MS).
:- type section_appender(MS) ==
(pred(list(item_block(MS)), module_and_imports, module_and_imports)).
:- inst section_appender ==
(pred(in, in, out) is det).
%---------------------------------------------------------------------------%
% process_module_private_interfaces(Globals, HaveReadModuleMapInt, Why,
% Ancestors, NewIntSection, NewImpSection, SectionAppend,
% !DirectImports, !DirectUses, !ModuleAndImports, !IO):
%
% Read the complete private interfaces (.int0 files) for all the modules
% in Ancestors. For each ancestor read, append any imports/uses of modules
% to the !DirectImports or !DirectUses.
%
% Append all the item blocks in the read-in files to !ModuleAndImports,
% putting all the ms_interface blocks in the int_module_section kind
% generated by NewIntSection, and putting all the ms_implementation blocks
% in the int_module_section section kind generated by NewImpSection.
%
:- pred process_module_private_interfaces(globals::in,
have_read_module_int_map::in, string::in, set(module_name)::in,
int_section_maker(MS)::in, int_section_maker(MS)::in,
section_appender(MS)::in(section_appender),
set(module_name)::in, set(module_name)::out,
set(module_name)::in, set(module_name)::out,
module_and_imports::in, module_and_imports::out, io::di, io::uo) is det.
process_module_private_interfaces(Globals, HaveReadModuleMapInt, Why,
Ancestors, NewIntSection, NewImpSection, SectionAppend,
!DirectImports, !DirectUses, !ModuleAndImports, !IO) :-
( if set.remove_least(FirstAncestor, Ancestors, LaterAncestors) then
ModuleName = !.ModuleAndImports ^ mai_module_name,
expect_not(unify(FirstAncestor, ModuleName), $module, $pred,
"module is its own ancestor?"),
ModAncestors0 = !.ModuleAndImports ^ mai_parent_deps,
( if set.member(FirstAncestor, ModAncestors0) then
% We have already read it.
maybe_log_augment_decision(Why, "private", FirstAncestor, ifk_int0,
no, !IO)
else
maybe_log_augment_decision(Why, "private", FirstAncestor, ifk_int0,
yes, !IO),
process_module_private_interface(Globals, HaveReadModuleMapInt,
FirstAncestor, NewIntSection, NewImpSection, SectionAppend,
!DirectImports, !DirectUses, !ModuleAndImports, !IO)
),
process_module_private_interfaces(Globals, HaveReadModuleMapInt, Why,
LaterAncestors, NewIntSection, NewImpSection, SectionAppend,
!DirectImports, !DirectUses, !ModuleAndImports, !IO)
else
true
).
%---------------------------------------------------------------------------%
% process_module_long_interfaces(Globals, HaveReadModuleMapInt, Why,
% NeedQual, Modules, IntFileKind,
% NewIntSection, NewImpSection, SectionAppend,
% !IndirectImports, !ImpIndirectImports, !ModuleAndImports, !IO):
%
% Read the interface files (.int or .int2, as indicated by IntFileKind)
% for all the modules in Modules (unless they have already been read in).
% Append any imports/uses in those modules to the IndirectImports list,
% and append any imports/uses in the implementation sections of those
% modules to the ImpIndirectImports list.
%
% Append all the item blocks in the read-in files to !ModuleAndImports,
% putting all the ms_interface blocks in the int_module_section kind
% generated by NewIntSection, and putting all the ms_implementation blocks
% in the int_module_section kind generated by NewImpSection.
%
% XXX Despite the above, this predicate is sometimes invoked with
% IntFileKind NOT in {ifk_int, ifk_int2}.
%
:- pred process_module_long_interfaces(globals::in,
have_read_module_int_map::in, string::in,
need_qualifier::in, set(module_name)::in, int_file_kind::in,
int_section_maker(MS)::in, int_section_maker(MS)::in,
section_appender(MS)::in(section_appender),
set(module_name)::in, set(module_name)::out,
set(module_name)::in, set(module_name)::out,
module_and_imports::in, module_and_imports::out, io::di, io::uo) is det.
process_module_long_interfaces(Globals, HaveReadModuleMapInt, Why, NeedQual,
Modules, IntFileKind, NewIntSection, NewImpSection,
SectionAppend, !IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO) :-
( if set.remove_least(FirstModule, Modules, LaterModules) then
ModuleName = !.ModuleAndImports ^ mai_module_name,
( if
% Have we already processed FirstModule.IntFileKind?
( FirstModule = ModuleName
; set.member(FirstModule, !.ModuleAndImports ^ mai_parent_deps)
; map.search(!.ModuleAndImports ^ mai_int_deps, FirstModule, _)
; map.search(!.ModuleAndImports ^ mai_imp_deps, FirstModule, _)
)
then
maybe_log_augment_decision(Why, "long", FirstModule, IntFileKind,
no, !IO)
else
maybe_log_augment_decision(Why, "long", FirstModule, IntFileKind,
yes, !IO),
process_module_long_interface(Globals, HaveReadModuleMapInt,
NeedQual, FirstModule, IntFileKind,
NewIntSection, NewImpSection, SectionAppend,
!IndirectImports, !ImpIndirectImports, !ModuleAndImports, !IO)
),
process_module_long_interfaces(Globals, HaveReadModuleMapInt, Why,
NeedQual, LaterModules, IntFileKind, NewIntSection, NewImpSection,
SectionAppend, !IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO)
else
true
).
%---------------------------------------------------------------------------%
% process_module_short_interfaces_and_impls_transitively(Globals,
% HaveReadModuleMapInt, Why, Modules, IntFileKind,
% NewIntSection, NewImpSection, SectionAppend, !ModuleAndImports, !IO):
%
% Read the short interfaces for modules in Modules (unless
% they have already been read in) and any modules that those modules
% import (transitively) in the interface or implementation.
%
% Append all the item blocks in the read-in files to !ModuleAndImports,
% putting all the ms_interface blocks in the int_module_section kind
% generated by NewIntSection, and putting all the ms_implementation blocks
% in the int_module_section kind generated by NewImpSection.
%
% XXX Despite the above, this predicate is sometimes invoked with
% IntFileKind != ifk_int3.
%
:- pred process_module_short_interfaces_and_impls_transitively(globals::in,
have_read_module_int_map::in, string::in,
set(module_name)::in, int_file_kind::in,
int_section_maker(MS)::in, int_section_maker(MS)::in,
section_appender(MS)::in(section_appender),
module_and_imports::in, module_and_imports::out, io::di, io::uo) is det.
process_module_short_interfaces_and_impls_transitively(Globals,
HaveReadModuleMapInt, Why, Modules, IntFileKind,
NewIntSection, NewImpSection, SectionAppend, !ModuleAndImports, !IO) :-
process_module_short_interfaces_transitively(Globals, HaveReadModuleMapInt,
Why, Modules, IntFileKind, NewIntSection, NewImpSection, SectionAppend,
set.init, ImpIndirectImports, !ModuleAndImports, !IO),
( if set.is_empty(ImpIndirectImports) then
true
else
process_module_short_interfaces_and_impls_transitively(Globals,
HaveReadModuleMapInt, Why, ImpIndirectImports, IntFileKind,
NewIntSection, NewImpSection, SectionAppend,
!ModuleAndImports, !IO)
).
% process_module_short_interfaces_transitively(Globals,
% HaveReadModuleMapInt, Why, Modules, IntFileKind,
% NewIntSection, NewImpSection, SectionAppend,
% !ImpIndirectImports, !ModuleAndImports):
%
% Read the short interfaces (.int3) for modules in Modules (unless
% they have already been read in) and any modules that those modules
% import (transitively) in the interface.
%
% Append all the item blocks in the read-in files to !ModuleAndImports,
% putting all the ms_interface blocks in the int_module_section kind
% generated by NewIntSection, and putting all the ms_implementation blocks
% in the int_module_section kind generated by NewImpSection.
%
% XXX Despite the above, this predicate is sometimes invoked with
% IntFileKind != ifk_int3.
%
:- pred process_module_short_interfaces_transitively(globals::in,
have_read_module_int_map::in, string::in,
set(module_name)::in, int_file_kind::in,
int_section_maker(MS)::in, int_section_maker(MS)::in,
section_appender(MS)::in(section_appender),
set(module_name)::in, set(module_name)::out,
module_and_imports::in, module_and_imports::out, io::di, io::uo) is det.
process_module_short_interfaces_transitively(Globals, HaveReadModuleMapInt,
Why, Modules, IntFileKind, NewIntSection, NewImpSection, SectionAppend,
!ImpIndirectImports, !ModuleAndImports, !IO) :-
process_module_short_interfaces(Globals, HaveReadModuleMapInt,
Why, Modules, IntFileKind, NewIntSection, NewImpSection, SectionAppend,
set.init, IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO),
( if set.is_empty(IndirectImports) then
true
else
process_module_short_interfaces_transitively(Globals,
HaveReadModuleMapInt, Why, IndirectImports, IntFileKind,
NewIntSection, NewImpSection, SectionAppend, !ImpIndirectImports,
!ModuleAndImports, !IO)
).
% process_module_short_interfaces(Globals, HaveReadModuleMapInt,
% Modules, IntFileKind, NewIntSection, NewImpSection, SectionAppend,
% !IndirectImports, !ImpIndirectImports, !ModuleAndImports, !IO):
%
% Read the short interfaces for modules in Modules (unless they have
% already been read in). Append the modules imported by the interface
% of Modules to !IndirectImports. Append the modules imported by the
% implementation of Modules to !ImpIndirectImports.
%
% Append all the item blocks in the read-in files to !ModuleAndImports,
% putting all the ms_interface blocks in the int_module_section kind
% generated by NewIntSection, and putting all the ms_implementation blocks
% in the int_module_section kind generated by NewImpSection.
%
:- pred process_module_short_interfaces(globals::in,
have_read_module_int_map::in, string::in,
set(module_name)::in, int_file_kind::in,
int_section_maker(MS)::in, int_section_maker(MS)::in,
section_appender(MS)::in(section_appender),
set(module_name)::in, set(module_name)::out,
set(module_name)::in, set(module_name)::out,
module_and_imports::in, module_and_imports::out, io::di, io::uo) is det.
process_module_short_interfaces(Globals, HaveReadModuleMapInt, Why,
Modules, IntFileKind, NewIntSection, NewImpSection,
SectionAppend, !IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO) :-
( if set.remove_least(FirstModule, Modules, LaterModules) then
( if
% Have we already processed FirstModule.IntFileKind?
( FirstModule = !.ModuleAndImports ^ mai_module_name
; set.member(FirstModule, !.ModuleAndImports ^ mai_parent_deps)
; map.search(!.ModuleAndImports ^ mai_int_deps, FirstModule, _)
; map.search(!.ModuleAndImports ^ mai_imp_deps, FirstModule, _)
; set.member(FirstModule, !.ModuleAndImports ^ mai_indirect_deps)
)
then
maybe_log_augment_decision(Why, "short", FirstModule, IntFileKind,
no, !IO)
else
maybe_log_augment_decision(Why, "short", FirstModule, IntFileKind,
yes, !IO),
process_module_short_interface(Globals, HaveReadModuleMapInt,
FirstModule, IntFileKind, NewIntSection, NewImpSection,
SectionAppend, !IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO)
),
process_module_short_interfaces(Globals, HaveReadModuleMapInt, Why,
LaterModules, IntFileKind, NewIntSection, NewImpSection,
SectionAppend, !IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO)
else
true
).
%---------------------------------------------------------------------------%
:- pred process_module_private_interface(globals::in,
have_read_module_int_map::in, module_name::in,
int_section_maker(MS)::in, int_section_maker(MS)::in,
section_appender(MS)::in(section_appender),
set(module_name)::in, set(module_name)::out,
set(module_name)::in, set(module_name)::out,
module_and_imports::in, module_and_imports::out, io::di, io::uo) is det.
process_module_private_interface(Globals, HaveReadModuleMapInt,
Module, NewIntSection, NewImpSection, SectionAppend,
!DirectImports, !DirectUses, !ModuleAndImports, !IO) :-
ProcessInterfaceKind = pik_private,
process_module_interface_general(Globals, ProcessInterfaceKind,
HaveReadModuleMapInt, Module,
NewIntSection, NewImpSection, SectionAppend,
_IntAvails, _ImpAvails, ItemBlocks, !ModuleAndImports, !IO),
get_dependencies_in_item_blocks(ItemBlocks,
AncDirectImportsMap, AncDirectUsesMap),
set.sorted_list_to_set(map.keys(AncDirectImportsMap), AncDirectImports),
set.sorted_list_to_set(map.keys(AncDirectUsesMap), AncDirectUses),
set.union(AncDirectImports, !DirectImports),
set.union(AncDirectUses, !DirectUses).
:- pred process_module_long_interface(globals::in,
have_read_module_int_map::in, need_qualifier::in,
module_name::in, int_file_kind::in,
int_section_maker(MS)::in, int_section_maker(MS)::in,
section_appender(MS)::in(section_appender),
set(module_name)::in, set(module_name)::out,
set(module_name)::in, set(module_name)::out,
module_and_imports::in, module_and_imports::out, io::di, io::uo) is det.
process_module_long_interface(Globals, HaveReadModuleMapInt, NeedQual,
Module, IntFileKind, NewIntSection, NewImpSection, SectionAppend,
!IntImportsUses, !ImpImportsUses, !ModuleAndImports, !IO) :-
% XXX It should be possible to factor our the differences between
% process_module_{long,short}_interface.
ProcessInterfaceKind = pik_long(IntFileKind, NeedQual),
process_module_interface_general(Globals, ProcessInterfaceKind,
HaveReadModuleMapInt, Module,
NewIntSection, NewImpSection, SectionAppend,
IntAvails, ImpAvails, _ItemBlocks, !ModuleAndImports, !IO),
get_dependencies_in_avails(IntAvails, IntImportsMap, IntUsesMap),
get_dependencies_in_avails(ImpAvails, ImpImportsMap, ImpUsesMap),
set.sorted_list_to_set(map.keys(IntImportsMap), IntImports),
set.sorted_list_to_set(map.keys(IntUsesMap), IntUses),
set.sorted_list_to_set(map.keys(ImpImportsMap), ImpImports),
set.sorted_list_to_set(map.keys(ImpUsesMap), ImpUses),
!:IntImportsUses = set.union_list([!.IntImportsUses, IntImports, IntUses]),
!:ImpImportsUses = set.union_list([!.ImpImportsUses, ImpImports, ImpUses]).
:- pred process_module_short_interface(globals::in,
have_read_module_int_map::in, module_name::in, int_file_kind::in,
int_section_maker(MS)::in, int_section_maker(MS)::in,
section_appender(MS)::in(section_appender),
set(module_name)::in, set(module_name)::out,
set(module_name)::in, set(module_name)::out,
module_and_imports::in, module_and_imports::out, io::di, io::uo) is det.
process_module_short_interface(Globals, HaveReadModuleMapInt,
Module, IntFileKind, NewIntSection, NewImpSection, SectionAppend,
!IntImportsUses, !ImpImportsUses, !ModuleAndImports, !IO) :-
% XXX It should be possible to factor our the differences between
% process_module_{long,short}_interface.
ProcessInterfaceKind = pik_short(IntFileKind),
process_module_interface_general(Globals, ProcessInterfaceKind,
HaveReadModuleMapInt, Module,
NewIntSection, NewImpSection, SectionAppend,
IntAvails, ImpAvails, _ItemBlocks, !ModuleAndImports, !IO),
get_dependencies_in_avails(IntAvails, IntImportsMap, IntUsesMap),
get_dependencies_in_avails(ImpAvails, ImpImportsMap, ImpUsesMap),
set.sorted_list_to_set(map.keys(IntImportsMap), IntImports),
set.sorted_list_to_set(map.keys(IntUsesMap), IntUses),
set.sorted_list_to_set(map.keys(ImpImportsMap), ImpImports),
set.sorted_list_to_set(map.keys(ImpUsesMap), ImpUses),
!:IntImportsUses = set.union_list([!.IntImportsUses, IntImports, IntUses]),
!:ImpImportsUses = set.union_list([!.ImpImportsUses, ImpImports, ImpUses]).
:- type process_interface_kind
---> pik_private
; pik_short(int_file_kind)
; pik_long(int_file_kind, need_qualifier).
:- pred process_module_interface_general(globals::in,
process_interface_kind::in, have_read_module_int_map::in,
module_name::in,
int_section_maker(MS)::in, int_section_maker(MS)::in,
section_appender(MS)::in(section_appender),
list(item_avail)::out, list(item_avail)::out, list(item_block(MS))::out,
module_and_imports::in, module_and_imports::out, io::di, io::uo) is det.
process_module_interface_general(Globals, ProcessInterfaceKind,
HaveReadModuleMapInt, Module, NewIntSection, NewImpSection,
SectionAppend, IntAvails, ImpAvails, ItemBlocks,
!ModuleAndImports, !IO) :-
(
ProcessInterfaceKind = pik_private,
IntFileKind = ifk_int0
;
ProcessInterfaceKind = pik_short(IntFileKind)
;
ProcessInterfaceKind = pik_long(IntFileKind, _)
),
IFKStr = int_file_kind_to_extension(IntFileKind),
MsgPrefix = "Reading " ++ IFKStr ++ " interface for module",
maybe_return_timestamp(!.ModuleAndImports ^ mai_maybe_timestamp_map,
ReturnTimestamp),
maybe_read_module_int(Globals, HaveReadModuleMapInt, MsgPrefix, do_search,
Module, IntFileKind, _FileName, ReturnTimestamp, MaybeTimestamp,
ParseTree, Specs, Errors, !IO),
ParseTree = parse_tree_int(ModuleName, IntKind,
Context, MaybeVersionNumbers,
IntIncls, ImpIncls, IntAvails, ImpAvails, IntItems, ImpItems),
module_and_imports_maybe_add_module_version_numbers(
ModuleName, MaybeVersionNumbers, !ModuleAndImports),
int_imp_items_to_item_blocks(Context,
NewIntSection(Module, IntKind), NewImpSection(Module, IntKind),
IntIncls, ImpIncls, IntAvails, ImpAvails, IntItems, ImpItems,
ItemBlocks),
SectionAppend(ItemBlocks, !ModuleAndImports),
module_and_imports_add_specs_errors(Specs, Errors, !ModuleAndImports),
globals.lookup_bool_option(Globals, detailed_statistics, Statistics),
maybe_report_stats(Statistics, !IO),
% XXX To me (zs), the differences here seem accidental rather than
% deliberate.
(
ProcessInterfaceKind = pik_private,
% XXX Why do we ignore Errors here for the timestamp (only)?
maybe_record_timestamp(Module, ifk_int0, may_be_unqualified,
MaybeTimestamp, !ModuleAndImports),
set.intersect(Errors, fatal_read_module_errors, FatalErrors),
( if set.is_empty(FatalErrors) then
ModAncestors0 = !.ModuleAndImports ^ mai_parent_deps,
set.insert(Module, ModAncestors0, ModAncestors),
!ModuleAndImports ^ mai_parent_deps := ModAncestors
else
true
)
;
ProcessInterfaceKind = pik_short(_),
% XXX Why do we ignore Errors here for (a) the timestamp,
% and (b) for the update of !ModuleAndImports?
maybe_record_timestamp(Module, IntFileKind, must_be_qualified,
MaybeTimestamp, !ModuleAndImports),
ModIndirectImports0 = !.ModuleAndImports ^ mai_indirect_deps,
set.insert(Module, ModIndirectImports0, ModIndirectImports),
!ModuleAndImports ^ mai_indirect_deps := ModIndirectImports
;
ProcessInterfaceKind = pik_long(_, NeedQual),
set.intersect(Errors, fatal_read_module_errors, FatalIntErrors),
( if set.is_empty(FatalIntErrors) then
maybe_record_timestamp(Module, IntFileKind, NeedQual,
MaybeTimestamp, !ModuleAndImports),
ModImpImports0 = !.ModuleAndImports ^ mai_imp_deps,
% XXX We should get our caller to give us a nondummy context.
multi_map.add(Module, term.context_init,
ModImpImports0, ModImpImports),
!ModuleAndImports ^ mai_imp_deps := ModImpImports
else
true
)
).
%---------------------------------------------------------------------------%
:- pred maybe_log_augment_decision(string::in, string::in, module_name::in,
int_file_kind::in, bool::in, io::di, io::uo) is det.
% Inlining calls to this predicate effectively optimizes it away
% if the trace condition is not met, as it usually won't be.
:- pragma inline(maybe_log_augment_decision/7).
maybe_log_augment_decision(Why, Kind, ModuleName, IntFileKind, Read, !IO) :-
trace [compile_time(flag("log_augment_decisions")), io(!TIO)] (
(
Read = no,
ReadStr = "decided not to read"
;
Read = yes,
ReadStr = "decided to read"
),
ModuleNameStr = sym_name_to_string(ModuleName),
ExtensionStr = int_file_kind_to_extension(IntFileKind),
io.format("%s %s: %s %s%s\n",
[s(Why), s(Kind), s(ReadStr),
s(ModuleNameStr), s(ExtensionStr)], !TIO)
).
%---------------------------------------------------------------------------%
:- pred make_module_and_imports(file_name::in,
module_name::in, module_name::in, prog_context::in,
list(src_item_block)::in, list(error_spec)::in,
multi_map(module_name, prog_context)::in, set(module_name)::in,
list(string)::in, foreign_include_file_infos::in,
maybe(module_timestamp_map)::in, module_and_imports::out) is det.
make_module_and_imports(SourceFileName, SourceFileModuleName,
ModuleName, ModuleNameContext, SrcItemBlocks0, Specs,
PublicChildren, NestedChildren, FactDeps, ForeignIncludeFiles,
MaybeTimestampMap, Module) :-
% XXX The reason why make_module_and_imports is here and not in
% module_imports.m is this call. This should be fixed, preferably
% by changing the module_and_imports structure.
% XXX ITEM_LIST oms_interface is a guess. The original code (whose
% behavior the current code is trying to emulate) simply added
% the generated items to a raw item list, seemingly without caring
% about what section those items would end up (it certainly did not
% look for any section markers).
add_needed_foreign_import_module_items_to_item_blocks(ModuleName,
sms_interface, SrcItemBlocks0, SrcItemBlocks),
set.init(Ancestors),
map.init(IntDeps),
map.init(ImpDeps),
set.init(IndirectDeps),
map.init(IncludeDeps),
ForeignImports = init_foreign_import_modules,
set.init(Errors),
Module = module_and_imports(SourceFileName, SourceFileModuleName,
ModuleName, ModuleNameContext,
Ancestors, IntDeps, ImpDeps, IndirectDeps, IncludeDeps,
PublicChildren, NestedChildren, FactDeps,
ForeignImports, ForeignIncludeFiles,
contains_foreign_code_unknown, contains_no_foreign_export,
SrcItemBlocks, cord.init, cord.init, cord.init, cord.init, map.init,
Specs, Errors, MaybeTimestampMap, no_main, dir.this_directory).
%---------------------------------------------------------------------------%
:- pred maybe_return_timestamp(maybe(T)::in, maybe_return_timestamp::out)
is det.
maybe_return_timestamp(yes(_), do_return_timestamp).
maybe_return_timestamp(no, dont_return_timestamp).
:- pred maybe_record_timestamp(module_name::in, int_file_kind::in,
need_qualifier::in, maybe(timestamp)::in,
module_and_imports::in, module_and_imports::out) is det.
maybe_record_timestamp(ModuleName, IntFileKind, NeedQual, MaybeTimestamp,
!ModuleAndImports) :-
(
!.ModuleAndImports ^ mai_maybe_timestamp_map = yes(TimestampMap0),
(
MaybeTimestamp = yes(Timestamp),
FileKind = fk_int(IntFileKind),
TimestampInfo =
module_timestamp(FileKind, Timestamp, NeedQual),
map.set(ModuleName, TimestampInfo, TimestampMap0, TimestampMap),
!ModuleAndImports ^ mai_maybe_timestamp_map := yes(TimestampMap)
;
MaybeTimestamp = no
)
;
!.ModuleAndImports ^ mai_maybe_timestamp_map = no
).
%---------------------------------------------------------------------------%
% check_imports_accessibility(ModuleName, AugItemBlocks, ImportedModules,
% !Specs):
%
% By the time we are called, we should have read in all the appropriate
% interface files, including, for every imported/used module, at least
% the short interface for that module's parent module, which will contain
% the `include_module' declarations for any exported submodules
% of the parent. So the set of accessible submodules can be determined
% by looking at every include_module declaration in AugItemBlocks.
%
% We then go through all of the imported/used modules (ImportedModules),
% checking that each one is accessible, and generating an error message
% for each one that is not accessible.
%
% XXX ITEM_LIST I (zs) don't know whether our caller will always give us
% an ImportedModules list that covers every module listed in ImportUseMap,
% or whether some modules may be missing for good reason. If the former,
% then being given ImportedModules is unnecessary; we could just use
% the set of keys of ImportUseMap.
%
:- pred check_imports_accessibility(aug_compilation_unit::in,
set(module_name)::in, list(error_spec)::in, list(error_spec)::out) is det.
check_imports_accessibility(AugCompUnit, ImportedModules,
!Specs) :-
AugCompUnit = aug_compilation_unit(ModuleName, _ModuleNameContext,
_ModuleVersionNumbers, SrcItemBlocks,
DirectIntItemBlocks, IndirectIntItemBlocks,
OptItemBlocks, IntForOptItemBlocks),
IntItemBlocks = DirectIntItemBlocks ++ IndirectIntItemBlocks,
record_includes_imports_uses(SrcItemBlocks, IntItemBlocks, OptItemBlocks,
IntForOptItemBlocks, InclMap, ImportUseMap),
% XXX ITEM_LIST We should either record in an updated AugCompUnit
% the set of imported modules that are inaccessible, or remove their
% imports from it, so that we don't generate "unused module" warnings
% for them when --warn-unused-imports is enabled.
set.foldl(check_module_accessibility(ModuleName, InclMap, ImportUseMap),
ImportedModules, !Specs).
%---------------------%
%
% The module_inclusion_map and module_inclusion_map are computed by
% record_includes_imports_uses, for use by check_module_accessibility.
% For their documentation, see those predicates below.
%
:- type module_inclusion_map == map(module_name, one_or_more(term.context)).
:- type import_or_use_context
---> import_or_use_context(
import_or_use,
term.context
).
:- type module_import_or_use_map ==
map(module_name, one_or_more(import_or_use_context)).
% record_includes_imports_uses(SrcItemBlocks, IntItemBlocks, OptItemBlocks,
% IntForOptItemBlocks, InclMap, ImportUseMap):
%
% Given all these item blocks, return two maps. The first, InclMap, maps
% the name of each included module to the location(s) of its inclusions(s).
% The second, ImportUseMap, maps each the name of every imported and/or
% used module to an import_or_use_context, which records whether
% the module is being imported or used, and where.
%
% XXX ITEM_LIST The result of this should be stored in both raw and
% augmented compilation units. (The raw version would of course be computed
% from raw_item_blocks.)
%
:- pred record_includes_imports_uses(list(src_item_block)::in,
list(int_item_block)::in, list(opt_item_block)::in,
list(int_for_opt_item_block)::in,
module_inclusion_map::out, module_import_or_use_map::out) is det.
record_includes_imports_uses(SrcItemBlocks, IntItemBlocks, OptItemBlocks,
IntForOptItemBlocks, !:InclMap, !:ImportUseMap) :-
map.init(!:InclMap),
map.init(!:ImportUseMap),
record_includes_imports_uses_in_item_blocks_acc(SrcItemBlocks,
src_section_visibility, !InclMap, !ImportUseMap),
record_includes_imports_uses_in_item_blocks_acc(IntItemBlocks,
int_section_visibility, !InclMap, !ImportUseMap),
record_includes_imports_uses_in_item_blocks_acc(OptItemBlocks,
opt_section_visibility, !InclMap, !ImportUseMap),
record_includes_imports_uses_in_item_blocks_acc(IntForOptItemBlocks,
int_for_opt_section_visibility, !InclMap, !ImportUseMap).
:- type section_visibility(MS) == (func(MS) = bool).
:- func src_section_visibility(src_module_section) = bool.
:- func int_section_visibility(int_module_section) = bool.
:- func opt_section_visibility(opt_module_section) = bool.
:- func int_for_opt_section_visibility(int_for_opt_module_section) = bool.
src_section_visibility(sms_interface) = yes.
src_section_visibility(sms_implementation) = yes.
src_section_visibility(sms_impl_but_exported_to_submodules) = yes.
int_section_visibility(ims_imported_or_used(_, _, _, _)) = yes.
int_section_visibility(ims_abstract_imported(_, _)) = no.
opt_section_visibility(oms_opt_imported(_, _)) = no.
int_for_opt_section_visibility(ioms_opt_imported(_, _)) = no.
:- pred record_includes_imports_uses_in_item_blocks_acc(
list(item_block(MS))::in, section_visibility(MS)::in,
module_inclusion_map::in, module_inclusion_map::out,
module_import_or_use_map::in, module_import_or_use_map::out) is det.
record_includes_imports_uses_in_item_blocks_acc([], _,
!InclMap, !ImportUseMap).
record_includes_imports_uses_in_item_blocks_acc([ItemBlock | ItemBlocks],
SectionVisibility, !InclMap, !ImportUseMap) :-
ItemBlock = item_block(Section, _, Incls, Avails, _Items),
Visible = SectionVisibility(Section),
(
Visible = yes,
record_includes_acc(Incls, !InclMap)
;
Visible = no
),
% XXX Should we be ignoring Visible here?
record_avails_acc(Avails, !ImportUseMap),
record_includes_imports_uses_in_item_blocks_acc(ItemBlocks,
SectionVisibility, !InclMap, !ImportUseMap).
:- pred record_includes_acc(list(item_include)::in,
module_inclusion_map::in, module_inclusion_map::out) is det.
record_includes_acc([], !InclMap).
record_includes_acc([Include | Includes], !InclMap) :-
Include = item_include(ModuleName, Context, _SeqNum),
( if map.search(!.InclMap, ModuleName, OneOrMore0) then
OneOrMore0 = one_or_more(HeadContext, TailContexts),
OneOrMore = one_or_more(Context, [HeadContext | TailContexts]),
map.det_update(ModuleName, OneOrMore, !InclMap)
else
OneOrMore = one_or_more(Context, []),
map.det_insert(ModuleName, OneOrMore, !InclMap)
),
record_includes_acc(Includes, !InclMap).
:- pred record_avails_acc(list(item_avail)::in,
module_import_or_use_map::in, module_import_or_use_map::out) is det.
record_avails_acc([], !ImportUseMap).
record_avails_acc([Avail | Avails], !ImportUseMap) :-
(
Avail = avail_import(avail_import_info(ModuleName, Context, _SeqNum)),
ImportOrUse = import_decl
;
Avail = avail_use(avail_use_info(ModuleName, Context, _SeqNum)),
ImportOrUse = use_decl
),
IoUC = import_or_use_context(ImportOrUse, Context),
( if map.search(!.ImportUseMap, ModuleName, OneOrMore0) then
OneOrMore0 = one_or_more(HeadIoUC, TailIoUCs),
OneOrMore = one_or_more(IoUC, [HeadIoUC | TailIoUCs]),
map.det_update(ModuleName, OneOrMore, !ImportUseMap)
else
OneOrMore = one_or_more(IoUC, []),
map.det_insert(ModuleName, OneOrMore, !ImportUseMap)
),
record_avails_acc(Avails, !ImportUseMap).
%---------------------%
% check_module_accessibility(ModuleName, InclMap, ImportUseMap,
% ImportedModule, !Specs) :-
%
% Given the InclMap and ImportUseMap computed by the
% record_includes_imports_uses_in_items predicate above,
% check whether ImportedModule is accessible, and generate an error
% message if it isn't.
%
% InclMap tells us what modules are accessible, and ImportUseMap tells
% the location(s) where each imported module is imported (or used).
%
:- pred check_module_accessibility(module_name::in, module_inclusion_map::in,
module_import_or_use_map::in, module_name::in,
list(error_spec)::in, list(error_spec)::out) is det.
check_module_accessibility(ModuleName, InclMap, ImportUseMap, ImportedModule,
!Specs) :-
(
ImportedModule = qualified(ParentModule, SubModule),
( if map.search(InclMap, ImportedModule, _ImportedInclContexts) then
true
else
map.lookup(ImportUseMap, ImportedModule, ImportsUses),
ImportsUses = one_or_more(HeadIU, TailIUs),
report_inaccessible_module_error(ModuleName,
ParentModule, SubModule, HeadIU, !Specs),
list.foldl(
report_inaccessible_module_error(ModuleName,
ParentModule, SubModule),
TailIUs, !Specs)
)
;
ImportedModule = unqualified(_)
% For modules without parent modules, accessibility is moot.
).
:- pred report_inaccessible_module_error(module_name::in, module_name::in,
string::in, import_or_use_context::in,
list(error_spec)::in, list(error_spec)::out) is det.
% The error message should come out like this
% (the second sentence is included only with --verbose-errors):
% very_long_name.m:123: In module `very_long_name':
% very_long_name.m:123: error in `import_module' declaration:
% very_long_name.m:123: module `parent_module.sub_module' is inaccessible.
% very_long_name.m:123: Either there was no prior `import_module' or
% very_long_name.m:123: `use_module' declaration to import module
% very_long_name.m:123: `parent_module', or the interface for module
% very_long_name.m:123: `parent_module' does not contain an `include_module'
% very_long_name.m:123: declaration for module `sub_module'.
report_inaccessible_module_error(ModuleName, ParentModule, SubModule,
ImportOrUseContext, !Specs) :-
ImportOrUseContext = import_or_use_context(ImportOrUse, Context),
( ImportOrUse = import_decl, DeclName = "import_module"
; ImportOrUse = use_decl, DeclName = "use_module"
),
MainPieces = [words("In module"), qual_sym_name(ModuleName),
suffix(":"), nl,
words("error in"), quote(DeclName), words("declaration:"), nl,
words("module"), qual_sym_name(qualified(ParentModule, SubModule)),
words("is inaccessible."), nl],
VerbosePieces = [words("Either there was no prior"),
quote("import_module"),
words("or"), quote("use_module"),
words("declaration to import module"), qual_sym_name(ParentModule),
suffix(","), words("or the interface for module"),
qual_sym_name(ParentModule), words("does not contain an"),
quote("include_module"), words("declaration for module"),
quote(SubModule), suffix("."), nl],
Msg = simple_msg(Context,
[always(MainPieces), verbose_only(verbose_always, VerbosePieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs].
%---------------------------------------------------------------------------%
grab_opt_files(Globals, !ModuleAndImports, FoundError, !IO) :-
% Read in the .opt files for imported and ancestor modules.
ModuleName = !.ModuleAndImports ^ mai_module_name,
Ancestors0 = !.ModuleAndImports ^ mai_parent_deps,
IntDepsMap0 = !.ModuleAndImports ^ mai_int_deps,
ImpDepsMap0 = !.ModuleAndImports ^ mai_imp_deps,
set.sorted_list_to_set(map.keys(IntDepsMap0), IntDeps0),
set.sorted_list_to_set(map.keys(ImpDepsMap0), ImpDeps0),
OptFiles = set.union_list([Ancestors0, IntDeps0, ImpDeps0]),
globals.lookup_bool_option(Globals, read_opt_files_transitively,
Transitive),
set.insert(ModuleName, OptFiles, ModulesProcessed),
read_optimization_interfaces(Globals, Transitive,
set.to_sorted_list(OptFiles), ModulesProcessed,
cord.empty, OptItemBlocksCord, [], OptSpecs, no, OptError, !IO),
OptItemBlocks = cord.list(OptItemBlocksCord),
module_and_imports_add_opt_item_blocks(OptItemBlocks, !ModuleAndImports),
module_and_imports_add_specs(OptSpecs, !ModuleAndImports),
% Get the :- pragma unused_args(...) declarations created when writing
% the .opt file for the current module. These are needed because we can
% probably remove more arguments with intermod_unused_args, but the
% interface for other modules must remain the same.
%
% Similarly for the :- pragma structure_reuse(...) declarations. With more
% information available when making the target code than when writing the
% `.opt' file, it can turn out that a procedure which seemed to have
% condition reuse actually has none. But we have to maintain the interface
% for modules that use the conditional reuse information from the `.opt'
% file.
globals.lookup_bool_option(Globals, intermod_unused_args, UnusedArgs),
globals.lookup_bool_option(Globals, structure_reuse_analysis,
StructureReuse),
( if
( UnusedArgs = yes
; StructureReuse = yes
)
then
read_optimization_interfaces(Globals, no, [ModuleName], set.init,
cord.empty, LocalItemBlocksCord, [], LocalSpecs, no, UA_SR_Error,
!IO),
LocalItemBlocks = cord.list(LocalItemBlocksCord),
keep_only_unused_and_reuse_pragmas_in_blocks(UnusedArgs,
StructureReuse, LocalItemBlocks, FilteredItemBlocks),
module_and_imports_add_opt_item_blocks(FilteredItemBlocks,
!ModuleAndImports),
module_and_imports_add_specs(LocalSpecs, !ModuleAndImports)
else
UA_SR_Error = no
),
% Read .int0 files required by the `.opt' files.
map.init(HaveReadModuleMapInt),
OptFileAncestors = set.power_union(set.map(get_ancestors_set, OptFiles)),
Int0Files = set.delete(OptFileAncestors, ModuleName),
process_module_private_interfaces(Globals, HaveReadModuleMapInt,
"opt_int0s", Int0Files, make_ioms_opt_imported, make_ioms_opt_imported,
module_and_imports_add_int_for_opt_item_blocks,
set.init, AncestorImports1, set.init, AncestorImports2,
!ModuleAndImports, !IO),
% Figure out which .int files are needed by the .opt files
get_dependencies_in_item_blocks(OptItemBlocks,
NewImportDepsMap0, NewUseDepsMap0),
get_implicit_dependencies_in_item_blocks(Globals, OptItemBlocks,
NewImplicitImportDepsMap0, NewImplicitUseDepsMap0),
set.sorted_list_to_set(map.keys(NewUseDepsMap0), NewUseDeps0),
set.sorted_list_to_set(map.keys(NewImportDepsMap0), NewImportDeps0),
set.sorted_list_to_set(map.keys(NewImplicitImportDepsMap0),
NewImplicitImportDeps0),
set.sorted_list_to_set(map.keys(NewImplicitUseDepsMap0),
NewImplicitUseDeps0),
NewDeps = set.union_list(
[NewImportDeps0, NewUseDeps0,
NewImplicitImportDeps0, NewImplicitUseDeps0,
AncestorImports1, AncestorImports2]),
% Read in the .int, and .int2 files needed by the .opt files.
process_module_long_interfaces(Globals, HaveReadModuleMapInt,
"opt_new_deps", must_be_qualified, NewDeps, ifk_int,
make_ioms_opt_imported, make_ioms_opt_imported,
module_and_imports_add_int_for_opt_item_blocks,
set.init, NewIndirectDeps, set.init, NewImplIndirectDeps,
!ModuleAndImports, !IO),
process_module_short_interfaces_and_impls_transitively(Globals,
HaveReadModuleMapInt, "opt_new_indirect_deps",
set.union(NewIndirectDeps, NewImplIndirectDeps), ifk_int2,
make_ioms_opt_imported, make_ioms_opt_imported,
module_and_imports_add_int_for_opt_item_blocks,
!ModuleAndImports, !IO),
% Figure out whether anything went wrong.
% XXX We should try to put all the relevant error indications into
% !ModuleAndImports, and let our caller figure out what to do with them.
module_and_imports_get_errors(!.ModuleAndImports, ModuleErrors),
( if
( set.is_non_empty(ModuleErrors)
; OptError = yes
; UA_SR_Error = yes
)
then
FoundError = yes
else
FoundError = no
).
:- pred keep_only_unused_and_reuse_pragmas_in_blocks(bool::in, bool::in,
list(item_block(MS))::in, list(item_block(MS))::out) is det.
keep_only_unused_and_reuse_pragmas_in_blocks(_, _, [], []).
keep_only_unused_and_reuse_pragmas_in_blocks(UnusedArgs, StructureReuse,
[ItemBlock0 | ItemBlocks0], [ItemBlock | ItemBlocks]) :-
ItemBlock0 = item_block(Section, Context, _Incls0, _Imports0, Items0),
Incls = [],
Imports = [],
keep_only_unused_and_reuse_pragmas_acc(UnusedArgs, StructureReuse,
Items0, cord.init, ItemCord),
Items = cord.list(ItemCord),
ItemBlock = item_block(Section, Context, Incls, Imports, Items),
keep_only_unused_and_reuse_pragmas_in_blocks(UnusedArgs, StructureReuse,
ItemBlocks0, ItemBlocks).
:- pred keep_only_unused_and_reuse_pragmas_acc(bool::in, bool::in,
list(item)::in, cord(item)::in, cord(item)::out) is det.
keep_only_unused_and_reuse_pragmas_acc(_, _, [], !ItemCord).
keep_only_unused_and_reuse_pragmas_acc(UnusedArgs, StructureReuse,
[Item0 | Items0], !ItemCord) :-
( if
Item0 = item_pragma(ItemPragma0),
ItemPragma0 = item_pragma_info(Pragma0, _, _, _),
(
UnusedArgs = yes,
Pragma0 = pragma_unused_args(_)
;
StructureReuse = yes,
Pragma0 = pragma_structure_reuse(_)
)
then
!:ItemCord = cord.snoc(!.ItemCord, Item0)
else
true
),
keep_only_unused_and_reuse_pragmas_acc(UnusedArgs, StructureReuse, Items0,
!ItemCord).
:- pred read_optimization_interfaces(globals::in, bool::in,
list(module_name)::in, set(module_name)::in,
cord(opt_item_block)::in, cord(opt_item_block)::out,
list(error_spec)::in, list(error_spec)::out,
bool::in, bool::out, io::di, io::uo) is det.
read_optimization_interfaces(_, _, [], _, !ItemBlocksCord,
!Specs, !Error, !IO).
read_optimization_interfaces(Globals, Transitive,
[ModuleToRead | ModulesToRead], ModulesProcessed0,
!OptItemBlocksCord, !Specs, !Error, !IO) :-
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
maybe_write_out_errors_no_module(VeryVerbose, Globals, !Specs, !IO),
maybe_write_string(VeryVerbose,
"% Reading optimization interface for module", !IO),
maybe_write_string(VeryVerbose, " `", !IO),
ModuleToReadString = sym_name_to_string(ModuleToRead),
maybe_write_string(VeryVerbose, ModuleToReadString, !IO),
maybe_write_string(VeryVerbose, "'...\n", !IO),
maybe_flush_output(VeryVerbose, !IO),
module_name_to_search_file_name(Globals, ".opt", ModuleToRead, FileName,
!IO),
actually_read_module_opt(ofk_opt, Globals, FileName, ModuleToRead, [],
ParseTreeOpt, OptSpecs, OptError, !IO),
ParseTreeOpt = parse_tree_opt(OptModuleName, OptFileKind,
OptModuleContext, OptUses, OptItems),
OptSection = oms_opt_imported(OptModuleName, OptFileKind),
OptAvails = list.map(wrap_avail_use, OptUses),
OptItemBlock = item_block(OptSection, OptModuleContext,
[], OptAvails, OptItems),
!:OptItemBlocksCord = cord.snoc(!.OptItemBlocksCord, OptItemBlock),
update_opt_error_status(Globals, opt_file, FileName, OptSpecs, OptError,
!Specs, !Error),
maybe_write_out_errors_no_module(VeryVerbose, Globals, !Specs, !IO),
maybe_write_string(VeryVerbose, "% done.\n", !IO),
(
Transitive = yes,
NewUseDeps0 = set.list_to_set(
list.map(avail_use_info_module_name, OptUses)),
get_implicit_dependencies_in_items(Globals, OptItems,
NewImplicitImportDepsMap0, NewImplicitUseDepsMap0),
set.sorted_list_to_set(map.keys(NewImplicitImportDepsMap0),
NewImplicitImportDeps0),
set.sorted_list_to_set(map.keys(NewImplicitUseDepsMap0),
NewImplicitUseDeps0),
NewDepsSet0 = set.union_list([NewUseDeps0,
NewImplicitImportDeps0, NewImplicitUseDeps0]),
set.difference(NewDepsSet0, ModulesProcessed0, NewDepsSet),
set.union(ModulesProcessed0, NewDepsSet, ModulesProcessed),
set.to_sorted_list(NewDepsSet, NewDeps)
;
Transitive = no,
ModulesProcessed = ModulesProcessed0,
NewDeps = []
),
read_optimization_interfaces(Globals, Transitive,
NewDeps ++ ModulesToRead, ModulesProcessed,
!OptItemBlocksCord, !Specs, !Error, !IO).
%---------------------------------------------------------------------------%
grab_trans_opt_files(Globals, TransOptDeps, !Module, FoundError, !IO) :-
globals.lookup_bool_option(Globals, verbose, Verbose),
maybe_write_string(Verbose, "% Reading .trans_opt files..\n", !IO),
maybe_flush_output(Verbose, !IO),
read_trans_opt_files(Globals, TransOptDeps,
cord.empty, OptItemBlocksCord, [], OptSpecs, no, FoundError, !IO),
OptItemBlocks = cord.list(OptItemBlocksCord),
module_and_imports_add_opt_item_blocks(OptItemBlocks, !Module),
module_and_imports_add_specs(OptSpecs, !Module),
% XXX why ignore any existing errors?
module_and_imports_set_errors(set.init, !Module),
maybe_write_string(Verbose, "% Done.\n", !IO).
:- pred read_trans_opt_files(globals::in, list(module_name)::in,
cord(opt_item_block)::in, cord(opt_item_block)::out,
list(error_spec)::in, list(error_spec)::out,
bool::in, bool::out, io::di, io::uo) is det.
read_trans_opt_files(_, [], !OptItemBlocks, !Specs, !Error, !IO).
read_trans_opt_files(Globals, [Import | Imports], !OptItemBlocks,
!Specs, !Error, !IO) :-
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
maybe_write_out_errors_no_module(VeryVerbose, Globals, !Specs, !IO),
maybe_write_string(VeryVerbose,
"% Reading transitive optimization interface for module", !IO),
maybe_write_string(VeryVerbose, " `", !IO),
ImportString = sym_name_to_string(Import),
maybe_write_string(VeryVerbose, ImportString, !IO),
maybe_write_string(VeryVerbose, "'... ", !IO),
maybe_flush_output(VeryVerbose, !IO),
module_name_to_search_file_name(Globals, ".trans_opt", Import, FileName,
!IO),
actually_read_module_opt(ofk_trans_opt, Globals, FileName, Import, [],
ParseTreeOpt, OptSpecs, OptError, !IO),
maybe_write_string(VeryVerbose, " done.\n", !IO),
!:Specs = OptSpecs ++ !.Specs,
update_opt_error_status(Globals, trans_opt_file, FileName,
OptSpecs, OptError, !Specs, !Error),
maybe_write_out_errors_no_module(VeryVerbose, Globals, !Specs, !IO),
ParseTreeOpt = parse_tree_opt(OptModuleName, _OptFileKind, OptContext,
OptUses, OptItems),
OptSection = oms_opt_imported(OptModuleName, ofk_trans_opt),
OptAvails = list.map(wrap_avail_use, OptUses),
OptItemBlock = item_block(OptSection, OptContext,
[], OptAvails, OptItems),
!:OptItemBlocks = cord.snoc(!.OptItemBlocks, OptItemBlock),
read_trans_opt_files(Globals, Imports, !OptItemBlocks,
!Specs, !Error, !IO).
%---------------------------------------------------------------------------%
:- type opt_file_type
---> opt_file
; trans_opt_file.
% update_opt_error_status(Globals, OptFileType, FileName,
% ModuleSpecs, !Specs, ModuleErrors, !Error):
%
% Work out whether any fatal errors have occurred while reading
% `.opt' or `.trans_opt' files, updating !Errors if there were
% fatal errors.
%
% A missing `.opt' or `.trans_opt' file is only a fatal error if
% `--halt-at-warn' was passed the compiler together with
% `--warn-missing-opt-files' or `--warn-missing-trans-opt-files'
% respectively.
%
% Syntax errors in these files are always fatal.
%
:- pred update_opt_error_status(globals::in, opt_file_type::in, string::in,
list(error_spec)::in, read_module_errors::in,
list(error_spec)::in, list(error_spec)::out, bool::in, bool::out) is det.
update_opt_error_status(_Globals, FileType, FileName,
ModuleSpecs, ModuleErrors, !Specs, !Error) :-
( if set.is_empty(ModuleErrors) then
% OptSpecs contains no errors. I (zs) don't know whether it could
% contain any warnings or informational messages, but if it could,
% we should add those error_specs to !Specs. Not doing so preserves
% old behavior.
true
else if set.contains(ModuleErrors, rme_could_not_open_file) then
% We get here if we couldn't find and/or open the file.
% ModuleSpecs will already contain an error_severity error_spec
% about that, with more details than the message we generate below,
% but the test case hard_coded/intermod_unused_args insists on
% there being no error, only a warning, and on the text below.
% That is why we do not add ModuleSpecs to !Specs here.
%
% I (zs) don't know whether we should add a version of ModuleSpecs
% with downgraded severity to !Specs instead of the Spec we generate
% below.
(
FileType = opt_file,
WarningOption = warn_missing_opt_files
;
FileType = trans_opt_file,
WarningOption = warn_missing_trans_opt_files
),
Severity =
severity_conditional(WarningOption, yes, severity_warning, no),
Pieces = [option_is_set(WarningOption, yes,
[always([words("Warning: cannot open"), quote(FileName),
suffix("."), nl])])],
Msg = error_msg(no, treat_as_first, 0, Pieces),
Spec = error_spec(Severity, phase_read_files, [Msg]),
!:Specs = [Spec | !.Specs]
% NOTE: We do NOT update !Error, since a missing optimization
% interface file is not necessarily an error.
else
% ModuleErrors may or may not contain fatal errors other than
% rme_could_not_open_file, but we do not care.
!:Specs = ModuleSpecs ++ !.Specs,
!:Error = yes
).
%---------------------------------------------------------------------------%
:- end_module parse_tree.modules.
%---------------------------------------------------------------------------%