Files
mercury/compiler/prog_parse_tree.m
2025-05-12 21:36:09 +10:00

1027 lines
49 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1996-2011 The University of Melbourne.
% Copyright (C) 2014-2025 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: prog_parse_tree.m.
% Main author: zs.
%
% The Mercury implementation uses several different kinds of files.
% Besides source files, it uses four kinds of interface files and
% two kinds of optimization files. The parse trees of these files
% contain a structured representation of the information in these files.
% This module defines the top levels of these parse trees, which are
% the parts that differ between the different kinds of files. The lower
% levels of the representation, which are used by most or all kinds
% of Mercury files, are defined in prog_item.m, and in prog_data*.m.
%
% This module and prog_item.m together define the parts of parse trees
% that *are not* needed after the construction of the initial HLDS, while
% the parts that *are* needed after that point in time are contained in
% prog_data*.m.
%
%---------------------------------------------------------------------------%
%
% One important consideration in the design of the parse trees is that
% they have two different use cases:
%
% - to represent files being read in, and
% - to represent files being written out.
%
% The two have slightly different requirements, because
%
% - we will never knowingly write out erroneous Mercury code, but
% - we know that we *will* read in some.
%
% In the past, we used to include some information (such as which modules
% are imported and/or used in which section of a module) in two different
% forms, one of which allowed the presence of errors, and one which did not.
% We have now (mostly) transitioned to a scheme where we check for and report
% (in the sense of generating warnings or errors for) any problems in the
% input, and include in the parse tree only the cleaned-up form in which any
% contradictions in the input have been resolved. This allows us to continue
% to process the input, and find and report as many more problems as we can.
% If the problems we found while constructing the parse tree included errors
% and not just warnings, we will of course have to stop at a the point where
% the risk of any errors we find and report has too high a chance of being
% an avalanche error, caused not by the code we are looking at, but by
% an incorrect resolution by the compiler of an earlier problem.
%
%---------------------------------------------------------------------------%
:- module parse_tree.prog_parse_tree.
:- interface.
:- import_module libs.
:- import_module libs.globals.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.error_spec.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_data_foreign.
:- import_module parse_tree.prog_item.
:- import_module recompilation.
:- import_module recompilation.item_types.
:- import_module cord.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module one_or_more_map.
:- import_module set.
%---------------------------------------------------------------------------%
:- type module_names_contexts == one_or_more_map(module_name, prog_context).
%---------------------------------------------------------------------------%
:- type include_module_map == map(module_name, include_module_info).
:- type int_include_module_map == map(module_name, int_include_module_info).
:- type include_module_info
---> include_module_info(module_section, prog_context).
% The "include_module" declaration occurs in the given section
% of the relevant file, and at the given context.
:- type int_include_module_info =< include_module_info
---> include_module_info(int_module_section, prog_context).
:- type int_module_section =< module_section
---> ms_interface.
%---------------------%
% The module being compiled can have another module made available to it
% either explicitly or implicitly.
%
% An explicit availability can happen either through an `:- import_module'
% or a `:- use_module' declaration (import or use, for short), and these
% declarations can occur in either the interface section or in the
% implementation section.
%
% The values of section_import_and_or_use specify the possible valid ways
% that a module may be made available explicitly. The first four specify
% the usual ways: an import or use in either section, with the one context.
% The last one says that the module was named in an use_module declaration
% in the interface section and in an import_module declaration
% in the implementation section, and give the two contexts respectively.
%
% The values of the implicit_import_or_use type specify the possible ways
% that a module may be made available implicitly. Most implicit availability
% is to give the compiler access to the declarations of predicates and
% functions that the compiler will automatically insert calls to as part of
% the implementation of some language feature, such as table resets for
% memoed procedures. Since these automatically generated references
% will be created fully module qualified, an import of the target modules
% is not needed; a use is enough. However, the public builtin module
% is implicitly imported into every Mercury module, and this one
% does get imported, not used.
%
% Note that we do not record contexts for implicit uses. In general,
% there is no *single specific* context that makes an implicit use needed,
% and we don't need contexts for any error messages about implicit imports,
% since we don't want to require Mercury programmers to have to know
% such details of the Mercury implementation. We *could* collect the set of
% contexts that make a given implicit use needed, for internal compiler
% purposes, but we do not (yet) have any need for that information.
:- type section_import_and_or_use
---> int_import(prog_context)
; int_use(prog_context)
; imp_import(prog_context)
; imp_use(prog_context)
; int_use_imp_import(prog_context, prog_context).
:- type section_use =< section_import_and_or_use
---> int_use(prog_context)
; imp_use(prog_context).
:- type int_section_import =< section_import_and_or_use
---> int_import(prog_context).
:- type implicit_import_or_use
---> implicit_int_import
; implicit_int_use
; implicit_imp_use.
:- type maybe_implicit_import_and_or_use
---> explicit_avail(
section_import_and_or_use
)
; implicit_avail(
implicit_import_or_use,
maybe(section_import_and_or_use)
).
% Values of this type specify how each module we have available
% was *made* available. If a module had redundant import_module
% and/or use_module declarations, each of these has had a warning
% generated for it and was then discarded. One of these declarations
% can be made redundant redundant not only by another declaration
% of the same kind in the same section, but also by more permissive
% declarations; import_module declarations grant more permissions
% than use_module declarations, and declarations in the interface
% give more permissions than the declarations of the same kind
% in the implementation section.
:- type section_import_and_or_use_map ==
map(module_name, section_import_and_or_use).
:- type section_use_map ==
map(module_name, section_use).
:- type int_import_map ==
map(module_name, int_section_import).
:- type import_and_or_use_map ==
map(module_name, maybe_implicit_import_and_or_use).
%---------------------------------------------------------------------------%
%
% The parse_tree_{src,int,opt} types define the ASTs we use for source files,
% interface files and optimization files respectively.
%
% Nested submodules may appear in source files, but not in interface files
% or optimization files.
%
% We use cords of items instead of lists of items where we may need to add
% items to an already-existing partial parse tree.
%
% The contexts of module declarations below may be term_context.dummy_context
% if the actual context isn't known, but if the recorded context is
% not term_context.dummy_context, then it is valid.
%
% Values of this type represent the contents of a .m source file.
%
:- type parse_tree_src
---> parse_tree_src(
pts_module_name :: module_name,
% The context of the `:- module' declaration.
pts_module_name_context :: prog_context,
% The contents of the module.
pts_components :: cord(module_component)
).
:- type module_component
---> mc_section(
mcs_module_name :: module_name,
mcs_section_kind :: module_section,
% The context of the `:- interface' or `:- implementation'
% declaration.
mcs_section_context :: prog_context,
mcs_includes :: cord(item_include),
mcs_avails :: cord(item_avail),
pti_fims :: cord(item_fim),
mcs_items :: cord(item)
)
; mc_nested_submodule(
% The name of the *including* module.
mcns_module_name :: module_name,
% What kind of section is the submodule in?
mcns_in_section_kind :: module_section,
% The context of the section that the submodule is in.
mcns_in_section_context :: prog_context,
% The submodule itself.
mcns_submodule :: parse_tree_src
).
% Values of this type represent one module in a .m source file.
% (A source file may contain more than one module, though most Mercury
% source files contain just one.)
%
:- type parse_tree_module_src
---> parse_tree_module_src(
ptms_module_name :: module_name,
% The context of the `:- module' declaration.
ptms_module_name_context :: prog_context,
% The set of modules mentioned in `:- include_module'
% declarations in the interface and in the implementation,
% in a cleaned-up form. The cleanup requires that
%
% - no module be included more than once in a section, and
% - no module be included in both sections.
%
% Any violations of these requirements will have had
% an error message generated for it before the
% parse_tree_module_src structure is constructed.
ptms_include_map :: include_module_map,
% A specification of the set of modules mentioned in
% `:- import_module' and/or `:- use_module' declarations
% in each section, possibly augmented with implicit
% imports/uses, in a cleaned-up form. The cleanup requires
% that
%
% - no module be imported more than once in a section,
% - no module be used more than once in a section,
% - no module be both imported and used used in a section, and
% - no module be imported or used in more than once section,
% with the one permitted exception is when a module is
% used in the interface and imported in the implementation.
%
% Any violations of these requirements will have had
% an error message generated for it before the
% parse_tree_module_src structure is constructed.
ptms_import_use_map :: import_and_or_use_map,
% A cleaned-up version of the set of explicit
% `:- pragma foreign_import_module' declarations
% in the interface and in the implementation.
% The cleaned-up part means that we we have reported both
%
% - FIMs that occur more than once in a given section, and
% - FIMs that occur in both sections.
%
% We keep the context of only the first FIM for a given
% fim_spec in each section, and if a fim_spec occurs in
% both sections, we keep only the (first) occurrence in the
% interface section.
%
% We don't have a field containing the original, non-cleaned-up
% data, since no part of the compiler (yet) need this.
ptms_int_fims :: map(fim_spec, prog_context),
ptms_imp_fims :: map(fim_spec, prog_context),
% The set of foreign languages for which this module
% should have implicit foreign_import_module declaration
% for itself, in the interface and implementation respectively.
ptms_int_self_fim_langs :: set(foreign_language),
ptms_imp_self_fim_langs :: set(foreign_language),
ptms_type_defns :: type_ctor_checked_map,
ptms_inst_defns :: inst_ctor_checked_map,
ptms_mode_defns :: mode_ctor_checked_map,
% The error messages generated during the construction
% of ptms_type_defns. We have found some invalid types if
% some of these error_specs (a) are severity_error, and
% (b) are phase_tim_check_invalid_type.
ptms_type_specs :: list(error_spec),
% The error messages generated during the construction
% of ptms_inst_defns and ptms_mode_defns. We have found
% some invalid insts and/or modes if some of these error_specs
% (a) are severity_error, and (b) are
% phase_tim_check_invalid_inst_mode.
ptms_inst_mode_specs :: list(error_spec),
% Items of various kinds in the interface.
% All these items are to be treated as being in the
% interface section, with one exception.
% If this module has some submodules, i.e. if the
% ptms_include_map field above is nonempty, then we handle
% any nonabstract instance items in the interface by
% - treating only an abstract version of the item as being
% in the interface, and
% - treating the original version as being in the
% implementation section, but exported to submodules.
% (For abstract instances, there is no point in adding them
% twice, once in each section, so we treat them as only
% being in the interface.)
ptms_int_typeclasses :: list(item_typeclass_info),
ptms_int_instances :: list(item_instance_info),
ptms_int_pred_decls :: list(item_pred_decl_info),
ptms_int_mode_decls :: list(item_mode_decl_info),
ptms_int_decl_pragmas :: list(item_decl_pragma_info),
ptms_int_decl_markers :: list(item_decl_marker_info),
ptms_int_promises :: list(item_promise_info),
% The set of predicate names for which the interface contains
% either attempts at a definition (i.e. a clause or a
% foreign_proc), or something else that tells us that
% generating a warning about a lack of a definition
% in the implementation section (if in fact there is
% no definition there) would be more misleading than useful.
ptms_int_bad_clauses :: set(pred_pf_name_arity),
% A repeat of everything above, but in the implementation
% section, with the addition of some item kinds that may occur
% *only* in implementation sections.
%
% However, note that the conversion process we now use
% to generate parse_tree_module_srcs will put any impl pragmas,
% initialises, finalises and mutables that were wrongly placed
% in the interface section into their fields below, so that
% if there is something wrong with them *beyond* their
% location, the compiler can detect and report it in the
% same compiler invocation. It would be easy to put these
% misplaced items into separate fields of their own,
% but so far there has been no need for that.
%
% If this module has no submodules, i.e. if the
% ptms_include_map field above is empty, then all the items
% in these fields are to be treated as in being in the
% implementation section. However, if this module HAS
% at least one submodule (in either section), then only
% the following kinds of items are to be treated as being
% private to this module:
%
% clauses
% foreign_procs
% foreign_export_enums
% impl_pragmas
% initialises
% finalises
%
% All the other kinds of items are to be treated as being
% exported to submodules.
ptms_imp_typeclasses :: list(item_typeclass_info),
ptms_imp_instances :: list(item_instance_info),
ptms_imp_pred_decls :: list(item_pred_decl_info),
ptms_imp_mode_decls :: list(item_mode_decl_info),
ptms_imp_clauses :: list(item_clause_info),
ptms_imp_foreign_procs :: list(item_foreign_proc_info),
ptms_imp_foreign_export_enums ::
list(item_foreign_export_enum_info),
ptms_imp_decl_pragmas :: list(item_decl_pragma_info),
ptms_imp_decl_markers :: list(item_decl_marker_info),
ptms_imp_impl_pragmas :: list(item_impl_pragma_info),
ptms_imp_impl_markers :: list(item_impl_marker_info),
ptms_imp_promises :: list(item_promise_info),
ptms_imp_initialises :: list(item_initialise_info),
ptms_imp_finalises :: list(item_finalise_info),
ptms_imp_mutables :: list(item_mutable_info)
).
%---------------------------------------------------------------------------%
%
% The representations specific to .int0, .int, .int2 and .int3 files.
%
% A representation of the contents of .int0 files.
%
:- type parse_tree_int0
---> parse_tree_int0(
pti0_module_name :: module_name,
% The context of the `:- module' declaration.
pti0_module_name_context :: prog_context,
pti0_maybe_version_numbers :: maybe_version_numbers,
% The set of modules mentioned in `:- include_module'
% declarations in the interface and implementation,
% and their locations.
pti0_include_map :: include_module_map,
% The set of modules mentioned in `:- import_module'
% declarations in the interface and implementation,
% and their locations.
pti0_import_use_map :: section_import_and_or_use_map,
% `:- pragma foreign_import_module' declarations
% in the interface and in the implementation.
pti0_int_fims :: set(fim_spec),
pti0_imp_fims :: set(fim_spec),
% Type, inst and mode definitions from both
% the interface and implementation sections.
pti0_type_defns :: type_ctor_checked_map,
pti0_inst_defns :: inst_ctor_checked_map,
pti1_mode_defns :: mode_ctor_checked_map,
% Items of various kinds in the interface.
% XXX For the consumers of the .int0 file, in most cases
% it makes no difference whether an item was in the parent's
% interface or implementation section. We should make that
% distinction here ONLY when we have to.
pti0_int_typeclasses :: list(item_typeclass_info),
pti0_int_instances :: list(item_abstract_instance_info),
pti0_int_pred_decls :: list(item_pred_decl_info),
pti0_int_mode_decls :: list(item_mode_decl_info),
pti0_int_decl_pragmas :: list(item_decl_pragma_info),
pti0_int_decl_markers :: list(item_decl_marker_info),
pti0_int_promises :: list(item_promise_info),
% Items of various kinds in the implementation section.
pti0_imp_typeclasses :: list(item_typeclass_info),
pti0_imp_instances :: list(item_abstract_instance_info),
pti0_imp_pred_decls :: list(item_pred_decl_info),
pti0_imp_mode_decls :: list(item_mode_decl_info),
pti0_imp_decl_pragmas :: list(item_decl_pragma_info),
pti0_imp_decl_markers :: list(item_decl_marker_info),
pti0_imp_promises :: list(item_promise_info)
).
% A representation of the contents of .int files.
%
:- type parse_tree_int1
---> parse_tree_int1(
pti1_module_name :: module_name,
% The context of the `:- module' declaration.
pti1_module_name_context :: prog_context,
pti1_maybe_version_numbers :: maybe_version_numbers,
% The set of modules mentioned in `:- include_module'
% declarations in the interface and implementation,
% and their contexts.
pti1_include_map :: include_module_map,
% The set of modules mentioned in `:- use_module'
% declarations in the interface and implementation,
% and their locations.
pti1_use_map :: section_use_map,
% `:- pragma foreign_import_module' declarations
% in the interface and in the implementation.
pti1_int_fims :: set(fim_spec),
pti1_imp_fims :: set(fim_spec),
% Type, inst and mode definitions, all of which are
% in the interface, with the exception of some type
% definitions from the implementation section
% (which should not be needed after we start actually
% *using* type_repn items).
pti1_type_defns :: type_ctor_checked_map,
pti1_inst_defns :: inst_ctor_checked_map,
pti1_mode_defns :: mode_ctor_checked_map,
% Items of various kinds in the interface.
pti1_int_typeclasses :: list(item_typeclass_info),
pti1_int_instances :: list(item_abstract_instance_info),
pti1_int_pred_decls :: list(item_pred_decl_info),
pti1_int_mode_decls :: list(item_mode_decl_info),
pti1_int_decl_pragmas :: list(item_decl_pragma_info),
pti1_int_decl_markers :: list(item_decl_marker_info),
pti1_int_promises :: list(item_promise_info),
% The representations of all types defined in the module,
% whether exported or not.
pti1_type_repns :: type_ctor_repn_map,
% Items of various kinds in the implementation.
pti1_imp_typeclasses :: list(item_abstract_typeclass_info)
).
% A representation of the contents of .int2 files.
%
:- type parse_tree_int2
---> parse_tree_int2(
pti2_module_name :: module_name,
% The context of the `:- module' declaration.
pti2_module_name_context :: prog_context,
% XXX While it is clear that .int files need version number
% fields while .int3 files do not, I (zs) don't see any
% clear argument either way for .int2 files. Having
% the field here preserves old behavior.
pti2_maybe_version_numbers :: maybe_version_numbers,
% The set of modules mentioned in `:- include_module'
% declarations in the interface, and their locations.
pti2_int_includes :: int_include_module_map,
% The set of modules mentioned in `:- use_module'
% declarations in the interface, and their locations.
pti2_use_map :: section_use_map,
% `:- pragma foreign_import_module' declarations
% in the interface and in the implementation.
pti2_int_fims :: set(fim_spec),
pti2_imp_fims :: set(fim_spec),
% Type, inst and mode definitions, all of which are
% in the interface, with the exception of some type
% definitions from the implementation section
% (which should not be needed after we start actually
% *using* type_repn items).
pti2_type_defns :: type_ctor_checked_map,
pti2_inst_defns :: inst_ctor_checked_map,
pti2_mode_defns :: mode_ctor_checked_map,
% Items of various kinds in the interface.
pti2_int_typeclasses :: list(item_typeclass_info),
pti2_int_instances :: list(item_abstract_instance_info),
% The representations of all types defined in the module,
% whether exported or not.
pti2_type_repns :: type_ctor_repn_map
).
% A representation of the contents of .int3 files.
%
:- type parse_tree_int3
---> parse_tree_int3(
pti3_module_name :: module_name,
% The context of the `:- module' declaration.
pti3_module_name_context :: prog_context,
% The set of modules mentioned in `:- include_module'
% declarations in the interface, and their locations.
pti3_int_includes :: int_include_module_map,
% The set of modules mentioned in `:- import_module'
% declarations in the interface, and their locations.
pti3_int_import_map :: int_import_map,
% Type, inst and mode definitions, all of which are
% in the interface.
pti3_type_defns :: type_ctor_checked_map,
pti3_inst_defns :: inst_ctor_checked_map,
pti3_mode_defns :: mode_ctor_checked_map,
% Items of various kinds in the interface.
% XXX The typeclass definitions in .int3 files have invariants
% that we cannot yet encode in a subtype. Specifically,
% these typeclass definitions are not only guaranteed to have
% class_interface_abstract as their tc_class_methods fields
% (which is enforced by our use of item_abstract_typeclass_info
% here), they are guaranteed to have empty lists as their
% tc_superclasses and tc_fundeps fields. However, having
% empty_list as a subtype of list is not yet feasible,
% for the reason described next to the commented out
% empty_list subtype definition in library/list.m.
pti3_int_typeclasses :: list(item_abstract_typeclass_info),
pti3_int_instances :: list(item_abstract_instance_info),
pti3_int_type_repns :: type_ctor_repn_map
).
% When comp_unit_interface.m creates the contents of an interface file,
% it will always set the maybe_version_numbers field of that interface file
% to `no_version_numbers'. If the value of that field is needed,
% it will be filled in by the actually_write_interface_file predicate
% in write_module_interface_files.m, which (unlike comp_unit_interface.m)
% has access to the I/O state to read in the *previous* version
% of that interface file.
:- type maybe_version_numbers
---> no_version_numbers
; version_numbers(module_item_version_numbers).
%---------------------------------------------------------------------------%
% A representation of the contents of .opt files.
%
:- type parse_tree_plain_opt
---> parse_tree_plain_opt(
ptpo_module_name :: module_name,
% The context of the `:- module' declaration.
ptpo_module_name_context :: prog_context,
% `:- use_module' (not `:- import_module') declarations.
ptpo_uses :: module_names_contexts,
ptpo_fims :: set(fim_spec),
ptpo_type_defns :: list(item_type_defn_info),
ptpo_foreign_enums :: list(item_foreign_enum_info),
ptpo_inst_defns :: list(item_inst_defn_info),
ptpo_mode_defns :: list(item_mode_defn_info),
ptpo_typeclasses :: list(item_typeclass_info),
ptpo_instances :: list(item_instance_info),
ptpo_pred_decls :: list(item_pred_decl_info),
ptpo_mode_decls :: list(item_mode_decl_info),
ptpo_clauses :: list(item_clause_info),
ptpo_foreign_procs :: list(item_foreign_proc_info),
ptpo_promises :: list(item_promise_info),
ptpo_decl_markers :: list(item_decl_marker_info_opt),
ptpo_impl_markers :: list(item_impl_marker_info_opt),
ptpo_type_specs :: list(decl_pragma_type_spec_info),
ptpo_unused_args :: list(gen_pragma_unused_args_info),
ptpo_termination :: list(decl_pragma_termination_info),
ptpo_termination2 :: list(decl_pragma_termination2_info),
ptpo_exceptions :: list(gen_pragma_exceptions_info),
ptpo_trailing :: list(gen_pragma_trailing_info),
ptpo_mm_tabling :: list(gen_pragma_mm_tabling_info),
ptpo_struct_sharing :: list(decl_pragma_struct_sharing_info),
ptpo_struct_reuse :: list(decl_pragma_struct_reuse_info)
).
% A representation of the contents of .trans_opt files.
%
:- type parse_tree_trans_opt
---> parse_tree_trans_opt(
ptto_module_name :: module_name,
% The context of the `:- module' declaration.
ptto_module_name_context :: prog_context,
ptto_termination :: list(decl_pragma_termination_info),
ptto_termination2 :: list(decl_pragma_termination2_info),
ptto_exceptions :: list(gen_pragma_exceptions_info),
ptto_trailing :: list(gen_pragma_trailing_info),
ptto_mm_tabling :: list(gen_pragma_mm_tabling_info),
ptto_struct_sharing :: list(decl_pragma_struct_sharing_info),
ptto_struct_reuse :: list(decl_pragma_struct_reuse_info)
).
%---------------------------------------------------------------------------%
%
% A parse_tree_module_src is one module to be compiled. A parse_tree_src that
% contains N nested submodules corresponds to 1 + N parse_tree_module_srcs,
% one for the top level module, and one for each (possibly deeply) nested
% submodule. The conversion from a parse_tree_src to one or more
% parse_tree_module_srcs is done by two modules. The split_parse_tree_src.m
% module splits up the source file into one or more modules, and then
% convert_parse_tree.m replaces the raw item lists in parse_tree_srcs
% with the more structured representation used by parse_tree_module_srcs.
%
% Before we convert a parse_tree_module_src into the HLDS, we augment it
% with the contents of the interface files of the modules it imports
% (directly or indirectly), and if requested, with the contents of the
% optimization files of those modules as well. The augmented compilation unit
% will consist of the following for compiler invocations that generate
% target language code. (Compiler invocations that generate .int0, .int
% and .int2 files will construct an aug_make_int_unit, not an
% aug_compilation_unit, while compiler invocations that generate .int3 files
% will construct neither.)
%
% - The module_src field contains the original parse_tree_module_src.
%
% - The ancestor_int_specs field contains the .int0 interface files of
% the ancestors of this module, which are always implicitly imported.
%
% - The direct_int_specs field contains the .int files of the modules
% directly imported or used by this module, with the "override" exception
% noted below.
%
% - The indirect_int_specs field contains the .int2 files of the modules
% indirectly imported or used by this module, again with the "override"
% exception noted below.
%
% In this case, module A "indirectly imports or uses" module C if
% module A imports or uses a module B whose .int file uses module C.
% (.int files only use modules; they do not import them.)
%
% The exceptions above are that
%
% o if a module's .int0 file is in the ancestor_int_specs field,
% we don't include its .int1 file in the direct_int_specs field,
% or its .int2 file in the indirect_int_specs field. In effect,
% the appearance of a module in the ancestor_int_specs field
% overrides (i.e. prevents) its appearance in the direct_int_specs
% or the indirect_int_specs fields.
%
% o if a module's .int file is in the direct_int_specs field,
% we don't include its .int2 file in the indirect_int_specs field.
% Again, the appearance of a module in the direct_int_specs field
% overrides its appearance in the indirect_int_specs field.
%
% The reason for the exceptions is that an .int0 file contains (or at least
% is intended to contain, which *may* be different) every item that
% the .int file for the same module contains, and the same relationship
% holds between .int and .int2 files. The exceptions thus save the compiler
% from doing work that (a) is unnecessary, and (b) would lead things
% being declared or defined more than once.
%
% - Provided intermodule optimization is enabled, the plain_opts field
% will contain
%
% o the .opt files of the modules whose .int0, .int or .int2 files
% are in the ancestor_int_sprcs, direct_int_specs and indirect_int_specs
% fields above, and
%
% o unless the compiler is invoked with --no-read-opt-files-transitively,
% the .opt files of every other module the .opt files specified
% by either the previous bullet point or *this* bullet point
% import either explicitly or implicitly.
%
% These .opt files are supposed to contain more information about
% the ancestor-, direct- or indirect-imported modules than their
% .int0, .int or .int2 files do. Unfortunately, they often also
% *duplicate* items in those interface files, which leads to
% double definitions, which the submodules of make_hlds.m have to
% be prepared to detect and ignore.
%
% - Provided transitive intermodule optimization is enabled, the trans_opts
% field will contain the .trans_opt files of the modules named in
% the module's .d file as the module's trans_opt dependencies.
% XXX This seems to me (zs) a bit too indirect.
%
% - If intermodule optimization is enabled, the int_for_opt_specs field
% will contain
%
% o the .int0 files of the ancestor modules of the modules whose .opt files
% are in the plain_opts field,
%
% o the .int files of the modules imported or used either explicitly
% or implicitly by the modules whose .opt files are in the plain_opts
% field, or by their ancestors, and
%
% o the .int2 files of the modules used by the .int files in the previous
% bullet point.
%
% The idea is that these interface files may in general be needed to define
% entities (such as types, insts or modes) that the .opt files in the
% plain_opts field may need.
%
% XXX There is a problem here, which is that override exception does *not*
% apply to the int_for_opt_specs field. It is possible for e.g. a module's
% .int2 file to appear in the indirect_int_specs field, but its .int0 or
% .int file to appear in the int_for_opt_specs field. This may also lead
% to double definitions of e.g. types, insts or modes. The compiler does
% ignore such double definitions, under the principle of generating error
% messages for double definitions *only* when the entity being double-defined
% has the module currently being compiled as its module qualifier.
% Nevertheless, including more than one interface file for any given module
% in the augmented compilation unit will lead to wasted work, which means
% that we should avoid doing that if possible.
%
:- type aug_compilation_unit
---> aug_compilation_unit(
% The source code of the module.
acu_module_src :: parse_tree_module_src,
% The interface files of the ancestors of this module.
% (If we have e.g. module foo.bar among the modules
% we import int_for_opt, we also need to grab its ancestor foo,
% but such .int0 files also go into the int_for_opt field.
acu_ancestor_ints :: map(module_name, ancestor_int_spec),
% The interface files of directly imported modules.
acu_direct_int1s :: map(module_name, direct_int1_spec),
% The interface files of indirectly imported modules.
acu_indirect_int2s :: map(module_name, indirect_int2_spec),
% The optimization files of directly or indirectly
% imported modules.
acu_plain_opts :: map(module_name, parse_tree_plain_opt),
acu_trans_opts :: map(module_name, parse_tree_trans_opt),
% The interface files needed to make sense
% of those optimization files.
acu_int_for_opts :: map(module_name, int_for_opt_spec),
% Interface files that we read in only for the type
% representation information they contain
acu_type_repns :: map(module_name, type_repn_spec),
% The module_version_numbers records in all the imported
% interface files.
acu_item_version_map:: module_item_version_numbers_map
).
:- type aug_make_int_unit
---> aug_make_int_unit(
% The source code of the module.
%
% Note that some kinds of errors, such as "importing a.b.c
% without importing a.b", will have been corrected
% automatically.
amiu_module_src :: parse_tree_module_src,
% The list of messages for errors in amiu_module_src
% whose reporting we would like to delay until a compiler
% invocation that generates target code.
%
% We report these messages *only* if the creation of the
% .int[012] file for the module cannot succeed for
% *other* reasons. In that case, not reporting them
% would give the programmer an incomplete picture
% of what is wrong.
amiu_delayed_specs :: list(error_spec),
% The interface files of the ancestors of this module.
% (The read_why_int0 is always implicitly rwi0_section.)
amiu_ancestor_ints :: map(module_name, parse_tree_int0),
% The interface files of directly imported modules.
amiu_direct_int3s :: map(module_name, direct_int3_spec),
% The interface files of indirectly imported modules.
amiu_indirect_int3s :: map(module_name, indirect_int3_spec),
% The module_version_numbers records in all the imported
% interface files.
amiu_item_version_map :: module_item_version_numbers_map
).
% init_aug_compilation_unit(ParseTreeModuleSrc, AugCompUnit):
%
% Initialize an augmented compilation unit structure. Put the given
% ParseTreeModuleSrc into it, and leave the rest of the structure empty.
% Our caller is the expected to fill in (i.e. augment) the structure
% by calling the aug_compilation_unit_add_X predicates in grab_modules.
% to add the parse trees of the interface and optimization files needed
% to compile ParseTreeModuleSrc.
%
:- pred init_aug_compilation_unit(parse_tree_module_src::in,
aug_compilation_unit::out) is det.
:- type ancestor_int_spec
---> ancestor_int0(parse_tree_int0, read_why_int0).
:- type direct_int1_spec
---> direct_int1(parse_tree_int1, read_why_int1).
:- type direct_int3_spec
---> direct_int3(parse_tree_int3, read_why_int3).
:- type indirect_int2_spec
---> indirect_int2(parse_tree_int2, read_why_int2).
:- type indirect_int3_spec
---> indirect_int3(parse_tree_int3, read_why_int3).
:- type int_for_opt_spec
---> for_opt_int0(parse_tree_int0, read_why_int0)
; for_opt_int1(parse_tree_int1, read_why_int1)
; for_opt_int2(parse_tree_int2, read_why_int2).
:- type type_repn_spec
---> type_repn_spec_int1(parse_tree_int1).
% All these record recomp_avail_int_import as recompilation reason.
% (Since there is no recomp_avail_ancestor_import, yet).
:- type read_why_int0
---> rwi0_section
% Add the parse tree to the set of directly-read interfaces.
; rwi0_opt.
% Add the parse tree to the set of read-int-for-opt interfaces.
:- type read_why_int1
---> rwi1_int_import
% Add the parse tree to the set of directly-read interfaces.
%
% Record recomp_avail_int_import as recompilation reason.
; rwi1_int_use
% Add the parse tree to the set of directly-read interfaces.
%
% Record recomp_avail_int_use as recompilation reason.
; rwi1_imp_import
% Add the parse tree to the set of directly-read interfaces.
%
% Record recomp_avail_imp_import as recompilation reason.
; rwi1_imp_use
% Add the parse tree to the set of directly-read interfaces.
%
% Record recomp_avail_imp_use as recompilation reason.
; rwi1_int_use_imp_import
% Add the parse tree to the set of directly-read interfaces.
%
% Record recomp_avail_int_use_imp_import as recompilation reason.
; rwi1_opt
% Add the parse tree to the set of read-int-for-opt interfaces.
%
% Record recomp_avail_imp_use as recompilation reason.
; rwi1_type_repn.
% The only items that should be paid attention to from this
% .int file are the type_repn items. They don't need any
% section markers.
%
% Add the parse tree to the type-repn interfaces.
%
% Record recomp_avail_int_import as recompilation reason.
% XXX TYPE_REPN This is a lie, but it is the best we can do now,
% because smart recompilation "cannot handle the truth",
% due to not yet having been adapted to handle dependencies
% on interface files that are needed only for type representation
% information.
% All these record recomp_avail_imp_use as recompilation reason.
:- type read_why_int2
---> rwi2_int_use
% Add the parse tree to the set of indirectly-read interfaces.
; rwi2_imp_use
% Add the parse tree to the set of indirectly-read interfaces.
; rwi2_abstract
% Add the parse tree to the set of indirectly-read interfaces.
; rwi2_opt.
% Add the parse tree to the set of read-int-for-opt interfaces.
% XXX TYPE_REPN Do we need a rwi2_type_repn?
:- type read_why_int3
---> rwi3_direct_ancestor_import
% Add the parse tree to the set of directly-read interfaces.
%
% Record recomp_avail_int_import as recompilation reason.
% (Since there is no recomp_avail_ancestor_import, yet).
; rwi3_direct_int_import
% Add the parse tree to the set of directly-read interfaces.
%
% Record recomp_avail_int_import as recompilation reason.
; rwi3_direct_imp_import
% Add the parse tree to the set of directly-read interfaces.
%
% Record recomp_avail_imp_import as recompilation reason.
; rwi3_direct_ancestor_use
% Add the parse tree to the set of directly-read interfaces.
%
% Record recomp_avail_int_use as recompilation reason.
% (Since there is no recomp_avail_ancestor_use, yet).
; rwi3_direct_int_use
% Add the parse tree to the set of directly-read interfaces.
%
% Record recomp_avail_int_use as recompilation reason.
; rwi3_direct_imp_use
% Add the parse tree to the set of directly-read interfaces.
%
% Record recomp_avail_imp_use as recompilation reason.
; rwi3_direct_int_use_imp_import
% Add the parse tree to the set of directly-read interfaces.
%
% Record recomp_avail_int_use_imp_import as recompilation reason.
; rwi3_indirect_int_use
% Add the parse tree to the set of indirectly-read interfaces.
%
% Record recomp_avail_int_use as recompilation reason.
% (Since there is no recomp_avail_indirect_use_int, yet).
; rwi3_indirect_imp_use.
% Add the parse tree to the set of indirectly-read interfaces.
%
% Record recomp_avail_imp_use as recompilation reason.
% (Since there is no recomp_avail_indirect_use_imp, yet).
%---------------------------------------------------------------------------%
:- type module_section
---> ms_interface
; ms_implementation.
%---------------------------------------------------------------------------%
% An import_locn is used to describe the place where an item was
% imported from.
:- type import_locn
---> import_locn_implementation
% The item is from a module imported in the implementation.
; import_locn_interface
% The item is from a module imported in the interface.
; import_locn_import_by_ancestor
% The item is from a module imported by an ancestor.
% XXX Did the ancestor do the import in its interface, or not?
; import_locn_ancestor_int0_interface
; import_locn_ancestor_int0_implementation.
% The item is from the interface or implementation section
% of the .int0 file of an ancestor module.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
%---------------------------------------------------------------------------%
init_aug_compilation_unit(ParseTreeModuleSrc, AugCompUnit) :-
map.init(AncestorIntSpecs),
map.init(DirectIntSpecs),
map.init(IndirectIntSpecs),
map.init(PlainOpts),
map.init(TransOpts),
map.init(IntForOptSpecs),
map.init(TypeRepnSpecs),
map.init(VersionNumbers),
AugCompUnit = aug_compilation_unit(ParseTreeModuleSrc,
AncestorIntSpecs, DirectIntSpecs, IndirectIntSpecs,
PlainOpts, TransOpts, IntForOptSpecs, TypeRepnSpecs, VersionNumbers).
%---------------------------------------------------------------------------%
:- end_module parse_tree.prog_parse_tree.
%---------------------------------------------------------------------------%