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

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

545 lines
23 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.module_imports.
:- import_module parse_tree.parse_error.
:- import_module parse_tree.prog_item.
:- import_module io.
:- import_module list.
:- import_module map.
:- import_module maybe.
%-----------------------------------------------------------------------------%
%
% 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,
hrmm_int :: have_read_module_int_map,
hrmm_opt :: have_read_module_opt_map
).
:- type have_read_module_src_map ==
have_read_module_map(src_file_kind, parse_tree_src).
:- type have_read_module_int_map ==
have_read_module_map(int_file_kind, parse_tree_int).
:- type have_read_module_opt_map ==
have_read_module_map(opt_file_kind, parse_tree_opt).
:- type have_read_module_map(FK, PT) ==
map(have_read_module_key(FK), have_read_module(PT)).
:- type have_read_module_key(FK)
---> have_read_module_key(module_name, FK).
:- type have_read_module(PT)
---> have_read_module(
file_name,
module_timestamp,
PT,
list(error_spec),
read_module_errors
).
%-----------------------------------------------------------------------------%
:- 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 ModuleName is a nested module, then try searching for different
% filenames: for modules such as `foo.bar.baz.m', search first for
% `foo.bar.baz.m', then `bar.baz.m', then `baz.m'. 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, 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_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.
%
:- 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.
% read_module_src_from_file(Globals, SourceFileName, 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 (SourceFileName),
% 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 SourceFileName),
% 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,
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.
%-----------------------------------------------------------------------------%
% maybe_read_module_int(Globals, HaveReadModuleMap, Descr, Search,
% ModuleName, IntFileKind, FileName, ReturnTimestamp, MaybeTimestamp,
% ParseTreeInt, Specs, Errors, !IO):
%
% If HaveReadModuleMap contains the already-read contents of the
% IntFileKind 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_int, regardless of its
% timestamp.
%
:- pred maybe_read_module_int(globals::in, have_read_module_int_map::in,
string::in, maybe_search::in, module_name::in, int_file_kind::in,
file_name::out, maybe_return_timestamp::in, maybe(timestamp)::out,
parse_tree_int::out, list(error_spec)::out, read_module_errors::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, module_name::in,
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.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module libs.options.
:- import_module parse_tree.parse_module.
:- import_module parse_tree.find_module.
:- import_module bool.
:- import_module cord.
:- import_module dir.
:- import_module set.
:- import_module string.
:- import_module term.
%-----------------------------------------------------------------------------%
read_module_src(Globals, Descr, IgnoreErrors, Search, ModuleName, FileName,
ReadModuleAndTimestamps, MaybeTimestamp,
ParseTreeSrc, Specs, Errors, !IO) :-
read_module_begin(Globals, Descr, Search, ModuleName, fk_src,
FileName0, VeryVerbose, InterfaceSearchDirs, SearchDirs, !IO),
% For `.m' files we need to deal with the case where the module name
% does not match the file name, or where a partial match occurs
% in the current directory but the full match occurs in a search directory.
search_for_module_source_and_stream(Globals, SearchDirs,
InterfaceSearchDirs, ModuleName, MaybeFileNameAndStream, !IO),
actually_read_module_src(Globals, ModuleName, 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),
IsEmpty = (if cord.is_empty(ComponentsCord) then yes else no),
read_module_end(Globals, IgnoreErrors, VeryVerbose,
MaybeFileNameAndStream, FileName0, FileName,
MaybeTimestampRes, MaybeTimestamp,
IsEmpty, 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, _InterfaceSearchDirs, 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),
ParseTreeInt = parse_tree_int(_ActualModuleName, _IntFileKind,
_ActualModuleNameContext, _MaybeVersionNumbers,
IntIncls, ImpIncls, IntAvails, ImpAvails, IntItems, ImplItems),
( if
IntIncls = [],
ImpIncls = [],
IntAvails = [],
IntItems = [],
ImpAvails = [],
ImplItems = []
then
IsEmpty = yes
else
IsEmpty = no
),
read_module_end(Globals, IgnoreErrors, VeryVerbose,
MaybeFileNameAndStream, FileName0, FileName,
MaybeTimestampRes, MaybeTimestamp,
IsEmpty, ModuleSpecs, Specs, Errors, !IO).
read_module_src_from_file(Globals, FileName, Descr, Search,
ReadModuleAndTimestamps, MaybeTimestamp,
ParseTreeSrc, Specs, Errors, !IO) :-
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
maybe_write_string(VeryVerbose, "% ", !IO),
maybe_write_string(VeryVerbose, Descr, !IO),
maybe_write_string(VeryVerbose, " `", !IO),
maybe_write_string(VeryVerbose, FileName, !IO),
maybe_write_string(VeryVerbose, "'... ", !IO),
maybe_flush_output(VeryVerbose, !IO),
FullFileName = FileName ++ ".m",
( if dir.basename(FileName, BaseFileNamePrime) then
BaseFileName = BaseFileNamePrime
else
BaseFileName = ""
),
file_name_to_module_name(BaseFileName, DefaultModuleName),
(
Search = do_search,
globals.lookup_accumulating_option(Globals, search_directories,
SearchDirs)
;
Search = do_not_search,
SearchDirs = [dir.this_directory]
),
search_for_file_and_stream(SearchDirs, FullFileName,
MaybeFileNameAndStream, !IO),
actually_read_module_src(Globals, DefaultModuleName,
MaybeFileNameAndStream, ReadModuleAndTimestamps, MaybeTimestampRes,
ParseTreeSrc, Specs0, Errors, !IO),
check_timestamp(Globals, FullFileName, MaybeTimestampRes, MaybeTimestamp,
!IO),
handle_any_read_module_errors(Globals, VeryVerbose, Errors,
Specs0, Specs, !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, list(string)::out, io::di, io::uo) is det.
read_module_begin(Globals, Descr, Search, ModuleName, FileKind,
FileName0, VeryVerbose, InterfaceSearchDirs, SearchDirs, !IO) :-
Extension = file_kind_to_extension(FileKind),
(
Search = do_search,
module_name_to_search_file_name(Globals, Extension,
ModuleName, FileName0, !IO)
;
Search = do_not_search,
module_name_to_file_name(Globals, do_not_create_dirs, Extension,
ModuleName, FileName0, !IO)
),
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
Msg = "% " ++ Descr ++ " `" ++ FileName0 ++ "'... ",
maybe_write_string(VeryVerbose, Msg, !IO),
maybe_flush_output(VeryVerbose, !IO),
globals.lookup_accumulating_option(Globals, search_directories,
InterfaceSearchDirs),
(
Search = do_search,
SearchDirs = InterfaceSearchDirs
;
Search = do_not_search,
SearchDirs = [dir.this_directory]
).
:- pred read_module_end(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, bool::in,
list(error_spec)::in, list(error_spec)::out, read_module_errors::in,
io::di, io::uo) is det.
read_module_end(Globals, IgnoreErrors, VeryVerbose,
MaybeFileNameAndStream, FileName0, FileName,
MaybeTimestampRes, MaybeTimestamp, IsEmpty,
ModuleSpecs, Specs, Errors, !IO) :-
(
MaybeFileNameAndStream = ok(path_name_and_stream(FileName, _))
;
MaybeFileNameAndStream = error(_),
FileName = FileName0
),
check_timestamp(Globals, FileName0, MaybeTimestampRes, MaybeTimestamp,
!IO),
(
IgnoreErrors = ignore_errors,
( if
set.contains(Errors, rme_could_not_open_file),
% I (zs) think the test of IsEmpty is redundant, and could be
% buggy as well (since a file containing just ":- module x"
% would now yield an empty item list), but better safe than sorry.
IsEmpty = yes
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)
).
:- 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)
),
maybe_write_out_errors_no_module(VeryVerbose, Globals, !Specs, !IO),
io.set_exit_status(1, !IO)
).
%-----------------------------------------------------------------------------%
maybe_read_module_int(Globals, HaveReadModuleMap, Descr, Search,
ModuleName, IntFileKind, FileName, ReturnTimestamp, MaybeTimestamp,
ParseTreeInt, Specs, Errors, !IO) :-
( if
find_read_module_int(HaveReadModuleMap, ModuleName, IntFileKind,
ReturnTimestamp, FileNamePrime, MaybeTimestampPrime,
ParseTreeIntPrime, SpecsPrime, ErrorsPrime)
then
FileName = FileNamePrime,
MaybeTimestamp = MaybeTimestampPrime,
ParseTreeInt = ParseTreeIntPrime,
Specs = SpecsPrime,
Errors = ErrorsPrime
else
read_module_int(Globals, Descr, do_not_ignore_errors, Search,
ModuleName, IntFileKind, FileName,
always_read_module(ReturnTimestamp), MaybeTimestamp,
ParseTreeInt, Specs, Errors, !IO)
).
%-----------------------------------------------------------------------------%
find_read_module_src(HaveReadModuleMap, ModuleName, ReturnTimestamp,
FileName, MaybeTimestamp, ParseTreeSrc, Specs, Errors) :-
Key = have_read_module_key(ModuleName, sfk_src),
map.search(HaveReadModuleMap, Key, HaveReadModule),
HaveReadModule = have_read_module(FileName, ModuleTimestamp,
ParseTreeSrc, Specs, Errors),
(
ReturnTimestamp = do_return_timestamp,
ModuleTimestamp = module_timestamp(_, Timestamp, _),
MaybeTimestamp = yes(Timestamp)
;
ReturnTimestamp = dont_return_timestamp,
MaybeTimestamp = no
).
find_read_module_int(HaveReadModuleMap, ModuleName, IntFileKind,
ReturnTimestamp, FileName, MaybeTimestamp,
ParseTreeInt, Specs, Errors) :-
Key = have_read_module_key(ModuleName, IntFileKind),
map.search(HaveReadModuleMap, Key, HaveReadModule),
HaveReadModule = have_read_module(FileName, ModuleTimestamp,
ParseTreeInt, Specs, Errors),
(
ReturnTimestamp = do_return_timestamp,
ModuleTimestamp = module_timestamp(_, Timestamp, _),
MaybeTimestamp = yes(Timestamp)
;
ReturnTimestamp = dont_return_timestamp,
MaybeTimestamp = no
).
%-----------------------------------------------------------------------------%
:- pred check_timestamp(globals::in, file_name::in,
maybe(io.res(timestamp))::in, maybe(timestamp)::out,
io::di, io::uo) is det.
check_timestamp(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,
report_modification_time_warning(Globals, FileName, IOError, !IO)
;
SmartRecompilation = no
)
;
MaybeTimestampRes = no,
MaybeTimestamp = no
).
:- pred report_modification_time_warning(globals::in, file_name::in,
io.error::in, io::di, io::uo) is det.
report_modification_time_warning(Globals, SourceFileName, Error, !IO) :-
io_set_disable_smart_recompilation(yes, !IO),
io_set_disable_generate_item_version_numbers(yes, !IO),
globals.lookup_bool_option(Globals, warn_smart_recompilation, Warn),
(
Warn = yes,
io.write_string("Warning: cannot find modification time for ", !IO),
io.write_string(SourceFileName, !IO),
io.write_string(":\n", !IO),
io.error_message(Error, Msg),
io.write_string(" ", !IO),
io.write_string(Msg, !IO),
io.write_string(".\n", !IO),
io.write_string(" 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.
%-----------------------------------------------------------------------------%