mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
1027 lines
49 KiB
Mathematica
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.
|
|
%---------------------------------------------------------------------------%
|