mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-16 06:14:59 +00:00
Estimated hours taken: 30
Branches: main
Implement semantic checks for oisu (order independent state update) pragmas.
compiler/hlds_pred.m:
Record for each procedure whether it implements an operation
on a oisu type, and if yes, what kind of operation.
compiler/hlds_module.m:
Add to module_infos a data structure that lists the oisu pragmas
in the module, and the procedures mentioned in them. These are intended
to be used later during code generation.
compiler/add_pragma.m:
Add such pragmas to the HLDS, after checking whatever properties
can be checked during the creation of the HLDS.
compiler/oisu_check.m:
A new module, whose job it is to check those aspects of oisu pragmas
that can be checked only after other semantics are complete on the
module.
compiler/check_hlds.m:
Add the new module.
compiler/notes/compiler_design.html:
Document the new module.
compiler/mercury_compile_front_end.m:
Invoke the new module.
compiler/error_util.m:
Add the new semantic check as a phase.
compiler/mercury_to_mercury.m:
Fix typos in the code for writing out oisu pragmas.
compiler/prog_io_pragmas.m:
Fix typos in the code for reading in oisu pragmas.
compiler/module_qual.m:
Improve the error messages generated for any problems discovered during
module qualification inside pragmas, by writing out what *kind* of
pragma the problem was discovered in.
compiler/modules.m:
Fix a bug: oisu pragmas *can* appear in module interfaces.
compiler/stratify.m:
Give a predicate a better interface and a name.
compiler/hlds_goal.m:
Remove a duplicate comment.
compiler/make_hlds_passes.m:
Fix formatting.
tests/hard_coded/oisu_check_main.{m,exp}:
tests/hard_coded/oisu_check_db.m:
A new multimodule test case, which uses oisu pragmas correctly.
tests/invalid/oisu_check_add_pragma_errors.{m,err_exp}:
A new test case, which tests add_pragma.m's ability to diagnose
the problems it is supposed to diagnose.
tests/invalid/oisu_check_semantic_errors.{m,err_exp}:
A new test case, which tests oisu_check.m's ability to diagnose
the problems it is supposed to diagnose.
tests/hard_coded/Mmakefile:
tests/invalid/Mmakefile:
Enable the new test cases.
4623 lines
184 KiB
Mathematica
4623 lines
184 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.
|
|
%
|
|
% This module contains all the code for handling module imports and exports,
|
|
% for computing module dependencies, and for generating makefile fragments to
|
|
% record those dependencies.
|
|
%
|
|
% The interface system works as follows:
|
|
%
|
|
% 1. a .int3 file is written, which contains all the types, typeclasses, insts
|
|
% and modes defined in the interface. Equivalence types, solver types, insts
|
|
% and modes are written in full, others are written in abstract form. These
|
|
% are module qualified as far as possible given the information present in the
|
|
% current module. The datestamp on the .date3 file gives the last time the
|
|
% .int3 file was checked for consistency.
|
|
%
|
|
% 2. The .int and .int2 files are created, using the .int3 files
|
|
% of imported modules to fully module qualify all items.
|
|
% The .int2 file is mostly just a fully qualified version of the .int3 file,
|
|
% however it also includes some extra information, such as functors for
|
|
% discriminated union types, which may be needed for mode analysis.
|
|
% The .int3 file must be kept for datestamping purposes. The datestamp
|
|
% on the .date file gives the last time the .int and .int2 files
|
|
% were checked.
|
|
%
|
|
% 3. The .int0 file is similar to the .int file except that it also
|
|
% includes declarations (but not clauses) from the implementation section.
|
|
% It is used when compiling sub-modules. The datestamp on the .date0
|
|
% file gives the last time the .int0 file was checked.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module parse_tree.modules.
|
|
:- interface.
|
|
|
|
:- import_module libs.file_util.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.timestamp.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module parse_tree.error_util.
|
|
:- import_module parse_tree.module_imports.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_item.
|
|
:- import_module parse_tree.read_modules.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module digraph.
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% make_private_interface(Globals, SourceFileName, SourceFileModuleName,
|
|
% ModuleName, MaybeTimestamp, Items):
|
|
%
|
|
% Given a source file name and module name, the timestamp of the source
|
|
% file, and the list of items in that module, output the private (`.int0')
|
|
% interface file for the module. (The private interface contains all the
|
|
% declarations in the module, including those in the `implementation'
|
|
% section; it is used when compiling sub-modules.)
|
|
%
|
|
:- pred make_private_interface(globals::in, file_name::in,
|
|
module_name::in, module_name::in, maybe(timestamp)::in, list(item)::in,
|
|
io::di, io::uo) is det.
|
|
|
|
% make_interface(Globals, SourceFileName, SourceFileModuleName,
|
|
% ModuleName, MaybeTimestamp, Items):
|
|
%
|
|
% Given a source file name and module name, the timestamp of the source
|
|
% file, and the list of items in that module, output the long (`.int')
|
|
% and short (`.int2') interface files for the module.
|
|
%
|
|
:- pred make_interface(globals::in, file_name::in,
|
|
module_name::in, module_name::in, maybe(timestamp)::in, list(item)::in,
|
|
io::di, io::uo) is det.
|
|
|
|
% Output the unqualified short interface file to <module>.int3.
|
|
%
|
|
:- pred make_short_interface(globals::in, file_name::in, module_name::in,
|
|
list(item)::in, io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Make an item for a module declaration or pseudo-declaration
|
|
% such as `:- imported' (which is inserted by the compiler, but can't be
|
|
% used in user code).
|
|
%
|
|
:- func make_pseudo_decl(module_defn) = item.
|
|
|
|
% append_pseudo_decl(PseudoDecl, Module0, Module):
|
|
%
|
|
% Append the specified module declaration to the list of items in Module0
|
|
% to give Module.
|
|
%
|
|
:- pred append_pseudo_decl(module_defn::in,
|
|
module_and_imports::in, module_and_imports::out) is det.
|
|
|
|
% replace_section_decls(IntStatusItem, ImpStatusItem, !Items):
|
|
%
|
|
% Replace all occurrences of `:- interface' with IntStatusItem
|
|
% (this will usually be an item which sets the import status).
|
|
% Replace all occurrences of `:- implementation' with ImpStatusItem.
|
|
%
|
|
:- pred replace_section_decls(item::in, item::in,
|
|
list(item)::in, list(item)::out) is det.
|
|
|
|
% Remove all the imported items the list.
|
|
%
|
|
:- pred strip_imported_items(list(item)::in, list(item)::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type module_list == assoc_list(module_name, list(item)).
|
|
|
|
% Given a module (well, a list of items), split it into its constituent
|
|
% sub-modules, in top-down order.
|
|
% Also do some error checking:
|
|
% - report an error if the `implementation' section of a sub-module
|
|
% is contained inside the `interface' section of its parent module
|
|
% - check for modules declared as both nested and separate sub-modules.
|
|
% - check for non-abstract typeclass instance declarations in module
|
|
% interfaces.
|
|
%
|
|
:- pred split_into_submodules(module_name::in, list(item)::in,
|
|
module_list::out, list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% grab_imported_modules(Globals, SourceFileName, SourceFileModuleName,
|
|
% ModuleName, NestedSubModules, HaveReadModuleMap, ModuleTimestamp,
|
|
% Items, Module, !IO):
|
|
%
|
|
% Given a source file name and the top-level module name in that file,
|
|
% the current module name, the nested sub-modules in the file if this
|
|
% module is the top-level module, the timestamp of the file SourceFileName
|
|
% and the list of items in the current module, read in the private
|
|
% interface files for all the parent modules, the long interface files
|
|
% for all the imported modules, and the short interface files for all
|
|
% the indirectly imported modules, and return a `module_and_imports'
|
|
% structure containing the relevant information. HaveReadModuleMap contains
|
|
% the interface files read during recompilation checking.
|
|
%
|
|
:- pred grab_imported_modules(globals::in, file_name::in,
|
|
module_name::in, module_name::in, list(module_name)::in,
|
|
have_read_module_map::in, maybe(timestamp)::in, list(item)::in,
|
|
module_and_imports::out, io::di, io::uo) is det.
|
|
|
|
% grab_unqual_imported_modules(Globals, SourceFileName,
|
|
% SourceFileModuleName, ModuleName, Items, Module, !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' or `FactDeps'
|
|
% fields of the module_and_imports structure.
|
|
%
|
|
:- pred grab_unqual_imported_modules(globals::in, file_name::in,
|
|
module_name::in, module_name::in, list(item)::in, module_and_imports::out,
|
|
io::di, io::uo) is det.
|
|
|
|
% process_module_private_interfaces(Globals, Ancestors,
|
|
% IntStatusItem, ImpStatusItem, !DirectImports, !DirectUses,
|
|
% !Module, !IO):
|
|
%
|
|
% Read the complete private interfaces for modules in Ancestors, and
|
|
% append any imports/uses in the ancestors to the corresponding previous
|
|
% lists.
|
|
%
|
|
:- pred process_module_private_interfaces(globals::in,
|
|
have_read_module_map::in, list(module_name)::in, item::in, item::in,
|
|
list(module_name)::in, list(module_name)::out,
|
|
list(module_name)::in, list(module_name)::out,
|
|
module_and_imports::in, module_and_imports::out, io::di, io::uo) is det.
|
|
|
|
% process_module_long_interfaces(Globals, HaveReadModuleMap, NeedQualifier,
|
|
% Imports, Ext, IntStatusItem, ImpStatusItem,
|
|
% !IndirectImports, !ImplIndirectImports, !Module, !IO):
|
|
%
|
|
% Read the long interfaces for modules in Imports (unless they've already
|
|
% been read in) from files with filename extension Ext, and append any
|
|
% imports/uses in those modules to the IndirectImports list, and append
|
|
% any imports/uses in the implementation of those modules to the
|
|
% ImplIndirectImports list. Replace the `:- interface' declarations with
|
|
% IntStatusItem, which should set the import_status of the following items.
|
|
% Replace the `:- implementation' declarations with ImpStatusItem, which
|
|
% should set the import_status of the following items.
|
|
%
|
|
:- pred process_module_long_interfaces(globals::in, have_read_module_map::in,
|
|
need_qualifier::in, list(module_name)::in, string::in, item::in, item::in,
|
|
list(module_name)::in, list(module_name)::out,
|
|
list(module_name)::in, list(module_name)::out,
|
|
module_and_imports::in, module_and_imports::out, io::di, io::uo) is det.
|
|
|
|
% process_module_short_interfaces_transitively(Globals, HaveReadModuleMap,
|
|
% IndirectImports, Ext, IntStatusItem, ImpStatusItem,
|
|
% !ImpIndirectImports, !Module):
|
|
%
|
|
% 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.
|
|
%
|
|
% Replace the `:- interface' declarations with IntStatusItem, which
|
|
% should set the import_status of the following items. Replace the
|
|
% `:- implementation' declarations with ImpStatusItem, which should set
|
|
% the import_status of the following items.
|
|
%
|
|
:- pred process_module_short_interfaces_transitively(globals::in,
|
|
have_read_module_map::in, list(module_name)::in, string::in,
|
|
item::in, item::in, list(module_name)::in, list(module_name)::out,
|
|
module_and_imports::in, module_and_imports::out, io::di, io::uo) is det.
|
|
|
|
% process_module_short_interfaces_and_impls_transitively(Globals,
|
|
% HaveReadModuleMap, IndirectImports, Ext, IntStatusItem, ImpStatusItem,
|
|
% !Module):
|
|
%
|
|
% 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.
|
|
%
|
|
% Replace the `:- interface' declarations with IntStatusItem, which
|
|
% should set the import_status of the following items.
|
|
% Replace the `:- implementation' declarations with ImpStatusItem,
|
|
% which should set the import_status of the following items.
|
|
%
|
|
:- pred process_module_short_interfaces_and_impls_transitively(globals::in,
|
|
have_read_module_map::in, list(module_name)::in, string::in,
|
|
item::in, item::in, module_and_imports::in, module_and_imports::out,
|
|
io::di, io::uo) is det.
|
|
|
|
% process_module_short_interfaces(Globals, HaveReadModuleMap,
|
|
% IntStatusItem, ImpStatusItem, Modules, Ext,
|
|
% !IndirectImports, !ImpIndirectImports, !Module):
|
|
%
|
|
% Read the short interfaces for modules in Modules (unless they've 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.
|
|
%
|
|
% Replace the `:- interface' declarations with IntStatusItem, which should
|
|
% set the import_status of the following items. Replace the
|
|
% `:- implementation' declarations with ImpStatusItem, which should set
|
|
% the import_status of the following items.
|
|
%
|
|
:- pred process_module_short_interfaces(globals::in, have_read_module_map::in,
|
|
list(module_name)::in, string::in, item::in, item::in,
|
|
list(module_name)::in, list(module_name)::out,
|
|
list(module_name)::in, list(module_name)::out,
|
|
module_and_imports::in, module_and_imports::out, io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% maybe_read_dependency_file(Globals, ModuleName, MaybeTransOptDeps, !IO):
|
|
%
|
|
% If transitive intermodule optimization has been enabled, then read
|
|
% <ModuleName>.d to find the modules which <ModuleName>.trans_opt may
|
|
% depend on. Otherwise return `no'.
|
|
%
|
|
:- pred maybe_read_dependency_file(globals::in, module_name::in,
|
|
maybe(list(module_name))::out, io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% generate_module_dependencies(Globals, ModuleName, !IO):
|
|
%
|
|
% Generate the per-program makefile dependencies (`.dep') file for a
|
|
% program whose top-level module is `ModuleName'. This involves first
|
|
% transitively reading in all imported or ancestor modules. While we're
|
|
% at it, we also save the per-module makefile dependency (`.d') files
|
|
% for all those modules.
|
|
%
|
|
:- pred generate_module_dependencies(globals::in, module_name::in,
|
|
io::di, io::uo) is det.
|
|
|
|
% generate_file_dependencies(Globals, FileName, !IO):
|
|
%
|
|
% Same as generate_module_dependencies, but takes a file name instead of
|
|
% a module name.
|
|
%
|
|
:- pred generate_file_dependencies(globals::in, file_name::in,
|
|
io::di, io::uo) is det.
|
|
|
|
% generate_module_dependency_file(Globals, ModuleName, !IO):
|
|
%
|
|
% Generate the per module makefile dependency ('.d') file for the
|
|
% given module.
|
|
%
|
|
:- pred generate_module_dependency_file(globals::in, module_name::in,
|
|
io::di, io::uo) is det.
|
|
|
|
% generate_file_dependency_file(Globals, FileName, !IO):
|
|
%
|
|
% Same as generate_module_dependency_file, but takes a file name instead of
|
|
% a module name.
|
|
%
|
|
:- pred generate_file_dependency_file(globals::in, file_name::in,
|
|
io::di, io::uo) is det.
|
|
|
|
% add_module_relations(LookupModuleImports, ModuleName,
|
|
% !IntDepsRel, !ImplDepsRel)
|
|
%
|
|
% Add a module's interface and implementation dependencies to IntDepsRel
|
|
% and ImplDepsRel respectively. Dependencies are found using the
|
|
% LookupModuleImports function.
|
|
%
|
|
:- pred add_module_relations(
|
|
lookup_module_and_imports::lookup_module_and_imports,
|
|
module_name::in, digraph(module_name)::in, digraph(module_name)::out,
|
|
digraph(module_name)::in, digraph(module_name)::out) is det.
|
|
|
|
:- type lookup_module_and_imports == (func(module_name) = module_and_imports).
|
|
:- mode lookup_module_and_imports == in(func(in) = out is det).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% These predicates are exported for use by module_imports.m.
|
|
%
|
|
% XXX They shouldn't be needed; the representation of the program should have
|
|
% all this information readily accessible.
|
|
|
|
% get_children(Items, IncludeDeps):
|
|
%
|
|
% IncludeDeps is the list of sub-modules declared with `:- include_module'
|
|
% in Items.
|
|
%
|
|
:- pred get_children(list(item)::in, list(module_name)::out) is det.
|
|
|
|
% Given a module (well, a list of items), extract the interface
|
|
% part of that module, i.e. all the items between `:- interface'
|
|
% and `:- implementation'.
|
|
% The bodies of instance definitions are removed because
|
|
% the instance methods have not yet been module qualified.
|
|
%
|
|
:- pred get_interface(module_name::in, bool::in,
|
|
list(item)::in, list(item)::out) is det.
|
|
|
|
:- pred get_foreign_self_imports(list(item)::in, list(foreign_language)::out)
|
|
is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Check whether a particular `pragma' declaration is allowed
|
|
% in the interface section of a module.
|
|
%
|
|
:- func pragma_allowed_in_interface(pragma_type) = bool.
|
|
|
|
% Given a module name and a list of the items in that module,
|
|
% this procedure checks if the module doesn't export anything,
|
|
% and if so, and --warn-nothing-exported is set, it reports
|
|
% a warning.
|
|
%
|
|
:- pred check_for_no_exports(globals::in, list(item)::in, module_name::in,
|
|
list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module libs.options.
|
|
:- import_module parse_tree.deps_map.
|
|
:- import_module parse_tree.file_names.
|
|
:- import_module parse_tree.mercury_to_mercury.
|
|
:- import_module parse_tree.module_cmds.
|
|
:- import_module parse_tree.module_qual.
|
|
:- import_module parse_tree.prog_foreign.
|
|
:- import_module parse_tree.prog_io.
|
|
:- import_module parse_tree.prog_mutable.
|
|
:- import_module parse_tree.prog_out.
|
|
:- import_module parse_tree.prog_type.
|
|
:- import_module parse_tree.write_deps_file.
|
|
:- import_module recompilation.
|
|
:- import_module recompilation.version.
|
|
|
|
:- import_module char.
|
|
:- import_module cord.
|
|
:- import_module dir.
|
|
:- import_module getopt_io.
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module multi_map.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module solutions.
|
|
:- import_module sparse_bitset.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
:- import_module unit.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Private interfaces (.int0 files)
|
|
%
|
|
|
|
% Read in the .int3 files that the current module depends on, and use
|
|
% these to qualify all the declarations as much as possible. Then write
|
|
% out the .int0 file.
|
|
%
|
|
make_private_interface(Globals, SourceFileName, SourceFileModuleName,
|
|
ModuleName, MaybeTimestamp, Items0, !IO) :-
|
|
grab_unqual_imported_modules(Globals, SourceFileName, SourceFileModuleName,
|
|
ModuleName, Items0, Module, !IO),
|
|
|
|
% Check whether we succeeded.
|
|
% XXX zs: why is fatal_module_errors with no_module_errors instead of
|
|
% some_module_errors?
|
|
module_and_imports_get_results(Module, Items1, Specs0, Error),
|
|
(
|
|
Error = some_module_errors,
|
|
module_name_to_file_name(Globals, ModuleName, ".int0",
|
|
do_not_create_dirs, FileName, !IO),
|
|
% XXX _NumErrors
|
|
write_error_specs(Specs0, Globals, 0, _NumWarnings, 0, _NumErrors,
|
|
!IO),
|
|
io.write_strings(["Error reading interface files.\n",
|
|
"`", FileName, "' not written.\n"], !IO)
|
|
;
|
|
( Error = no_module_errors
|
|
; Error = fatal_module_errors
|
|
),
|
|
% Module-qualify all items.
|
|
module_name_to_file_name(Globals, ModuleName, ".m",
|
|
do_not_create_dirs, FileName, !IO),
|
|
module_qualify_items(Items1, Items2, map.init, _, Globals, ModuleName,
|
|
yes(FileName), "", _, _, _, Specs0, Specs),
|
|
(
|
|
Specs = [_ | _],
|
|
% XXX _NumErrors
|
|
write_error_specs(Specs, Globals, 0, _NumWarnings, 0, _NumErrors,
|
|
!IO),
|
|
io.write_strings(["`", FileName, "' not written.\n"], !IO)
|
|
;
|
|
Specs = [],
|
|
|
|
% Write out the `.int0' file.
|
|
|
|
strip_imported_items(Items2, Items3),
|
|
some [!IntItems, !ImplItems] (
|
|
list.foldl3(strip_clauses_private_interface, Items3,
|
|
section_interface, _Section,
|
|
[], !:IntItems, [], !:ImplItems),
|
|
handle_mutables_in_private_interface(ModuleName, !IntItems),
|
|
handle_mutables_in_private_interface(ModuleName, !ImplItems),
|
|
list.map(make_any_instances_abstract, !IntItems),
|
|
list.map(make_any_instances_abstract, !ImplItems),
|
|
order_items(!IntItems),
|
|
order_items(!ImplItems),
|
|
Items4 = [make_pseudo_decl(md_interface) | !.IntItems],
|
|
(
|
|
!.ImplItems = [],
|
|
Items = Items4
|
|
;
|
|
!.ImplItems = [_ | _],
|
|
Items = Items4 ++
|
|
[make_pseudo_decl(md_implementation) | !.ImplItems]
|
|
)
|
|
),
|
|
write_interface_file(Globals, SourceFileName, ModuleName,
|
|
".int0", MaybeTimestamp, Items, !IO),
|
|
touch_interface_datestamp(Globals, ModuleName, ".date0", !IO)
|
|
)
|
|
).
|
|
|
|
:- pred make_any_instances_abstract(item::in, item::out) is det.
|
|
|
|
make_any_instances_abstract(Item0, Item) :-
|
|
( Item0 = item_instance(InstanceInfo0) ->
|
|
InstanceInfo = make_instance_abstract(InstanceInfo0),
|
|
Item = item_instance(InstanceInfo)
|
|
;
|
|
Item = Item0
|
|
).
|
|
|
|
% Expand any mutable declarations in the item list into the pred and mode
|
|
% declarations for their access predicates. Only these components of a
|
|
% mutable declaration should be written to a private interface file.
|
|
%
|
|
:- pred handle_mutables_in_private_interface(module_name::in,
|
|
list(item)::in, list(item)::out) is det.
|
|
|
|
handle_mutables_in_private_interface(ModuleName, !Items) :-
|
|
list.foldl(handle_mutable_in_private_interface(ModuleName), !.Items,
|
|
[], !:Items).
|
|
|
|
:- pred handle_mutable_in_private_interface(module_name::in,
|
|
item::in, list(item)::in, list(item)::out) is det.
|
|
|
|
handle_mutable_in_private_interface(ModuleName, Item, !Items) :-
|
|
( Item = item_mutable(ItemMutable) ->
|
|
ItemMutable = item_mutable_info(MutableName, Type, _Value, Inst, Attrs,
|
|
_Varset, Context, _SeqNum),
|
|
ConstantInterface = mutable_var_constant(Attrs),
|
|
(
|
|
ConstantInterface = yes,
|
|
ConstantGetPredDeclItem = constant_get_pred_decl(ModuleName,
|
|
MutableName, Type, Inst, Context),
|
|
ConstantSetPredDeclItem = constant_set_pred_decl(ModuleName,
|
|
MutableName, Type, Inst, Context),
|
|
list.cons(ConstantGetPredDeclItem, !Items),
|
|
list.cons(ConstantSetPredDeclItem, !Items)
|
|
;
|
|
ConstantInterface = no,
|
|
StdGetPredDeclItem = std_get_pred_decl(ModuleName,
|
|
MutableName, Type, Inst, Context),
|
|
StdSetPredDeclItem = std_set_pred_decl(ModuleName,
|
|
MutableName, Type, Inst, Context),
|
|
list.cons(StdGetPredDeclItem, !Items),
|
|
list.cons(StdSetPredDeclItem, !Items),
|
|
IOStateInterface = mutable_var_attach_to_io_state(Attrs),
|
|
(
|
|
IOStateInterface = yes,
|
|
PureGetPredDeclItem = io_get_pred_decl(ModuleName,
|
|
MutableName, Type, Inst, Context),
|
|
PureSetPredDeclItem = io_set_pred_decl(ModuleName,
|
|
MutableName, Type, Inst, Context),
|
|
list.cons(PureGetPredDeclItem, !Items),
|
|
list.cons(PureSetPredDeclItem, !Items)
|
|
;
|
|
IOStateInterface = no
|
|
)
|
|
)
|
|
;
|
|
list.cons(Item, !Items)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Read in the .int3 files that the current module depends on, and use these
|
|
% to qualify all items in the interface as much as possible. Then write out
|
|
% the .int and .int2 files.
|
|
%
|
|
make_interface(Globals, SourceFileName, SourceFileModuleName, ModuleName,
|
|
MaybeTimestamp, Items0, !IO) :-
|
|
some [!InterfaceItems] (
|
|
get_interface(ModuleName, yes, Items0, !:InterfaceItems),
|
|
|
|
% Get the .int3 files for imported modules.
|
|
grab_unqual_imported_modules(Globals, SourceFileName,
|
|
SourceFileModuleName, ModuleName, !.InterfaceItems, Module0, !IO),
|
|
|
|
% Check whether we succeeded.
|
|
module_and_imports_get_results(Module0, !:InterfaceItems,
|
|
Specs0, Error),
|
|
% XXX zs: why is fatal_module_errors with no_module_errors instead of
|
|
% some_module_errors?
|
|
(
|
|
Error = some_module_errors,
|
|
% XXX _NumErrors
|
|
write_error_specs(Specs0, Globals, 0, _NumWarnings, 0, _NumErrors,
|
|
!IO),
|
|
module_name_to_file_name(Globals, ModuleName, ".int",
|
|
do_not_create_dirs, IntFileName, !IO),
|
|
module_name_to_file_name(Globals, ModuleName, ".int2",
|
|
do_not_create_dirs, Int2FileName, !IO),
|
|
io.write_strings(["Error reading short interface files.\n",
|
|
"`", IntFileName, "' and ",
|
|
"`", Int2FileName, "' not written.\n"], !IO)
|
|
;
|
|
( Error = no_module_errors
|
|
; Error = fatal_module_errors
|
|
),
|
|
% Module-qualify all items.
|
|
module_name_to_file_name(Globals, ModuleName, ".m",
|
|
do_not_create_dirs, FileName, !IO),
|
|
module_qualify_items(!InterfaceItems, map.init, _, Globals,
|
|
ModuleName, yes(FileName), "", _, _, _, Specs0, Specs),
|
|
|
|
% We want to finish writing the interface file (and keep
|
|
% the exit status at zero) if we found some warnings.
|
|
globals.set_option(halt_at_warn, bool(no),
|
|
Globals, NoHaltAtWarnGlobals),
|
|
write_error_specs(Specs, NoHaltAtWarnGlobals,
|
|
0, _NumWarnings, 0, NumErrors, !IO),
|
|
( NumErrors > 0 ->
|
|
module_name_to_file_name(Globals, ModuleName, ".int",
|
|
do_not_create_dirs, IntFileName, !IO),
|
|
io.write_strings(["`", IntFileName, "' ", "not written.\n"],
|
|
!IO)
|
|
;
|
|
% Strip out the imported interfaces, assertions are also
|
|
% stripped since they should only be written to .opt files,
|
|
% check for some warnings, and then write out the `.int'
|
|
% and `int2' files and touch the `.date' file.
|
|
|
|
strip_imported_items(!InterfaceItems),
|
|
strip_assertions(!InterfaceItems),
|
|
strip_unnecessary_impl_defns(!InterfaceItems),
|
|
check_for_clauses_in_interface(!InterfaceItems, [],
|
|
InterfaceSpecs0),
|
|
check_int_for_no_exports(Globals, !.InterfaceItems, ModuleName,
|
|
InterfaceSpecs0, InterfaceSpecs, !IO),
|
|
write_error_specs(InterfaceSpecs, Globals,
|
|
0, _NumWarnings2, 0, _NumErrors2, !IO),
|
|
% XXX _NumErrors
|
|
order_items(!InterfaceItems),
|
|
write_interface_file(Globals, SourceFileName, ModuleName,
|
|
".int", MaybeTimestamp, !.InterfaceItems, !IO),
|
|
get_short_interface(!.InterfaceItems, int2,
|
|
ShortInterfaceItems),
|
|
write_interface_file(Globals, SourceFileName, ModuleName,
|
|
".int2", MaybeTimestamp, ShortInterfaceItems, !IO),
|
|
touch_interface_datestamp(Globals, ModuleName, ".date", !IO)
|
|
)
|
|
)
|
|
).
|
|
|
|
make_short_interface(Globals, SourceFileName, ModuleName, Items0, !IO) :-
|
|
% This qualifies everything as much as it can given the information
|
|
% in the current module and writes out the .int3 file.
|
|
|
|
some [!Specs] (
|
|
!:Specs = [],
|
|
get_interface(ModuleName, no, Items0, InterfaceItems0),
|
|
% Assertions are also stripped since they should only be written
|
|
% to .opt files.
|
|
strip_assertions(InterfaceItems0, InterfaceItems1),
|
|
check_for_clauses_in_interface(InterfaceItems1, InterfaceItems,
|
|
!Specs),
|
|
get_short_interface(InterfaceItems, int3, ShortInterfaceItems0),
|
|
module_qualify_items(ShortInterfaceItems0, ShortInterfaceItems,
|
|
map.init, _, Globals, ModuleName, no, "", _, _, _, !Specs),
|
|
% XXX _NumErrors
|
|
write_error_specs(!.Specs, Globals, 0, _NumWarnings, 0, _NumErrors,
|
|
!IO),
|
|
% XXX why do we do this even if there are some errors?
|
|
write_interface_file(Globals, SourceFileName, ModuleName, ".int3",
|
|
no, ShortInterfaceItems, !IO),
|
|
touch_interface_datestamp(Globals, ModuleName, ".date3", !IO)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
strip_imported_items(Items0, Items) :-
|
|
strip_imported_items_2(Items0, [], RevItems),
|
|
list.reverse(RevItems, Items).
|
|
|
|
:- pred strip_imported_items_2(list(item)::in, list(item)::in, list(item)::out)
|
|
is det.
|
|
|
|
strip_imported_items_2([], !RevItems).
|
|
strip_imported_items_2([Item | Items], !RevItems) :-
|
|
(
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
(
|
|
( ModuleDefn = md_imported(_)
|
|
; ModuleDefn = md_used(_)
|
|
; ModuleDefn = md_abstract_imported
|
|
)
|
|
% The lack of a recursive call here effectively deletes both
|
|
% Item and everything in Items from the list.
|
|
;
|
|
% XXX Some of these should probably cause an error message.
|
|
( ModuleDefn = md_interface
|
|
; ModuleDefn = md_implementation
|
|
; ModuleDefn = md_implementation_but_exported_to_submodules
|
|
; ModuleDefn = md_opt_imported
|
|
; ModuleDefn = md_transitively_imported
|
|
; ModuleDefn = md_external(_, _)
|
|
; ModuleDefn = md_export(_)
|
|
; ModuleDefn = md_import(_)
|
|
; ModuleDefn = md_use(_)
|
|
; ModuleDefn = md_include_module(_)
|
|
; ModuleDefn = md_version_numbers(_, _)
|
|
),
|
|
!:RevItems = [Item | !.RevItems],
|
|
strip_imported_items_2(Items, !RevItems)
|
|
)
|
|
;
|
|
( Item = item_module_start(_)
|
|
; Item = item_module_end(_)
|
|
; Item = item_clause(_)
|
|
; Item = item_type_defn(_)
|
|
; Item = item_inst_defn(_)
|
|
; Item = item_mode_defn(_)
|
|
; Item = item_pred_decl(_)
|
|
; Item = item_mode_decl(_)
|
|
; Item = item_pragma(_)
|
|
; Item = item_promise(_)
|
|
; Item = item_typeclass(_)
|
|
; Item = item_instance(_)
|
|
; Item = item_initialise(_)
|
|
; Item = item_finalise(_)
|
|
; Item = item_mutable(_)
|
|
; Item = item_nothing(_)
|
|
),
|
|
!:RevItems = [Item | !.RevItems],
|
|
strip_imported_items_2(Items, !RevItems)
|
|
).
|
|
|
|
:- pred strip_assertions(list(item)::in, list(item)::out) is det.
|
|
|
|
strip_assertions([], []).
|
|
strip_assertions([Head | Tail], Items) :-
|
|
(
|
|
Head = item_promise(ItemPromise),
|
|
ItemPromise = item_promise_info(promise_type_true, _, _, _, _, _)
|
|
->
|
|
strip_assertions(Tail, Items)
|
|
;
|
|
strip_assertions(Tail, ItemsTail),
|
|
Items = [Head | ItemsTail]
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred strip_unnecessary_impl_defns(list(item)::in, list(item)::out) is det.
|
|
|
|
strip_unnecessary_impl_defns(Items0, Items) :-
|
|
some [!IntTypesMap, !ImplTypesMap, !ImplItems] (
|
|
gather_type_defns(Items0, IntItems0, !:ImplItems,
|
|
!:IntTypesMap, !:ImplTypesMap),
|
|
BothTypesMap = multi_map.merge(!.IntTypesMap, !.ImplTypesMap),
|
|
|
|
% Work out which module imports in the implementation section of
|
|
% the interface are required by the definitions of equivalence
|
|
% types and dummy types in the implementation.
|
|
get_requirements_of_impl_exported_types(!.IntTypesMap, !.ImplTypesMap,
|
|
BothTypesMap, NecessaryDummyTypeCtors,
|
|
NecessaryAbsImplExpTypeCtors, NecessaryTypeImplImports),
|
|
|
|
% Work out which module imports in the implementation section of
|
|
% the interface are required by the definitions of typeclasses
|
|
% in the implementation. Specifically, we require that ones
|
|
% that are needed by any constraints on the typeclasses.
|
|
get_requirements_of_impl_typeclasses(!.ImplItems,
|
|
NecessaryTypeclassImplImports),
|
|
|
|
NecessaryImplImports = NecessaryTypeImplImports `set.union`
|
|
NecessaryTypeclassImplImports,
|
|
|
|
% If a type in the implementation section isn't dummy and doesn't have
|
|
% foreign type alternatives, make it abstract.
|
|
map.map_values_only(make_impl_type_abstract(BothTypesMap),
|
|
!ImplTypesMap),
|
|
|
|
% If there is an exported type declaration for a type with an abstract
|
|
% declaration in the implementation (usually it will originally
|
|
% have been a d.u. type), remove the declaration in the implementation.
|
|
% Don't remove `type_is_abstract_enum' declarations, though.
|
|
FindRemovableAbsExpTypes =
|
|
(pred(TypeCtor::out) is nondet :-
|
|
map.member(!.ImplTypesMap, TypeCtor, Defns),
|
|
all [Defn] (
|
|
list.member(Defn - _, Defns)
|
|
=> (
|
|
Defn = parse_tree_abstract_type(Details),
|
|
Details \= abstract_enum_type(_)
|
|
)),
|
|
multi_map.contains(!.IntTypesMap, TypeCtor)
|
|
),
|
|
solutions(FindRemovableAbsExpTypes, RemovableAbstractExportedTypes),
|
|
RemoveFromImplTypesMap =
|
|
(pred(TypeCtor::in, !.ImplTypesMap::in, !:ImplTypesMap::out)
|
|
is det :-
|
|
multi_map.delete(TypeCtor, !ImplTypesMap)
|
|
),
|
|
list.foldl(RemoveFromImplTypesMap, RemovableAbstractExportedTypes,
|
|
!ImplTypesMap),
|
|
|
|
AddProjectedItem =
|
|
(pred((_ - ItemTypeDefn)::in, !.ImplItems::in, !:ImplItems::out)
|
|
is det :-
|
|
Item = item_type_defn(ItemTypeDefn),
|
|
!:ImplItems = [Item | !.ImplItems]
|
|
),
|
|
AddProjectedItems =
|
|
(pred(_::in, Defns::in, !.ImplItems::in, !:ImplItems::out)
|
|
is det :-
|
|
list.foldl(AddProjectedItem, Defns, !ImplItems)
|
|
),
|
|
map.foldl(AddProjectedItems, !.ImplTypesMap, !ImplItems),
|
|
|
|
IntItems = [make_pseudo_decl(md_interface) | IntItems0],
|
|
|
|
maybe_strip_import_decls(!ImplItems),
|
|
|
|
strip_unnecessary_impl_imports(NecessaryImplImports, !ImplItems),
|
|
set.union(NecessaryDummyTypeCtors, NecessaryAbsImplExpTypeCtors,
|
|
AllNecessaryTypeCtors),
|
|
strip_unnecessary_impl_types(AllNecessaryTypeCtors, !ImplItems),
|
|
strip_local_foreign_enum_pragmas(!.IntTypesMap, !ImplItems),
|
|
(
|
|
!.ImplItems = [],
|
|
Items = IntItems
|
|
;
|
|
!.ImplItems = [_ | _],
|
|
standardize_impl_items(!.ImplItems, StdImplItems),
|
|
ImplSectionItem = make_pseudo_decl(md_implementation),
|
|
list.condense([IntItems, [ImplSectionItem], StdImplItems], Items)
|
|
)
|
|
).
|
|
|
|
:- type module_specifier_in_defn
|
|
---> module_specifier_in_defn(
|
|
prog_context,
|
|
module_specifier
|
|
).
|
|
|
|
:- pred standardize_impl_items(list(item)::in, list(item)::out) is det.
|
|
|
|
standardize_impl_items(Items0, Items) :-
|
|
do_standardize_impl_items(Items0, no, Unexpected, [], RevRemainderItems,
|
|
[], ImportModuleSpecs, [], UseModuleSpecs, [], TypeDefnInfos),
|
|
(
|
|
Unexpected = yes,
|
|
unexpected($module, $pred, "unexpected items")
|
|
% XXX If the above exception is thrown and you need a
|
|
% workaround you can replace the call to unexpected with this code:
|
|
% Items = Items0
|
|
;
|
|
Unexpected = no,
|
|
list.reverse(RevRemainderItems, RemainderItems),
|
|
ImportItems = list.map(wrap_import_module_spec, ImportModuleSpecs),
|
|
UseItems = list.map(wrap_use_module_spec, UseModuleSpecs),
|
|
TypeDefnItems = list.map(wrap_type_defn_item, TypeDefnInfos),
|
|
list.condense([ImportItems, UseItems, TypeDefnItems, RemainderItems],
|
|
Items)
|
|
).
|
|
|
|
:- func wrap_type_defn_item(item_type_defn_info) = item.
|
|
|
|
wrap_type_defn_item(ItemTypeDefn) = item_type_defn(ItemTypeDefn).
|
|
|
|
:- func wrap_import_module_spec(module_specifier_in_defn) = item.
|
|
|
|
wrap_import_module_spec(ModuleSpecInDefn) = Item :-
|
|
ModuleSpecInDefn = module_specifier_in_defn(Context, ModuleSpec),
|
|
ModuleDefn = md_import([ModuleSpec]),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context, -1),
|
|
Item = item_module_defn(ItemModuleDefn).
|
|
|
|
:- func wrap_use_module_spec(module_specifier_in_defn) = item.
|
|
|
|
wrap_use_module_spec(ModuleSpecInDefn) = Item :-
|
|
ModuleSpecInDefn = module_specifier_in_defn(Context, ModuleSpec),
|
|
ModuleDefn = md_use([ModuleSpec]),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context, -1),
|
|
Item = item_module_defn(ItemModuleDefn).
|
|
|
|
:- pred do_standardize_impl_items(list(item)::in, bool::in, bool::out,
|
|
list(item)::in, list(item)::out,
|
|
list(module_specifier_in_defn)::in, list(module_specifier_in_defn)::out,
|
|
list(module_specifier_in_defn)::in, list(module_specifier_in_defn)::out,
|
|
list(item_type_defn_info)::in, list(item_type_defn_info)::out) is det.
|
|
|
|
do_standardize_impl_items([], !Unexpected, !RevRemainderItems,
|
|
!ImportSpecs, !UseSpecs, !TypeDefns).
|
|
do_standardize_impl_items([Item | Items], !Unexpected,
|
|
!RevRemainderItems, !ImportSpecs, !UseSpecs, !TypeDefns) :-
|
|
( Item = item_module_defn(ItemModuleDefn) ->
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context, _),
|
|
(
|
|
ModuleDefn = md_import(ImportModules),
|
|
( ImportModules = [ModuleSpec] ->
|
|
insert_module_spec(Context, ModuleSpec, !ImportSpecs)
|
|
;
|
|
unexpected($module, $pred, "non-singleton-module import")
|
|
)
|
|
;
|
|
ModuleDefn = md_use(UseModules),
|
|
( UseModules = [ModuleSpec] ->
|
|
insert_module_spec(Context, ModuleSpec, !UseSpecs)
|
|
;
|
|
unexpected($module, $pred, "non-singleton-module use")
|
|
)
|
|
;
|
|
( ModuleDefn = md_imported(_)
|
|
; ModuleDefn = md_used(_)
|
|
; ModuleDefn = md_abstract_imported
|
|
; ModuleDefn = md_opt_imported
|
|
; ModuleDefn = md_transitively_imported
|
|
; ModuleDefn = md_external(_, _)
|
|
; ModuleDefn = md_export(_)
|
|
; ModuleDefn = md_interface
|
|
; ModuleDefn = md_implementation
|
|
; ModuleDefn = md_implementation_but_exported_to_submodules
|
|
; ModuleDefn = md_version_numbers(_, _)
|
|
),
|
|
!:Unexpected = yes
|
|
;
|
|
ModuleDefn = md_include_module(_),
|
|
!:RevRemainderItems = [Item | !.RevRemainderItems]
|
|
)
|
|
; Item = item_type_defn(ItemTypeDefn) ->
|
|
insert_type_defn(ItemTypeDefn, !TypeDefns)
|
|
;
|
|
!:RevRemainderItems = [Item | !.RevRemainderItems]
|
|
),
|
|
do_standardize_impl_items(Items, !Unexpected,
|
|
!RevRemainderItems, !ImportSpecs, !UseSpecs, !TypeDefns).
|
|
|
|
:- pred insert_module_spec(prog_context::in, module_specifier::in,
|
|
list(module_specifier_in_defn)::in, list(module_specifier_in_defn)::out)
|
|
is det.
|
|
|
|
insert_module_spec(Context, NewModuleSpec, [], [New]) :-
|
|
New = module_specifier_in_defn(Context, NewModuleSpec).
|
|
insert_module_spec(Context, NewModuleSpec, [Head | Tail], Result) :-
|
|
Head = module_specifier_in_defn(_, HeadModuleSpec),
|
|
compare(CompareSymName, NewModuleSpec, HeadModuleSpec),
|
|
( CompareSymName = (<) ->
|
|
New = module_specifier_in_defn(Context, NewModuleSpec),
|
|
Result = [New, Head | Tail]
|
|
;
|
|
insert_module_spec(Context, NewModuleSpec, Tail, NewTail),
|
|
Result = [Head | NewTail]
|
|
).
|
|
|
|
:- pred insert_type_defn(item_type_defn_info::in,
|
|
list(item_type_defn_info)::in, list(item_type_defn_info)::out) is det.
|
|
|
|
insert_type_defn(New, [], [New]).
|
|
insert_type_defn(New, [Head | Tail], Result) :-
|
|
New = item_type_defn_info(_, NewSymName, NewParams, _, _, _, _),
|
|
Head = item_type_defn_info(_, HeadSymName, HeadParams, _, _, _, _),
|
|
compare(CompareSymName, NewSymName, HeadSymName),
|
|
(
|
|
(
|
|
CompareSymName = (<)
|
|
;
|
|
CompareSymName = (=),
|
|
list.length(NewParams, NewParamsLength),
|
|
list.length(HeadParams, HeadParamsLength),
|
|
compare(Compare, NewParamsLength, HeadParamsLength),
|
|
Compare = (<)
|
|
)
|
|
->
|
|
Result = [New, Head | Tail]
|
|
;
|
|
insert_type_defn(New, Tail, NewTail),
|
|
Result = [Head | NewTail]
|
|
).
|
|
|
|
:- pred make_impl_type_abstract(type_defn_map::in,
|
|
assoc_list(type_defn, item_type_defn_info)::in,
|
|
assoc_list(type_defn, item_type_defn_info)::out) is det.
|
|
|
|
make_impl_type_abstract(TypeDefnMap, !TypeDefnPairs) :-
|
|
(
|
|
!.TypeDefnPairs = [TypeDefn0 - ItemTypeDefn0],
|
|
TypeDefn0 = parse_tree_du_type(Ctors, MaybeEqCmp, MaybeDirectArgCtors)
|
|
->
|
|
(
|
|
constructor_list_represents_dummy_argument_type(TypeDefnMap, Ctors,
|
|
MaybeEqCmp, MaybeDirectArgCtors)
|
|
->
|
|
% Leave dummy types alone.
|
|
true
|
|
;
|
|
( du_type_is_enum(Ctors, NumBits) ->
|
|
Details = abstract_enum_type(NumBits)
|
|
;
|
|
Details = abstract_type_general
|
|
),
|
|
Defn = parse_tree_abstract_type(Details),
|
|
ItemTypeDefn = ItemTypeDefn0 ^ td_ctor_defn := Defn,
|
|
!:TypeDefnPairs = [Defn - ItemTypeDefn]
|
|
)
|
|
;
|
|
true
|
|
).
|
|
|
|
% Certain types, e.g. io.state and store.store(S), are just dummy types
|
|
% used to ensure logical semantics; there is no need to actually pass them,
|
|
% and so when importing or exporting procedures to/from C, we don't include
|
|
% arguments with these types.
|
|
%
|
|
% See the documentation for `type_util.check_dummy_type' for the definition
|
|
% of a dummy type.
|
|
%
|
|
% NOTE: changes here may require changes to `type_util.check_dummy_type'.
|
|
%
|
|
:- pred constructor_list_represents_dummy_argument_type(type_defn_map::in,
|
|
list(constructor)::in, maybe(unify_compare)::in,
|
|
maybe(list(sym_name_and_arity))::in) is semidet.
|
|
|
|
constructor_list_represents_dummy_argument_type(TypeDefnMap,
|
|
Ctors, MaybeEqCmp, MaybeDirectArgCtors) :-
|
|
constructor_list_represents_dummy_argument_type_2(TypeDefnMap,
|
|
Ctors, MaybeEqCmp, MaybeDirectArgCtors, []).
|
|
|
|
:- pred constructor_list_represents_dummy_argument_type_2(type_defn_map::in,
|
|
list(constructor)::in, maybe(unify_compare)::in,
|
|
maybe(list(sym_name_and_arity))::in, list(mer_type)::in) is semidet.
|
|
|
|
constructor_list_represents_dummy_argument_type_2(TypeDefnMap, [Ctor], no, no,
|
|
CoveredTypes) :-
|
|
Ctor = ctor(ExistQTVars, Constraints, _Name, Args, _Context),
|
|
ExistQTVars = [],
|
|
Constraints = [],
|
|
(
|
|
% A single zero-arity constructor.
|
|
Args = []
|
|
;
|
|
% A constructor with a single dummy argument.
|
|
Args = [ctor_arg(_, ArgType, _, _)],
|
|
ctor_arg_is_dummy_type(TypeDefnMap, ArgType, CoveredTypes) = yes
|
|
).
|
|
|
|
:- func ctor_arg_is_dummy_type(type_defn_map, mer_type, list(mer_type)) = bool.
|
|
|
|
ctor_arg_is_dummy_type(TypeDefnMap, Type, CoveredTypes0) = IsDummyType :-
|
|
(
|
|
Type = defined_type(SymName, TypeArgs, _Kind),
|
|
( list.member(Type, CoveredTypes0) ->
|
|
% The type is circular.
|
|
IsDummyType = no
|
|
;
|
|
Arity = list.length(TypeArgs),
|
|
TypeCtor = type_ctor(SymName, Arity),
|
|
(
|
|
check_builtin_dummy_type_ctor(TypeCtor)
|
|
= is_builtin_dummy_type_ctor
|
|
->
|
|
IsDummyType = yes
|
|
;
|
|
% Can we find a definition of the type that tells us it is a
|
|
% dummy type?
|
|
multi_map.search(TypeDefnMap, TypeCtor, TypeDefns),
|
|
list.member(TypeDefn - _, TypeDefns),
|
|
TypeDefn = parse_tree_du_type(TypeCtors, MaybeEqCmp,
|
|
MaybeDirectArgCtors),
|
|
CoveredTypes = [Type | CoveredTypes0],
|
|
constructor_list_represents_dummy_argument_type_2(TypeDefnMap,
|
|
TypeCtors, MaybeEqCmp, MaybeDirectArgCtors, CoveredTypes)
|
|
->
|
|
IsDummyType = yes
|
|
;
|
|
IsDummyType = no
|
|
)
|
|
)
|
|
;
|
|
( Type = type_variable(_, _)
|
|
; Type = builtin_type(_)
|
|
; Type = tuple_type(_, _)
|
|
; Type = higher_order_type(_, _, _, _)
|
|
; Type = apply_n_type(_, _, _)
|
|
; Type = kinded_type(_, _)
|
|
),
|
|
IsDummyType = no
|
|
).
|
|
|
|
% strip_unnecessary_impl_imports(NecessaryModules, !Items):
|
|
%
|
|
% Remove all import_module and use_module declarations for
|
|
% modules that are not in `NecessaryModules',
|
|
%
|
|
% NOTE: This will only work if each item corresponding
|
|
% to an import_module or use_module declaration only imports
|
|
% a single module. (This should be the case, see prog_io.m.)
|
|
%
|
|
:- pred strip_unnecessary_impl_imports(set(module_name)::in, list(item)::in,
|
|
list(item)::out) is det.
|
|
|
|
strip_unnecessary_impl_imports(NecessaryImports, !Items) :-
|
|
list.filter(is_not_unnecessary_impl_import(NecessaryImports), !Items).
|
|
|
|
:- pred is_not_unnecessary_impl_import(set(module_name)::in, item::in)
|
|
is semidet.
|
|
|
|
is_not_unnecessary_impl_import(NecessaryImports, Item) :-
|
|
( Item = item_module_defn(ItemModuleDefn) ->
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
(
|
|
( ModuleDefn = md_use(Modules)
|
|
; ModuleDefn = md_import(Modules)
|
|
)
|
|
->
|
|
( Modules = [ModuleName] ->
|
|
set.member(ModuleName, NecessaryImports)
|
|
;
|
|
unexpected($module, $pred, "non-singleton import or use decl")
|
|
)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
true
|
|
).
|
|
|
|
% strip_unnecessary_impl_types(NecessaryTypeCtors, !Items):
|
|
%
|
|
% Remove all type declarations for type constructors that are
|
|
% not in NecessaryTypeCtors.
|
|
%
|
|
:- pred strip_unnecessary_impl_types(set(type_ctor)::in,
|
|
list(item)::in, list(item)::out) is det.
|
|
|
|
strip_unnecessary_impl_types(NecessaryTypeCtors, !Items) :-
|
|
list.filter(is_not_unnecessary_impl_type(NecessaryTypeCtors), !Items).
|
|
|
|
:- pred is_not_unnecessary_impl_type(set(type_ctor)::in, item::in) is semidet.
|
|
|
|
is_not_unnecessary_impl_type(NecessaryTypeCtors, Item) :-
|
|
( Item = item_type_defn(ItemTypeDefn) ->
|
|
ItemTypeDefn = item_type_defn_info(_, SymName, Params, _, _, _, _),
|
|
TypeCtor = type_ctor(SymName, list.length(Params)),
|
|
set.member(TypeCtor, NecessaryTypeCtors)
|
|
;
|
|
true
|
|
).
|
|
|
|
% get_requirements_of_impl_exported_types(InterfaceTypeMap, ImplTypeMap,
|
|
% BothTypeMap, DummyTypeCtors, NecessaryTypeCtors, Modules):
|
|
%
|
|
% Figure out the set of abstract equivalence type constructors
|
|
% (i.e. the types that are exported as abstract types and which are defined
|
|
% in the implementation section as equivalence types or as foreign types).
|
|
% Return in NecessaryTypeCtors the smallest set containing those
|
|
% constructors, and the set of private type constructors referred to
|
|
% by the right hand side of any type in NecessaryTypeCtors.
|
|
%
|
|
% Return in DummyTypeCtors the set of dummy type constructors.
|
|
%
|
|
% Given a du type definition in the implementation section, we should
|
|
% include it in AbsImplExpLhsTypeCtors if the type constructor is abstract
|
|
% exported and the implementation section also contains a foreign_type
|
|
% definition of the type constructor.
|
|
%
|
|
% Given a enumeration type definition in the implementation section, we
|
|
% should include it in AbsImplExpEnumTypeCtors if the type constructor is
|
|
% abstract exported.
|
|
%
|
|
% Return in Modules the set of modules that define the type constructors
|
|
% in NecessaryTypeCtors.
|
|
%
|
|
:- pred get_requirements_of_impl_exported_types(type_defn_map::in,
|
|
type_defn_map::in, type_defn_map::in,
|
|
set(type_ctor)::out, set(type_ctor)::out, set(module_name)::out) is det.
|
|
|
|
get_requirements_of_impl_exported_types(InterfaceTypeMap, ImplTypeMap,
|
|
BothTypeMap, DummyTypeCtors, NecessaryTypeCtors, Modules) :-
|
|
multi_map.to_flat_assoc_list(ImplTypeMap, ImplTypes),
|
|
list.foldl3(
|
|
accumulate_abs_impl_exported_type_lhs(InterfaceTypeMap, BothTypeMap),
|
|
ImplTypes, set.init, AbsImplExpLhsTypeCtors,
|
|
set.init, AbsImplExpEnumTypeCtors, set.init, DummyTypeCtors),
|
|
set.fold3(accumulate_abs_impl_exported_type_rhs(ImplTypeMap),
|
|
AbsImplExpLhsTypeCtors,
|
|
set.init, AbsEqvRhsTypeCtors, set.init, ForeignDuFieldTypeCtors,
|
|
set.init, Modules),
|
|
NecessaryTypeCtors = set.union_list([AbsImplExpLhsTypeCtors,
|
|
AbsEqvRhsTypeCtors, ForeignDuFieldTypeCtors,
|
|
AbsImplExpEnumTypeCtors]).
|
|
|
|
:- pred accumulate_abs_impl_exported_type_lhs(type_defn_map::in,
|
|
type_defn_map::in,
|
|
pair(type_ctor, pair(type_defn, item_type_defn_info))::in,
|
|
set(type_ctor)::in, set(type_ctor)::out,
|
|
set(type_ctor)::in, set(type_ctor)::out,
|
|
set(type_ctor)::in, set(type_ctor)::out) is det.
|
|
|
|
accumulate_abs_impl_exported_type_lhs(InterfaceTypeMap, BothTypesMap,
|
|
TypeCtor - (TypeDefn - _Item), !AbsEqvLhsTypeCtors,
|
|
!AbsImplExpEnumTypeCtors, !DummyTypeCtors) :-
|
|
% A type may have multiple definitions because it may be defined both
|
|
% as a foreign type and as a Mercury type. We grab any equivalence types
|
|
% that are in there.
|
|
(
|
|
TypeDefn = parse_tree_eqv_type(_RhsType),
|
|
map.search(InterfaceTypeMap, TypeCtor, _)
|
|
->
|
|
set.insert(TypeCtor, !AbsEqvLhsTypeCtors)
|
|
;
|
|
TypeDefn = parse_tree_foreign_type(_, _, _),
|
|
map.search(InterfaceTypeMap, TypeCtor, _)
|
|
->
|
|
set.insert(TypeCtor, !AbsEqvLhsTypeCtors)
|
|
;
|
|
TypeDefn = parse_tree_du_type(Ctors, MaybeEqCmp, MaybeDirectArgCtors)
|
|
->
|
|
(
|
|
map.search(InterfaceTypeMap, TypeCtor, _),
|
|
du_type_is_enum(Ctors, _NumBits)
|
|
->
|
|
set.insert(TypeCtor, !AbsImplExpEnumTypeCtors)
|
|
;
|
|
constructor_list_represents_dummy_argument_type(BothTypesMap,
|
|
Ctors, MaybeEqCmp, MaybeDirectArgCtors)
|
|
->
|
|
set.insert(TypeCtor, !DummyTypeCtors)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred accumulate_abs_impl_exported_type_rhs(type_defn_map::in, type_ctor::in,
|
|
set(type_ctor)::in, set(type_ctor)::out,
|
|
set(type_ctor)::in, set(type_ctor)::out,
|
|
set(module_name)::in, set(module_name)::out) is det.
|
|
|
|
accumulate_abs_impl_exported_type_rhs(ImplTypeMap, TypeCtor,
|
|
!AbsEqvRhsTypeCtors, !ForeignDuFieldTypeCtors, !Modules) :-
|
|
( map.search(ImplTypeMap, TypeCtor, TypeDefns) ->
|
|
list.foldl3(accumulate_abs_eqv_type_rhs_2(ImplTypeMap), TypeDefns,
|
|
!AbsEqvRhsTypeCtors, !ForeignDuFieldTypeCtors, !Modules)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred accumulate_abs_eqv_type_rhs_2(type_defn_map::in,
|
|
pair(type_defn, item_type_defn_info)::in,
|
|
set(type_ctor)::in, set(type_ctor)::out,
|
|
set(type_ctor)::in, set(type_ctor)::out,
|
|
set(module_name)::in, set(module_name)::out) is det.
|
|
|
|
accumulate_abs_eqv_type_rhs_2(ImplTypeMap, TypeDefn - _,
|
|
!AbsEqvRhsTypeCtors, !ForeignDuFieldTypeCtors, !Modules) :-
|
|
( TypeDefn = parse_tree_eqv_type(RhsType) ->
|
|
type_to_type_ctor_set(RhsType, set.init, RhsTypeCtors),
|
|
set.difference(RhsTypeCtors, !.AbsEqvRhsTypeCtors, NewRhsTypeCtors),
|
|
set.fold(accumulate_modules, NewRhsTypeCtors, !Modules),
|
|
set.union(NewRhsTypeCtors, !AbsEqvRhsTypeCtors),
|
|
set.fold3(accumulate_abs_impl_exported_type_rhs(ImplTypeMap),
|
|
NewRhsTypeCtors, !AbsEqvRhsTypeCtors, set.init, _, !Modules)
|
|
; TypeDefn = parse_tree_du_type(Ctors, _, _) ->
|
|
% There must exist a foreign type alternative to this type. As the du
|
|
% type will be exported, we require the types of all the fields.
|
|
ctors_to_type_ctor_set(Ctors, set.init, RhsTypeCtors),
|
|
set.union(RhsTypeCtors, !ForeignDuFieldTypeCtors),
|
|
set.fold(accumulate_modules, RhsTypeCtors, !Modules)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred accumulate_modules(type_ctor::in,
|
|
set(module_name)::in, set(module_name)::out) is det.
|
|
|
|
accumulate_modules(TypeCtor, !Modules) :-
|
|
% NOTE: This assumes that everything has been module qualified.
|
|
TypeCtor = type_ctor(SymName, _Arity),
|
|
( sym_name_get_module_name(SymName, ModuleName) ->
|
|
set.insert(ModuleName, !Modules)
|
|
;
|
|
unexpected($module, $pred, "unknown type encountered")
|
|
).
|
|
|
|
% Given a type, return the set of user-defined type constructors
|
|
% occurring in it.
|
|
%
|
|
:- pred type_to_type_ctor_set(mer_type::in,
|
|
set(type_ctor)::in, set(type_ctor)::out) is det.
|
|
|
|
type_to_type_ctor_set(Type, !TypeCtors) :-
|
|
( type_to_ctor_and_args(Type, TypeCtor, Args) ->
|
|
TypeCtor = type_ctor(SymName, _Arity),
|
|
(
|
|
type_ctor_is_higher_order(TypeCtor, _, _, _)
|
|
->
|
|
% Higher-order types are builtin so just get the type_ctors
|
|
% from the arguments.
|
|
true
|
|
;
|
|
type_ctor_is_tuple(TypeCtor)
|
|
->
|
|
% Tuples are builtin so just get the type_ctors from the
|
|
% arguments.
|
|
true
|
|
;
|
|
( SymName = unqualified("int")
|
|
; SymName = unqualified("float")
|
|
; SymName = unqualified("string")
|
|
; SymName = unqualified("character")
|
|
)
|
|
->
|
|
% We don't need to import these modules as the types are builtin.
|
|
true
|
|
;
|
|
set.insert(TypeCtor, !TypeCtors)
|
|
),
|
|
list.foldl(type_to_type_ctor_set, Args, !TypeCtors)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred ctors_to_type_ctor_set(list(constructor)::in,
|
|
set(type_ctor)::in, set(type_ctor)::out) is det.
|
|
|
|
ctors_to_type_ctor_set([], !TypeCtors).
|
|
ctors_to_type_ctor_set([Ctor | Ctors], !TypeCtors) :-
|
|
Ctor = ctor(_, _, _, ConsArgs, _),
|
|
cons_args_to_type_ctor_set(ConsArgs, !TypeCtors),
|
|
ctors_to_type_ctor_set(Ctors, !TypeCtors).
|
|
|
|
:- pred cons_args_to_type_ctor_set(list(constructor_arg)::in,
|
|
set(type_ctor)::in, set(type_ctor)::out) is det.
|
|
|
|
cons_args_to_type_ctor_set([], !TypeCtors).
|
|
cons_args_to_type_ctor_set([Arg | Args], !TypeCtors) :-
|
|
Arg = ctor_arg(_, Type, _, _),
|
|
type_to_type_ctor_set(Type, !TypeCtors),
|
|
cons_args_to_type_ctor_set(Args, !TypeCtors).
|
|
|
|
:- type type_defn_map ==
|
|
multi_map(type_ctor, pair(type_defn, item_type_defn_info)).
|
|
:- type type_defn_pair ==
|
|
pair(type_ctor, pair(type_defn, item_type_defn_info)).
|
|
|
|
:- pred gather_type_defns(list(item)::in, list(item)::out, list(item)::out,
|
|
type_defn_map::out, type_defn_map::out) is det.
|
|
|
|
gather_type_defns(Items0, IntItems, ImplItems, IntTypesMap, ImplTypesMap) :-
|
|
gather_type_defns_2(no, Items0, [], RevIntItems, [], RevImplItems,
|
|
map.init, IntTypesMap, map.init, ImplTypesMap),
|
|
list.reverse(RevIntItems, IntItems),
|
|
list.reverse(RevImplItems, ImplItems).
|
|
|
|
:- pred gather_type_defns_2(bool::in, list(item)::in,
|
|
list(item)::in, list(item)::out, list(item)::in, list(item)::out,
|
|
type_defn_map::in, type_defn_map::out,
|
|
type_defn_map::in, type_defn_map::out) is det.
|
|
|
|
gather_type_defns_2(_, [], !RevIntItems, !RevImplItems,
|
|
!IntTypesMap, !ImplTypesMap).
|
|
gather_type_defns_2(!.InInterface, [Item | Items],
|
|
!RevIntItems, !RevImplItems, !IntTypesMap, !ImplTypesMap) :-
|
|
(
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
(
|
|
ModuleDefn = md_interface,
|
|
NewInInterface = yes
|
|
;
|
|
ModuleDefn = md_implementation,
|
|
NewInInterface = no
|
|
)
|
|
->
|
|
!:InInterface = NewInInterface
|
|
;
|
|
Item = item_type_defn(ItemTypeDefn)
|
|
->
|
|
ItemTypeDefn = item_type_defn_info(_, Name, Args, Body, _, _, _),
|
|
TypeCtor = type_ctor(Name, length(Args)),
|
|
(
|
|
!.InInterface = yes,
|
|
!:RevIntItems = [Item | !.RevIntItems],
|
|
gather_type_defn(TypeCtor, Body, ItemTypeDefn, !IntTypesMap)
|
|
;
|
|
!.InInterface = no,
|
|
% We don't add this to !RevImplItems yet -- we may be removing
|
|
% this item.
|
|
gather_type_defn(TypeCtor, Body, ItemTypeDefn, !ImplTypesMap)
|
|
)
|
|
;
|
|
(
|
|
!.InInterface = yes,
|
|
!:RevIntItems = [Item | !.RevIntItems]
|
|
;
|
|
!.InInterface = no,
|
|
!:RevImplItems = [Item | !.RevImplItems]
|
|
)
|
|
),
|
|
gather_type_defns_2(!.InInterface, Items, !RevIntItems, !RevImplItems,
|
|
!IntTypesMap, !ImplTypesMap).
|
|
|
|
:- pred gather_type_defn(type_ctor::in, type_defn::in, item_type_defn_info::in,
|
|
type_defn_map::in, type_defn_map::out) is det.
|
|
|
|
gather_type_defn(TypeCtor, Body, ItemTypeDefn, !DefnMap) :-
|
|
multi_map.set(TypeCtor, Body - ItemTypeDefn, !DefnMap).
|
|
|
|
:- pred get_requirements_of_impl_typeclasses(list(item)::in,
|
|
set(module_name)::out) is det.
|
|
|
|
get_requirements_of_impl_typeclasses(ImplItems, Modules) :-
|
|
list.foldl(get_requirements_of_impl_typeclass,
|
|
ImplItems, set.init, Modules).
|
|
|
|
:- pred get_requirements_of_impl_typeclass(item::in,
|
|
set(module_name)::in, set(module_name)::out) is det.
|
|
|
|
get_requirements_of_impl_typeclass(Item, !Modules) :-
|
|
(
|
|
Item = item_typeclass(ItemTypeClass),
|
|
Constraints = ItemTypeClass ^ tc_constraints,
|
|
list.foldl(get_requirements_of_impl_from_constraint, Constraints,
|
|
!Modules)
|
|
;
|
|
( Item = item_module_start(_)
|
|
; Item = item_module_end(_)
|
|
; Item = item_module_defn(_)
|
|
; Item = item_clause(_)
|
|
; Item = item_type_defn(_)
|
|
; Item = item_inst_defn(_)
|
|
; Item = item_mode_defn(_)
|
|
; Item = item_pred_decl(_)
|
|
; Item = item_mode_decl(_)
|
|
; Item = item_pragma(_)
|
|
; Item = item_promise(_)
|
|
; Item = item_instance(_)
|
|
; Item = item_initialise(_)
|
|
; Item = item_finalise(_)
|
|
; Item = item_mutable(_)
|
|
; Item = item_nothing(_)
|
|
)
|
|
).
|
|
|
|
:- pred get_requirements_of_impl_from_constraint(prog_constraint::in,
|
|
set(module_name)::in, set(module_name)::out) is det.
|
|
|
|
get_requirements_of_impl_from_constraint(Constraint, !Modules) :-
|
|
Constraint = constraint(ClassName, Args),
|
|
% NOTE: This assumes that everything has been module qualified.
|
|
( sym_name_get_module_name(ClassName, ModuleName) ->
|
|
set.insert(ModuleName, !Modules)
|
|
;
|
|
unexpected($module, $pred, "unknown typeclass in constraint")
|
|
),
|
|
get_modules_from_constraint_arg_types(Args, !Modules).
|
|
|
|
:- pred get_modules_from_constraint_arg_types(list(mer_type)::in,
|
|
set(module_name)::in, set(module_name)::out) is det.
|
|
|
|
get_modules_from_constraint_arg_types(ArgTypes, !Modules) :-
|
|
list.foldl(get_modules_from_constraint_arg_type, ArgTypes, !Modules).
|
|
|
|
:- pred get_modules_from_constraint_arg_type(mer_type::in,
|
|
set(module_name)::in, set(module_name)::out) is det.
|
|
|
|
get_modules_from_constraint_arg_type(ArgType, !Modules) :-
|
|
(
|
|
% Do nothing for these types - they cannot affect the set of
|
|
% implementation imports in an interface file.
|
|
( ArgType = type_variable(_, _)
|
|
; ArgType = builtin_type(_)
|
|
)
|
|
;
|
|
ArgType = defined_type(TypeName, Args, _),
|
|
( sym_name_get_module_name(TypeName, ModuleName) ->
|
|
set.insert(ModuleName, !Modules)
|
|
;
|
|
unexpected($module, $pred, "unknown type encountered")
|
|
),
|
|
get_modules_from_constraint_arg_types(Args, !Modules)
|
|
;
|
|
(
|
|
ArgType = tuple_type(Args, _)
|
|
;
|
|
ArgType = apply_n_type(_, Args, _)
|
|
;
|
|
ArgType = kinded_type(KindedType, _), Args = [KindedType]
|
|
;
|
|
ArgType = higher_order_type(Args0, MaybeRetType, _, _),
|
|
(
|
|
MaybeRetType = yes(RetType),
|
|
Args = [RetType | Args0]
|
|
;
|
|
MaybeRetType = no,
|
|
Args = Args0
|
|
)
|
|
),
|
|
get_modules_from_constraint_arg_types(Args, !Modules)
|
|
).
|
|
|
|
% Retain only those foreign_enum pragmas that correspond to types
|
|
% defined in the interface of a module.
|
|
%
|
|
:- pred strip_local_foreign_enum_pragmas(type_defn_map::in,
|
|
list(item)::in, list(item)::out) is det.
|
|
|
|
strip_local_foreign_enum_pragmas(IntTypeMap, !ImplItems) :-
|
|
list.filter(foreign_enum_is_local(IntTypeMap), !ImplItems).
|
|
|
|
:- pred foreign_enum_is_local(type_defn_map::in, item::in) is semidet.
|
|
|
|
foreign_enum_is_local(TypeDefnMap, Item) :-
|
|
(
|
|
Item = item_pragma(ItemPragma),
|
|
ItemPragma = item_pragma_info(_, Pragma, _, _),
|
|
Pragma = pragma_foreign_enum(FEInfo),
|
|
FEInfo = pragma_info_foreign_enum(_Lang, TypeCtor, _Values)
|
|
->
|
|
% We only add a pragma foreign_enum pragma to the interface file
|
|
% if it corresponds to a type _definition_ in the interface of the
|
|
% module.
|
|
map.search(TypeDefnMap, TypeCtor, Defns),
|
|
Defns \= [parse_tree_abstract_type(_) - _]
|
|
;
|
|
true
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred check_for_clauses_in_interface(list(item)::in, list(item)::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
check_for_clauses_in_interface([], [], !Specs).
|
|
check_for_clauses_in_interface([Item0 | Items0], Items, !Specs) :-
|
|
(
|
|
Item0 = item_clause(ItemClause0),
|
|
Context = ItemClause0 ^ cl_context,
|
|
Spec = clause_in_interface_warning("clause", Context),
|
|
!:Specs = [Spec | !.Specs],
|
|
check_for_clauses_in_interface(Items0, Items, !Specs)
|
|
;
|
|
Item0 = item_pragma(ItemPragma),
|
|
ItemPragma = item_pragma_info(_, Pragma, Context, _),
|
|
AllowedInInterface = pragma_allowed_in_interface(Pragma),
|
|
(
|
|
AllowedInInterface = no,
|
|
Spec = clause_in_interface_warning("pragma", Context),
|
|
!:Specs = [Spec | !.Specs],
|
|
check_for_clauses_in_interface(Items0, Items, !Specs)
|
|
;
|
|
AllowedInInterface = yes,
|
|
check_for_clauses_in_interface(Items0, Items1, !Specs),
|
|
Items = [Item0 | Items1]
|
|
)
|
|
;
|
|
( Item0 = item_module_start(_)
|
|
; Item0 = item_module_end(_)
|
|
; Item0 = item_module_defn(_)
|
|
; Item0 = item_type_defn(_)
|
|
; Item0 = item_inst_defn(_)
|
|
; Item0 = item_mode_defn(_)
|
|
; Item0 = item_pred_decl(_)
|
|
; Item0 = item_mode_decl(_)
|
|
; Item0 = item_promise(_)
|
|
; Item0 = item_typeclass(_)
|
|
; Item0 = item_instance(_)
|
|
; Item0 = item_initialise(_)
|
|
; Item0 = item_finalise(_)
|
|
; Item0 = item_mutable(_)
|
|
; Item0 = item_nothing(_)
|
|
),
|
|
check_for_clauses_in_interface(Items0, Items1, !Specs),
|
|
Items = [Item0 | Items1]
|
|
).
|
|
|
|
:- func clause_in_interface_warning(string, prog_context) = error_spec.
|
|
|
|
clause_in_interface_warning(ClauseOrPragma, Context) = Spec :-
|
|
Pieces = [words("Warning:"), words(ClauseOrPragma),
|
|
words("in module interface.")],
|
|
Spec = error_spec(severity_warning, phase_term_to_parse_tree,
|
|
[simple_msg(Context, [always(Pieces)])]).
|
|
|
|
% strip_clauses_private_interface is used when creating the private
|
|
% interface (`.int0') files for packages with sub-modules. It removes
|
|
% unnecessary items and separates interface and implementation items.
|
|
%
|
|
% The `.int0' file contains items which are available to any module in the
|
|
% interface section, and items which are only available to sub-modules in
|
|
% the implementation section. The term "private interface" is ambiguous:
|
|
% sometimes it refers to the `.int0' file which, as just explained,
|
|
% contains the public interface as well. The term "private interface
|
|
% proper" may be used to refer to the information in the implementation
|
|
% section of the `.int0' file.
|
|
%
|
|
% (Historically, the `.int0' file did not distinguish between the public
|
|
% and private interfaces.)
|
|
%
|
|
% We treat initialise and finalise declarations as special kinds of
|
|
% clause, since they should always be grouped together with the clauses
|
|
% and should not appear in private interfaces.
|
|
%
|
|
:- pred strip_clauses_private_interface(item::in, section::in, section::out,
|
|
list(item)::in, list(item)::out, list(item)::in, list(item)::out) is det.
|
|
|
|
strip_clauses_private_interface(Item, !Section, !InterfaceItems, !ImplItems) :-
|
|
(
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
(
|
|
ModuleDefn = md_interface,
|
|
!:Section = section_interface
|
|
;
|
|
ModuleDefn = md_implementation,
|
|
!:Section = section_implementation
|
|
;
|
|
ModuleDefn = md_import(_),
|
|
% Only imports listed in the implementation section will be
|
|
% directly imported by sub-modules. Import declarations in the
|
|
% interface section must be duplicated into the implementation
|
|
% section of the `.int0' file.
|
|
(
|
|
!.Section = section_interface,
|
|
list.cons(Item, !InterfaceItems),
|
|
list.cons(Item, !ImplItems)
|
|
;
|
|
!.Section = section_implementation,
|
|
list.cons(Item, !ImplItems)
|
|
)
|
|
)
|
|
->
|
|
true
|
|
;
|
|
(
|
|
Item = item_clause(_)
|
|
;
|
|
Item = item_pragma(ItemPragma),
|
|
ItemPragma = item_pragma_info(_, Pragma, _, _),
|
|
pragma_allowed_in_interface(Pragma) = no
|
|
;
|
|
Item = item_initialise(_)
|
|
;
|
|
Item = item_finalise(_)
|
|
)
|
|
->
|
|
true
|
|
;
|
|
(
|
|
!.Section = section_interface,
|
|
list.cons(Item, !InterfaceItems)
|
|
;
|
|
!.Section = section_implementation,
|
|
list.cons(Item, !ImplItems)
|
|
)
|
|
).
|
|
|
|
:- pred split_clauses_and_decls(list(item)::in,
|
|
list(item)::out, list(item)::out) is det.
|
|
|
|
split_clauses_and_decls([], [], []).
|
|
split_clauses_and_decls([Item | Items], !:ClauseItems, !:InterfaceItems) :-
|
|
(
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
( ModuleDefn = md_interface
|
|
; ModuleDefn = md_implementation
|
|
)
|
|
->
|
|
split_clauses_and_decls(Items, !:ClauseItems, !:InterfaceItems)
|
|
;
|
|
(
|
|
Item = item_clause(_)
|
|
;
|
|
Item = item_pragma(ItemPragma),
|
|
ItemPragma = item_pragma_info(_, Pragma, _, _),
|
|
pragma_allowed_in_interface(Pragma) = no
|
|
;
|
|
Item = item_initialise(_)
|
|
;
|
|
Item = item_finalise(_)
|
|
)
|
|
->
|
|
split_clauses_and_decls(Items, !:ClauseItems, !:InterfaceItems),
|
|
!:ClauseItems = [Item | !.ClauseItems]
|
|
;
|
|
split_clauses_and_decls(Items, !:ClauseItems, !:InterfaceItems),
|
|
!:InterfaceItems = [Item | !.InterfaceItems]
|
|
).
|
|
|
|
pragma_allowed_in_interface(Pragma) = Allowed :-
|
|
% XXX This comment is out of date.
|
|
% pragma `obsolete', `terminates', `does_not_terminate'
|
|
% `termination_info', `check_termination', `reserve_tag' and
|
|
% `foreign_enum' pragma declarations are supposed to go in the
|
|
% interface, but all other pragma declarations are implementation details
|
|
% only, and should go in the implementation.
|
|
|
|
(
|
|
( Pragma = pragma_foreign_code(_)
|
|
; Pragma = pragma_foreign_decl(_)
|
|
; Pragma = pragma_foreign_proc_export(_)
|
|
; Pragma = pragma_foreign_export_enum(_)
|
|
; Pragma = pragma_foreign_proc(_)
|
|
; Pragma = pragma_inline(_)
|
|
; Pragma = pragma_no_detism_warning(_)
|
|
; Pragma = pragma_no_inline(_)
|
|
; Pragma = pragma_fact_table(_)
|
|
; Pragma = pragma_tabled(_)
|
|
; Pragma = pragma_promise_pure(_)
|
|
; Pragma = pragma_promise_semipure(_)
|
|
; Pragma = pragma_promise_eqv_clauses(_)
|
|
; Pragma = pragma_unused_args(_)
|
|
; Pragma = pragma_exceptions(_)
|
|
; Pragma = pragma_trailing_info(_)
|
|
; Pragma = pragma_mm_tabling_info(_)
|
|
; Pragma = pragma_require_feature_set(_)
|
|
),
|
|
Allowed = no
|
|
;
|
|
% Note that the parser will strip out `source_file' pragmas anyway,
|
|
% and that `reserve_tag' and `direct_arg' must be in the interface iff
|
|
% the corresponding type definition is in the interface. This is
|
|
% checked in make_hlds.
|
|
( Pragma = pragma_foreign_enum(_)
|
|
; Pragma = pragma_foreign_import_module(_)
|
|
; Pragma = pragma_obsolete(_)
|
|
; Pragma = pragma_source_file(_)
|
|
; Pragma = pragma_reserve_tag(_)
|
|
; Pragma = pragma_type_spec(_)
|
|
; Pragma = pragma_termination_info(_)
|
|
; Pragma = pragma_termination2_info(_)
|
|
; Pragma = pragma_terminates(_)
|
|
; Pragma = pragma_does_not_terminate(_)
|
|
; Pragma = pragma_check_termination(_)
|
|
; Pragma = pragma_structure_sharing(_)
|
|
; Pragma = pragma_structure_reuse(_)
|
|
; Pragma = pragma_mode_check_clauses(_)
|
|
; Pragma = pragma_oisu(_)
|
|
),
|
|
Allowed = yes
|
|
).
|
|
|
|
check_for_no_exports(Globals, Items, ModuleName, !Specs, !IO) :-
|
|
globals.lookup_bool_option(Globals, warn_nothing_exported, ExportWarning),
|
|
(
|
|
ExportWarning = no
|
|
;
|
|
ExportWarning = yes,
|
|
get_interface(ModuleName, no, Items, InterfaceItems),
|
|
check_int_for_no_exports(Globals, InterfaceItems, ModuleName,
|
|
!Specs, !IO)
|
|
).
|
|
|
|
% Given a module name and a list of the items in that module's interface,
|
|
% this procedure checks if the module doesn't export anything, and if so,
|
|
% and --warn-nothing-exported is set, it returns a warning.
|
|
%
|
|
:- pred check_int_for_no_exports(globals::in, list(item)::in, module_name::in,
|
|
list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det.
|
|
|
|
check_int_for_no_exports(Globals, [], ModuleName, !Specs, !IO) :-
|
|
generate_no_exports_warning(Globals, ModuleName, WarnSpec, !IO),
|
|
!:Specs = [WarnSpec | !.Specs].
|
|
check_int_for_no_exports(Globals, [Item | Items], ModuleName, !Specs, !IO) :-
|
|
(
|
|
(
|
|
Item = item_nothing(_)
|
|
;
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
ModuleDefn \= md_include_module(_)
|
|
)
|
|
->
|
|
% Nothing useful - keep searching.
|
|
check_int_for_no_exports(Globals, Items, ModuleName, !Specs, !IO)
|
|
;
|
|
% We found something useful - don't issue the warning.
|
|
true
|
|
).
|
|
|
|
:- pred generate_no_exports_warning(globals::in, module_name::in,
|
|
error_spec::out, io::di, io::uo) is det.
|
|
|
|
generate_no_exports_warning(Globals, ModuleName, Spec, !IO) :-
|
|
% XXX The FileName should be passed down to here; we shouldn't have to
|
|
% compute it again.
|
|
module_name_to_file_name(Globals, ModuleName, ".m", do_not_create_dirs,
|
|
FileName, !IO),
|
|
% XXX We should use the module declaration's context, not the arbitrary
|
|
% line number 1.
|
|
Context = context_init(FileName, 1),
|
|
Severity = severity_conditional(warn_nothing_exported, yes,
|
|
severity_warning, no),
|
|
Component = option_is_set(warn_nothing_exported, yes,
|
|
[always([words("Warning: interface for module"),
|
|
sym_name(ModuleName), words("does not export anything.")]),
|
|
verbose_only(
|
|
[words("To be useful, a module should export something."),
|
|
words("A file should contain at least one declaration"),
|
|
words("other than"), fixed("`:- import_module'"),
|
|
words("in its interface section(s)."),
|
|
words("This would normally be a"),
|
|
fixed("`:- pred',"), fixed("`:- func',"),
|
|
fixed("`:- type',"), fixed("`:- inst'"),
|
|
fixed("or `:- mode'"), words("declaration.")])
|
|
]),
|
|
Msg = simple_msg(Context, [Component]),
|
|
Spec = error_spec(Severity, phase_term_to_parse_tree, [Msg]).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred write_interface_file(globals::in, file_name::in, module_name::in,
|
|
string::in, maybe(timestamp)::in, list(item)::in, io::di, io::uo) is det.
|
|
|
|
write_interface_file(Globals, _SourceFileName, ModuleName, Suffix,
|
|
MaybeTimestamp, InterfaceItems0, !IO) :-
|
|
% Create (e.g.) `foo.int.tmp'.
|
|
string.append(Suffix, ".tmp", TmpSuffix),
|
|
module_name_to_file_name(Globals, ModuleName, Suffix,
|
|
do_create_dirs, OutputFileName, !IO),
|
|
module_name_to_file_name(Globals, ModuleName, TmpSuffix,
|
|
do_not_create_dirs, TmpOutputFileName, !IO),
|
|
|
|
globals.set_option(line_numbers, bool(no), Globals, NoLineNumGlobals),
|
|
globals.lookup_bool_option(NoLineNumGlobals, generate_item_version_numbers,
|
|
GenerateVersionNumbers),
|
|
io_get_disable_generate_item_version_numbers(DisableVersionNumbers, !IO),
|
|
(
|
|
GenerateVersionNumbers = yes,
|
|
DisableVersionNumbers = no
|
|
->
|
|
% Find the timestamp of the current module.
|
|
(
|
|
MaybeTimestamp = yes(Timestamp),
|
|
|
|
% Read in the previous version of the file.
|
|
read_module_ignore_errors(NoLineNumGlobals, ModuleName, Suffix,
|
|
"Reading old interface for module",
|
|
do_search, do_not_return_timestamp, OldItems, OldError,
|
|
_OldIntFileName, _OldTimestamp, !IO),
|
|
(
|
|
OldError = no_module_errors,
|
|
MaybeOldItems = yes(OldItems)
|
|
;
|
|
( OldError = some_module_errors
|
|
; OldError = fatal_module_errors
|
|
),
|
|
% If we can't read in the old file, the timestamps will
|
|
% all be set to the modification time of the source file.
|
|
MaybeOldItems = no
|
|
),
|
|
recompilation.version.compute_version_numbers(Timestamp,
|
|
InterfaceItems0, MaybeOldItems, VersionNumbers),
|
|
VersionNumberItemModuleDefn = item_module_defn_info(
|
|
md_version_numbers(ModuleName, VersionNumbers),
|
|
term.context_init, -1),
|
|
VersionNumberItem = item_module_defn(VersionNumberItemModuleDefn),
|
|
(
|
|
InterfaceItems0 = [FirstItem | InterfaceItems1],
|
|
FirstItem = item_module_defn(FirstItemModuleDefn),
|
|
FirstItemModuleDefn =
|
|
item_module_defn_info(FirstModuleDefn, _, _),
|
|
FirstModuleDefn = md_interface
|
|
->
|
|
InterfaceItems = [FirstItem, VersionNumberItem
|
|
| InterfaceItems1]
|
|
;
|
|
InterfaceItems = [make_pseudo_decl(md_interface),
|
|
VersionNumberItem | InterfaceItems0]
|
|
)
|
|
;
|
|
MaybeTimestamp = no,
|
|
unexpected($module, $pred,
|
|
"with `--smart-recompilation', timestamp not read")
|
|
)
|
|
;
|
|
InterfaceItems = InterfaceItems0
|
|
),
|
|
convert_to_mercury(NoLineNumGlobals, ModuleName, TmpOutputFileName,
|
|
InterfaceItems, !IO),
|
|
% Start using the original globals again.
|
|
update_interface(Globals, OutputFileName, !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
grab_imported_modules(Globals, SourceFileName, SourceFileModuleName,
|
|
ModuleName, NestedChildren, HaveReadModuleMap, MaybeTimestamp, Items0,
|
|
!:Module, !IO) :-
|
|
% Find out which modules this one depends on.
|
|
AncestorModules = get_ancestors(ModuleName),
|
|
get_dependencies_int_imp(Items0, IntImportedModules0, IntUsedModules0,
|
|
ImpImportedModules0, ImpUsedModules0),
|
|
|
|
ImportedModules0 = IntImportedModules0 ++ ImpImportedModules0,
|
|
UsedModules0 = IntUsedModules0 ++ ImpUsedModules0,
|
|
|
|
some [!Specs] (
|
|
!:Specs = [],
|
|
|
|
module_name_to_file_name(Globals, ModuleName, ".m", do_not_create_dirs,
|
|
FileName, !IO),
|
|
warn_if_import_self_or_ancestor(ModuleName, FileName, AncestorModules,
|
|
ImportedModules0, UsedModules0, !Specs),
|
|
|
|
warn_if_duplicate_use_import_decls(ModuleName, FileName,
|
|
IntImportedModules0, IntImportedModules1,
|
|
IntUsedModules0, IntUsedModules1,
|
|
ImpImportedModules0, ImpImportedModules,
|
|
ImpUsedModules0, ImpUsedModules, !Specs),
|
|
|
|
get_fact_table_dependencies(Items0, FactDeps),
|
|
get_interface_and_implementation(ModuleName, no, Items0,
|
|
InterfaceItems, ImplItems),
|
|
get_children(InterfaceItems, PublicChildren),
|
|
(
|
|
MaybeTimestamp = yes(Timestamp),
|
|
MaybeTimestamps = yes(map.singleton(ModuleName,
|
|
module_timestamp(".m", Timestamp, may_be_unqualified)))
|
|
;
|
|
MaybeTimestamp = no,
|
|
MaybeTimestamps = no
|
|
|
|
),
|
|
init_module_and_imports(SourceFileName, SourceFileModuleName,
|
|
ModuleName, Items0, !.Specs, PublicChildren, NestedChildren,
|
|
FactDeps, MaybeTimestamps, !:Module),
|
|
|
|
% If this module has any separately-compiled sub-modules, 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
|
|
% `implementation_but_exported_to_submodules' section.
|
|
|
|
get_children(Items0, Children),
|
|
(
|
|
Children = [],
|
|
Items1 = Items0
|
|
;
|
|
Children = [_ | _],
|
|
split_clauses_and_decls(ImplItems, Clauses, ImplDecls),
|
|
Items1 =
|
|
[make_pseudo_decl(md_interface) | InterfaceItems] ++
|
|
[make_pseudo_decl(md_implementation_but_exported_to_submodules)
|
|
| ImplDecls] ++
|
|
[make_pseudo_decl(md_implementation) | Clauses],
|
|
!Module ^ mai_items_cord := cord.from_list(Items1)
|
|
),
|
|
|
|
% Add `builtin' and `private_builtin' to the list of imported modules.
|
|
add_implicit_imports(Items1, Globals,
|
|
IntImportedModules1, IntImportedModules2,
|
|
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, HaveReadModuleMap,
|
|
AncestorModules,
|
|
make_pseudo_decl(md_imported(import_locn_interface)),
|
|
make_pseudo_decl(md_imported(
|
|
import_locn_ancestor_private_interface_proper)),
|
|
IntImportedModules2, IntImportedModules,
|
|
IntUsedModules2, IntUsedModules, !Module, !IO),
|
|
|
|
% Process the modules imported using `import_module'.
|
|
% Uses of these items do not need module qualifiers.
|
|
IntIndirectImports0 = [],
|
|
IntImpIndirectImports0 = [],
|
|
process_module_long_interfaces(Globals, HaveReadModuleMap,
|
|
may_be_unqualified, IntImportedModules, ".int",
|
|
make_pseudo_decl(md_imported(import_locn_interface)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
IntIndirectImports0, IntIndirectImports1,
|
|
IntImpIndirectImports0, IntImpIndirectImports1,
|
|
!Module, !IO),
|
|
|
|
ImpIndirectImports0 = [],
|
|
ImpImpIndirectImports0 = [],
|
|
process_module_long_interfaces(Globals, HaveReadModuleMap,
|
|
may_be_unqualified, ImpImportedModules, ".int",
|
|
make_pseudo_decl(md_imported(import_locn_implementation)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
ImpIndirectImports0, ImpIndirectImports1,
|
|
ImpImpIndirectImports0, ImpImpIndirectImports1,
|
|
!Module, !IO),
|
|
|
|
% Process the modules imported using `use_module' .
|
|
process_module_long_interfaces(Globals, HaveReadModuleMap,
|
|
must_be_qualified, IntUsedModules, ".int",
|
|
make_pseudo_decl(md_used(import_locn_interface)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
IntIndirectImports1, IntIndirectImports,
|
|
IntImpIndirectImports1, IntImpIndirectImports2,
|
|
!Module, !IO),
|
|
process_module_long_interfaces(Globals, HaveReadModuleMap,
|
|
must_be_qualified, ImpUsedModules, ".int",
|
|
make_pseudo_decl(md_used(import_locn_implementation)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
ImpIndirectImports1, ImpIndirectImports,
|
|
ImpImpIndirectImports1, ImpImpIndirectImports2,
|
|
!Module, !IO),
|
|
|
|
% Process the short interfaces for indirectly imported modules.
|
|
% The short interfaces are treated as if they are imported
|
|
% using `use_module'.
|
|
append_pseudo_decl(md_transitively_imported, !Module),
|
|
process_module_short_interfaces_transitively(Globals,
|
|
HaveReadModuleMap, IntIndirectImports, ".int2",
|
|
make_pseudo_decl(md_used(import_locn_interface)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
IntImpIndirectImports2, IntImpIndirectImports, !Module, !IO),
|
|
process_module_short_interfaces_transitively(Globals,
|
|
HaveReadModuleMap, ImpIndirectImports, ".int2",
|
|
make_pseudo_decl(md_used(import_locn_implementation)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
ImpImpIndirectImports2, ImpImpIndirectImports, !Module, !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,
|
|
HaveReadModuleMap, IntImpIndirectImports, ".int2",
|
|
make_pseudo_decl(md_abstract_imported),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
!Module, !IO),
|
|
process_module_short_interfaces_and_impls_transitively(Globals,
|
|
HaveReadModuleMap, ImpImpIndirectImports, ".int2",
|
|
make_pseudo_decl(md_abstract_imported),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
!Module, !IO),
|
|
|
|
module_and_imports_get_results(!.Module, Items, _, _),
|
|
check_imports_accessibility(ModuleName,
|
|
IntImportedModules ++ IntUsedModules ++
|
|
ImpImportedModules ++ ImpUsedModules, Items, [], AccessSpecs),
|
|
module_and_imports_add_specs(AccessSpecs, !Module)
|
|
).
|
|
|
|
% grab_unqual_imported_modules:
|
|
%
|
|
% Like grab_imported_modules, but gets the `.int3' files
|
|
% instead of the `.int' and `.int2' files.
|
|
%
|
|
grab_unqual_imported_modules(Globals, SourceFileName, SourceFileModuleName,
|
|
ModuleName, Items0, !:Module, !IO) :-
|
|
% Find out which modules this one depends on.
|
|
ParentDeps = get_ancestors(ModuleName),
|
|
get_dependencies_int_imp(Items0, IntImportDeps0, IntUseDeps0,
|
|
ImpImportDeps, ImpUseDeps),
|
|
|
|
% Construct the initial module import structure.
|
|
init_module_and_imports(SourceFileName, SourceFileModuleName, ModuleName,
|
|
Items0, [], [], [], [], no, !:Module),
|
|
|
|
% Add `builtin' and `private_builtin' to the imported modules.
|
|
add_implicit_imports(Items0, Globals,
|
|
IntImportDeps0, IntImportDeps, IntUseDeps0, IntUseDeps),
|
|
|
|
% Get the .int3s and .int0s that the current module depends on.
|
|
map.init(HaveReadModuleMap),
|
|
|
|
% First the .int0s for parent modules.
|
|
process_module_private_interfaces(Globals, HaveReadModuleMap, ParentDeps,
|
|
make_pseudo_decl(md_imported(import_locn_interface)),
|
|
make_pseudo_decl(md_imported(
|
|
import_locn_ancestor_private_interface_proper)),
|
|
[], ParentImportDeps, [], ParentUseDeps, !Module, !IO),
|
|
|
|
% Then the .int3s for `:- import'-ed modules.
|
|
process_module_long_interfaces(Globals, HaveReadModuleMap,
|
|
may_be_unqualified, ParentImportDeps, ".int3",
|
|
make_pseudo_decl(md_imported(import_locn_ancestor)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
[], IntIndirectImportDeps0, [], _, !Module, !IO),
|
|
process_module_long_interfaces(Globals, HaveReadModuleMap,
|
|
may_be_unqualified, IntImportDeps, ".int3",
|
|
make_pseudo_decl(md_imported(import_locn_interface)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
IntIndirectImportDeps0, IntIndirectImportDeps1,
|
|
[], _, !Module, !IO),
|
|
process_module_long_interfaces(Globals, HaveReadModuleMap,
|
|
may_be_unqualified, ImpImportDeps, ".int3",
|
|
make_pseudo_decl(md_imported(import_locn_implementation)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
[], ImpIndirectImportDeps0,
|
|
[], _, !Module, !IO),
|
|
|
|
% Then (after appropriate `:- used' decls) the .int3s for `:- use'-ed
|
|
% modules.
|
|
process_module_long_interfaces(Globals, HaveReadModuleMap,
|
|
may_be_unqualified, ParentUseDeps, ".int3",
|
|
make_pseudo_decl(md_imported(import_locn_ancestor)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
IntIndirectImportDeps1, IntIndirectImportDeps2,
|
|
[], _, !Module, !IO),
|
|
process_module_long_interfaces(Globals, HaveReadModuleMap,
|
|
must_be_qualified, IntUseDeps, ".int3",
|
|
make_pseudo_decl(md_used(import_locn_interface)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
IntIndirectImportDeps2, IntIndirectImportDeps,
|
|
[], _, !Module, !IO),
|
|
process_module_long_interfaces(Globals, HaveReadModuleMap,
|
|
must_be_qualified, ImpUseDeps, ".int3",
|
|
make_pseudo_decl(md_used(import_locn_implementation)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
ImpIndirectImportDeps0, ImpIndirectImportDeps,
|
|
[], _, !Module, !IO),
|
|
|
|
% Then (after appropriate `:- used' decl) the .int3s for indirectly
|
|
% imported modules.
|
|
process_module_short_interfaces_transitively(Globals, HaveReadModuleMap,
|
|
IntIndirectImportDeps, ".int3",
|
|
make_pseudo_decl(md_used(import_locn_interface)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
[], _, !Module, !IO),
|
|
|
|
process_module_short_interfaces_transitively(Globals, HaveReadModuleMap,
|
|
ImpIndirectImportDeps, ".int3",
|
|
make_pseudo_decl(md_used(import_locn_implementation)),
|
|
make_pseudo_decl(md_abstract_imported),
|
|
[], _, !Module, !IO),
|
|
|
|
module_and_imports_get_results(!.Module, Items, _, _),
|
|
check_imports_accessibility(ModuleName,
|
|
IntImportDeps ++ IntUseDeps ++ ImpImportDeps ++ ImpUseDeps,
|
|
Items, [], AccessSpecs),
|
|
module_and_imports_add_specs(AccessSpecs, !Module).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
append_pseudo_decl(PseudoDecl, !Module) :-
|
|
module_and_imports_add_items(cord.singleton(make_pseudo_decl(PseudoDecl)),
|
|
!Module).
|
|
|
|
make_pseudo_decl(PseudoDecl) = Item :-
|
|
ItemModuleDefn = item_module_defn_info(PseudoDecl, term.context_init, -1),
|
|
Item = item_module_defn(ItemModuleDefn).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Warn if a module imports itself, or an ancestor.
|
|
%
|
|
:- pred warn_if_import_self_or_ancestor(module_name::in, string::in,
|
|
list(module_name)::in, list(module_name)::in, list(module_name)::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
warn_if_import_self_or_ancestor(ModuleName, FileName, AncestorModules,
|
|
ImportedModules, UsedModules, !Specs) :-
|
|
IsImportedAncestor = (pred(Import::out) is nondet :-
|
|
list.member(Import, AncestorModules),
|
|
( list.member(Import, ImportedModules)
|
|
; list.member(Import, UsedModules)
|
|
)
|
|
),
|
|
solutions.aggregate(IsImportedAncestor,
|
|
warn_imported_ancestor(ModuleName, FileName), !Specs),
|
|
(
|
|
( list.member(ModuleName, ImportedModules)
|
|
; list.member(ModuleName, UsedModules)
|
|
)
|
|
->
|
|
term.context_init(FileName, 1, Context),
|
|
SelfPieces = [words("Warning: module"),
|
|
sym_name(ModuleName), words("imports itself!")],
|
|
SelfMsg = simple_msg(Context,
|
|
[option_is_set(warn_simple_code, yes, [always(SelfPieces)])]),
|
|
Severity = severity_conditional(warn_simple_code, yes,
|
|
severity_warning, no),
|
|
SelfSpec = error_spec(Severity, phase_parse_tree_to_hlds,
|
|
[SelfMsg]),
|
|
!:Specs = [SelfSpec | !.Specs]
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred warn_imported_ancestor(module_name::in, string::in, module_name::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
warn_imported_ancestor(ModuleName, FileName, AncestorName, !Specs) :-
|
|
term.context_init(FileName, 1, Context),
|
|
MainPieces = [words("Module"), sym_name(ModuleName),
|
|
words("imports its own ancestor, module"),
|
|
sym_name(AncestorName), words(".")],
|
|
VerbosePieces = [words("Every sub-module"),
|
|
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(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 all 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, string::in,
|
|
list(module_name)::in, list(module_name)::out,
|
|
list(module_name)::in, list(module_name)::out,
|
|
list(module_name)::in, list(module_name)::out,
|
|
list(module_name)::in, list(module_name)::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
warn_if_duplicate_use_import_decls(ModuleName, FileName,
|
|
IntImportedModules0, IntImportedModules,
|
|
IntUsedModules0, IntUsedModules,
|
|
ImpImportedModules0, ImpImportedModules,
|
|
ImpUsedModules0, ImpUsedModules, !Specs) :-
|
|
|
|
do_warn_if_duplicate_use_import_decls(ModuleName, FileName,
|
|
IntImportedModules0, IntImportedModules1,
|
|
IntUsedModules0, IntUsedModules, !Specs),
|
|
do_warn_if_duplicate_use_import_decls(ModuleName, FileName,
|
|
IntImportedModules1, IntImportedModules,
|
|
ImpUsedModules0, ImpUsedModules1, !Specs),
|
|
|
|
do_warn_if_duplicate_use_import_decls(ModuleName, FileName,
|
|
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, string::in,
|
|
list(module_name)::in, list(module_name)::out,
|
|
list(module_name)::in, list(module_name)::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
do_warn_if_duplicate_use_import_decls(_ModuleName, FileName,
|
|
!ImportedModules, !UsedModules, !Specs) :-
|
|
set.list_to_set(!.ImportedModules, ImportedSet),
|
|
set.list_to_set(!.UsedModules, UsedSet),
|
|
set.intersect(ImportedSet, UsedSet, BothSet),
|
|
( set.empty(BothSet) ->
|
|
true
|
|
;
|
|
set.to_sorted_list(BothSet, BothList),
|
|
|
|
term.context_init(FileName, 1, Context),
|
|
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 `:- import_module'"),
|
|
words("`:- use_module' 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.'
|
|
list.delete_elems(!.UsedModules, BothList, !:UsedModules)
|
|
).
|
|
|
|
:- func wrap_symname(module_name) = format_component.
|
|
|
|
wrap_symname(ModuleName) = sym_name(ModuleName).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
maybe_read_dependency_file(Globals, ModuleName, MaybeTransOptDeps, !IO) :-
|
|
globals.lookup_bool_option(Globals, transitive_optimization, TransOpt),
|
|
(
|
|
TransOpt = yes,
|
|
globals.lookup_bool_option(Globals, verbose, Verbose),
|
|
module_name_to_file_name(Globals, ModuleName, ".d", do_not_create_dirs,
|
|
DependencyFileName, !IO),
|
|
maybe_write_string(Verbose, "% Reading auto-dependency file `", !IO),
|
|
maybe_write_string(Verbose, DependencyFileName, !IO),
|
|
maybe_write_string(Verbose, "'...", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
io.open_input(DependencyFileName, OpenResult, !IO),
|
|
(
|
|
OpenResult = ok(Stream),
|
|
io.set_input_stream(Stream, OldStream, !IO),
|
|
module_name_to_file_name(Globals, ModuleName, ".trans_opt_date",
|
|
do_not_create_dirs, TransOptDateFileName0, !IO),
|
|
string.to_char_list(TransOptDateFileName0, TransOptDateFileName),
|
|
SearchPattern = TransOptDateFileName ++ [' ', ':'],
|
|
read_dependency_file_find_start(SearchPattern, FindResult, !IO),
|
|
(
|
|
FindResult = yes,
|
|
read_dependency_file_get_modules(TransOptDeps, !IO),
|
|
MaybeTransOptDeps = yes(TransOptDeps)
|
|
;
|
|
FindResult = no,
|
|
% error reading .d file
|
|
MaybeTransOptDeps = no
|
|
),
|
|
io.set_input_stream(OldStream, _, !IO),
|
|
io.close_input(Stream, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO)
|
|
;
|
|
OpenResult = error(IOError),
|
|
maybe_write_string(Verbose, " failed.\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
io.error_message(IOError, IOErrorMessage),
|
|
string.append_list(["error opening file `", DependencyFileName,
|
|
"' for input: ", IOErrorMessage], Message),
|
|
report_error(Message, !IO),
|
|
MaybeTransOptDeps = no
|
|
)
|
|
;
|
|
TransOpt = no,
|
|
MaybeTransOptDeps = no
|
|
).
|
|
|
|
% Read lines from the dependency file (module.d) until one is found
|
|
% which begins with SearchPattern.
|
|
%
|
|
:- pred read_dependency_file_find_start(list(char)::in, bool::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_dependency_file_find_start(SearchPattern, Success, !IO) :-
|
|
io.read_line(Result, !IO),
|
|
( Result = ok(CharList) ->
|
|
( list.append(SearchPattern, _, CharList) ->
|
|
% Have found the start.
|
|
Success = yes
|
|
;
|
|
read_dependency_file_find_start(SearchPattern, Success, !IO)
|
|
)
|
|
;
|
|
Success = no
|
|
).
|
|
|
|
% Read lines until one is found which does not contain whitespace
|
|
% followed by a word which ends in .trans_opt. Remove the .trans_opt
|
|
% ending from all the words which are read in and return the resulting
|
|
% list of modules.
|
|
%
|
|
:- pred read_dependency_file_get_modules(list(module_name)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_dependency_file_get_modules(TransOptDeps, !IO) :-
|
|
io.read_line(Result, !IO),
|
|
(
|
|
Result = ok(CharList0),
|
|
% Remove any whitespace from the beginning of the line,
|
|
% then take all characters until another whitespace occurs.
|
|
list.takewhile(char.is_whitespace, CharList0, _, CharList1),
|
|
NotIsWhitespace = (pred(Char::in) is semidet :-
|
|
\+ char.is_whitespace(Char)
|
|
),
|
|
list.takewhile(NotIsWhitespace, CharList1, CharList, _),
|
|
string.from_char_list(CharList, FileName0),
|
|
string.remove_suffix(FileName0, ".trans_opt", FileName)
|
|
->
|
|
( string.append("Mercury/trans_opts/", BaseFileName, FileName) ->
|
|
ModuleFileName = BaseFileName
|
|
;
|
|
ModuleFileName = FileName
|
|
),
|
|
file_name_to_module_name(ModuleFileName, Module),
|
|
read_dependency_file_get_modules(TransOptDeps0, !IO),
|
|
TransOptDeps = [Module | TransOptDeps0]
|
|
;
|
|
TransOptDeps = []
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
generate_module_dependencies(Globals, ModuleName, !IO) :-
|
|
map.init(DepsMap),
|
|
generate_dependencies(Globals, output_all_dependencies, do_not_search,
|
|
ModuleName, DepsMap, !IO).
|
|
|
|
generate_file_dependencies(Globals, FileName, !IO) :-
|
|
build_deps_map(Globals, FileName, ModuleName, DepsMap, !IO),
|
|
generate_dependencies(Globals, output_all_dependencies, do_not_search,
|
|
ModuleName, DepsMap, !IO).
|
|
|
|
generate_module_dependency_file(Globals, ModuleName, !IO) :-
|
|
map.init(DepsMap),
|
|
generate_dependencies(Globals, output_d_file_only, do_search, ModuleName,
|
|
DepsMap, !IO).
|
|
|
|
generate_file_dependency_file(Globals, FileName, !IO) :-
|
|
build_deps_map(Globals, FileName, ModuleName, DepsMap, !IO),
|
|
generate_dependencies(Globals, output_d_file_only, do_search, ModuleName,
|
|
DepsMap, !IO).
|
|
|
|
:- pred build_deps_map(globals::in, file_name::in,
|
|
module_name::out, deps_map::out, io::di, io::uo) is det.
|
|
|
|
build_deps_map(Globals, FileName, ModuleName, DepsMap, !IO) :-
|
|
% Read in the top-level file (to figure out its module name).
|
|
read_module_from_file(Globals, FileName, ".m", "Reading file",
|
|
do_not_search, do_not_return_timestamp, Items, Specs0, Error,
|
|
ModuleName, _, !IO),
|
|
SourceFileName = FileName ++ ".m",
|
|
split_into_submodules(ModuleName, Items, SubModuleList, Specs0, Specs),
|
|
% XXX _NumErrors
|
|
write_error_specs(Specs, Globals, 0, _NumWarnings, 0, _NumErrors, !IO),
|
|
assoc_list.keys(SubModuleList, SubModuleNames),
|
|
list.map(init_dependencies(SourceFileName, ModuleName, SubModuleNames,
|
|
[], Error, Globals), SubModuleList, ModuleImportsList),
|
|
map.init(DepsMap0),
|
|
list.foldl(insert_into_deps_map, ModuleImportsList, DepsMap0, DepsMap).
|
|
|
|
:- type generate_dependencies_mode
|
|
---> output_d_file_only
|
|
; output_all_dependencies.
|
|
|
|
:- pred generate_dependencies(globals::in, generate_dependencies_mode::in,
|
|
maybe_search::in, module_name::in, deps_map::in, io::di, io::uo) is det.
|
|
|
|
generate_dependencies(Globals, Mode, Search, ModuleName, DepsMap0, !IO) :-
|
|
% First, build up a map of the dependencies.
|
|
generate_deps_map(Globals, ModuleName, Search, DepsMap0, DepsMap, !IO),
|
|
|
|
% Check whether we could read the main `.m' file.
|
|
|
|
map.lookup(DepsMap, ModuleName, ModuleDep),
|
|
ModuleDep = deps(_, ModuleImports),
|
|
Error = ModuleImports ^ mai_error,
|
|
(
|
|
Error = fatal_module_errors,
|
|
ModuleString = sym_name_to_string(ModuleName),
|
|
string.append_list(["can't read source file for module `",
|
|
ModuleString, "'."], Message),
|
|
report_error(Message, !IO)
|
|
;
|
|
( Error = no_module_errors
|
|
; Error = some_module_errors
|
|
),
|
|
(
|
|
Mode = output_d_file_only
|
|
;
|
|
Mode = output_all_dependencies,
|
|
module_and_imports_get_source_file_name(ModuleImports,
|
|
SourceFileName),
|
|
generate_dependencies_write_dv_file(Globals, SourceFileName,
|
|
ModuleName, DepsMap, !IO),
|
|
generate_dependencies_write_dep_file(Globals, SourceFileName,
|
|
ModuleName, DepsMap, !IO)
|
|
),
|
|
|
|
% Compute the interface deps graph and the implementation deps
|
|
% graph from the deps map.
|
|
|
|
digraph.init(IntDepsGraph0),
|
|
digraph.init(ImplDepsGraph0),
|
|
map.values(DepsMap, DepsList),
|
|
deps_list_to_deps_graph(DepsList, DepsMap, IntDepsGraph0, IntDepsGraph,
|
|
ImplDepsGraph0, ImplDepsGraph),
|
|
maybe_output_imports_graph(Globals, ModuleName,
|
|
IntDepsGraph, ImplDepsGraph, !IO),
|
|
|
|
% Compute the trans-opt deps ordering, by doing an approximate
|
|
% topological sort of the implementation deps, and then finding
|
|
% the subset of those for which of those we have (or can make)
|
|
% trans-opt files.
|
|
|
|
digraph.atsort(ImplDepsGraph, ImplDepsOrdering0),
|
|
maybe_output_module_order(Globals, ModuleName, ImplDepsOrdering0, !IO),
|
|
list.map(set.to_sorted_list, ImplDepsOrdering0, ImplDepsOrdering),
|
|
list.condense(ImplDepsOrdering, TransOptDepsOrdering0),
|
|
globals.lookup_accumulating_option(Globals, intermod_directories,
|
|
IntermodDirs),
|
|
get_opt_deps(Globals, yes, TransOptDepsOrdering0, IntermodDirs,
|
|
".trans_opt", TransOptDepsOrdering, !IO),
|
|
|
|
trace [compiletime(flag("deps_graph")), runtime(env("DEPS_GRAPH")),
|
|
io(!TIO)]
|
|
(
|
|
digraph.to_assoc_list(ImplDepsGraph, ImplDepsAL),
|
|
io.print("ImplDepsAL:\n", !TIO),
|
|
io.write_list(ImplDepsAL, "\n", print, !TIO),
|
|
io.nl(!TIO)
|
|
),
|
|
|
|
% Compute the indirect dependencies: they are equal to the composition
|
|
% of the implementation dependencies with the transitive closure of the
|
|
% implementation dependencies. (We used to take the transitive closure
|
|
% of the interface dependencies, but we now include implementation
|
|
% details in the interface files).
|
|
|
|
digraph.tc(ImplDepsGraph, TransImplDepsGraph),
|
|
digraph.compose(ImplDepsGraph, TransImplDepsGraph, IndirectDepsGraph),
|
|
|
|
% Compute the indirect optimization dependencies: indirect
|
|
% dependencies including those via `.opt' or `.trans_opt' files.
|
|
% Actually we can't compute that, since we don't know
|
|
% which modules the `.opt' files will import!
|
|
% Instead, we need to make a conservative (over-)approximation,
|
|
% and assume that the each module's `.opt' file might import any
|
|
% of that module's implementation dependencies; in actual fact,
|
|
% it will be some subset of that.
|
|
|
|
digraph.tc(ImplDepsGraph, IndirectOptDepsGraph),
|
|
|
|
(
|
|
Mode = output_d_file_only,
|
|
DFilesToWrite = [ModuleDep]
|
|
;
|
|
Mode = output_all_dependencies,
|
|
DFilesToWrite = DepsList
|
|
),
|
|
generate_dependencies_write_d_files(Globals, DFilesToWrite,
|
|
IntDepsGraph, ImplDepsGraph,
|
|
IndirectDepsGraph, IndirectOptDepsGraph,
|
|
TransOptDepsOrdering, DepsMap, !IO)
|
|
),
|
|
|
|
% For Java, the main target is actually a shell script which will
|
|
% set CLASSPATH appropriately and invoke java on the appropriate
|
|
% .class file. Rather than generating an Mmake rule to build this
|
|
% file when it is needed, we just generate this file "mmake depend"
|
|
% time, since that is simpler and probably more efficient anyway.
|
|
|
|
globals.get_target(Globals, Target),
|
|
(
|
|
Target = target_java,
|
|
Mode = output_all_dependencies
|
|
->
|
|
create_java_shell_script(Globals, ModuleName, _Succeeded, !IO)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred maybe_output_imports_graph(globals::in, module_name::in,
|
|
digraph(sym_name)::in, digraph(sym_name)::in,
|
|
io::di, io::uo) is det.
|
|
|
|
maybe_output_imports_graph(Globals, Module, IntDepsGraph, ImplDepsGraph, !IO) :-
|
|
globals.lookup_bool_option(Globals, imports_graph, ImportsGraph),
|
|
globals.lookup_bool_option(Globals, verbose, Verbose),
|
|
(
|
|
ImportsGraph = yes,
|
|
module_name_to_file_name(Globals, Module, ".imports_graph",
|
|
do_create_dirs, FileName, !IO),
|
|
maybe_write_string(Verbose, "% Creating imports graph file `", !IO),
|
|
maybe_write_string(Verbose, FileName, !IO),
|
|
maybe_write_string(Verbose, "'...", !IO),
|
|
io.open_output(FileName, ImpResult, !IO),
|
|
(
|
|
ImpResult = ok(ImpStream),
|
|
|
|
Deps0 = list.foldl(filter_imports_graph,
|
|
digraph.to_assoc_list(IntDepsGraph), digraph.init),
|
|
Deps = list.foldl(filter_imports_graph,
|
|
digraph.to_assoc_list(ImplDepsGraph), Deps0),
|
|
|
|
write_graph(ImpStream, "imports", sym_name_to_node_id, Deps, !IO),
|
|
|
|
io.close_output(ImpStream, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO)
|
|
;
|
|
ImpResult = error(IOError),
|
|
maybe_write_string(Verbose, " failed.\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
io.error_message(IOError, IOErrorMessage),
|
|
string.append_list(["error opening file `", FileName,
|
|
"' for output: ", IOErrorMessage], ImpMessage),
|
|
report_error(ImpMessage, !IO)
|
|
)
|
|
;
|
|
ImportsGraph = no
|
|
).
|
|
|
|
:- func filter_imports_graph(pair(sym_name, sym_name), digraph(sym_name)) =
|
|
digraph(sym_name).
|
|
|
|
filter_imports_graph(A - B, DepsGraph) =
|
|
(
|
|
% Don't keep the edge if it points to a builtin-module or if the
|
|
% relationship is between two standard library modules.
|
|
% XXX it would be better to change this to be only keep those
|
|
% edges for which the left-hand side is in the current directory.
|
|
(
|
|
any_mercury_builtin_module(B)
|
|
;
|
|
is_std_lib_module_name(A, _),
|
|
is_std_lib_module_name(B, _)
|
|
)
|
|
->
|
|
DepsGraph
|
|
;
|
|
digraph.add_vertices_and_edge(A, B, DepsGraph)
|
|
).
|
|
|
|
:- type gen_node_name(T) == (func(T) = string).
|
|
|
|
:- pred write_graph(io.output_stream::in, string::in,
|
|
gen_node_name(T)::in, digraph(T)::in, io::di, io::uo) is det.
|
|
|
|
write_graph(Stream, Name, GenNodeName, Graph, !IO) :-
|
|
io.write_string(Stream, "digraph " ++ Name ++ " {\n", !IO),
|
|
io.write_string(Stream, "label=\"" ++ Name ++ "\";\n", !IO),
|
|
io.write_string(Stream, "center=true;\n", !IO),
|
|
digraph.traverse(Graph, write_node(Stream, GenNodeName),
|
|
write_edge(Stream, GenNodeName), !IO),
|
|
io.write_string(Stream, "}\n", !IO).
|
|
|
|
:- pred write_node(io.output_stream::in,
|
|
gen_node_name(T)::in, T::in, io::di, io::uo) is det.
|
|
|
|
write_node(Stream, GenNodeName, Node, !IO) :-
|
|
% Names can't contain "." so use "__"
|
|
io.write_string(Stream, GenNodeName(Node), !IO),
|
|
io.write_string(Stream, ";\n", !IO).
|
|
|
|
:- pred write_edge(io.output_stream::in, gen_node_name(T)::in, T::in, T::in,
|
|
io::di, io::uo) is det.
|
|
|
|
write_edge(Stream, GenNodeName, A, B, !IO) :-
|
|
io.write_string(Stream, GenNodeName(A), !IO),
|
|
io.write_string(Stream, " -> ", !IO),
|
|
io.write_string(Stream, GenNodeName(B), !IO),
|
|
io.write_string(Stream, ";\n", !IO).
|
|
|
|
:- func sym_name_to_node_id(sym_name) = string.
|
|
|
|
sym_name_to_node_id(Name) =
|
|
"\"" ++ sym_name_to_string(Name) ++ "\"".
|
|
|
|
:- pred maybe_output_module_order(globals::in, module_name::in,
|
|
list(set(module_name))::in, io::di, io::uo) is det.
|
|
|
|
maybe_output_module_order(Globals, Module, DepsOrdering, !IO) :-
|
|
globals.lookup_bool_option(Globals, generate_module_order, Order),
|
|
globals.lookup_bool_option(Globals, verbose, Verbose),
|
|
(
|
|
Order = yes,
|
|
module_name_to_file_name(Globals, Module, ".order",
|
|
do_create_dirs, OrdFileName, !IO),
|
|
maybe_write_string(Verbose, "% Creating module order file `", !IO),
|
|
maybe_write_string(Verbose, OrdFileName, !IO),
|
|
maybe_write_string(Verbose, "'...", !IO),
|
|
io.open_output(OrdFileName, OrdResult, !IO),
|
|
(
|
|
OrdResult = ok(OrdStream),
|
|
io.write_list(OrdStream, DepsOrdering, "\n\n",
|
|
write_module_scc(OrdStream), !IO),
|
|
io.close_output(OrdStream, !IO),
|
|
maybe_write_string(Verbose, " done.\n", !IO)
|
|
;
|
|
OrdResult = error(IOError),
|
|
maybe_write_string(Verbose, " failed.\n", !IO),
|
|
maybe_flush_output(Verbose, !IO),
|
|
io.error_message(IOError, IOErrorMessage),
|
|
string.append_list(["error opening file `", OrdFileName,
|
|
"' for output: ", IOErrorMessage], OrdMessage),
|
|
report_error(OrdMessage, !IO)
|
|
)
|
|
;
|
|
Order = no
|
|
).
|
|
|
|
:- pred write_module_scc(io.output_stream::in, set(module_name)::in,
|
|
io::di, io::uo) is det.
|
|
|
|
write_module_scc(Stream, SCC0, !IO) :-
|
|
set.to_sorted_list(SCC0, SCC),
|
|
io.write_list(Stream, SCC, "\n", prog_out.write_sym_name, !IO).
|
|
|
|
% generate_dependencies_write_d_files(Globals, Modules,
|
|
% IntDepsRel, ImplDepsRel, IndirectDepsRel, IndirectOptDepsRel,
|
|
% TransOptOrder, DepsMap, !IO):
|
|
%
|
|
% This predicate writes out the .d files for all the modules in the
|
|
% Modules list.
|
|
% IntDepsGraph gives the interface dependency graph.
|
|
% ImplDepsGraph gives the implementation dependency graph.
|
|
% IndirectDepsGraph gives the indirect dependency graph
|
|
% (this includes dependencies on `*.int2' files).
|
|
% IndirectOptDepsGraph gives the indirect optimization dependencies
|
|
% (this includes dependencies via `.opt' and `.trans_opt' files).
|
|
% These are all computed from the DepsMap.
|
|
% TransOptOrder gives the ordering that is used to determine
|
|
% which other modules the .trans_opt files may depend on.
|
|
%
|
|
:- pred generate_dependencies_write_d_files(globals::in, list(deps)::in,
|
|
deps_graph::in, deps_graph::in, deps_graph::in, deps_graph::in,
|
|
list(module_name)::in, deps_map::in, io::di, io::uo) is det.
|
|
|
|
generate_dependencies_write_d_files(_, [], _, _, _, _, _, _, !IO).
|
|
generate_dependencies_write_d_files(Globals, [Dep | Deps],
|
|
IntDepsGraph, ImplDepsGraph, IndirectDepsGraph, IndirectOptDepsGraph,
|
|
TransOptOrder, DepsMap, !IO) :-
|
|
some [!Module] (
|
|
Dep = deps(_, !:Module),
|
|
|
|
% Look up the interface/implementation/indirect dependencies
|
|
% for this module from the respective dependency graphs,
|
|
% and save them in the module_and_imports structure.
|
|
|
|
module_and_imports_get_module_name(!.Module, ModuleName),
|
|
get_dependencies_from_graph(IndirectOptDepsGraph, ModuleName,
|
|
IndirectOptDeps),
|
|
globals.lookup_bool_option(Globals, intermodule_optimization,
|
|
Intermod),
|
|
(
|
|
Intermod = yes,
|
|
% Be conservative with inter-module optimization -- assume a
|
|
% module depends on the `.int', `.int2' and `.opt' files
|
|
% for all transitively imported modules.
|
|
IntDeps = IndirectOptDeps,
|
|
ImplDeps = IndirectOptDeps,
|
|
IndirectDeps = IndirectOptDeps
|
|
;
|
|
Intermod = no,
|
|
get_dependencies_from_graph(IntDepsGraph, ModuleName, IntDeps),
|
|
get_dependencies_from_graph(ImplDepsGraph, ModuleName, ImplDeps),
|
|
get_dependencies_from_graph(IndirectDepsGraph, ModuleName,
|
|
IndirectDeps)
|
|
),
|
|
|
|
globals.get_target(Globals, Target),
|
|
( Target = target_c, Lang = lang_c
|
|
; Target = target_asm, Lang = lang_c
|
|
; Target = target_java, Lang = lang_java
|
|
; Target = target_csharp, Lang = lang_csharp
|
|
; Target = target_il, Lang = lang_il
|
|
; Target = target_x86_64, Lang = lang_c
|
|
; Target = target_erlang, Lang = lang_erlang
|
|
),
|
|
% Assume we need the `.mh' files for all imported modules
|
|
% (we will if they define foreign types).
|
|
ForeignImports = list.map(
|
|
(func(ThisDep) = foreign_import_module_info(Lang, ThisDep,
|
|
term.context_init)),
|
|
IndirectOptDeps),
|
|
!Module ^ mai_foreign_import_modules := ForeignImports,
|
|
|
|
module_and_imports_set_int_deps(IntDeps, !Module),
|
|
module_and_imports_set_impl_deps(ImplDeps, !Module),
|
|
module_and_imports_set_indirect_deps(IndirectDeps, !Module),
|
|
|
|
% Compute the trans-opt dependencies for this module. To avoid
|
|
% the possibility of cycles, each module is only allowed to depend
|
|
% on modules that occur later than it in the TransOptOrder.
|
|
|
|
FindModule = (pred(OtherModule::in) is semidet :-
|
|
ModuleName \= OtherModule
|
|
),
|
|
list.takewhile(FindModule, TransOptOrder, _, TransOptDeps0),
|
|
( TransOptDeps0 = [_ | TransOptDeps1] ->
|
|
% The module was found in the list.
|
|
TransOptDeps = TransOptDeps1
|
|
;
|
|
TransOptDeps = []
|
|
),
|
|
|
|
% Note that even if a fatal error occured for one of the files
|
|
% that the current Module depends on, a .d file is still produced,
|
|
% even though it probably contains incorrect information.
|
|
Error = !.Module ^ mai_error,
|
|
(
|
|
( Error = no_module_errors
|
|
; Error = some_module_errors
|
|
),
|
|
write_dependency_file(Globals, !.Module,
|
|
set.list_to_set(IndirectOptDeps), yes(TransOptDeps), !IO)
|
|
;
|
|
Error = fatal_module_errors
|
|
),
|
|
generate_dependencies_write_d_files(Globals, Deps,
|
|
IntDepsGraph, ImplDepsGraph,
|
|
IndirectDepsGraph, IndirectOptDepsGraph,
|
|
TransOptOrder, DepsMap, !IO)
|
|
).
|
|
|
|
:- pred get_dependencies_from_graph(deps_graph::in, module_name::in,
|
|
list(module_name)::out) is det.
|
|
|
|
get_dependencies_from_graph(DepsGraph0, ModuleName, Deps) :-
|
|
digraph.add_vertex(ModuleName, ModuleKey, DepsGraph0, DepsGraph),
|
|
digraph.lookup_key_set_from(DepsGraph, ModuleKey, DepsKeysSet),
|
|
sparse_bitset.foldl(
|
|
(pred(Key::in, Deps0::in, [Dep | Deps0]::out) is det :-
|
|
digraph.lookup_vertex(DepsGraph, Key, Dep)
|
|
), DepsKeysSet, [], Deps).
|
|
|
|
% (Module1 -> Module2) means Module1 is imported by Module2.
|
|
:- type deps_graph == digraph(module_name).
|
|
:- type deps_graph_key == digraph_key(module_name).
|
|
|
|
% Construct a pair of dependency graphs (the interface dependencies
|
|
% and the implementation dependencies) for all the modules in the program.
|
|
%
|
|
:- pred deps_list_to_deps_graph(list(deps)::in, deps_map::in,
|
|
deps_graph::in, deps_graph::out, deps_graph::in, deps_graph::out) is det.
|
|
|
|
deps_list_to_deps_graph([], _, !IntDepsGraph, !ImplDepsGraph).
|
|
deps_list_to_deps_graph([Deps | DepsList], DepsMap, !IntDepsGraph,
|
|
!ImplDepsGraph) :-
|
|
Deps = deps(_, ModuleImports),
|
|
ModuleError = ModuleImports ^ mai_error,
|
|
( ModuleError \= fatal_module_errors ->
|
|
module_and_imports_to_deps_graph(ModuleImports,
|
|
lookup_module_and_imports(DepsMap), !IntDepsGraph, !ImplDepsGraph)
|
|
;
|
|
true
|
|
),
|
|
deps_list_to_deps_graph(DepsList, DepsMap, !IntDepsGraph, !ImplDepsGraph).
|
|
|
|
:- func lookup_module_and_imports(deps_map, module_name) = module_and_imports.
|
|
|
|
lookup_module_and_imports(DepsMap, ModuleName) = ModuleImports :-
|
|
map.lookup(DepsMap, ModuleName, deps(_, ModuleImports)).
|
|
|
|
add_module_relations(LookupModuleImports, ModuleName, !IntDepsGraph,
|
|
!ImplDepsGraph) :-
|
|
ModuleImports = LookupModuleImports(ModuleName),
|
|
module_and_imports_to_deps_graph(ModuleImports, LookupModuleImports,
|
|
!IntDepsGraph, !ImplDepsGraph).
|
|
|
|
:- pred module_and_imports_to_deps_graph(module_and_imports::in,
|
|
lookup_module_and_imports::lookup_module_and_imports,
|
|
deps_graph::in, deps_graph::out, deps_graph::in, deps_graph::out) is det.
|
|
|
|
module_and_imports_to_deps_graph(ModuleImports, LookupModuleImports,
|
|
!IntDepsGraph, !ImplDepsGraph) :-
|
|
% Add interface dependencies to the interface deps graph.
|
|
%
|
|
% Note that we need to do this both for the interface imports of this
|
|
% module and for the *implementation* imports of its ancestors.
|
|
% This is because if this module is defined in the implementation section
|
|
% of its parent, then the interface of this module may depend on things
|
|
% imported only by its parent's implementation.
|
|
%
|
|
% If this module was actually defined in the interface section of one
|
|
% of its ancestors, then it should only depend on the interface imports
|
|
% of that ancestor, so the dependencies added here are in fact more
|
|
% conservative than they need to be in that case. However, that should
|
|
% not be a major problem.
|
|
|
|
ModuleName = ModuleImports ^ mai_module_name,
|
|
ParentDeps = ModuleImports ^ mai_parent_deps,
|
|
digraph.add_vertex(ModuleName, IntModuleKey, !IntDepsGraph),
|
|
add_int_deps(IntModuleKey, ModuleImports, !IntDepsGraph),
|
|
add_parent_impl_deps_list(LookupModuleImports, IntModuleKey, ParentDeps,
|
|
!IntDepsGraph),
|
|
|
|
% Add implementation dependencies to the impl. deps graph.
|
|
% (The implementation dependencies are a superset of the interface
|
|
% dependencies.)
|
|
%
|
|
% Note that we need to do this both for the imports of this module
|
|
% and for the imports of its parents, because this module may depend on
|
|
% things imported only by its parents.
|
|
|
|
digraph.add_vertex(ModuleName, ImplModuleKey, !ImplDepsGraph),
|
|
add_impl_deps(ImplModuleKey, ModuleImports, !ImplDepsGraph),
|
|
add_parent_impl_deps_list(LookupModuleImports, ImplModuleKey, ParentDeps,
|
|
!ImplDepsGraph).
|
|
|
|
% Add interface dependencies to the interface deps graph.
|
|
%
|
|
:- pred add_int_deps(deps_graph_key::in, module_and_imports::in,
|
|
deps_graph::in, deps_graph::out) is det.
|
|
|
|
add_int_deps(ModuleKey, ModuleImports, !DepsGraph) :-
|
|
AddDep = add_dep(ModuleKey),
|
|
list.foldl(AddDep, ModuleImports ^ mai_parent_deps, !DepsGraph),
|
|
list.foldl(AddDep, ModuleImports ^ mai_int_deps, !DepsGraph).
|
|
|
|
% Add direct implementation dependencies for a module to the
|
|
% implementation deps graph.
|
|
%
|
|
:- pred add_impl_deps(deps_graph_key::in, module_and_imports::in,
|
|
deps_graph::in, deps_graph::out) is det.
|
|
|
|
add_impl_deps(ModuleKey, ModuleImports, !DepsGraph) :-
|
|
% The implementation dependencies are a superset of the
|
|
% interface dependencies, so first we add the interface deps.
|
|
add_int_deps(ModuleKey, ModuleImports, !DepsGraph),
|
|
% then we add the impl deps
|
|
module_and_imports_get_impl_deps(ModuleImports, ImplDeps),
|
|
list.foldl(add_dep(ModuleKey), ImplDeps, !DepsGraph).
|
|
|
|
% Add parent implementation dependencies for the given Parent module
|
|
% to the impl. deps graph values for the given ModuleKey.
|
|
%
|
|
:- pred add_parent_impl_deps(
|
|
lookup_module_and_imports::lookup_module_and_imports,
|
|
deps_graph_key::in, module_name::in, deps_graph::in, deps_graph::out)
|
|
is det.
|
|
|
|
add_parent_impl_deps(LookupModuleImports, ModuleKey, Parent, !DepsGraph) :-
|
|
ParentModuleImports = LookupModuleImports(Parent),
|
|
add_impl_deps(ModuleKey, ParentModuleImports, !DepsGraph).
|
|
|
|
:- pred add_parent_impl_deps_list(
|
|
lookup_module_and_imports::lookup_module_and_imports,
|
|
deps_graph_key::in, list(module_name)::in, deps_graph::in, deps_graph::out)
|
|
is det.
|
|
|
|
add_parent_impl_deps_list(LookupModuleImports, ModuleKey, Parents,
|
|
!DepsGraph) :-
|
|
list.foldl(add_parent_impl_deps(LookupModuleImports, ModuleKey), Parents,
|
|
!DepsGraph).
|
|
|
|
% Add a single dependency to a graph.
|
|
%
|
|
:- pred add_dep(digraph_key(T)::in, T::in, digraph(T)::in, digraph(T)::out)
|
|
is det.
|
|
|
|
add_dep(ModuleKey, Dep, !DepsGraph) :-
|
|
digraph.add_vertex(Dep, DepKey, !DepsGraph),
|
|
digraph.add_edge(ModuleKey, DepKey, !DepsGraph).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred append_to_init_list(io.output_stream::in, file_name::in,
|
|
module_name::in, io::di, io::uo) is det.
|
|
|
|
append_to_init_list(DepStream, InitFileName, Module, !IO) :-
|
|
InitFuncName0 = make_init_name(Module),
|
|
string.append(InitFuncName0, "init", InitFuncName),
|
|
io.write_strings(DepStream, [
|
|
"\techo ""INIT ", InitFuncName, """ >> ", InitFileName, "\n"
|
|
], !IO).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Find out which modules we need to generate C header files for,
|
|
% assuming we're compiling with `--target asm'.
|
|
%
|
|
:- func modules_that_need_headers(list(module_name), deps_map)
|
|
= list(module_name).
|
|
|
|
modules_that_need_headers(Modules, DepsMap) =
|
|
list.filter(module_needs_header(DepsMap), Modules).
|
|
|
|
% Succeed iff we need to generate a C header file for the specified
|
|
% module, assuming we're compiling with `--target asm'.
|
|
%
|
|
:- pred module_needs_header(deps_map::in, module_name::in) is semidet.
|
|
|
|
module_needs_header(DepsMap, Module) :-
|
|
map.lookup(DepsMap, Module, deps(_, ModuleImports)),
|
|
ModuleImports ^ mai_has_foreign_code = contains_foreign_code(Langs),
|
|
set.member(lang_c, Langs).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
process_module_private_interfaces(_, _, [], _, _, !DirectImports,
|
|
!DirectUses, !Module, !IO).
|
|
process_module_private_interfaces(Globals, HaveReadModuleMap,
|
|
[Ancestor | Ancestors], IntStatusItem, ImpStatusItem, !DirectImports,
|
|
!DirectUses, !Module, !IO) :-
|
|
ModuleName = !.Module ^ mai_module_name,
|
|
ModAncestors0 = !.Module ^ mai_parent_deps,
|
|
( Ancestor = ModuleName ->
|
|
unexpected($module, $pred, "module is its own ancestor?")
|
|
; list.member(Ancestor, ModAncestors0) ->
|
|
% We've already read it.
|
|
process_module_private_interfaces(Globals, HaveReadModuleMap,
|
|
Ancestors, IntStatusItem, ImpStatusItem,
|
|
!DirectImports, !DirectUses, !Module, !IO)
|
|
;
|
|
maybe_return_timestamp(!.Module ^ mai_maybe_timestamps,
|
|
ReturnTimestamp),
|
|
maybe_read_module(Globals, HaveReadModuleMap, Ancestor, ".int0",
|
|
"Reading private interface for module", do_search, ReturnTimestamp,
|
|
PrivateIntItems0, PrivateIntSpecs, PrivateIntError,
|
|
_AncestorFileName, MaybeTimestamp, !IO),
|
|
|
|
maybe_record_timestamp(Ancestor, ".int0", may_be_unqualified,
|
|
MaybeTimestamp, !Module),
|
|
|
|
replace_section_decls(IntStatusItem, ImpStatusItem,
|
|
PrivateIntItems0, PrivateIntItems),
|
|
|
|
module_and_imports_add_items(cord.from_list(PrivateIntItems), !Module),
|
|
module_and_imports_add_specs(PrivateIntSpecs, !Module),
|
|
module_and_imports_add_interface_error(PrivateIntError, !Module),
|
|
|
|
globals.lookup_bool_option(Globals, detailed_statistics, Statistics),
|
|
maybe_report_stats(Statistics, !IO),
|
|
|
|
(
|
|
PrivateIntError = fatal_module_errors,
|
|
ModAncestors = ModAncestors0
|
|
;
|
|
( PrivateIntError = no_module_errors
|
|
; PrivateIntError = some_module_errors
|
|
),
|
|
ModAncestors = [Ancestor | ModAncestors0]
|
|
),
|
|
get_dependencies(PrivateIntItems, AncDirectImports, AncDirectUses),
|
|
!:DirectImports = !.DirectImports ++ AncDirectImports,
|
|
!:DirectUses = !.DirectUses ++ AncDirectUses,
|
|
!Module ^ mai_parent_deps := ModAncestors,
|
|
|
|
process_module_private_interfaces(Globals, HaveReadModuleMap,
|
|
Ancestors, IntStatusItem, ImpStatusItem,
|
|
!DirectImports, !DirectUses, !Module, !IO)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
process_module_long_interfaces(_, _, _, [], _Ext, _, _,
|
|
!IndirectImports, !ImplIndirectImports, !Module, !IO).
|
|
process_module_long_interfaces(Globals, HaveReadModuleMap, NeedQualifier,
|
|
[Import | Imports], Ext, IntStatusItem, ImpStatusItem,
|
|
!IndirectImports, !ImplIndirectImports, !Module, !IO) :-
|
|
ModuleName = !.Module ^ mai_module_name,
|
|
ModImplementationImports0 = !.Module ^ mai_impl_deps,
|
|
(
|
|
% Have we already read it?
|
|
( Import = ModuleName
|
|
; list.member(Import, !.Module ^ mai_parent_deps)
|
|
; list.member(Import, !.Module ^ mai_int_deps)
|
|
; list.member(Import, ModImplementationImports0)
|
|
)
|
|
->
|
|
process_module_long_interfaces(Globals, HaveReadModuleMap,
|
|
NeedQualifier, Imports, Ext, IntStatusItem, ImpStatusItem,
|
|
!IndirectImports, !ImplIndirectImports, !Module, !IO)
|
|
;
|
|
maybe_return_timestamp(!.Module ^ mai_maybe_timestamps,
|
|
ReturnTimestamp),
|
|
maybe_read_module(Globals, HaveReadModuleMap, Import, Ext,
|
|
"Reading interface for module", do_search, ReturnTimestamp,
|
|
LongIntItems0, LongIntSpecs, LongIntError, _LongIntFileName,
|
|
MaybeTimestamp, !IO),
|
|
|
|
get_dependencies_int_imp(LongIntItems0,
|
|
IndirectImports1, IndirectUses1,
|
|
ImplIndirectImports1, ImplIndirectUses1),
|
|
replace_section_decls(IntStatusItem, ImpStatusItem,
|
|
LongIntItems0, LongIntItems),
|
|
|
|
module_and_imports_add_items(cord.from_list(LongIntItems), !Module),
|
|
module_and_imports_add_specs(LongIntSpecs, !Module),
|
|
module_and_imports_add_interface_error(LongIntError, !Module),
|
|
|
|
globals.lookup_bool_option(Globals, detailed_statistics, Statistics),
|
|
maybe_report_stats(Statistics, !IO),
|
|
|
|
(
|
|
LongIntError = fatal_module_errors,
|
|
ModImplementationImports = ModImplementationImports0
|
|
;
|
|
( LongIntError = no_module_errors
|
|
; LongIntError = some_module_errors
|
|
),
|
|
maybe_record_timestamp(Import, Ext, NeedQualifier, MaybeTimestamp,
|
|
!Module),
|
|
ModImplementationImports = [Import | ModImplementationImports0]
|
|
),
|
|
!:IndirectImports = !.IndirectImports ++ IndirectImports1
|
|
++ IndirectUses1,
|
|
!:ImplIndirectImports = !.ImplIndirectImports
|
|
++ ImplIndirectImports1 ++ ImplIndirectUses1,
|
|
!Module ^ mai_impl_deps := ModImplementationImports,
|
|
|
|
process_module_long_interfaces(Globals, HaveReadModuleMap,
|
|
NeedQualifier, Imports, Ext, IntStatusItem, ImpStatusItem,
|
|
!IndirectImports, !ImplIndirectImports, !Module, !IO)
|
|
).
|
|
|
|
% At this point, we've 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 sub-modules
|
|
% of the parent. So the accessible sub-modules can be determined
|
|
% by just calling get_accessible_children on the complete item list.
|
|
%
|
|
% We then go through all of the imported/used modules,
|
|
% checking that each one is accessible.
|
|
%
|
|
:- pred check_imports_accessibility(module_name::in, list(module_name)::in,
|
|
list(item)::in, list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
check_imports_accessibility(ModuleName, Imports, Items, !Specs) :-
|
|
get_accessible_children(Items, AccessibleSubModules),
|
|
list.foldl(check_module_accessibility(ModuleName,
|
|
AccessibleSubModules, Items), Imports, !Specs).
|
|
|
|
:- pred check_module_accessibility(module_name::in, list(module_name)::in,
|
|
list(item)::in, module_name::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
check_module_accessibility(ModuleName, AccessibleSubModules, Items,
|
|
ImportedModule, !Specs) :-
|
|
( ImportedModule = qualified(ParentModule, SubModule) ->
|
|
( list.member(ImportedModule, AccessibleSubModules) ->
|
|
true
|
|
;
|
|
% The user attempted to import an inaccessible submodule,
|
|
% so report an error. Unfortunately we didn't get passed the
|
|
% context(s) of the `import_module' or `use_module' declaration(s),
|
|
% so we need to search the item list again to find them.
|
|
FindImports = (pred(Item::in, ImportInfo::out) is semidet :-
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context, _),
|
|
(
|
|
ModuleDefn = md_import(ItemModuleSpecs),
|
|
DeclName = "import_module"
|
|
;
|
|
ModuleDefn = md_use(ItemModuleSpecs),
|
|
DeclName = "use_module"
|
|
),
|
|
list.member(ImportedModule, ItemModuleSpecs),
|
|
ImportInfo = DeclName - Context
|
|
),
|
|
list.filter_map(FindImports, Items, ImportInfos),
|
|
(
|
|
ImportInfos = [],
|
|
unexpected($module, $pred, "check_parent_module")
|
|
;
|
|
ImportInfos = [_ | _],
|
|
list.foldl(
|
|
report_inaccessible_module_error(ModuleName,
|
|
ParentModule, SubModule),
|
|
ImportInfos, !Specs)
|
|
)
|
|
)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred report_inaccessible_module_error(module_name::in, module_name::in,
|
|
string::in, pair(string, prog_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,
|
|
DeclName - Context, !Specs) :-
|
|
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(VerbosePieces)]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs].
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
process_module_short_interfaces_and_impls_transitively(Globals,
|
|
HaveReadModuleMap, Imports, Ext, IntStatusItem, ImpStatusItem,
|
|
!Module, !IO) :-
|
|
process_module_short_interfaces_transitively(Globals, HaveReadModuleMap,
|
|
Imports, Ext, IntStatusItem, ImpStatusItem, [], ImpIndirectImports,
|
|
!Module, !IO),
|
|
(
|
|
ImpIndirectImports = []
|
|
;
|
|
ImpIndirectImports = [_ | _],
|
|
process_module_short_interfaces_and_impls_transitively(Globals,
|
|
HaveReadModuleMap, ImpIndirectImports, Ext,
|
|
IntStatusItem, ImpStatusItem, !Module, !IO)
|
|
).
|
|
|
|
process_module_short_interfaces_transitively(Globals, HaveReadModuleMap,
|
|
Imports, Ext, IntStatusItem, ImpStatusItem, !ImpIndirectImports,
|
|
!Module, !IO) :-
|
|
process_module_short_interfaces(Globals, HaveReadModuleMap, Imports, Ext,
|
|
IntStatusItem, ImpStatusItem, [], IndirectImports,
|
|
!ImpIndirectImports, !Module, !IO),
|
|
(
|
|
IndirectImports = []
|
|
;
|
|
IndirectImports = [_ | _],
|
|
process_module_short_interfaces_transitively(Globals,
|
|
HaveReadModuleMap, IndirectImports, Ext,
|
|
IntStatusItem, ImpStatusItem, !ImpIndirectImports, !Module, !IO)
|
|
).
|
|
|
|
process_module_short_interfaces(_, _, [], _, _, _, !IndirectImports,
|
|
!ImpIndirectImports, !Module, !IO).
|
|
process_module_short_interfaces(Globals, HaveReadModuleMap, [Import | Imports],
|
|
Ext, IntStatusItem, ImpStatusItem, !IndirectImports,
|
|
!ImpIndirectImports, !Module, !IO) :-
|
|
ModIndirectImports0 = !.Module ^ mai_indirect_deps,
|
|
(
|
|
% check if the imported module has already been imported
|
|
( Import = !.Module ^ mai_module_name
|
|
; list.member(Import, !.Module ^ mai_parent_deps)
|
|
; list.member(Import, !.Module ^ mai_int_deps)
|
|
; list.member(Import, !.Module ^ mai_impl_deps)
|
|
; list.member(Import, ModIndirectImports0)
|
|
)
|
|
->
|
|
process_module_short_interfaces(Globals, HaveReadModuleMap, Imports,
|
|
Ext, IntStatusItem, ImpStatusItem, !IndirectImports,
|
|
!ImpIndirectImports, !Module, !IO)
|
|
;
|
|
maybe_return_timestamp(!.Module ^ mai_maybe_timestamps,
|
|
ReturnTimestamp),
|
|
maybe_read_module(Globals, HaveReadModuleMap, Import, Ext,
|
|
"Reading short interface for module", do_search,
|
|
ReturnTimestamp, ShortIntItems0, ShortIntSpecs, ShortIntError,
|
|
_ImportFileName, MaybeTimestamp, !IO),
|
|
maybe_record_timestamp(Import, Ext, must_be_qualified,
|
|
MaybeTimestamp, !Module),
|
|
|
|
get_dependencies_int_imp(ShortIntItems0, IntImports1, IntUses1,
|
|
ImpImports1, ImpUses1),
|
|
replace_section_decls(IntStatusItem, ImpStatusItem,
|
|
ShortIntItems0, ShortIntItems),
|
|
|
|
module_and_imports_add_items(cord.from_list(ShortIntItems), !Module),
|
|
module_and_imports_add_specs(ShortIntSpecs, !Module),
|
|
module_and_imports_add_interface_error(ShortIntError, !Module),
|
|
|
|
globals.lookup_bool_option(Globals, detailed_statistics, Statistics),
|
|
maybe_report_stats(Statistics, !IO),
|
|
|
|
ModIndirectImports = [Import | ModIndirectImports0],
|
|
!:IndirectImports = !.IndirectImports ++ IntImports1 ++ IntUses1,
|
|
!:ImpIndirectImports = !.ImpIndirectImports ++ ImpImports1 ++ ImpUses1,
|
|
!Module ^ mai_indirect_deps := ModIndirectImports,
|
|
|
|
process_module_short_interfaces(Globals, HaveReadModuleMap, Imports,
|
|
Ext, IntStatusItem, ImpStatusItem, !IndirectImports,
|
|
!ImpIndirectImports, !Module, !IO)
|
|
).
|
|
|
|
replace_section_decls(IntStatusItem, ImpStatusItem, !Items) :-
|
|
list.map(replace_section_decl(IntStatusItem, ImpStatusItem), !Items).
|
|
|
|
:- pred replace_section_decl(item::in, item::in, item::in, item::out) is det.
|
|
|
|
replace_section_decl(IntStatusItem, ImpStatusItem, Item0, Item) :-
|
|
(
|
|
Item0 = item_module_defn(ItemModuleDefn0),
|
|
ItemModuleDefn0 = item_module_defn_info(ModuleDefn0, _, _),
|
|
(
|
|
ModuleDefn0 = md_interface,
|
|
ItemPrime = IntStatusItem
|
|
;
|
|
ModuleDefn0 = md_implementation,
|
|
ItemPrime = ImpStatusItem
|
|
)
|
|
->
|
|
Item = ItemPrime
|
|
;
|
|
Item = Item0
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
get_children(Items, IncludeDeps) :-
|
|
get_children_2(Items, [], IncludeDeps).
|
|
|
|
:- pred get_children_2(list(item)::in,
|
|
list(module_name)::in, list(module_name)::out) is det.
|
|
|
|
get_children_2([], !IncludeDeps).
|
|
get_children_2([Item | Items], !IncludeDeps) :-
|
|
(
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
ModuleDefn = md_include_module(Modules)
|
|
->
|
|
!:IncludeDeps = !.IncludeDeps ++ Modules
|
|
;
|
|
true
|
|
),
|
|
get_children_2(Items, !IncludeDeps).
|
|
|
|
% get_accessible_children(Items, IncludeDeps):
|
|
%
|
|
% IncludeDeps is the list of sub-modules declared with `:- include_module'
|
|
% in Items which are visible in the current module.
|
|
%
|
|
:- pred get_accessible_children(list(item)::in, list(module_name)::out) is det.
|
|
|
|
get_accessible_children(Items, IncludeDeps) :-
|
|
get_accessible_children_2(yes, Items, [], IncludeDeps).
|
|
|
|
:- pred get_accessible_children_2(bool::in, list(item)::in,
|
|
list(module_name)::in, list(module_name)::out) is det.
|
|
|
|
get_accessible_children_2(_, [], !IncludeDeps).
|
|
get_accessible_children_2(!.Visible, [Item | Items], !IncludeDeps) :-
|
|
(
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
(
|
|
( ModuleDefn = md_abstract_imported
|
|
; ModuleDefn = md_opt_imported
|
|
; ModuleDefn = md_transitively_imported
|
|
),
|
|
!:Visible = no
|
|
;
|
|
( ModuleDefn = md_imported(_)
|
|
; ModuleDefn = md_used(_)
|
|
; ModuleDefn = md_interface
|
|
; ModuleDefn = md_implementation
|
|
; ModuleDefn = md_implementation_but_exported_to_submodules
|
|
),
|
|
!:Visible = yes
|
|
;
|
|
ModuleDefn = md_include_module(Modules),
|
|
(
|
|
!.Visible = yes,
|
|
!:IncludeDeps = !.IncludeDeps ++ Modules
|
|
;
|
|
!.Visible = no
|
|
)
|
|
;
|
|
( ModuleDefn = md_external(_, _)
|
|
; ModuleDefn = md_export(_)
|
|
; ModuleDefn = md_import(_)
|
|
; ModuleDefn = md_use(_)
|
|
; ModuleDefn = md_version_numbers(_, _)
|
|
)
|
|
% Do nothing.
|
|
)
|
|
;
|
|
( Item = item_module_start(_)
|
|
; Item = item_module_end(_)
|
|
; Item = item_clause(_)
|
|
; Item = item_type_defn(_)
|
|
; Item = item_inst_defn(_)
|
|
; Item = item_mode_defn(_)
|
|
; Item = item_pred_decl(_)
|
|
; Item = item_mode_decl(_)
|
|
; Item = item_pragma(_)
|
|
; Item = item_promise(_)
|
|
; Item = item_typeclass(_)
|
|
; Item = item_instance(_)
|
|
; Item = item_initialise(_)
|
|
; Item = item_finalise(_)
|
|
; Item = item_mutable(_)
|
|
; Item = item_nothing(_)
|
|
)
|
|
),
|
|
get_accessible_children_2(!.Visible, Items, !IncludeDeps).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type submodule_map == map(module_name, list(item)).
|
|
|
|
% Given a module (well, a list of items), split it into
|
|
% its constituent sub-modules, in top-down order.
|
|
%
|
|
split_into_submodules(ModuleName, Items0, ModuleList, !Specs) :-
|
|
InParentInterface = no,
|
|
split_into_submodules_2(ModuleName, Items0, InParentInterface,
|
|
LeftOverItems, ModuleList, !Specs),
|
|
|
|
% Check that there are no items after the end_module declaration.
|
|
(
|
|
LeftOverItems = []
|
|
;
|
|
LeftOverItems = [FirstLeftOverItem | _],
|
|
Context = get_item_context(FirstLeftOverItem),
|
|
report_items_after_end_module(Context, !Specs)
|
|
),
|
|
|
|
% Check for modules declared as both nested and separate sub-modules.
|
|
get_children(Items0, NestedSubmodules),
|
|
assoc_list.keys(ModuleList, SeparateSubModules),
|
|
Duplicates = set.intersect(
|
|
set.list_to_set(NestedSubmodules),
|
|
set.list_to_set(SeparateSubModules)),
|
|
( set.empty(Duplicates) ->
|
|
true
|
|
;
|
|
report_duplicate_modules(Duplicates, Items0, !Specs)
|
|
).
|
|
|
|
:- pred split_into_submodules_2(module_name::in, list(item)::in, bool::in,
|
|
list(item)::out, module_list::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
split_into_submodules_2(ModuleName, Items0, InParentInterface, Items,
|
|
ModuleList, !Specs) :-
|
|
InInterface0 = no,
|
|
split_into_submodules_3(ModuleName, Items0,
|
|
InParentInterface, InInterface0,
|
|
ThisModuleItems, Items, SubModules, !Specs),
|
|
map.to_assoc_list(SubModules, SubModuleList),
|
|
ModuleList = [ModuleName - ThisModuleItems | SubModuleList].
|
|
|
|
:- pred split_into_submodules_3(module_name::in, list(item)::in, bool::in,
|
|
bool::in, list(item)::out, list(item)::out,
|
|
map(module_name, list(item))::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
split_into_submodules_3(_ModuleName, [], _, _, [], [], SubModules, !Specs) :-
|
|
map.init(SubModules).
|
|
split_into_submodules_3(ModuleName, [Item | Items1],
|
|
InParentInterface, !.InInterface,
|
|
ThisModuleItems, OtherItems, SubModules, !Specs) :-
|
|
(
|
|
% Check for a `module' declaration, which signals the start
|
|
% of a nested module.
|
|
Item = item_module_start(ItemModuleStart),
|
|
ItemModuleStart =
|
|
item_module_start_info(SubModuleName, Context, SeqNum)
|
|
->
|
|
% Parse in the items for the nested submodule.
|
|
split_into_submodules_2(SubModuleName, Items1, !.InInterface,
|
|
Items2, SubModules0, !Specs),
|
|
|
|
% Parse in the remaining items for this module.
|
|
split_into_submodules_3(ModuleName, Items2,
|
|
InParentInterface, !.InInterface,
|
|
ThisModuleItems0, Items3, SubModules1, !Specs),
|
|
|
|
% Combine the submodule declarations from the previous two steps.
|
|
list.foldl(add_submodule, SubModules0, SubModules1, SubModules),
|
|
|
|
% Replace the nested submodule with an `include_module' declaration.
|
|
IncludeSubModModuleDefn = md_include_module([SubModuleName]),
|
|
IncludeSubModItemModuleDefn = item_module_defn_info(
|
|
IncludeSubModModuleDefn, Context, SeqNum),
|
|
IncludeSubModItem = item_module_defn(IncludeSubModItemModuleDefn),
|
|
ThisModuleItems = [IncludeSubModItem | ThisModuleItems0],
|
|
OtherItems = Items3
|
|
;
|
|
% Check for a matching `end_module' declaration.
|
|
Item = item_module_end(ItemModuleEnd),
|
|
ItemModuleEnd = item_module_end_info(EndModuleName, _, _),
|
|
EndModuleName = ModuleName
|
|
->
|
|
% If so, that's the end of this module.
|
|
ThisModuleItems = [],
|
|
OtherItems = Items1,
|
|
map.init(SubModules)
|
|
;
|
|
% Otherwise, process the next item in this module.
|
|
|
|
% Update the flag which records whether we're currently in the
|
|
% interface section, and report an error if there is an
|
|
% `implementation' section inside an `interface' section.
|
|
( Item = item_module_defn(ItemModuleDefn) ->
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context,
|
|
_SeqNum),
|
|
( ModuleDefn = md_interface ->
|
|
!:InInterface = yes
|
|
; ModuleDefn = md_implementation ->
|
|
!:InInterface = no,
|
|
(
|
|
InParentInterface = yes,
|
|
report_error_implementation_in_interface(ModuleName,
|
|
Context, !Specs)
|
|
;
|
|
InParentInterface = no
|
|
)
|
|
;
|
|
true
|
|
)
|
|
;
|
|
true
|
|
),
|
|
|
|
% Check to make sure that a non-abstract instance declaration
|
|
% does not occur in a module interface.
|
|
(
|
|
!.InInterface = yes,
|
|
Item = item_instance(ItemInstance),
|
|
ItemInstance ^ ci_method_instances \= instance_body_abstract
|
|
->
|
|
InstanceContext = ItemInstance ^ ci_context,
|
|
report_non_abstract_instance_in_interface(InstanceContext, !Specs)
|
|
;
|
|
true
|
|
),
|
|
|
|
% Parse the remaining items for this module.
|
|
split_into_submodules_3(ModuleName, Items1,
|
|
InParentInterface, !.InInterface,
|
|
ThisModuleItems0, Items2, SubModules, !Specs),
|
|
|
|
% Put the current item back onto the front of the item list
|
|
% for this module.
|
|
ThisModuleItems = [Item | ThisModuleItems0],
|
|
OtherItems = Items2
|
|
).
|
|
|
|
:- pred add_submodule(pair(module_name, list(item))::in,
|
|
submodule_map::in, submodule_map::out) is det.
|
|
|
|
add_submodule(ModuleName - ModuleItemList, !SubModules) :-
|
|
% If the same module name occurs twice, then just append the lists of items
|
|
% together. Perhaps we should be a bit more strict about this, for example
|
|
% by only allowing one `:- implementation' section and one `:- interface'
|
|
% section for each module? (That is what the Mercury language reference
|
|
% manual mandates. On the other hand, it also says that top-level modules
|
|
% should only have one `:- interface' and one `:- implementation' section,
|
|
% and we don't enforce that either...)
|
|
( map.search(!.SubModules, ModuleName, ItemList0) ->
|
|
list.append(ModuleItemList, ItemList0, ItemList),
|
|
map.det_update(ModuleName, ItemList, !SubModules)
|
|
;
|
|
map.det_insert(ModuleName, ModuleItemList, !SubModules)
|
|
).
|
|
|
|
:- pred report_error_implementation_in_interface(module_name::in,
|
|
prog_context::in, list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
report_error_implementation_in_interface(ModuleName, Context, !Specs) :-
|
|
(
|
|
ModuleName = qualified(ParentModule0, ChildModule0),
|
|
ParentModule = ParentModule0,
|
|
ChildModule = ChildModule0
|
|
;
|
|
ModuleName = unqualified(_),
|
|
unexpected($module, $pred, "unqualified module name")
|
|
),
|
|
Pieces = [words("In interface for module"), sym_name(ParentModule),
|
|
suffix(":"), nl,
|
|
words("in definition of sub-module `" ++ ChildModule ++ "':"), nl,
|
|
words("error: `:- implementation.' declaration for sub-module\n"),
|
|
words("occurs in interface section of parent module.")],
|
|
Msg = simple_msg(Context, [always(Pieces)]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs].
|
|
|
|
:- pred report_duplicate_modules(set(module_name)::in, list(item)::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
report_duplicate_modules(Duplicates, Items, !Specs) :-
|
|
list.filter_map(is_duplicate_error(Duplicates), Items,
|
|
DuplicateErrorLists),
|
|
list.condense(DuplicateErrorLists, DuplicateErrors),
|
|
list.foldl(report_error_duplicate_module_decl, DuplicateErrors, !Specs).
|
|
|
|
:- pred is_duplicate_error(set(module_name)::in, item::in,
|
|
list(pair(module_name, prog_context))::out) is semidet.
|
|
|
|
is_duplicate_error(DuplicatesSet, Item, SubModuleNameContexts) :-
|
|
(
|
|
Item = item_module_start(ItemModuleStart),
|
|
ItemModuleStart = item_module_start_info(SubModuleName, Context, _),
|
|
set.member(SubModuleName, DuplicatesSet),
|
|
SubModuleNameContexts = [SubModuleName - Context]
|
|
;
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context, _),
|
|
ModuleDefn = md_include_module(SubModuleNames),
|
|
set.list_to_set(SubModuleNames, SubModuleNamesSet),
|
|
set.intersect(SubModuleNamesSet, DuplicatesSet,
|
|
DuplicatedSubModuleNamesSet),
|
|
set.to_sorted_list(DuplicatedSubModuleNamesSet,
|
|
DuplicatedSubModuleNames),
|
|
SubModuleNameContexts =
|
|
list.map(pair_with_context(Context), DuplicatedSubModuleNames)
|
|
).
|
|
|
|
:- func pair_with_context(prog_context, module_name) =
|
|
pair(module_name, prog_context).
|
|
|
|
pair_with_context(Context, ModuleName) = ModuleName - Context.
|
|
|
|
:- pred report_error_duplicate_module_decl(pair(module_name, prog_context)::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
report_error_duplicate_module_decl(ModuleName - Context, !Specs) :-
|
|
(
|
|
ModuleName = qualified(ParentModule0, ChildModule0),
|
|
ParentModule = ParentModule0,
|
|
ChildModule = ChildModule0
|
|
;
|
|
ModuleName = unqualified(_),
|
|
unexpected($module, $pred, "unqualified module name")
|
|
),
|
|
Pieces = [words("In module"), sym_name(ParentModule), suffix(":"), nl,
|
|
words("error: sub-module `" ++ ChildModule ++ "' declared"),
|
|
words("as both a separate sub-module and a nested sub-module.")],
|
|
Msg = simple_msg(Context, [always(Pieces)]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs].
|
|
|
|
:- pred report_items_after_end_module(prog_context::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
report_items_after_end_module(Context, !Specs) :-
|
|
Pieces = [words("Error: item(s) after end_module declaration.")],
|
|
Msg = simple_msg(Context, [always(Pieces)]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs].
|
|
|
|
:- pred report_non_abstract_instance_in_interface(prog_context::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
report_non_abstract_instance_in_interface(Context, !Specs) :-
|
|
Pieces = [words("Error: non-abstract instance declaration"),
|
|
words("in module interface.")],
|
|
Msg = simple_msg(Context, [always(Pieces)]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs].
|
|
|
|
get_interface(ModuleName, IncludeImplTypes, Items0, Items) :-
|
|
AddToImpl = (func(_, ImplItems) = ImplItems),
|
|
get_interface_and_implementation_2(IncludeImplTypes, Items0, no,
|
|
[], RevItems, AddToImpl, unit, _),
|
|
list.reverse(RevItems, Items1),
|
|
maybe_add_foreign_import_module(ModuleName, Items1, Items2),
|
|
order_items(Items2, Items).
|
|
|
|
:- pred get_interface_and_implementation(module_name::in, bool::in,
|
|
list(item)::in, list(item)::out, list(item)::out) is det.
|
|
|
|
get_interface_and_implementation(ModuleName, IncludeImplTypes,
|
|
Items0, InterfaceItems, ImplementationItems) :-
|
|
AddToImpl = (func(ImplItem, ImplItems) = [ImplItem | ImplItems]),
|
|
get_interface_and_implementation_2(IncludeImplTypes, Items0, no,
|
|
[], RevIntItems, AddToImpl, [], RevImplItems),
|
|
list.reverse(RevIntItems, InterfaceItems0),
|
|
list.reverse(RevImplItems, ImplementationItems),
|
|
maybe_add_foreign_import_module(ModuleName,
|
|
InterfaceItems0, InterfaceItems).
|
|
|
|
:- pred init_module_and_imports(file_name::in,
|
|
module_name::in, module_name::in, list(item)::in, list(error_spec)::in,
|
|
list(module_name)::in, list(module_name)::in, list(string)::in,
|
|
maybe(module_timestamps)::in, module_and_imports::out) is det.
|
|
|
|
init_module_and_imports(SourceFileName, SourceFileModuleName, ModuleName,
|
|
Items0, Specs, PublicChildren, NestedChildren, FactDeps,
|
|
MaybeTimestamps, Module) :-
|
|
% XXX The reason why init_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.
|
|
maybe_add_foreign_import_module(ModuleName, Items0, Items),
|
|
ItemsCord = cord.from_list(Items),
|
|
Module = module_and_imports(SourceFileName, SourceFileModuleName,
|
|
ModuleName, [], [], [], [], [], PublicChildren,
|
|
NestedChildren, FactDeps, contains_foreign_code_unknown, [],
|
|
contains_no_foreign_export, ItemsCord, Specs, no_module_errors,
|
|
MaybeTimestamps, no_main, dir.this_directory).
|
|
|
|
:- pred maybe_add_foreign_import_module(module_name::in,
|
|
list(item)::in, list(item)::out) is det.
|
|
|
|
maybe_add_foreign_import_module(ModuleName, Items0, Items) :-
|
|
get_foreign_self_imports(Items0, Langs),
|
|
ImportItems = list.map(make_foreign_import(ModuleName), Langs),
|
|
Items = ImportItems ++ Items0.
|
|
|
|
:- func make_foreign_import(module_name, foreign_language) = item.
|
|
|
|
make_foreign_import(ModuleName, Lang) = Item :-
|
|
Origin = compiler(foreign_imports),
|
|
Info = pragma_info_foreign_import_module(Lang, ModuleName),
|
|
Pragma = pragma_foreign_import_module(Info),
|
|
ItemPragma = item_pragma_info(Origin, Pragma, term.context_init, -1),
|
|
Item = item_pragma(ItemPragma).
|
|
|
|
get_foreign_self_imports(Items, Langs) :-
|
|
list.foldl(accumulate_item_foreign_import_langs, Items, set.init, LangSet),
|
|
set.to_sorted_list(LangSet, Langs).
|
|
|
|
:- pred accumulate_item_foreign_import_langs(item::in,
|
|
set(foreign_language)::in, set(foreign_language)::out) is det.
|
|
|
|
accumulate_item_foreign_import_langs(Item, !LangSet) :-
|
|
Langs = item_needs_foreign_imports(Item),
|
|
set.insert_list(Langs, !LangSet).
|
|
|
|
:- pred get_interface_and_implementation_2(bool::in, list(item)::in, bool::in,
|
|
list(item)::in, list(item)::out,
|
|
func(item, T) = T::in, T::in, T::out) is det.
|
|
|
|
get_interface_and_implementation_2(_, [], _, !RevIntItems, _, !RevImplItems).
|
|
get_interface_and_implementation_2(IncludeImplTypes, [Item | Rest],
|
|
!.InInterface, !RevIntItems, AddImplItem, !RevImplItems) :-
|
|
(
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
( ModuleDefn = md_interface
|
|
; ModuleDefn = md_implementation
|
|
; ModuleDefn = md_imported(_)
|
|
; ModuleDefn = md_used(_)
|
|
)
|
|
->
|
|
(
|
|
ModuleDefn = md_interface,
|
|
!:RevIntItems = [Item | !.RevIntItems],
|
|
!:InInterface = yes,
|
|
get_interface_and_implementation_2(IncludeImplTypes, Rest,
|
|
!.InInterface, !RevIntItems, AddImplItem, !RevImplItems)
|
|
;
|
|
ModuleDefn = md_implementation,
|
|
!:RevIntItems = [Item | !.RevIntItems],
|
|
!:InInterface = no,
|
|
get_interface_and_implementation_2(IncludeImplTypes, Rest,
|
|
!.InInterface, !RevIntItems, AddImplItem, !RevImplItems)
|
|
;
|
|
( ModuleDefn = md_imported(_)
|
|
; ModuleDefn = md_used(_)
|
|
)
|
|
% Items after here are not part of this module, which is why
|
|
% we don't have a recursive call here.
|
|
)
|
|
;
|
|
(
|
|
!.InInterface = yes,
|
|
( Item = item_instance(ItemInstance) ->
|
|
% Include the abstract version of the instance in the
|
|
% interface, ...
|
|
AbstractItemInstance = make_instance_abstract(ItemInstance),
|
|
AbstractItem = item_instance(AbstractItemInstance),
|
|
!:RevIntItems = [AbstractItem | !.RevIntItems],
|
|
|
|
% ... and the concrete version in the implementation.
|
|
!:RevImplItems = AddImplItem(Item, !.RevImplItems)
|
|
;
|
|
!:RevIntItems = [Item | !.RevIntItems]
|
|
)
|
|
;
|
|
!.InInterface = no,
|
|
!:RevImplItems = AddImplItem(Item, !.RevImplItems),
|
|
(
|
|
IncludeImplTypes = yes,
|
|
include_in_int_file_implementation(Item) = yes
|
|
->
|
|
( make_abstract_defn(Item, int2, AbstractItem) ->
|
|
ItemToAdd = AbstractItem
|
|
; make_abstract_unify_compare(Item, int2, AbstractItem) ->
|
|
ItemToAdd = AbstractItem
|
|
;
|
|
ItemToAdd = Item
|
|
),
|
|
!:RevIntItems = [ItemToAdd | !.RevIntItems]
|
|
;
|
|
true
|
|
)
|
|
),
|
|
get_interface_and_implementation_2(IncludeImplTypes, Rest,
|
|
!.InInterface, !RevIntItems, AddImplItem, !RevImplItems)
|
|
).
|
|
|
|
:- type short_interface_kind
|
|
---> int2 % the qualified short interface, for the .int2 file
|
|
; int3. % the unqualified short interface, for the .int3 file
|
|
|
|
% Given a module interface (well, a list of items), extract the
|
|
% short interface part of that module, i.e. the exported
|
|
% type/typeclass/inst/mode declarations, but not the exported pred or
|
|
% constructor declarations. If the module interface imports
|
|
% other modules, then the short interface only needs to include
|
|
% those import_module declarations only if the short interface
|
|
% contains some equivalence types or some mode or inst definitions
|
|
% that might use declarations in the imported modules.
|
|
% If the short interface is empty, or only contains abstract
|
|
% type declarations, then it doesn't need any import_module
|
|
% declarations.
|
|
%
|
|
:- pred get_short_interface(list(item)::in, short_interface_kind::in,
|
|
list(item)::out) is det.
|
|
|
|
get_short_interface(Items0, Kind, Items) :-
|
|
get_short_interface_2(Items0, Kind, [], RevItems),
|
|
list.reverse(RevItems, Items1),
|
|
maybe_strip_import_decls(Items1, Items2),
|
|
order_items(Items2, Items).
|
|
|
|
:- pred get_short_interface_2(list(item)::in, short_interface_kind::in,
|
|
list(item)::in, list(item)::out) is det.
|
|
|
|
get_short_interface_2([], _Kind, !RevItems).
|
|
get_short_interface_2([Item | Items], Kind, !RevItems) :-
|
|
( make_abstract_defn(Item, Kind, AbstractItem) ->
|
|
!:RevItems = [AbstractItem | !.RevItems]
|
|
; make_abstract_unify_compare(Item, Kind, AbstractItem) ->
|
|
!:RevItems = [AbstractItem | !.RevItems]
|
|
;
|
|
Include = include_in_short_interface(Item),
|
|
(
|
|
Include = yes,
|
|
!:RevItems = [Item | !.RevItems]
|
|
;
|
|
Include = no
|
|
)
|
|
),
|
|
get_short_interface_2(Items, Kind, !RevItems).
|
|
|
|
:- func include_in_short_interface(item) = bool.
|
|
|
|
include_in_short_interface(Item) = Include :-
|
|
(
|
|
( Item = item_module_start(_)
|
|
; Item = item_module_end(_)
|
|
; Item = item_module_defn(_)
|
|
; Item = item_type_defn(_)
|
|
; Item = item_inst_defn(_)
|
|
; Item = item_mode_defn(_)
|
|
; Item = item_instance(_)
|
|
),
|
|
Include = yes
|
|
;
|
|
Item = item_pragma(ItemPragma),
|
|
ItemPragma = item_pragma_info(_, Pragma, _, _),
|
|
% XXX This if-then-else should be a switch, or (even better)
|
|
% we should take pragma_foreign_import_modules out of the pragma items
|
|
% and given them their own item type.
|
|
( Pragma = pragma_foreign_import_module(_) ->
|
|
Include = yes
|
|
;
|
|
Include = no
|
|
)
|
|
;
|
|
( Item = item_clause(_)
|
|
; Item = item_pred_decl(_)
|
|
; Item = item_mode_decl(_)
|
|
; Item = item_promise(_)
|
|
; Item = item_typeclass(_)
|
|
; Item = item_initialise(_)
|
|
; Item = item_finalise(_)
|
|
; Item = item_mutable(_)
|
|
; Item = item_nothing(_)
|
|
),
|
|
Include = no
|
|
).
|
|
|
|
% Could this item use items from imported modules.
|
|
%
|
|
:- func item_needs_imports(item) = bool.
|
|
|
|
item_needs_imports(Item) = NeedsImports :-
|
|
(
|
|
Item = item_type_defn(ItemTypeDefn),
|
|
( ItemTypeDefn ^ td_ctor_defn = parse_tree_abstract_type(_) ->
|
|
NeedsImports = no
|
|
;
|
|
NeedsImports = yes
|
|
)
|
|
;
|
|
( Item = item_clause(_)
|
|
; Item = item_inst_defn(_)
|
|
; Item = item_mode_defn(_)
|
|
; Item = item_pragma(_)
|
|
; Item = item_pred_decl(_)
|
|
; Item = item_mode_decl(_)
|
|
; Item = item_typeclass(_)
|
|
; Item = item_instance(_)
|
|
; Item = item_promise(_)
|
|
; Item = item_initialise(_)
|
|
; Item = item_finalise(_)
|
|
; Item = item_mutable(_)
|
|
),
|
|
NeedsImports = yes
|
|
;
|
|
( Item = item_module_start(_)
|
|
; Item = item_module_end(_)
|
|
; Item = item_module_defn(_)
|
|
; Item = item_nothing(_)
|
|
),
|
|
NeedsImports = no
|
|
).
|
|
|
|
:- func item_needs_foreign_imports(item) = list(foreign_language).
|
|
|
|
item_needs_foreign_imports(Item) = Langs :-
|
|
(
|
|
Item = item_mutable(_ItemMutable),
|
|
% We can use all foreign languages.
|
|
Langs = all_foreign_languages
|
|
;
|
|
Item = item_type_defn(ItemTypeDefn),
|
|
(
|
|
ItemTypeDefn ^ td_ctor_defn =
|
|
parse_tree_foreign_type(ForeignType, _, _)
|
|
->
|
|
Langs = [foreign_type_language(ForeignType)]
|
|
;
|
|
Langs = []
|
|
)
|
|
;
|
|
Item = item_pragma(ItemPragma),
|
|
ItemPragma = item_pragma_info(_, Pragma, _, _),
|
|
(
|
|
(
|
|
Pragma = pragma_foreign_decl(FDInfo),
|
|
FDInfo = pragma_info_foreign_decl(Lang, _, _)
|
|
;
|
|
Pragma = pragma_foreign_code(FCInfo),
|
|
FCInfo = pragma_info_foreign_code(Lang, _)
|
|
;
|
|
Pragma = pragma_foreign_enum(FEInfo),
|
|
FEInfo = pragma_info_foreign_enum(Lang, _, _)
|
|
;
|
|
Pragma = pragma_foreign_proc_export(FPEInfo),
|
|
FPEInfo = pragma_info_foreign_proc_export(Lang, _, _)
|
|
),
|
|
Langs = [Lang]
|
|
;
|
|
Pragma = pragma_foreign_proc(FPInfo),
|
|
FPInfo = pragma_info_foreign_proc(Attrs, _, _, _, _, _, _),
|
|
Langs = [get_foreign_language(Attrs)]
|
|
;
|
|
( Pragma = pragma_foreign_import_module(_)
|
|
; Pragma = pragma_foreign_export_enum(_)
|
|
; Pragma = pragma_type_spec(_)
|
|
; Pragma = pragma_inline(_)
|
|
; Pragma = pragma_no_inline(_)
|
|
; Pragma = pragma_unused_args(_)
|
|
; Pragma = pragma_exceptions(_)
|
|
; Pragma = pragma_trailing_info(_)
|
|
; Pragma = pragma_mm_tabling_info(_)
|
|
; Pragma = pragma_obsolete(_)
|
|
; Pragma = pragma_no_detism_warning(_)
|
|
; Pragma = pragma_source_file(_)
|
|
; Pragma = pragma_oisu(_)
|
|
; Pragma = pragma_tabled(_)
|
|
; Pragma = pragma_fact_table(_)
|
|
; Pragma = pragma_reserve_tag(_)
|
|
; Pragma = pragma_promise_eqv_clauses(_)
|
|
; Pragma = pragma_promise_pure(_)
|
|
; Pragma = pragma_promise_semipure(_)
|
|
; Pragma = pragma_termination_info(_)
|
|
; Pragma = pragma_termination2_info(_)
|
|
; Pragma = pragma_terminates(_)
|
|
; Pragma = pragma_does_not_terminate(_)
|
|
; Pragma = pragma_check_termination(_)
|
|
; Pragma = pragma_mode_check_clauses(_)
|
|
; Pragma = pragma_structure_sharing(_)
|
|
; Pragma = pragma_structure_reuse(_)
|
|
; Pragma = pragma_require_feature_set(_)
|
|
),
|
|
Langs = []
|
|
)
|
|
;
|
|
( Item = item_module_start(_)
|
|
; Item = item_module_end(_)
|
|
; Item = item_module_defn(_)
|
|
; Item = item_clause(_)
|
|
; Item = item_inst_defn(_)
|
|
; Item = item_mode_defn(_)
|
|
; Item = item_pred_decl(_)
|
|
; Item = item_mode_decl(_)
|
|
; Item = item_typeclass(_)
|
|
; Item = item_instance(_)
|
|
; Item = item_promise(_)
|
|
; Item = item_initialise(_)
|
|
; Item = item_finalise(_)
|
|
; Item = item_nothing(_)
|
|
),
|
|
Langs = []
|
|
).
|
|
|
|
:- func include_in_int_file_implementation(item) = bool.
|
|
|
|
include_in_int_file_implementation(Item) = Include :-
|
|
(
|
|
% `:- typeclass declarations' may be referred to by the constructors
|
|
% in type declarations. Since these constructors are abstractly
|
|
% exported, we won't need the local instance declarations.
|
|
( Item = item_type_defn(_)
|
|
; Item = item_typeclass(_)
|
|
),
|
|
Include = yes
|
|
;
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
(
|
|
% XXX Some of these should yield an exception.
|
|
( ModuleDefn = md_interface
|
|
; ModuleDefn = md_implementation
|
|
; ModuleDefn = md_implementation_but_exported_to_submodules
|
|
; ModuleDefn = md_imported(_)
|
|
; ModuleDefn = md_used(_)
|
|
; ModuleDefn = md_abstract_imported
|
|
; ModuleDefn = md_opt_imported
|
|
; ModuleDefn = md_transitively_imported
|
|
; ModuleDefn = md_export(_)
|
|
; ModuleDefn = md_import(_)
|
|
; ModuleDefn = md_use(_)
|
|
; ModuleDefn = md_include_module(_)
|
|
; ModuleDefn = md_version_numbers(_, _)
|
|
),
|
|
Include = yes
|
|
;
|
|
ModuleDefn = md_external(_, _),
|
|
Include = no
|
|
)
|
|
;
|
|
Item = item_pragma(ItemPragma),
|
|
ItemPragma = item_pragma_info(_, Pragma, _, _),
|
|
(
|
|
( Pragma = pragma_foreign_import_module(_)
|
|
; Pragma = pragma_foreign_enum(_)
|
|
),
|
|
Include = yes
|
|
;
|
|
% XXX I am not sure about the proper value of Include
|
|
% for some of these. -zs
|
|
( Pragma = pragma_foreign_decl(_)
|
|
; Pragma = pragma_foreign_code(_)
|
|
; Pragma = pragma_foreign_proc(_)
|
|
; Pragma = pragma_foreign_proc_export(_)
|
|
; Pragma = pragma_foreign_export_enum(_)
|
|
; Pragma = pragma_type_spec(_)
|
|
; Pragma = pragma_inline(_)
|
|
; Pragma = pragma_no_inline(_)
|
|
; Pragma = pragma_unused_args(_)
|
|
; Pragma = pragma_exceptions(_)
|
|
; Pragma = pragma_trailing_info(_)
|
|
; Pragma = pragma_mm_tabling_info(_)
|
|
; Pragma = pragma_obsolete(_)
|
|
; Pragma = pragma_no_detism_warning(_)
|
|
; Pragma = pragma_source_file(_)
|
|
; Pragma = pragma_oisu(_)
|
|
; Pragma = pragma_tabled(_)
|
|
; Pragma = pragma_fact_table(_)
|
|
; Pragma = pragma_reserve_tag(_)
|
|
; Pragma = pragma_promise_eqv_clauses(_)
|
|
; Pragma = pragma_promise_pure(_)
|
|
; Pragma = pragma_promise_semipure(_)
|
|
; Pragma = pragma_termination_info(_)
|
|
; Pragma = pragma_termination2_info(_)
|
|
; Pragma = pragma_terminates(_)
|
|
; Pragma = pragma_does_not_terminate(_)
|
|
; Pragma = pragma_check_termination(_)
|
|
; Pragma = pragma_mode_check_clauses(_)
|
|
; Pragma = pragma_structure_sharing(_)
|
|
; Pragma = pragma_structure_reuse(_)
|
|
; Pragma = pragma_require_feature_set(_)
|
|
),
|
|
Include = no
|
|
)
|
|
;
|
|
( Item = item_module_start(_)
|
|
; Item = item_module_end(_)
|
|
; Item = item_clause(_)
|
|
; Item = item_inst_defn(_)
|
|
; Item = item_mode_defn(_)
|
|
; Item = item_pred_decl(_)
|
|
; Item = item_mode_decl(_)
|
|
; Item = item_promise(_)
|
|
; Item = item_instance(_)
|
|
; Item = item_initialise(_)
|
|
; Item = item_finalise(_)
|
|
; Item = item_mutable(_)
|
|
; Item = item_nothing(_)
|
|
),
|
|
Include = no
|
|
).
|
|
|
|
% XXX make_abstract_defn should be merged with make_abstract_unify_compare
|
|
% and made det, returning the unchanged item if it does not need to be made
|
|
% abstract (so we can use det switches instead semidet tests in the code).
|
|
|
|
:- pred make_abstract_defn(item::in, short_interface_kind::in, item::out)
|
|
is semidet.
|
|
|
|
make_abstract_defn(Item, ShortInterfaceKind, AbstractItem) :-
|
|
(
|
|
Item = item_type_defn(ItemTypeDefn),
|
|
TypeDefn = ItemTypeDefn ^ td_ctor_defn,
|
|
(
|
|
TypeDefn = parse_tree_du_type(Ctors, _, _),
|
|
( du_type_is_enum(Ctors, NumBits) ->
|
|
AbstractDetails = abstract_enum_type(NumBits)
|
|
;
|
|
AbstractDetails = abstract_type_general
|
|
),
|
|
% For the `.int2' files, we need the full definitions of
|
|
% discriminated union types. Even if the functors for a type
|
|
% are not used within a module, we may need to know them for
|
|
% comparing insts, e.g. for comparing `ground' and `bound(...)'.
|
|
ShortInterfaceKind = int3
|
|
;
|
|
TypeDefn = parse_tree_abstract_type(AbstractDetails)
|
|
;
|
|
TypeDefn = parse_tree_solver_type(_, _),
|
|
% rafe: XXX we need to also export the details of the
|
|
% forwarding type for the representation and the forwarding
|
|
% pred for initialization.
|
|
AbstractDetails = abstract_solver_type
|
|
;
|
|
TypeDefn = parse_tree_eqv_type(_),
|
|
% XXX is this right for solver types?
|
|
AbstractDetails = abstract_type_general,
|
|
% For the `.int2' files, we need the full definitions of
|
|
% equivalence types. They are needed to ensure that
|
|
% non-abstract equivalence types always get fully expanded
|
|
% before code generation, even in modules that only indirectly
|
|
% import the definition of the equivalence type.
|
|
% But the full definitions are not needed for the `.int3'
|
|
% files. So we convert equivalence types into abstract
|
|
% types only for the `.int3' files.
|
|
ShortInterfaceKind = int3
|
|
;
|
|
TypeDefn = parse_tree_foreign_type(_, _, _),
|
|
% We always need the definitions of foreign types
|
|
% to handle inter-language interfacing correctly.
|
|
AbstractDetails = abstract_type_general,
|
|
semidet_fail
|
|
),
|
|
AbstractItemTypeDefn = ItemTypeDefn ^ td_ctor_defn
|
|
:= parse_tree_abstract_type(AbstractDetails),
|
|
AbstractItem = item_type_defn(AbstractItemTypeDefn)
|
|
;
|
|
Item = item_instance(ItemInstance),
|
|
ShortInterfaceKind = int2,
|
|
AbstractItemInstance = make_instance_abstract(ItemInstance),
|
|
AbstractItem = item_instance(AbstractItemInstance)
|
|
;
|
|
Item = item_typeclass(ItemTypeClass),
|
|
AbstractItemTypeClass = ItemTypeClass ^ tc_class_methods
|
|
:= class_interface_abstract,
|
|
AbstractItem = item_typeclass(AbstractItemTypeClass)
|
|
).
|
|
|
|
:- pred du_type_is_enum(list(constructor)::in, int::out) is semidet.
|
|
|
|
du_type_is_enum(Ctors, NumBits) :-
|
|
Ctors = [_, _ | _],
|
|
all [Ctor] (
|
|
list.member(Ctor, Ctors)
|
|
=> (
|
|
Ctor = ctor(ExistQTVars, ExistConstraints, _Name, Args, _Context),
|
|
ExistQTVars = [],
|
|
ExistConstraints = [],
|
|
Args = []
|
|
)),
|
|
list.length(Ctors, NumFunctors),
|
|
int.log2(NumFunctors, NumBits).
|
|
|
|
:- pred make_abstract_unify_compare(item::in, short_interface_kind::in,
|
|
item::out) is semidet.
|
|
|
|
make_abstract_unify_compare(Item, int2, AbstractItem) :-
|
|
Item = item_type_defn(ItemTypeDefn),
|
|
TypeDefn = ItemTypeDefn ^ td_ctor_defn,
|
|
(
|
|
TypeDefn = parse_tree_du_type(Constructors, yes(_UserEqComp),
|
|
MaybeDirectArgCtors),
|
|
MaybeUserEqComp = yes(abstract_noncanonical_type(non_solver_type)),
|
|
AbstractTypeDefn = parse_tree_du_type(Constructors, MaybeUserEqComp,
|
|
MaybeDirectArgCtors)
|
|
;
|
|
TypeDefn = parse_tree_foreign_type(ForeignType,
|
|
yes(_UserEqComp), Assertions),
|
|
AbstractTypeDefn = parse_tree_foreign_type(ForeignType,
|
|
yes(abstract_noncanonical_type(non_solver_type)), Assertions)
|
|
;
|
|
TypeDefn = parse_tree_solver_type(SolverTypeDetails, yes(_UserEqComp)),
|
|
AbstractTypeDefn = parse_tree_solver_type(SolverTypeDetails,
|
|
yes(abstract_noncanonical_type(solver_type)))
|
|
),
|
|
AbstractItemTypeDefn = ItemTypeDefn ^ td_ctor_defn := AbstractTypeDefn,
|
|
AbstractItem = item_type_defn(AbstractItemTypeDefn).
|
|
|
|
% All instance declarations must be written to `.int' files as
|
|
% abstract instance declarations, because the method names have not yet
|
|
% been module qualified. This could cause the wrong predicate to be
|
|
% used if calls to the method are specialized.
|
|
%
|
|
:- func make_instance_abstract(item_instance_info) = item_instance_info.
|
|
|
|
make_instance_abstract(Info0) = Info :-
|
|
Info = Info0 ^ ci_method_instances := instance_body_abstract.
|
|
|
|
:- pred maybe_strip_import_decls(list(item)::in, list(item)::out) is det.
|
|
|
|
maybe_strip_import_decls(!Items) :-
|
|
(
|
|
some [Item] (
|
|
list.member(Item, !.Items),
|
|
item_needs_imports(Item) = yes
|
|
)
|
|
->
|
|
true
|
|
;
|
|
list.filter(not_import_or_use_item, !Items)
|
|
),
|
|
(
|
|
some [Item] (
|
|
list.member(Item, !.Items),
|
|
item_needs_foreign_imports(Item) = [_ | _]
|
|
)
|
|
->
|
|
true
|
|
;
|
|
NotPragmaForeignImport =
|
|
(pred(ThisItem::in) is semidet :-
|
|
\+ (
|
|
ThisItem = item_pragma(ThisItemPragma),
|
|
ThisItemPragma = item_pragma_info(_, Pragma, _, _),
|
|
Pragma = pragma_foreign_import_module(_)
|
|
)
|
|
),
|
|
list.filter(NotPragmaForeignImport, !Items)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Put the given list of items into a sort of standard order. The idea is
|
|
% that just reordering the contents of e.g. an interface section without
|
|
% changing the set of exported entities should not cause a change in the
|
|
% interface files. The "sort of" is because we are not doing as good a job
|
|
% as we could. Unfortunately, doing significantly better is quite hard
|
|
% with the current representation of the module, which is just a list of
|
|
% items without further structure.
|
|
%
|
|
:- pred order_items(list(item)::in, list(item)::out) is det.
|
|
|
|
order_items(Items0, Items) :-
|
|
filter_unnecessary_flips(Items0, other, Items1),
|
|
do_order_items(Items1, Items2),
|
|
% Delete any redundant :- interface and :- implementation markers at the
|
|
% end, to make Items as insensitive as we can to the number of interface
|
|
% sections in the source file. If some of the implementation sections
|
|
% are not empty, we won't be fully successful.
|
|
list.reverse(Items2, RevItems2),
|
|
list.takewhile(interface_or_import_marker, RevItems2, _, RevItems),
|
|
list.reverse(RevItems, Items).
|
|
|
|
:- pred interface_or_import_marker(item::in) is semidet.
|
|
|
|
interface_or_import_marker(Item) :-
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
( ModuleDefn = md_interface
|
|
; ModuleDefn = md_implementation
|
|
).
|
|
|
|
:- pred not_import_or_use_item(item::in) is semidet.
|
|
|
|
not_import_or_use_item(Item) :-
|
|
not import_or_use_item(Item).
|
|
|
|
:- pred import_or_use_item(item::in) is semidet.
|
|
|
|
import_or_use_item(Item) :-
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
( ModuleDefn = md_import(_)
|
|
; ModuleDefn = md_use(_)
|
|
).
|
|
|
|
% Which section of the module we are in. The "other" alternative
|
|
% reflects my ignorance (based on the lack of documentation) of
|
|
% the invariants that govern the items involved in the representation
|
|
% of nested modules. -zs
|
|
|
|
:- type cur_pos
|
|
---> in_interface
|
|
; in_implementation
|
|
; other.
|
|
|
|
:- pred filter_unnecessary_flips(list(item)::in, cur_pos::in, list(item)::out)
|
|
is det.
|
|
|
|
filter_unnecessary_flips([], _, []).
|
|
filter_unnecessary_flips([Item], _, [Item]).
|
|
filter_unnecessary_flips([Item1, Item2 | Items0], CurPos, Items) :-
|
|
(
|
|
CurPos = in_interface,
|
|
Item1 = item_module_defn(ItemModuleDefn1),
|
|
ItemModuleDefn1 = item_module_defn_info(md_implementation, _, _),
|
|
Item2 = item_module_defn(ItemModuleDefn2),
|
|
ItemModuleDefn2 = item_module_defn_info(md_interface, _, _)
|
|
->
|
|
filter_unnecessary_flips(Items0, CurPos, Items)
|
|
;
|
|
CurPos = in_implementation,
|
|
Item1 = item_module_defn(ItemModuleDefn1),
|
|
ItemModuleDefn1 = item_module_defn_info(md_interface, _, _),
|
|
Item2 = item_module_defn(ItemModuleDefn2),
|
|
ItemModuleDefn2 = item_module_defn_info(md_implementation, _, _)
|
|
->
|
|
filter_unnecessary_flips(Items0, CurPos, Items)
|
|
;
|
|
(
|
|
Item1 = item_module_defn(ItemModuleDefn1),
|
|
ItemModuleDefn1 = item_module_defn_info(md_implementation, _, _)
|
|
->
|
|
NextPos = in_implementation
|
|
;
|
|
Item1 = item_module_defn(ItemModuleDefn1),
|
|
ItemModuleDefn1 = item_module_defn_info(md_interface, _, _)
|
|
->
|
|
NextPos = in_interface
|
|
;
|
|
Chunkable1 = chunkable_item(Item1),
|
|
(
|
|
Chunkable1 = yes,
|
|
NextPos = CurPos
|
|
;
|
|
Chunkable1 = no,
|
|
NextPos = other
|
|
)
|
|
),
|
|
filter_unnecessary_flips([Item2 | Items0], NextPos, ItemsTail),
|
|
Items = [Item1 | ItemsTail]
|
|
).
|
|
|
|
% Find a chunk of items which should in most cases (but unfortunately
|
|
% not all cases) be all the exported items, and put them in a standard
|
|
% order, with import_module and use_module items first in lexical order,
|
|
% then type, inst and mode definitions, again in lexical order, then
|
|
% pred and predmode declarations, in lexical order by sym_name, and
|
|
% finally all other items in the chunk. The chunk consists of the initial
|
|
% prefix of items for which this reordering is safe. The chunk will then
|
|
% be followed by the ordered versions of later chunks, if any.
|
|
%
|
|
:- pred do_order_items(list(item)::in, list(item)::out) is det.
|
|
|
|
do_order_items([], []).
|
|
do_order_items([Item0 | Items0], OrderedItems) :-
|
|
Chunkable0 = chunkable_item(Item0),
|
|
(
|
|
Chunkable0 = yes,
|
|
list.takewhile(is_chunkable, Items0, FrontItems, RemainItems),
|
|
list.filter(is_reorderable, [Item0 | FrontItems],
|
|
ReorderableItems, NonReorderableItems),
|
|
list.filter(import_or_use, ReorderableItems,
|
|
ImportReorderableItems, NonImportReorderableItems),
|
|
list.filter(symname_orderable, NonReorderableItems,
|
|
SymNameItems, NonSymNameItems),
|
|
% We rely on the sort being stable to keep the items with the same
|
|
% sym_names in their original order.
|
|
list.sort(compare_by_symname, SymNameItems, OrderedSymNameItems),
|
|
do_order_items(RemainItems, OrderedRemainItems),
|
|
OrderedItems = list.sort(ImportReorderableItems) ++
|
|
list.sort(NonImportReorderableItems) ++
|
|
OrderedSymNameItems ++ NonSymNameItems ++ OrderedRemainItems
|
|
;
|
|
Chunkable0 = no,
|
|
do_order_items(Items0, OrderedItemsTail),
|
|
OrderedItems = [Item0 | OrderedItemsTail]
|
|
).
|
|
|
|
:- pred import_or_use(item::in) is semidet.
|
|
|
|
import_or_use(item_module_defn(ItemModuleDefn)) :-
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
( ModuleDefn = md_import(_)
|
|
; ModuleDefn = md_use(_)
|
|
).
|
|
|
|
:- pred is_reorderable(item::in) is semidet.
|
|
|
|
is_reorderable(Item) :-
|
|
reorderable_item(Item) = yes.
|
|
|
|
% The kinds of items for which reorderable_item returns yes can be
|
|
% arbitrarily reordered with respect to each other and with respect to
|
|
% other chunkable items in all kinds of interface files (.int, .int2,
|
|
% .int3, and .int0). This predicate is not relevant to .opt and
|
|
% .trans_opt files, since those are generated from the HLDS, not
|
|
% from item lists.
|
|
%
|
|
% We should make this predicate call "unexpected" for items that should
|
|
% never occur in interface files. However, I don't have a reliable list
|
|
% of exactly which items those are.
|
|
%
|
|
:- func reorderable_item(item) = bool.
|
|
|
|
reorderable_item(Item) = Reorderable :-
|
|
(
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
Reorderable = reorderable_module_defn(ModuleDefn)
|
|
;
|
|
Item = item_pragma(ItemPragma),
|
|
ItemPragma = item_pragma_info(_, Pragma, _, _),
|
|
Reorderable = reorderable_pragma_type(Pragma)
|
|
;
|
|
( Item = item_type_defn(_)
|
|
; Item = item_inst_defn(_)
|
|
; Item = item_mode_defn(_)
|
|
; Item = item_promise(_)
|
|
; Item = item_typeclass(_)
|
|
; Item = item_instance(_)
|
|
),
|
|
Reorderable = yes
|
|
;
|
|
( Item = item_module_start(_)
|
|
; Item = item_module_end(_)
|
|
; Item = item_clause(_)
|
|
; Item = item_pred_decl(_)
|
|
; Item = item_mode_decl(_)
|
|
; Item = item_initialise(_)
|
|
; Item = item_finalise(_)
|
|
; Item = item_mutable(_)
|
|
; Item = item_nothing(_)
|
|
),
|
|
Reorderable = no
|
|
).
|
|
|
|
:- func reorderable_module_defn(module_defn) = bool.
|
|
|
|
reorderable_module_defn(ModuleDefn) = Reorderable :-
|
|
(
|
|
( ModuleDefn = md_import(_)
|
|
; ModuleDefn = md_export(_)
|
|
; ModuleDefn = md_external(_, _)
|
|
; ModuleDefn = md_use(_)
|
|
),
|
|
Reorderable = yes
|
|
;
|
|
( ModuleDefn = md_abstract_imported
|
|
; ModuleDefn = md_implementation
|
|
; ModuleDefn = md_imported(_)
|
|
; ModuleDefn = md_include_module(_)
|
|
; ModuleDefn = md_interface
|
|
; ModuleDefn = md_implementation_but_exported_to_submodules
|
|
; ModuleDefn = md_opt_imported
|
|
; ModuleDefn = md_transitively_imported
|
|
; ModuleDefn = md_used(_)
|
|
; ModuleDefn = md_version_numbers(_, _)
|
|
),
|
|
Reorderable = no
|
|
).
|
|
|
|
:- func reorderable_pragma_type(pragma_type) = bool.
|
|
|
|
reorderable_pragma_type(Pragma) = Reorderable :-
|
|
(
|
|
( Pragma = pragma_check_termination( _)
|
|
; Pragma = pragma_does_not_terminate( _)
|
|
; Pragma = pragma_exceptions(_)
|
|
; Pragma = pragma_trailing_info(_)
|
|
; Pragma = pragma_mm_tabling_info(_)
|
|
; Pragma = pragma_foreign_proc_export(_)
|
|
; Pragma = pragma_foreign_export_enum(_)
|
|
; Pragma = pragma_foreign_enum(_)
|
|
; Pragma = pragma_inline(_)
|
|
; Pragma = pragma_mode_check_clauses(_)
|
|
; Pragma = pragma_no_inline(_)
|
|
; Pragma = pragma_obsolete(_)
|
|
; Pragma = pragma_no_detism_warning(_)
|
|
; Pragma = pragma_promise_pure(_)
|
|
; Pragma = pragma_promise_semipure(_)
|
|
; Pragma = pragma_promise_eqv_clauses(_)
|
|
; Pragma = pragma_reserve_tag(_)
|
|
; Pragma = pragma_oisu(_)
|
|
; Pragma = pragma_tabled(_)
|
|
; Pragma = pragma_terminates(_)
|
|
; Pragma = pragma_termination_info(_)
|
|
; Pragma = pragma_structure_sharing(_)
|
|
; Pragma = pragma_structure_reuse(_)
|
|
; Pragma = pragma_type_spec(_)
|
|
; Pragma = pragma_unused_args(_)
|
|
; Pragma = pragma_require_feature_set(_)
|
|
),
|
|
Reorderable = yes
|
|
;
|
|
( Pragma = pragma_foreign_code(_)
|
|
; Pragma = pragma_foreign_decl(_)
|
|
; Pragma = pragma_foreign_import_module(_)
|
|
; Pragma = pragma_foreign_proc(_)
|
|
; Pragma = pragma_source_file(_)
|
|
; Pragma = pragma_termination2_info(_)
|
|
; Pragma = pragma_fact_table(_)
|
|
),
|
|
Reorderable = no
|
|
).
|
|
|
|
:- pred is_chunkable(item::in) is semidet.
|
|
|
|
is_chunkable(Item) :-
|
|
chunkable_item(Item) = yes.
|
|
|
|
% Given a list of items for which chunkable_item returns yes, we need
|
|
% to keep the relative order of the non-reorderable items, but we can
|
|
% move the reorderable items around arbitrarily.
|
|
%
|
|
% We should make this predicate call "unexpected" for items that should
|
|
% never occur in interface files. However, I don't have a reliable list
|
|
% of exactly which items those are.
|
|
%
|
|
:- func chunkable_item(item) = bool.
|
|
|
|
chunkable_item(Item) = Chunkable :-
|
|
(
|
|
Item = item_module_defn(ItemModuleDefn),
|
|
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _),
|
|
Chunkable = chunkable_module_defn(ModuleDefn)
|
|
;
|
|
Item = item_pragma(ItemPragma),
|
|
ItemPragma = item_pragma_info(_, Pragma, _, _),
|
|
Chunkable = chunkable_pragma_type(Pragma)
|
|
;
|
|
( Item = item_clause(_)
|
|
; 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_initialise(_)
|
|
; Item = item_finalise(_)
|
|
; Item = item_nothing(_)
|
|
),
|
|
Chunkable = yes
|
|
;
|
|
( Item = item_module_start(_)
|
|
; Item = item_module_end(_)
|
|
; Item = item_mutable(_)
|
|
),
|
|
Chunkable = no
|
|
).
|
|
|
|
:- func chunkable_module_defn(module_defn) = bool.
|
|
|
|
chunkable_module_defn(ModuleDefn) = Reorderable :-
|
|
(
|
|
( ModuleDefn = md_export(_)
|
|
; ModuleDefn = md_external(_, _)
|
|
; ModuleDefn = md_import(_)
|
|
; ModuleDefn = md_use(_)
|
|
),
|
|
Reorderable = yes
|
|
;
|
|
( ModuleDefn = md_abstract_imported
|
|
; ModuleDefn = md_implementation
|
|
; ModuleDefn = md_imported(_)
|
|
; ModuleDefn = md_include_module(_)
|
|
; ModuleDefn = md_interface
|
|
; ModuleDefn = md_implementation_but_exported_to_submodules
|
|
; ModuleDefn = md_opt_imported
|
|
; ModuleDefn = md_transitively_imported
|
|
; ModuleDefn = md_used(_)
|
|
; ModuleDefn = md_version_numbers(_, _)
|
|
),
|
|
Reorderable = no
|
|
).
|
|
|
|
:- func chunkable_pragma_type(pragma_type) = bool.
|
|
|
|
chunkable_pragma_type(Pragma) = Chunkable :-
|
|
(
|
|
( Pragma = pragma_check_termination(_)
|
|
; Pragma = pragma_does_not_terminate(_)
|
|
; Pragma = pragma_foreign_proc_export(_)
|
|
; Pragma = pragma_foreign_export_enum(_)
|
|
; Pragma = pragma_foreign_enum(_)
|
|
; Pragma = pragma_inline(_)
|
|
; Pragma = pragma_mode_check_clauses(_)
|
|
; Pragma = pragma_no_inline(_)
|
|
; Pragma = pragma_obsolete(_)
|
|
; Pragma = pragma_no_detism_warning(_)
|
|
; Pragma = pragma_promise_pure(_)
|
|
; Pragma = pragma_promise_semipure(_)
|
|
; Pragma = pragma_promise_eqv_clauses(_)
|
|
; Pragma = pragma_reserve_tag(_)
|
|
; Pragma = pragma_oisu(_)
|
|
; Pragma = pragma_tabled(_)
|
|
; Pragma = pragma_terminates(_)
|
|
; Pragma = pragma_termination_info(_)
|
|
; Pragma = pragma_structure_sharing(_)
|
|
; Pragma = pragma_structure_reuse(_)
|
|
; Pragma = pragma_trailing_info(_)
|
|
; Pragma = pragma_mm_tabling_info(_)
|
|
; Pragma = pragma_type_spec(_)
|
|
; Pragma = pragma_unused_args(_)
|
|
; Pragma = pragma_require_feature_set(_)
|
|
),
|
|
Chunkable = yes
|
|
;
|
|
( Pragma = pragma_exceptions(_)
|
|
; Pragma = pragma_fact_table(_)
|
|
; Pragma = pragma_foreign_code(_)
|
|
; Pragma = pragma_foreign_decl(_)
|
|
; Pragma = pragma_foreign_import_module(_)
|
|
; Pragma = pragma_foreign_proc(_)
|
|
; Pragma = pragma_source_file(_)
|
|
; Pragma = pragma_termination2_info(_)
|
|
),
|
|
Chunkable = no
|
|
).
|
|
|
|
% Given a list of items for which symname_ordered succeeds, we need to keep
|
|
% the relative order of the items with the same sym_name as returned by
|
|
% symname_ordered, but the relative order of items with different sym_names
|
|
% doesn't matter.
|
|
%
|
|
:- pred symname_ordered(item::in, sym_name::out) is semidet.
|
|
|
|
symname_ordered(Item, Name) :-
|
|
(
|
|
Item = item_pred_decl(ItemPredDecl),
|
|
Name = ItemPredDecl ^ pf_name
|
|
;
|
|
Item = item_mode_decl(ItemModeDecl),
|
|
Name = ItemModeDecl ^ pfm_name
|
|
).
|
|
|
|
:- pred symname_orderable(item::in) is semidet.
|
|
|
|
symname_orderable(Item) :-
|
|
symname_ordered(Item, _).
|
|
|
|
:- pred compare_by_symname(item::in, item::in, comparison_result::out) is det.
|
|
|
|
compare_by_symname(ItemA, ItemB, Result) :-
|
|
(
|
|
symname_ordered(ItemA, SymNameA),
|
|
symname_ordered(ItemB, SymNameB)
|
|
->
|
|
compare(Result, SymNameA, SymNameB)
|
|
;
|
|
unexpected($module, $pred, "symname not found")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred maybe_return_timestamp(maybe(T)::in, maybe_return_timestamp::out)
|
|
is det.
|
|
|
|
maybe_return_timestamp(yes(_), do_return_timestamp).
|
|
maybe_return_timestamp(no, do_not_return_timestamp).
|
|
|
|
:- pred maybe_record_timestamp(module_name::in, string::in, need_qualifier::in,
|
|
maybe(timestamp)::in, module_and_imports::in, module_and_imports::out)
|
|
is det.
|
|
|
|
maybe_record_timestamp(ModuleName, Suffix, NeedQualifier, MaybeTimestamp,
|
|
!Module) :-
|
|
(
|
|
!.Module ^ mai_maybe_timestamps = yes(Timestamps0),
|
|
(
|
|
MaybeTimestamp = yes(Timestamp),
|
|
TimestampInfo = module_timestamp(Suffix, Timestamp, NeedQualifier),
|
|
map.set(ModuleName, TimestampInfo, Timestamps0, Timestamps),
|
|
!Module ^ mai_maybe_timestamps := yes(Timestamps)
|
|
;
|
|
MaybeTimestamp = no
|
|
)
|
|
;
|
|
!.Module ^ mai_maybe_timestamps = no
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module modules.
|
|
%-----------------------------------------------------------------------------%
|