Files
mercury/compiler/modules.m
2015-11-16 00:09:26 +11:00

1770 lines
79 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.prog_data.
:- import_module parse_tree.prog_io.
:- import_module parse_tree.prog_io_error.
:- import_module cord.
:- import_module dir.
:- import_module map.
:- import_module require.
:- import_module term.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
grab_imported_modules(Globals, SourceFileName, SourceFileModuleName,
MaybeTimestamp, NestedChildren, RawCompUnit0, HaveReadModuleMaps,
!:ModuleAndImports, !IO) :-
% XXX ITEM_LIST Why aren't we updating !HaveReadModuleMaps?
RawCompUnit0 = raw_compilation_unit(ModuleName, ModuleNameContext,
RawItemBlocks0),
% Find out which modules this one depends on.
get_dependencies_int_imp_in_raw_item_blocks(RawItemBlocks0,
IntImportedModules0, IntUsedModules0,
ImpImportedModules0, ImpUsedModules0),
set.union(IntImportedModules0, ImpImportedModules0, ImportedModules0),
set.union(IntUsedModules0, ImpUsedModules0, UsedModules0),
some [!Specs] (
!:Specs = [],
( if ModuleNameContext = term.context_init then
module_name_to_file_name(Globals, ModuleName, ".m",
do_not_create_dirs, FileName, !IO),
Context = term.context_init(FileName, 1)
else
Context = ModuleNameContext
),
AncestorModules = set.list_to_set(get_ancestors(ModuleName)),
warn_if_import_for_self_or_ancestor(ModuleName, RawItemBlocks0,
AncestorModules, ImportedModules0, UsedModules0, !Specs),
warn_if_duplicate_use_import_decls(ModuleName, Context,
IntImportedModules0, IntImportedModules1,
IntUsedModules0, IntUsedModules1,
ImpImportedModules0, ImpImportedModules,
ImpUsedModules0, ImpUsedModules, !Specs),
get_src_item_blocks_public_children(RawCompUnit0,
SrcItemBlocks1, PublicChildren),
% XXX ITEM_LIST Store the info we now find by these traversals
% in the raw_comp_unit.
get_fact_table_dependencies_in_item_blocks(RawItemBlocks0, FactDeps),
get_foreign_include_files_in_item_blocks(RawItemBlocks0,
ForeignIncludeFiles),
(
MaybeTimestamp = yes(Timestamp),
MaybeTimestampMap = yes(map.singleton(ModuleName,
module_timestamp(fk_src, Timestamp, may_be_unqualified)))
;
MaybeTimestamp = no,
MaybeTimestampMap = no
),
% 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
% `ams_impl_but_exported_to_submodules' section.
make_module_and_imports(SourceFileName, SourceFileModuleName,
ModuleName, ModuleNameContext, SrcItemBlocks1, !.Specs,
PublicChildren, NestedChildren, FactDeps,
ForeignIncludeFiles, MaybeTimestampMap, !:ModuleAndImports),
% 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, SrcItemBlocks1,
ImplicitIntImportedModules, ImplicitIntUsedModules),
set.union(ImplicitIntImportedModules, IntImportedModules1,
IntImportedModules2),
set.union(ImplicitIntUsedModules, IntUsedModules1, IntUsedModules2),
% Process 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.
process_module_private_interfaces(Globals, HaveReadModuleMaps,
AncestorModules,
make_ims_imported(import_locn_interface),
make_ims_imported(import_locn_ancestor_private_interface_proper),
module_and_imports_add_direct_int_item_blocks,
IntImportedModules2, IntImportedModules,
IntUsedModules2, IntUsedModules, !ModuleAndImports, !IO),
% Process the modules imported using `import_module'.
% Uses of these items do not need module qualifiers.
set.init(IntIndirectImports0),
set.init(IntImpIndirectImports0),
process_module_long_interfaces(Globals, HaveReadModuleMaps,
may_be_unqualified, IntImportedModules, ifk_int,
make_ims_imported(import_locn_interface),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
IntIndirectImports0, IntIndirectImports1,
IntImpIndirectImports0, IntImpIndirectImports1,
!ModuleAndImports, !IO),
set.init(ImpIndirectImports0),
set.init(ImpImpIndirectImports0),
process_module_long_interfaces(Globals, HaveReadModuleMaps,
may_be_unqualified, ImpImportedModules, ifk_int,
make_ims_imported(import_locn_implementation),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
ImpIndirectImports0, ImpIndirectImports1,
ImpImpIndirectImports0, ImpImpIndirectImports1,
!ModuleAndImports, !IO),
% Process the modules imported using `use_module' .
process_module_long_interfaces(Globals, HaveReadModuleMaps,
must_be_qualified, IntUsedModules, ifk_int,
make_ims_used(import_locn_interface),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
IntIndirectImports1, IntIndirectImports,
IntImpIndirectImports1, IntImpIndirectImports2,
!ModuleAndImports, !IO),
process_module_long_interfaces(Globals, HaveReadModuleMaps,
must_be_qualified, ImpUsedModules, ifk_int,
make_ims_used(import_locn_implementation),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
ImpIndirectImports1, ImpIndirectImports,
ImpImpIndirectImports1, ImpImpIndirectImports2,
!ModuleAndImports, !IO),
process_module_short_interfaces_transitively(Globals,
HaveReadModuleMaps, IntIndirectImports, ifk_int2,
make_ims_used(import_locn_interface),
make_ims_abstract_imported,
module_and_imports_add_indirect_int_item_blocks,
IntImpIndirectImports2, IntImpIndirectImports,
!ModuleAndImports, !IO),
process_module_short_interfaces_transitively(Globals,
HaveReadModuleMaps, ImpIndirectImports, ifk_int2,
make_ims_used(import_locn_implementation),
make_ims_abstract_imported,
module_and_imports_add_indirect_int_item_blocks,
ImpImpIndirectImports2, ImpImpIndirectImports,
!ModuleAndImports, !IO),
% 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,
HaveReadModuleMaps, IntImpIndirectImports, 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,
HaveReadModuleMaps, ImpImpIndirectImports, 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,
_, _),
check_imports_accessibility(AugCompUnit,
set.union_list([IntImportedModules, IntUsedModules,
ImpImportedModules, ImpUsedModules]),
[], AccessSpecs),
module_and_imports_add_specs(AccessSpecs, !ModuleAndImports)
).
grab_unqual_imported_modules(Globals, SourceFileName, SourceFileModuleName,
RawCompUnit0, !:ModuleAndImports, !IO) :-
RawCompUnit0 = raw_compilation_unit(ModuleName, ModuleNameContext,
RawItemBlocks0),
% Find out which modules this one depends on.
ParentDeps = get_ancestors(ModuleName),
get_dependencies_int_imp_in_raw_item_blocks(RawItemBlocks0,
IntImportDeps0, IntUseDeps0, ImpImportDeps, ImpUseDeps),
% Construct the initial module import structure.
raw_item_blocks_to_src(RawItemBlocks0, SrcItemBlocks),
make_module_and_imports(SourceFileName, SourceFileModuleName,
ModuleName, ModuleNameContext, SrcItemBlocks, [], set.init, set.init,
[], cord.init, no, !:ModuleAndImports),
% 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, RawItemBlocks0,
ImplicitIntImportDeps, ImplicitIntUseDeps),
set.union(ImplicitIntImportDeps, IntImportDeps0, IntImportDeps),
set.union(ImplicitIntUseDeps, IntUseDeps0, IntUseDeps),
% Get the .int3s and .int0s that the current module depends on.
HaveReadModuleMaps = have_read_module_maps(map.init, map.init, map.init),
% First the .int0s for parent modules.
process_module_private_interfaces(Globals, HaveReadModuleMaps,
set.list_to_set(ParentDeps),
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, ParentImportDeps, set.init, ParentUseDeps,
!ModuleAndImports, !IO),
% Then the .int3s for `:- import'-ed modules.
process_module_long_interfaces(Globals, HaveReadModuleMaps,
may_be_unqualified, ParentImportDeps, ifk_int3,
make_ims_imported(import_locn_import_by_ancestor),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
set.init, IntIndirectImportDeps0, set.init, _, !ModuleAndImports, !IO),
process_module_long_interfaces(Globals, HaveReadModuleMaps,
may_be_unqualified, IntImportDeps, ifk_int3,
make_ims_imported(import_locn_interface),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
IntIndirectImportDeps0, IntIndirectImportDeps1,
set.init, _, !ModuleAndImports, !IO),
process_module_long_interfaces(Globals, HaveReadModuleMaps,
may_be_unqualified, ImpImportDeps, ifk_int3,
make_ims_imported(import_locn_implementation),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
set.init, ImpIndirectImportDeps0, set.init, _,
!ModuleAndImports, !IO),
% Then (after appropriate `:- used' decls) the .int3s for `:- use'-ed
% modules.
process_module_long_interfaces(Globals, HaveReadModuleMaps,
may_be_unqualified, ParentUseDeps, ifk_int3,
make_ims_imported(import_locn_import_by_ancestor),
make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
IntIndirectImportDeps1, IntIndirectImportDeps2,
set.init, _, !ModuleAndImports, !IO),
process_module_long_interfaces(Globals, HaveReadModuleMaps,
must_be_qualified, IntUseDeps, ifk_int3,
make_ims_used(import_locn_interface), make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
IntIndirectImportDeps2, IntIndirectImportDeps,
set.init, _, !ModuleAndImports, !IO),
process_module_long_interfaces(Globals, HaveReadModuleMaps,
must_be_qualified, ImpUseDeps, ifk_int3,
make_ims_used(import_locn_implementation), make_ims_abstract_imported,
module_and_imports_add_direct_int_item_blocks,
ImpIndirectImportDeps0, ImpIndirectImportDeps,
set.init, _, !ModuleAndImports, !IO),
% Then (after appropriate `:- used' decl) the .int3s for indirectly
% imported modules.
process_module_short_interfaces_transitively(Globals, HaveReadModuleMaps,
IntIndirectImportDeps, 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, HaveReadModuleMaps,
ImpIndirectImportDeps, 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,
_, _),
check_imports_accessibility(AugCompUnit,
set.union_list([IntImportDeps, IntUseDeps, ImpImportDeps, ImpUseDeps]),
[], AccessSpecs),
module_and_imports_add_specs(AccessSpecs, !ModuleAndImports).
%---------------------------------------------------------------------------%
:- pred get_src_item_blocks_public_children(raw_compilation_unit::in,
list(src_item_block)::out, set(module_name)::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 set.is_empty(Children) then
raw_item_blocks_to_src(RawItemBlocks, SrcItemBlocks),
set.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_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, set(module_name)::in,
list(error_spec)::in, list(error_spec)::out) is det.
warn_if_import_for_self_or_ancestor(ModuleName, RawItemBlocks,
AncestorModules, ImportedModules, UsedModules, !Specs) :-
set.union(ImportedModules, UsedModules, ImportedOrUsedModules),
set.intersect(AncestorModules, ImportedOrUsedModules,
ImportedOrUsedAncestorModules),
set.fold(find_and_warn_import_for_ancestor(ModuleName, RawItemBlocks),
ImportedOrUsedAncestorModules, !Specs),
( if set.member(ModuleName, ImportedOrUsedModules) 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"), 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"), sym_name(ModuleName),
words("imports its own ancestor, module"),
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.'.
%
:- 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,
list(error_spec)::in, list(error_spec)::out) is det.
warn_if_duplicate_use_import_decls(ModuleName, Context,
IntImportedModules0, IntImportedModules,
IntUsedModules0, IntUsedModules,
ImpImportedModules0, ImpImportedModules,
ImpUsedModules0, ImpUsedModules, !Specs) :-
do_warn_if_duplicate_use_import_decls(ModuleName, Context,
IntImportedModules0, IntImportedModules1,
IntUsedModules0, IntUsedModules, !Specs),
do_warn_if_duplicate_use_import_decls(ModuleName, Context,
IntImportedModules1, IntImportedModules,
ImpUsedModules0, ImpUsedModules1, !Specs),
do_warn_if_duplicate_use_import_decls(ModuleName, Context,
ImpImportedModules0, ImpImportedModules,
ImpUsedModules1, ImpUsedModules, !Specs).
% 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,
!ImportedModules, !UsedModules, !Specs) :-
set.intersect(!.ImportedModules, !.UsedModules, BothSet),
( if set.is_empty(BothSet) then
true
else
set.to_sorted_list(BothSet, BothList),
Pieces = [words("Warning:"),
words(choose_number(BothList, "module", "modules"))] ++
component_list_to_pieces(list.map(wrap_symname, BothList)) ++
[words(choose_number(BothList, "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(!.UsedModules, BothSet, !:UsedModules)
).
:- func wrap_symname(module_name) = format_component.
wrap_symname(ModuleName) = 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, 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_maps::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, HaveReadModuleMaps,
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.
true
else
process_module_private_interface(Globals, HaveReadModuleMaps,
FirstAncestor, NewIntSection, NewImpSection, SectionAppend,
!DirectImports, !DirectUses, !ModuleAndImports, !IO)
),
process_module_private_interfaces(Globals, HaveReadModuleMaps,
LaterAncestors, NewIntSection, NewImpSection, SectionAppend,
!DirectImports, !DirectUses, !ModuleAndImports, !IO)
else
true
).
:- pred process_module_private_interface(globals::in,
have_read_module_maps::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, HaveReadModuleMaps,
Ancestor, NewIntSection, NewImpSection, SectionAppend,
!DirectImports, !DirectUses, !ModuleAndImports, !IO) :-
maybe_return_timestamp(!.ModuleAndImports ^ mai_maybe_timestamp_map,
ReturnTimestamp),
maybe_read_module_int(Globals, HaveReadModuleMaps ^ hrmm_int,
"Reading private interface for module", do_search,
Ancestor, ifk_int0, _AncestorFileName,
ReturnTimestamp, MaybeTimestamp,
PrivateIntParseTree, PrivateIntSpecs, PrivateIntErrors, !IO),
maybe_record_timestamp(Ancestor, ifk_int0, may_be_unqualified,
MaybeTimestamp, !ModuleAndImports),
PrivateIntParseTree = parse_tree_int(PrivateIntModuleName,
PrivateIntKind, PrivateIntContext, MaybeVersionNumbers,
PrivateIntIntIncls, PrivateIntImpIncls,
PrivateIntIntAvails, PrivateIntImpAvails,
PrivateIntIntItems, PrivateIntImpItems),
module_and_imports_maybe_add_module_version_numbers(
PrivateIntModuleName, MaybeVersionNumbers, !ModuleAndImports),
int_imp_items_to_item_blocks(PrivateIntContext,
NewIntSection(Ancestor, PrivateIntKind),
NewImpSection(Ancestor, PrivateIntKind),
PrivateIntIntIncls, PrivateIntImpIncls,
PrivateIntIntAvails, PrivateIntImpAvails,
PrivateIntIntItems, PrivateIntImpItems,
PrivateIntItemBlocks),
get_dependencies_in_item_blocks(PrivateIntItemBlocks,
AncDirectImports, AncDirectUses),
set.union(AncDirectImports, !DirectImports),
set.union(AncDirectUses, !DirectUses),
SectionAppend(PrivateIntItemBlocks, !ModuleAndImports),
module_and_imports_add_specs_errors(PrivateIntSpecs, PrivateIntErrors,
!ModuleAndImports),
globals.lookup_bool_option(Globals, detailed_statistics, Statistics),
maybe_report_stats(Statistics, !IO),
set.intersect(PrivateIntErrors, fatal_read_module_errors,
FatalPrivateIntErrors),
( if set.is_empty(FatalPrivateIntErrors) then
ModAncestors0 = !.ModuleAndImports ^ mai_parent_deps,
set.insert(Ancestor, ModAncestors0, ModAncestors),
!ModuleAndImports ^ mai_parent_deps := ModAncestors
else
true
).
%---------------------------------------------------------------------------%
% process_module_long_interfaces(Globals, HaveReadModuleMaps,
% NeedQual, Imports, IntFileKind,
% NewIntSection, NewImpSection, SectionAppend,
% !IndirectImports, !ImpIndirectImports, !ModuleAndImports, !IO):
%
% Read the interface files (.int or .int2, as indicated by IntFileKind)
% for all the modules in Imports (unless they have already been read in),
% and 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.
%
:- pred process_module_long_interfaces(globals::in, have_read_module_maps::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, HaveReadModuleMaps, NeedQual,
Imports, IntFileKind, NewIntSection, NewImpSection,
SectionAppend, !IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO) :-
( if set.remove_least(FirstImport, Imports, LaterImports) then
ModuleName = !.ModuleAndImports ^ mai_module_name,
( if
% Have we already read it?
( FirstImport = ModuleName
; set.member(FirstImport, !.ModuleAndImports ^ mai_parent_deps)
; set.member(FirstImport, !.ModuleAndImports ^ mai_int_deps)
; set.member(FirstImport, !.ModuleAndImports ^ mai_imp_deps)
)
then
true
else
process_module_long_interface(Globals, HaveReadModuleMaps,
NeedQual, FirstImport, IntFileKind,
NewIntSection, NewImpSection, SectionAppend,
!IndirectImports, !ImpIndirectImports, !ModuleAndImports, !IO)
),
process_module_long_interfaces(Globals, HaveReadModuleMaps, NeedQual,
LaterImports, IntFileKind, NewIntSection, NewImpSection,
SectionAppend, !IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO)
else
true
).
:- pred process_module_long_interface(globals::in, have_read_module_maps::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, HaveReadModuleMaps, NeedQual,
Import, IntFileKind, NewIntSection, NewImpSection,
SectionAppend, !IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO) :-
maybe_return_timestamp(!.ModuleAndImports ^ mai_maybe_timestamp_map,
ReturnTimestamp),
maybe_read_module_int(Globals, HaveReadModuleMaps ^ hrmm_int,
"Reading interface for module", do_search,
Import, IntFileKind, _LongIntFileName,
ReturnTimestamp, MaybeTimestamp,
LongIntParseTree, LongIntSpecs, LongIntErrors, !IO),
LongIntParseTree = parse_tree_int(LongIntModuleName, LongIntKind,
LongIntContext, MaybeVersionNumbers,
LongIntIntIncls, LongIntImpIncls,
LongIntIntAvails, LongIntImpAvails,
LongIntIntItems, LongIntImpItems),
module_and_imports_maybe_add_module_version_numbers(
LongIntModuleName, MaybeVersionNumbers, !ModuleAndImports),
get_dependencies_in_avails(LongIntIntAvails,
IndirectImports1, IndirectUses1),
get_dependencies_in_avails(LongIntImpAvails,
ImpIndirectImports1, ImpIndirectUses1),
int_imp_items_to_item_blocks(LongIntContext,
NewIntSection(Import, LongIntKind), NewImpSection(Import, LongIntKind),
LongIntIntIncls, LongIntImpIncls, LongIntIntAvails, LongIntImpAvails,
LongIntIntItems, LongIntImpItems, LongIntItemBlocks),
!:IndirectImports = set.union_list([!.IndirectImports,
IndirectImports1, IndirectUses1]),
!:ImpIndirectImports = set.union_list([!.ImpIndirectImports,
ImpIndirectImports1, ImpIndirectUses1]),
SectionAppend(LongIntItemBlocks, !ModuleAndImports),
module_and_imports_add_specs_errors(LongIntSpecs, LongIntErrors,
!ModuleAndImports),
globals.lookup_bool_option(Globals, detailed_statistics, Statistics),
maybe_report_stats(Statistics, !IO),
set.intersect(LongIntErrors, fatal_read_module_errors, FatalLongIntErrors),
( if set.is_empty(FatalLongIntErrors) then
maybe_record_timestamp(Import, IntFileKind, NeedQual,
MaybeTimestamp, !ModuleAndImports),
ModImpImports0 = !.ModuleAndImports ^ mai_imp_deps,
set.insert(Import, ModImpImports0, ModImpImports),
!ModuleAndImports ^ mai_imp_deps := ModImpImports
else
true
).
%---------------------------------------------------------------------------%
% process_module_short_interfaces_and_impls_transitively(Globals,
% HaveReadModuleMaps, IndirectImports, IntFileKind,
% NewIntSection, NewImpSection, SectionAppend, !ModuleAndImports):
%
% Read the short interfaces for modules in IndirectImports (unless they've
% 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.
%
:- pred process_module_short_interfaces_and_impls_transitively(globals::in,
have_read_module_maps::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,
HaveReadModuleMaps, Imports, IntFileKind,
NewIntSection, NewImpSection, SectionAppend, !ModuleAndImports, !IO) :-
process_module_short_interfaces_transitively(Globals, HaveReadModuleMaps,
Imports, 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,
HaveReadModuleMaps, ImpIndirectImports, IntFileKind,
NewIntSection, NewImpSection, SectionAppend,
!ModuleAndImports, !IO)
).
% process_module_short_interfaces_transitively(Globals, HaveReadModuleMaps,
% IndirectImports, IntFileKind,
% NewIntSection, NewImpSection, SectionAppend,
% !ImpIndirectImports, !ModuleAndImports):
%
% Read the short interfaces (.int3) for modules in IndirectImports
% (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.
%
:- pred process_module_short_interfaces_transitively(globals::in,
have_read_module_maps::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, HaveReadModuleMaps,
Imports, IntFileKind, NewIntSection, NewImpSection, SectionAppend,
!ImpIndirectImports, !ModuleAndImports, !IO) :-
process_module_short_interfaces(Globals, HaveReadModuleMaps, Imports,
IntFileKind, NewIntSection, NewImpSection, SectionAppend,
set.init, IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO),
( if set.is_empty(IndirectImports) then
true
else
process_module_short_interfaces_transitively(Globals,
HaveReadModuleMaps, IndirectImports, IntFileKind,
NewIntSection, NewImpSection, SectionAppend, !ImpIndirectImports,
!ModuleAndImports, !IO)
).
% process_module_short_interfaces(Globals, HaveReadModuleMaps,
% 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_maps::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, HaveReadModuleMaps,
Imports, IntFileKind, NewIntSection, NewImpSection,
SectionAppend, !IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO) :-
( if set.remove_least(FirstImport, Imports, LaterImports) then
( if
% Check if the imported module has already been imported.
% XXX ITEM_LIST These lists should all be sets.
( FirstImport = !.ModuleAndImports ^ mai_module_name
; set.member(FirstImport, !.ModuleAndImports ^ mai_parent_deps)
; set.member(FirstImport, !.ModuleAndImports ^ mai_int_deps)
; set.member(FirstImport, !.ModuleAndImports ^ mai_imp_deps)
; set.member(FirstImport, !.ModuleAndImports ^ mai_indirect_deps)
)
then
true
else
process_module_short_interface(Globals, HaveReadModuleMaps,
FirstImport, IntFileKind, NewIntSection, NewImpSection,
SectionAppend, !IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO)
),
process_module_short_interfaces(Globals, HaveReadModuleMaps,
LaterImports, IntFileKind, NewIntSection, NewImpSection,
SectionAppend, !IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO)
else
true
).
:- pred process_module_short_interface(globals::in,
have_read_module_maps::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, HaveReadModuleMaps,
Import, IntFileKind, NewIntSection, NewImpSection,
SectionAppend, !IndirectImports, !ImpIndirectImports,
!ModuleAndImports, !IO) :-
maybe_return_timestamp(!.ModuleAndImports ^ mai_maybe_timestamp_map,
ReturnTimestamp),
maybe_read_module_int(Globals, HaveReadModuleMaps ^ hrmm_int,
"Reading short interface for module", do_search,
Import, IntFileKind, _ImportFileName,
ReturnTimestamp, MaybeTimestamp,
ShortIntParseTree, ShortIntSpecs, ShortIntError, !IO),
maybe_record_timestamp(Import, IntFileKind, must_be_qualified,
MaybeTimestamp, !ModuleAndImports),
ShortIntParseTree = parse_tree_int(ShortIntModuleName, ShortIntKind,
ShortIntContext, MaybeVersionNumbers,
ShortIntIntIncls, ShortIntImpIncls,
ShortIntIntAvails, ShortIntImpAvails,
ShortIntIntItems, ShortIntImpItems),
module_and_imports_maybe_add_module_version_numbers(
ShortIntModuleName, MaybeVersionNumbers, !ModuleAndImports),
get_dependencies_in_avails(ShortIntIntAvails, IntImports1, IntUses1),
get_dependencies_in_avails(ShortIntImpAvails, ImpImports1, ImpUses1),
int_imp_items_to_item_blocks(ShortIntContext,
NewIntSection(Import, ShortIntKind),
NewImpSection(Import, ShortIntKind),
ShortIntIntIncls, ShortIntImpIncls,
ShortIntIntAvails, ShortIntImpAvails,
ShortIntIntItems, ShortIntImpItems,
ShortIntItemBlocks),
!:IndirectImports = set.union_list([!.IndirectImports,
IntImports1, IntUses1]),
!:ImpIndirectImports = set.union_list([!.ImpIndirectImports,
ImpImports1, ImpUses1]),
SectionAppend(ShortIntItemBlocks, !ModuleAndImports),
module_and_imports_add_specs_errors(ShortIntSpecs, ShortIntError,
!ModuleAndImports),
globals.lookup_bool_option(Globals, detailed_statistics, Statistics),
maybe_report_stats(Statistics, !IO),
ModIndirectImports0 = !.ModuleAndImports ^ mai_indirect_deps,
set.insert(Import, ModIndirectImports0, ModIndirectImports),
!ModuleAndImports ^ mai_indirect_deps := ModIndirectImports.
%---------------------------------------------------------------------------%
:- 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, set(module_name)::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(ParentDeps),
set.init(IntDeps),
set.init(ImpDeps),
set.init(IndirectDeps),
set.init(IncludeDeps),
ForeignImports = init_foreign_import_modules,
set.init(Errors),
Module = module_and_imports(SourceFileName, SourceFileModuleName,
ModuleName, ModuleNameContext,
ParentDeps, 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,
% InclMap, ImportUseMap):
%
% Given all these item blocks, return two maps. The first, InclMap, maps
% each the name of each included module to the location(s) of its
% inclusions(s). The second, ImportUseMap, maps each the name of every
% imported 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(_, _, _)) = yes.
int_section_visibility(ims_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"), sym_name(ModuleName), suffix(":"), nl,
words("error in"), quote(DeclName), words("declaration:"), nl,
words("module"), 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"), sym_name(ParentModule),
suffix(","), words("or the interface for module"),
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,
IntDeps0 = !.ModuleAndImports ^ mai_int_deps,
ImpDeps0 = !.ModuleAndImports ^ mai_imp_deps,
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),
% Append the items to the current item list, using a `opt_imported'
% pseudo-declaration to let make_hlds know the opt_imported stuff
% is coming.
%
% XXX Using this mechanism to let make_hlds know this is a bad design.
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 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.
HaveReadModuleMaps = have_read_module_maps(map.init, map.init, map.init),
OptFileAncestors = set.power_union(set.map(get_ancestors_set, OptFiles)),
Int0Files = set.delete(OptFileAncestors, ModuleName),
process_module_private_interfaces(Globals, HaveReadModuleMaps, 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,
NewImportDeps0, NewUseDeps0),
get_implicit_dependencies_in_item_blocks(Globals, OptItemBlocks,
NewImplicitImportDeps0, 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, HaveReadModuleMaps,
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,
HaveReadModuleMaps, 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, ModuleToRead, ".opt", 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,
NewImplicitImportDeps0, 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, Import, ".trans_opt", 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.
%---------------------------------------------------------------------------%