Files
mercury/compiler/read_modules.m
2020-08-25 12:33:02 +10:00

911 lines
40 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1996-2009, 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: read_modules.m.
%
%-----------------------------------------------------------------------------%
:- module parse_tree.read_modules.
:- interface.
:- import_module libs.
:- import_module libs.file_util.
:- import_module libs.globals.
:- import_module libs.timestamp.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.error_util.
:- import_module parse_tree.file_kind.
:- import_module parse_tree.file_names.
:- import_module parse_tree.parse_error.
:- import_module parse_tree.prog_item.
:- import_module io.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module term.
%-----------------------------------------------------------------------------%
%
% After we have read in Mercury code from a source file, interface file or
% optimization file, we record the parse tree we get from it, so we can
% avoid having to read it again.
% XXX ITEM_LIST We seem to sometimes re-read it anyway. Fix this.
%
% Since we use different types to represent the parse trees of source,
% interface and optimization files, we use three maps, one for each
% parse tree type. Each map maps a key, which consists of a module name
% and the kind of a file (e.g. .int0 vs .int2 for interface files)
% to the parse tree we got for that file.
%
% XXX ITEM_LIST The code that reads in optimization files does not
% record its results in hrmm_opt. I (zs) don't know whether that is
% a bug (leading to duplicate reads of optimization files) or a feature
% (keeping files that are by construction read exactly once out of a map
% where they won't be needed again).
%
:- type have_read_module_maps
---> have_read_module_maps(
hrmm_src :: have_read_module_src_map,
% XXX CLEANUP We should stop using and delete these two fields
% as soon as we can, using the last six fields instead.
hrmm_int :: have_read_module_int_map,
hrmm_opt :: have_read_module_opt_map,
% XXX CLEANUP We should store parse_tree_module_srcs,
% not raw_compilation_units.
hrmm_rcu :: have_read_module_rcu_map,
hrmm_int0 :: have_read_module_int0_map,
hrmm_int1 :: have_read_module_int1_map,
hrmm_int2 :: have_read_module_int2_map,
hrmm_int3 :: have_read_module_int3_map,
hrmm_plain_opt :: have_read_module_plain_opt_map,
hrmm_trans_opt :: have_read_module_trans_opt_map
).
:- func init_have_read_module_maps = have_read_module_maps.
:- type have_read_module_src_map ==
have_read_module_map(module_name, parse_tree_src).
:- type have_read_module_int_map ==
have_read_module_map(have_read_module_key(int_file_kind), parse_tree_int).
:- type have_read_module_opt_map ==
have_read_module_map(have_read_module_key(opt_file_kind), parse_tree_opt).
:- type have_read_module_key(Kind)
---> have_read_module_key(module_name, Kind).
:- type have_read_module_rcu_map ==
map(module_name, raw_compilation_unit).
:- type have_read_module_int0_map ==
have_read_module_map(module_name, parse_tree_int0).
:- type have_read_module_int1_map ==
have_read_module_map(module_name, parse_tree_int1).
:- type have_read_module_int2_map ==
have_read_module_map(module_name, parse_tree_int2).
:- type have_read_module_int3_map ==
have_read_module_map(module_name, parse_tree_int3).
:- type have_read_module_plain_opt_map ==
have_read_module_map(module_name, parse_tree_plain_opt).
:- type have_read_module_trans_opt_map ==
have_read_module_map(module_name, parse_tree_trans_opt).
:- type have_read_module_map(Key, PT) == map(Key, have_read_module(PT)).
:- type have_read_module(PT)
---> have_successfully_read_module(
file_name,
% Not all reads of e.g. interface files request
% that the read return the timestamp of the file.
maybe(timestamp),
PT,
list(error_spec),
read_module_errors
)
; tried_to_read_module_failed.
%-----------------------------------------------------------------------------%
:- type maybe_ignore_errors
---> ignore_errors
; do_not_ignore_errors.
% read_module_src(Globals, Descr, IgnoreErrors, Search,
% ModuleName, FileName, ReadModuleAndTimestamps, MaybeTimestamp,
% ParseTreeSrc, Specs, Errors, !IO):
%
% Given a module name, read in and parse the source code of that file,
% printing progress messages along the way if the verbosity level
% calls for that.
%
% If Search is do_search, search all directories given by the option
% search_directories for the module;
% otherwise, search for those filenames only in the current directory.
% Return in FileName the actual source file name found
% (excluding the directory part). If the actual module name
% (as determined by the `:- module' declaration) does not match
% the specified module name, then report an error message,
% but record the *expected* module name in the parse tree,
% not the one we actually found. This is because most parts
% of the compiler (including deps_map.m and make.module_dep_file.m)
% rely on the invariant which says that if Errors does not contain
% any fatal errors, then the returned ParseTreeSrc contains the
% module with the expected name. Invocations of the compiler
% that don't specify --make or request dependency map don't really
% care which module name we return here; they will work either way,
% the only difference being whether the names of the files they generate
% are based on the expected or the actual module name.
%
% N.B. This reads a module given the MODULE name. If you want to read
% a module given the FILE name, use `read_module_src_from_file'.
%
% If ReadModuleAndTimestamps is always_read_module(dont_return_timestamp),
% return `no' in MaybeTimestamp.
%
% If ReadModuleAndTimestamps is always_read_module(do_return_timestamp),
% attempt to return the modification time of the file in MaybeTimestamp.
%
% If ReadModuleAndTimestamps is dont_read_module_if_match(OldTimeStamp),
% then
%
% - if the timestamp of that file is exactly OldTimestamp, then
% don't read the file, but return OldTimestamp as the file's timestamp,
% alongside a dummy parse tree; while
% - if the timestamp of that file differs from OldTimestamp (virtually
% always because it is newer), then read the module from the file
% as usual, parse and return its contents as usual, and also return
% its actual timestamp.
%
% If the file could not be read, MaybeTimestamp will be `no'.
%
:- pred read_module_src(globals::in, string::in,
maybe_ignore_errors::in, maybe_search::in,
module_name::in, list(term.context)::in, file_name::out,
read_module_and_timestamps::in, maybe(timestamp)::out,
parse_tree_src::out, list(error_spec)::out, read_module_errors::out,
io::di, io::uo) is det.
% read_module_src_from_file(Globals, FileName, FileNameDotM,
% Descr, Search, ReadModuleAndTimestamps, MaybeTimestamp, ParseTreeSrc,
% Specs, Errors, !IO):
%
% Does pretty much the same job as read_module_src, but its job is
% to read the module stored in a specified file (FileNameDotM),
% discovering the name of the module stored there by reading the file,
% as opposed to looking for the file containing a module with a specified
% name. It does not search for the right filename (that is FileNameDotM),
% but if Search is do_search, it does search for that filename in the
% specified directories.
%
% The rest of the argument list has the same meaning as in read_module_src.
%
:- pred read_module_src_from_file(globals::in, file_name::in, file_name::in,
string::in, maybe_search::in, read_module_and_timestamps::in,
maybe(timestamp)::out, parse_tree_src::out,
list(error_spec)::out, read_module_errors::out, io::di, io::uo) is det.
% read_module_int(Globals, Descr, IgnoreErrors, Search,
% ModuleName, IntFileKind, FileName, ReturnTimestamp, MaybeTimestamp,
% ParseTreeInt, Specs, Errors, !IO):
%
% Given a module name, and the identity of one of its interface files,
% (.int0, .int, .int2 or .int3), read in and parse the contents of that
% interface file, printing progress messages along the way if the
% verbosity level calls for that.
%
% The meanings of the arguments are pretty much the same as for
% read_module_src, but while the names of the files that contain source
% files may not be fully module qualified, the names of interface files
% are always fully module qualified, so read_module_int does not search
% for the right filename. It knows what filename it looks for; the only
% search it does, if Search is do_search, is to decide which directory
% among the search directories contains the file with that filename.
%
% XXX CLEANUP Calls to this predicate should be replaced by calls
% to its int_file_kind-specific versions below. The only such calls
% are now used in the implementation of smart recompilation.
%
:- pred read_module_int(globals::in, string::in,
maybe_ignore_errors::in, maybe_search::in,
module_name::in, int_file_kind::in, file_name::out,
read_module_and_timestamps::in, maybe(timestamp)::out,
parse_tree_int::out, list(error_spec)::out, read_module_errors::out,
io::di, io::uo) is det.
% Versions of read_module_int that read and return a non-generic
% parse tree.
%
:- pred read_module_int0(globals::in, string::in,
maybe_ignore_errors::in, maybe_search::in,
module_name::in, file_name::out,
read_module_and_timestamps::in, maybe(timestamp)::out,
parse_tree_int0::out, list(error_spec)::out, read_module_errors::out,
io::di, io::uo) is det.
:- pred read_module_int1(globals::in, string::in,
maybe_ignore_errors::in, maybe_search::in,
module_name::in, file_name::out,
read_module_and_timestamps::in, maybe(timestamp)::out,
parse_tree_int1::out, list(error_spec)::out, read_module_errors::out,
io::di, io::uo) is det.
:- pred read_module_int2(globals::in, string::in,
maybe_ignore_errors::in, maybe_search::in,
module_name::in, file_name::out,
read_module_and_timestamps::in, maybe(timestamp)::out,
parse_tree_int2::out, list(error_spec)::out, read_module_errors::out,
io::di, io::uo) is det.
:- pred read_module_int3(globals::in, string::in,
maybe_ignore_errors::in, maybe_search::in,
module_name::in, file_name::out,
read_module_and_timestamps::in, maybe(timestamp)::out,
parse_tree_int3::out, list(error_spec)::out, read_module_errors::out,
io::di, io::uo) is det.
%-----------------------------------------------------------------------------%
% maybe_read_module_intN(Globals, Descr, Search,
% ModuleName, IntFileKind, FileName, ReturnTimestamp, MaybeTimestamp,
% ParseTreeInt, Specs, Errors, !HaveReadModuleMaps, !IO):
%
% If HaveReadModuleMap contains the already-read contents of the
% relevant kind of interface file for ModuleName, then return
% the information stored in HaveReadModuleMap for that file.
% If it is not there, read that interface file using read_module_intN,
% regardless of its timestamp.
%
:- pred maybe_read_module_int0(globals::in, maybe_search::in, module_name::in,
file_name::out, maybe_return_timestamp::in, maybe(timestamp)::out,
parse_tree_int0::out, list(error_spec)::out, read_module_errors::out,
have_read_module_maps::in, have_read_module_maps::out,
io::di, io::uo) is det.
:- pred maybe_read_module_int1(globals::in, maybe_search::in, module_name::in,
file_name::out, maybe_return_timestamp::in, maybe(timestamp)::out,
parse_tree_int1::out, list(error_spec)::out, read_module_errors::out,
have_read_module_maps::in, have_read_module_maps::out,
io::di, io::uo) is det.
:- pred maybe_read_module_int2(globals::in, maybe_search::in, module_name::in,
file_name::out, maybe_return_timestamp::in, maybe(timestamp)::out,
parse_tree_int2::out, list(error_spec)::out, read_module_errors::out,
have_read_module_maps::in, have_read_module_maps::out,
io::di, io::uo) is det.
:- pred maybe_read_module_int3(globals::in, maybe_search::in, module_name::in,
file_name::out, maybe_return_timestamp::in, maybe(timestamp)::out,
parse_tree_int3::out, list(error_spec)::out, read_module_errors::out,
have_read_module_maps::in, have_read_module_maps::out,
io::di, io::uo) is det.
%-----------------------------------------------------------------------------%
% find_read_module_src(HaveReadModuleMap, ModuleName,
% ReturnTimestamp, FileName, MaybeTimestamp, ParseTree, Specs, Errors):
% find_read_module_int(HaveReadModuleMap, ModuleName, IntFileKind,
% ReturnTimestamp, FileName, MaybeTimestamp, ParseTree, Specs, Errors):
%
% Check whether HaveReadModuleMap contains the already-read contents
% of the specified source file or interface file. If it does, return
% its contents. If it does not, fail.
%
:- pred find_read_module_src(have_read_module_src_map::in, module_name::in,
maybe_return_timestamp::in, file_name::out, maybe(timestamp)::out,
parse_tree_src::out, list(error_spec)::out, read_module_errors::out)
is semidet.
:- pred find_read_module_int(have_read_module_int_map::in,
have_read_module_key(int_file_kind)::in,
maybe_return_timestamp::in, file_name::out, maybe(timestamp)::out,
parse_tree_int::out, list(error_spec)::out, read_module_errors::out)
is semidet.
% Versions of find_read_module_int that return a non-generic parse tree.
%
:- pred find_read_module_int0(have_read_module_int0_map::in, module_name::in,
maybe_return_timestamp::in, file_name::out, maybe(timestamp)::out,
parse_tree_int0::out, list(error_spec)::out, read_module_errors::out)
is semidet.
:- pred find_read_module_int1(have_read_module_int1_map::in, module_name::in,
maybe_return_timestamp::in, file_name::out, maybe(timestamp)::out,
parse_tree_int1::out, list(error_spec)::out, read_module_errors::out)
is semidet.
:- pred find_read_module_int2(have_read_module_int2_map::in, module_name::in,
maybe_return_timestamp::in, file_name::out, maybe(timestamp)::out,
parse_tree_int2::out, list(error_spec)::out, read_module_errors::out)
is semidet.
:- pred find_read_module_int3(have_read_module_int3_map::in, module_name::in,
maybe_return_timestamp::in, file_name::out, maybe(timestamp)::out,
parse_tree_int3::out, list(error_spec)::out, read_module_errors::out)
is semidet.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module libs.options.
:- import_module parse_tree.find_module.
:- import_module parse_tree.parse_module.
:- import_module parse_tree.source_file_map.
:- import_module bool.
:- import_module cord.
:- import_module dir.
:- import_module require.
:- import_module set.
:- import_module string.
%-----------------------------------------------------------------------------%
init_have_read_module_maps =
have_read_module_maps(map.init, map.init, map.init,
map.init, map.init, map.init, map.init, map.init, map.init, map.init).
%-----------------------------------------------------------------------------%
read_module_src(Globals, Descr, IgnoreErrors, Search,
ModuleName, ExpectationContexts, FileName,
ReadModuleAndTimestamps, MaybeTimestamp,
ParseTreeSrc, Specs, Errors, !IO) :-
read_module_begin(Globals, Descr, Search, ModuleName, fk_src,
FileName0, VeryVerbose, SearchDirs, !IO),
% For `.m' files, we need to deal with the case where the module name
% does not match the file name.
search_for_module_source_and_stream(SearchDirs, ModuleName,
MaybeFileNameAndStream, !IO),
actually_read_module_src(Globals, ModuleName, ExpectationContexts,
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
ParseTreeSrc0, ModuleSpecs, Errors, !IO),
ParseTreeSrc0 = parse_tree_src(_ActualModuleName, ActualModuleNameContext,
ComponentsCord),
% If ModuleName = ActualModuleName, this obviously does the right thing.
% If ModuleName != ActualModuleName, then we must include ModuleName
% in ParseTreeSrc (see the comment above), and including recording
% ActualModuleNameContext as its context shouldn't mislead anyone
% who reads the error spec about the unexpected module name,
% which should be in Specs.
ParseTreeSrc = parse_tree_src(ModuleName, ActualModuleNameContext,
ComponentsCord),
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
MaybeFileNameAndStream, FileName0, FileName,
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
read_module_src_from_file(Globals, FileName, FileNameDotM, Descr, Search,
ReadModuleAndTimestamps, MaybeTimestamp,
ParseTreeSrc, Specs, Errors, !IO) :-
read_module_begin_from_file(Globals, Descr, Search,
FileName, FileNameDotM, DefaultModuleName,
VeryVerbose, SearchDirs, !IO),
search_for_file_and_stream(SearchDirs, FileNameDotM,
MaybeFileNameAndStream, !IO),
actually_read_module_src(Globals, DefaultModuleName, [],
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
ParseTreeSrc, ModuleSpecs, Errors, !IO),
read_module_end_file(Globals, VeryVerbose, FileNameDotM,
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
read_module_int(Globals, Descr, IgnoreErrors, Search, ModuleName, IntFileKind,
FileName, ReadModuleAndTimestamps, MaybeTimestamp,
ParseTreeInt, Specs, Errors, !IO) :-
read_module_begin(Globals, Descr, Search, ModuleName, fk_int(IntFileKind),
FileName0, VeryVerbose, SearchDirs, !IO),
search_for_file_and_stream(SearchDirs, FileName0,
MaybeFileNameAndStream, !IO),
actually_read_module_int(IntFileKind, Globals, ModuleName, [],
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
ParseTreeInt, ModuleSpecs, Errors, !IO),
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
MaybeFileNameAndStream, FileName0, FileName,
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
read_module_int0(Globals, Descr, IgnoreErrors, Search, ModuleName,
FileName, ReadModuleAndTimestamps, MaybeTimestamp,
ParseTreeInt0, Specs, Errors, !IO) :-
read_module_begin(Globals, Descr, Search, ModuleName, fk_int(ifk_int0),
FileName0, VeryVerbose, SearchDirs, !IO),
search_for_file_and_stream(SearchDirs, FileName0,
MaybeFileNameAndStream, !IO),
actually_read_module_int0(Globals, ModuleName, [],
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
ParseTreeInt0, ModuleSpecs, Errors, !IO),
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
MaybeFileNameAndStream, FileName0, FileName,
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
read_module_int1(Globals, Descr, IgnoreErrors, Search, ModuleName,
FileName, ReadModuleAndTimestamps, MaybeTimestamp,
ParseTreeInt1, Specs, Errors, !IO) :-
read_module_begin(Globals, Descr, Search, ModuleName, fk_int(ifk_int1),
FileName0, VeryVerbose, SearchDirs, !IO),
search_for_file_and_stream(SearchDirs, FileName0,
MaybeFileNameAndStream, !IO),
actually_read_module_int1(Globals, ModuleName, [],
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
ParseTreeInt1, ModuleSpecs, Errors, !IO),
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
MaybeFileNameAndStream, FileName0, FileName,
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
read_module_int2(Globals, Descr, IgnoreErrors, Search, ModuleName,
FileName, ReadModuleAndTimestamps, MaybeTimestamp,
ParseTreeInt2, Specs, Errors, !IO) :-
read_module_begin(Globals, Descr, Search, ModuleName, fk_int(ifk_int2),
FileName0, VeryVerbose, SearchDirs, !IO),
search_for_file_and_stream(SearchDirs, FileName0,
MaybeFileNameAndStream, !IO),
actually_read_module_int2(Globals, ModuleName, [],
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
ParseTreeInt2, ModuleSpecs, Errors, !IO),
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
MaybeFileNameAndStream, FileName0, FileName,
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
read_module_int3(Globals, Descr, IgnoreErrors, Search, ModuleName,
FileName, ReadModuleAndTimestamps, MaybeTimestamp,
ParseTreeInt3, Specs, Errors, !IO) :-
read_module_begin(Globals, Descr, Search, ModuleName, fk_int(ifk_int3),
FileName0, VeryVerbose, SearchDirs, !IO),
search_for_file_and_stream(SearchDirs, FileName0,
MaybeFileNameAndStream, !IO),
actually_read_module_int3(Globals, ModuleName, [],
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
ParseTreeInt3, ModuleSpecs, Errors, !IO),
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
MaybeFileNameAndStream, FileName0, FileName,
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO).
%-----------------------------------------------------------------------------%
:- pred read_module_begin_from_file(globals::in, string::in,
maybe_search::in, file_name::in, file_name::in, module_name::out,
bool::out, list(string)::out, io::di, io::uo) is det.
read_module_begin_from_file(Globals, Descr, Search, FileName, FileNameDotM,
DefaultModuleName, VeryVerbose, SearchDirs, !IO) :-
( if dir.basename(FileName, BaseFileNamePrime) then
BaseFileName = BaseFileNamePrime
else
BaseFileName = ""
),
have_source_file_map(HaveMap, !IO),
(
HaveMap = no,
file_name_to_module_name(BaseFileName, DefaultModuleName)
;
HaveMap = yes,
lookup_source_file_module(FileNameDotM, MaybeModuleName, !IO),
(
MaybeModuleName = yes(DefaultModuleName)
;
MaybeModuleName = no,
file_name_to_module_name(BaseFileName, DefaultModuleName)
)
),
% The rest of this predicate should be kept in sync
% with read_module_begin.
(
Search = do_search,
globals.lookup_accumulating_option(Globals, search_directories,
SearchDirs)
;
Search = do_not_search,
SearchDirs = [dir.this_directory]
),
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
(
VeryVerbose = no
;
VeryVerbose = yes,
io.format("%% Reading %s `%s'... ", [s(Descr), s(FileNameDotM)], !IO),
io.flush_output(!IO)
).
:- pred read_module_begin(globals::in, string::in,
maybe_search::in, module_name::in, file_kind::in, file_name::out,
bool::out, list(string)::out, io::di, io::uo) is det.
read_module_begin(Globals, Descr, Search, ModuleName, FileKind,
FileName0, VeryVerbose, SearchDirs, !IO) :-
file_kind_to_extension(FileKind, _ExtStr, Ext),
(
Search = do_search,
module_name_to_search_file_name(Globals, $pred, Ext,
ModuleName, FileName0, !IO)
;
Search = do_not_search,
module_name_to_file_name(Globals, $pred, do_not_create_dirs, Ext,
ModuleName, FileName0, !IO)
),
% The rest of this predicate should be kept in sync
% with read_module_begin_from_file.
(
Search = do_search,
globals.lookup_accumulating_option(Globals, search_directories,
SearchDirs)
;
Search = do_not_search,
SearchDirs = [dir.this_directory]
),
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
(
VeryVerbose = no
;
VeryVerbose = yes,
io.format("%% %s `%s'... ", [s(Descr), s(FileName0)], !IO),
io.flush_output(!IO)
).
:- pred read_module_end_module(globals::in, maybe_ignore_errors::in, bool::in,
maybe_error(path_name_and_stream)::in, file_name::in, file_name::out,
maybe(io.res(timestamp))::in, maybe(timestamp)::out,
list(error_spec)::in, list(error_spec)::out, read_module_errors::in,
io::di, io::uo) is det.
read_module_end_module(Globals, IgnoreErrors, VeryVerbose,
MaybeFileNameAndStream, FileName0, FileName,
MaybeTimestampRes, MaybeTimestamp, ModuleSpecs, Specs, Errors, !IO) :-
% The code of read_module_end_module and read_module_end_file
% should be kept in sync.
(
MaybeFileNameAndStream = ok(path_name_and_stream(FileName, _))
;
MaybeFileNameAndStream = error(_),
FileName = FileName0
),
check_timestamp_report_if_needed_and_missing(Globals, FileName0,
MaybeTimestampRes, MaybeTimestamp, !IO),
(
IgnoreErrors = ignore_errors,
( if set.contains(Errors, rme_could_not_open_file) then
maybe_write_string(VeryVerbose, "not found.\n", !IO),
Specs = []
else
maybe_write_string(VeryVerbose, "done.\n", !IO),
Specs = ModuleSpecs
)
;
IgnoreErrors = do_not_ignore_errors,
handle_any_read_module_errors(Globals, VeryVerbose, Errors,
ModuleSpecs, Specs, !IO)
),
globals.lookup_bool_option(Globals, detailed_statistics, Statistics),
maybe_report_stats(Statistics, !IO).
:- pred read_module_end_file(globals::in, bool::in, file_name::in,
maybe(io.res(timestamp))::in, maybe(timestamp)::out,
list(error_spec)::in, list(error_spec)::out, read_module_errors::in,
io::di, io::uo) is det.
read_module_end_file(Globals, VeryVerbose, FileName,
MaybeTimestampRes, MaybeTimestamp,
ModuleSpecs, Specs, Errors, !IO) :-
% The code of read_module_end_module and read_module_end_file
% should be kept in sync.
%
% Unlike read_module_end_module, we assume do_not_ignore_errors.
check_timestamp_report_if_needed_and_missing(Globals, FileName,
MaybeTimestampRes, MaybeTimestamp, !IO),
handle_any_read_module_errors(Globals, VeryVerbose, Errors,
ModuleSpecs, Specs, !IO).
:- pred handle_any_read_module_errors(globals::in, bool::in,
read_module_errors::in, list(error_spec)::in, list(error_spec)::out,
io::di, io::uo) is det.
handle_any_read_module_errors(Globals, VeryVerbose, Errors, !Specs, !IO) :-
( if set.is_empty(Errors) then
maybe_write_string(VeryVerbose, "successful parse.\n", !IO)
else
set.intersect(Errors, fatal_read_module_errors, FatalErrors),
( if set.is_empty(FatalErrors) then
maybe_write_string(VeryVerbose, "parse error(s).\n", !IO)
else
maybe_write_string(VeryVerbose, "fatal error(s).\n", !IO)
),
pre_hlds_maybe_write_out_errors(VeryVerbose, Globals, !Specs, !IO),
io.set_exit_status(1, !IO)
).
%-----------------------------------------------------------------------------%
maybe_read_module_int0(Globals, Search,
ModuleName, FileName, ReturnTimestamp, MaybeTimestamp,
ParseTreeInt0, Specs, Errors, !HaveReadModuleMaps, !IO) :-
OrigHaveReadModuleMapInt0 = !.HaveReadModuleMaps ^ hrmm_int0,
( if
find_read_module_int0(OrigHaveReadModuleMapInt0, ModuleName,
ReturnTimestamp, FileNamePrime, MaybeTimestampPrime,
ParseTreeInt0Prime, SpecsPrime, ErrorsPrime)
then
FileName = FileNamePrime,
MaybeTimestamp = MaybeTimestampPrime,
ParseTreeInt0 = ParseTreeInt0Prime,
Specs = SpecsPrime,
Errors = ErrorsPrime
else
Descr = "Reading .int0 interface for module",
read_module_int0(Globals, Descr, do_not_ignore_errors, Search,
ModuleName, FileName,
always_read_module(ReturnTimestamp), MaybeTimestamp,
ParseTreeInt0, Specs, Errors, !IO),
( if set.member(rme_could_not_open_file, Errors) then
HaveReadModule = tried_to_read_module_failed,
map.set(ModuleName, HaveReadModule,
OrigHaveReadModuleMapInt0, HaveReadModuleMapInt0)
else
expect(unify(ModuleName, ParseTreeInt0 ^ pti0_module_name), $pred,
"ModuleName != module name in ParseTreeInt0"),
HaveReadModule = have_successfully_read_module(FileName,
MaybeTimestamp, ParseTreeInt0, Specs, Errors),
map.det_insert(ModuleName, HaveReadModule,
OrigHaveReadModuleMapInt0, HaveReadModuleMapInt0)
),
!HaveReadModuleMaps ^ hrmm_int0 := HaveReadModuleMapInt0
).
maybe_read_module_int1(Globals, Search,
ModuleName, FileName, ReturnTimestamp, MaybeTimestamp,
ParseTreeInt1, Specs, Errors, !HaveReadModuleMaps, !IO) :-
OrigHaveReadModuleMapInt1 = !.HaveReadModuleMaps ^ hrmm_int1,
( if
find_read_module_int1(OrigHaveReadModuleMapInt1, ModuleName,
ReturnTimestamp, FileNamePrime, MaybeTimestampPrime,
ParseTreeInt1Prime, SpecsPrime, ErrorsPrime)
then
FileName = FileNamePrime,
MaybeTimestamp = MaybeTimestampPrime,
ParseTreeInt1 = ParseTreeInt1Prime,
Specs = SpecsPrime,
Errors = ErrorsPrime
else
Descr = "Reading .int interface for module",
read_module_int1(Globals, Descr, do_not_ignore_errors, Search,
ModuleName, FileName,
always_read_module(ReturnTimestamp), MaybeTimestamp,
ParseTreeInt1, Specs, Errors, !IO),
( if set.member(rme_could_not_open_file, Errors) then
HaveReadModule = tried_to_read_module_failed,
map.set(ModuleName, HaveReadModule,
OrigHaveReadModuleMapInt1, HaveReadModuleMapInt1)
else
expect(unify(ModuleName, ParseTreeInt1 ^ pti1_module_name), $pred,
"ModuleName != module name in ParseTreeInt1"),
HaveReadModule = have_successfully_read_module(FileName,
MaybeTimestamp, ParseTreeInt1, Specs, Errors),
map.det_insert(ModuleName, HaveReadModule,
OrigHaveReadModuleMapInt1, HaveReadModuleMapInt1)
),
!HaveReadModuleMaps ^ hrmm_int1 := HaveReadModuleMapInt1
).
maybe_read_module_int2(Globals, Search,
ModuleName, FileName, ReturnTimestamp, MaybeTimestamp,
ParseTreeInt2, Specs, Errors, !HaveReadModuleMaps, !IO) :-
OrigHaveReadModuleMapInt2 = !.HaveReadModuleMaps ^ hrmm_int2,
( if
find_read_module_int2(OrigHaveReadModuleMapInt2, ModuleName,
ReturnTimestamp, FileNamePrime, MaybeTimestampPrime,
ParseTreeInt2Prime, SpecsPrime, ErrorsPrime)
then
FileName = FileNamePrime,
MaybeTimestamp = MaybeTimestampPrime,
ParseTreeInt2 = ParseTreeInt2Prime,
Specs = SpecsPrime,
Errors = ErrorsPrime
else
Descr = "Reading .int2 interface for module",
read_module_int2(Globals, Descr, do_not_ignore_errors, Search,
ModuleName, FileName,
always_read_module(ReturnTimestamp), MaybeTimestamp,
ParseTreeInt2, Specs, Errors, !IO),
( if set.member(rme_could_not_open_file, Errors) then
HaveReadModule = tried_to_read_module_failed,
map.set(ModuleName, HaveReadModule,
OrigHaveReadModuleMapInt2, HaveReadModuleMapInt2)
else
expect(unify(ModuleName, ParseTreeInt2 ^ pti2_module_name), $pred,
"ModuleName != module name in ParseTreeInt2"),
HaveReadModule = have_successfully_read_module(FileName,
MaybeTimestamp, ParseTreeInt2, Specs, Errors),
map.det_insert(ModuleName, HaveReadModule,
OrigHaveReadModuleMapInt2, HaveReadModuleMapInt2)
),
!HaveReadModuleMaps ^ hrmm_int2 := HaveReadModuleMapInt2
).
maybe_read_module_int3(Globals, Search,
ModuleName, FileName, ReturnTimestamp, MaybeTimestamp,
ParseTreeInt3, Specs, Errors, !HaveReadModuleMaps, !IO) :-
OrigHaveReadModuleMapInt3 = !.HaveReadModuleMaps ^ hrmm_int3,
( if
find_read_module_int3(OrigHaveReadModuleMapInt3, ModuleName,
ReturnTimestamp, FileNamePrime, MaybeTimestampPrime,
ParseTreeInt3Prime, SpecsPrime, ErrorsPrime)
then
FileName = FileNamePrime,
MaybeTimestamp = MaybeTimestampPrime,
ParseTreeInt3 = ParseTreeInt3Prime,
Specs = SpecsPrime,
Errors = ErrorsPrime
else
Descr = "Reading .int3 interface for module",
read_module_int3(Globals, Descr, do_not_ignore_errors, Search,
ModuleName, FileName,
always_read_module(ReturnTimestamp), MaybeTimestamp,
ParseTreeInt3, Specs, Errors, !IO),
( if set.member(rme_could_not_open_file, Errors) then
HaveReadModule = tried_to_read_module_failed,
map.set(ModuleName, HaveReadModule,
OrigHaveReadModuleMapInt3, HaveReadModuleMapInt3)
else
expect(unify(ModuleName, ParseTreeInt3 ^ pti3_module_name), $pred,
"ModuleName != module name in ParseTreeInt3"),
HaveReadModule = have_successfully_read_module(FileName,
MaybeTimestamp, ParseTreeInt3, Specs, Errors),
map.det_insert(ModuleName, HaveReadModule,
OrigHaveReadModuleMapInt3, HaveReadModuleMapInt3)
),
!HaveReadModuleMaps ^ hrmm_int3 := HaveReadModuleMapInt3
).
%-----------------------------------------------------------------------------%
find_read_module_src(HaveReadModuleMapSrc, ModuleName, ReturnTimestamp,
FileName, MaybeTimestamp, ParseTreeSrc, Specs, Errors) :-
map.search(HaveReadModuleMapSrc, ModuleName, HaveReadModule),
HaveReadModule = have_successfully_read_module(FileName, MaybeTimestamp0,
ParseTreeSrc, Specs, Errors),
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0,
MaybeTimestamp).
find_read_module_int(HaveReadModuleMapInt, Key,
ReturnTimestamp, FileName, MaybeTimestamp,
ParseTreeInt, Specs, Errors) :-
map.search(HaveReadModuleMapInt, Key, HaveReadModule),
HaveReadModule = have_successfully_read_module(FileName, MaybeTimestamp0,
ParseTreeInt, Specs, Errors),
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0,
MaybeTimestamp).
find_read_module_int0(HaveReadModuleMapInt0, ModuleName,
ReturnTimestamp, FileName, MaybeTimestamp,
ParseTreeInt0, Specs, Errors) :-
map.search(HaveReadModuleMapInt0, ModuleName, HaveReadModule),
HaveReadModule = have_successfully_read_module(FileName, MaybeTimestamp0,
ParseTreeInt0, Specs, Errors),
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0,
MaybeTimestamp).
find_read_module_int1(HaveReadModuleMapInt1, ModuleName,
ReturnTimestamp, FileName, MaybeTimestamp,
ParseTreeInt1, Specs, Errors) :-
map.search(HaveReadModuleMapInt1, ModuleName, HaveReadModule),
HaveReadModule = have_successfully_read_module(FileName, MaybeTimestamp0,
ParseTreeInt1, Specs, Errors),
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0,
MaybeTimestamp).
find_read_module_int2(HaveReadModuleMapInt2, ModuleName,
ReturnTimestamp, FileName, MaybeTimestamp,
ParseTreeInt2, Specs, Errors) :-
map.search(HaveReadModuleMapInt2, ModuleName, HaveReadModule),
HaveReadModule = have_successfully_read_module(FileName, MaybeTimestamp0,
ParseTreeInt2, Specs, Errors),
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0,
MaybeTimestamp).
find_read_module_int3(HaveReadModuleMapInt3, ModuleName,
ReturnTimestamp, FileName, MaybeTimestamp,
ParseTreeInt3, Specs, Errors) :-
map.search(HaveReadModuleMapInt3, ModuleName, HaveReadModule),
HaveReadModule = have_successfully_read_module(FileName, MaybeTimestamp0,
ParseTreeInt3, Specs, Errors),
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0,
MaybeTimestamp).
%-----------------------------------------------------------------------------%
:- pred return_timestamp_if_needed(maybe_return_timestamp::in,
maybe(timestamp)::in, maybe(timestamp)::out) is det.
return_timestamp_if_needed(ReturnTimestamp, MaybeTimestamp0, MaybeTimestamp) :-
(
ReturnTimestamp = do_return_timestamp,
(
MaybeTimestamp0 = no,
% This can happen if
%
% - code that does not need a timestamp enters a parse tree
% into the have_read_module_map, and then later
%
% - code that does need a timestamp finds the parse tree there.
%
% We abort because I (zs) don't think this should happen:
% the use of have_read_module_maps in module_imports.m never
% needs timestamps, the smart recompilation modules always
% need timestamps, but the have_read_module_maps they use
% are completely separate (for now). If it turns out I am wrong,
% or we *do* want these two subsystems to use the same
% have_read_module_maps, then there are two obvious possibilities:
% either *always* store the timestamp of a file we read in, or
% get the timestamp from the OS the first time it is needed
% (have_read_module_maps entries include the filename, so this
% is possible). The first solution is simpler, the second can
% execute fewer system calls.
unexpected($pred, "do_return_timestamp but no timestamp")
;
MaybeTimestamp0 = yes(_),
MaybeTimestamp = MaybeTimestamp0
)
;
ReturnTimestamp = dont_return_timestamp,
MaybeTimestamp = no
).
%-----------------------------------------------------------------------------%
:- pred check_timestamp_report_if_needed_and_missing(globals::in,
file_name::in, maybe(io.res(timestamp))::in, maybe(timestamp)::out,
io::di, io::uo) is det.
check_timestamp_report_if_needed_and_missing(Globals, FileName,
MaybeTimestampRes, MaybeTimestamp, !IO) :-
(
MaybeTimestampRes = yes(ok(Timestamp)),
MaybeTimestamp = yes(Timestamp)
;
MaybeTimestampRes = yes(error(IOError)),
MaybeTimestamp = no,
globals.lookup_bool_option(Globals, smart_recompilation,
SmartRecompilation),
% Should we print the warning if smart recompilation has already been
% disabled by an earlier error? At the moment, we do.
(
SmartRecompilation = yes,
record_and_report_missing_timestamp(Globals, FileName,
IOError, !IO)
;
SmartRecompilation = no
)
;
MaybeTimestampRes = no,
MaybeTimestamp = no
).
:- pred record_and_report_missing_timestamp(globals::in, file_name::in,
io.error::in, io::di, io::uo) is det.
record_and_report_missing_timestamp(Globals, FileName, Error, !IO) :-
io_set_disable_smart_recompilation(disable_smart_recompilation, !IO),
io_set_disable_generate_item_version_numbers(disable_item_version_numbers,
!IO),
globals.lookup_bool_option(Globals, warn_smart_recompilation, Warn),
(
Warn = yes,
io.error_message(Error, Msg),
io.format("Warning: cannot find modification time for %s:\n",
[s(FileName)], !IO),
io.format(" %s.\n", [s(Msg)], !IO),
io.format(" Smart recompilation will not work.\n", [], !IO),
globals.lookup_bool_option(Globals, halt_at_warn, HaltAtWarn),
(
HaltAtWarn = yes,
io.set_exit_status(1, !IO)
;
HaltAtWarn = no
)
;
Warn = no
).
%-----------------------------------------------------------------------------%
:- end_module parse_tree.read_modules.
%-----------------------------------------------------------------------------%