Files
mercury/compiler/modules.m
Zoltan Somogyi 1af5bcf2f1 Make module_name_to_file_name currying-friendly.
compiler/file_names.m:
    Change the order of arguments of module_name_to_file_name and related
    predicates to make it easier to construct closures from them. Delete
    the previous higher-order-friendly versions, which the previous step
    has made unnecessary.

compiler/compile_target_code.m:
compiler/elds_to_erlang.m:
compiler/export.m:
compiler/find_module.m:
compiler/generate_dep_d_files.m:
compiler/intermod.m:
compiler/llds_out_file.m:
compiler/make.m:
compiler/make.module_dep_file.m:
compiler/make.module_target.m:
compiler/make.program_target.m:
compiler/make.util.m:
compiler/mercury_compile_front_end.m:
compiler/mercury_compile_llds_back_end.m:
compiler/mercury_compile_main.m:
compiler/mercury_compile_middle_passes.m:
compiler/mercury_compile_mlds_back_end.m:
compiler/mlds_to_c.m:
compiler/mlds_to_cs.m:
compiler/mlds_to_java.m:
compiler/mmc_analysis.m:
compiler/mode_constraints.m:
compiler/module_cmds.m:
compiler/modules.m:
compiler/read_modules.m:
compiler/recompilation.check.m:
compiler/recompilation.usage.m:
compiler/write_deps_file.m:
compiler/write_module_interface_files.m:
compiler/xml_documentation.m:
    Conform to the change above. In several places, this means replacing
    explicit lambda expressions with simple partial application of the
    relevant predicates.
2017-06-12 19:38:20 +02:00

1855 lines
83 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 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,
set(module_name)::out, set(module_name)::out,
set(module_name)::out, set(module_name)::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),
set.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,
!:IntImported, !:IntUsed, !:ImpImported, !:ImpUsed, !IO),
RawCompUnit = raw_compilation_unit(ModuleName, ModuleNameContext,
RawItemBlocks),
HaveReadModuleMapInt = HaveReadModuleMaps ^ hrmm_int,
Ancestors = set.list_to_set(get_ancestors(ModuleName)),
!:Specs = [],
IntOrImpImportedOrUsed = set.union_list([
!.IntImported, !.IntUsed, !.ImpImported, !.ImpUsed]),
warn_if_import_for_self_or_ancestor(ModuleName, RawItemBlocks,
Ancestors, IntOrImpImportedOrUsed, !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,
ImplicitIntImported, 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,
!:IntImported, !:IntUsed, !:ImpImported, !:ImpUsed, !IO),
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,
ImplicitIntImported, 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, 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 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 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,
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(
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)
; set.member(FirstModule, !.ModuleAndImports ^ mai_int_deps)
; set.member(FirstModule, !.ModuleAndImports ^ mai_imp_deps)
)
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)
; set.member(FirstModule, !.ModuleAndImports ^ mai_int_deps)
; set.member(FirstModule, !.ModuleAndImports ^ mai_imp_deps)
; 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,
AncDirectImports, 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, IntImports, IntUses),
get_dependencies_in_avails(ImpAvails, ImpImports, 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, IntImports, IntUses),
get_dependencies_in_avails(ImpAvails, ImpImports, 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,
set.insert(Module, 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, 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(Ancestors),
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,
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,
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),
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,
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, 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,
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, ".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.
%---------------------------------------------------------------------------%