compiler/comp_unit_interface.m:
As above.
tests/valid/int_imp_test.m:
tests/valid/int_imp_test_2.m:
A regression test for the abort that the sanity check caused.
tests/valid/Mmakefile:
Enable the new test case.
compiler/decide_type_repn.m:
Previously, this module computed type_repn items to put into .int3 files
for a subset of the type constructors defined in the current module:
the direct_dummy, enum and notag types (the *simple* types),
and the du types whose representation is guaranteed to be
a word-aligned pointer when targeting C. (We care about pointers
being word-aligned only when applying the direct arg optimization.
This optimization is applicable only with the low level data
representation, which we use only when targeting C.)
This diff adds code to decide the representations of *all* the
type constructors defined in the current module.
This code is based on the existing code in du_type_layout.m,
which it is intended to eventually replace, but its job is more general,
because it decides the representation of each type not just for
one platform (the one we want to generate code), but for all possible
platforms. This is because we want to put the descriptions of type
representations into the module's .int file to serve as a single source
of truth for all modules that use the types defined in this module,
and the contents of .int files should be platform-independent.
For our purposes, there are six kinds of platforms, which are
distinguished along three axes: 64 vs 32 bit machines, spf vs non-spf
grades, and direct arg optimization enabled vs disabled. That is eight
combinations, but on 64 bit machines, a float takes up one word whether
that float is single or double precision, so two combinations aren't valid.
Some of the change to this module consists of generalizing the existing
code so that it can decide simple types not just when targeting .int3 files
but .int files as well. However, the bulk of it is code for deciding
the representations of non-simple types. The code is not lifted straight
from du_type_layout.m. There are two main kinds of changes.
First, I took the opportunity to simplify the algorithms used.
For example, while du_type_layout.m passes over each function symbol
in the most general kind of type twice: once to assign it a cons_tag,
and once to decide how to pack its arguments, the code here does both jobs
in one pass. Another example is that for historical reasons,
du_type_layout.m computed the amount of space needed for an argument
in one place for sub-word-sized arguments, and in another place
for more-than-word-sized arguments; decide_type_repn.m does it all
in one place.
Second, since we compute a representation for each type six times,
I tried to avoid obvious inefficiencies, but only if the code
remained simple. In the future, we may want to use an approach
based on the idea that in the process of computing the first
representation, we look out for any indication that the representation
may be different on any of the other five platforms, and if not,
we just reuse the first representation on the other five platforms as well.
However, that would be appropriate only *after* we have a simpler
system that has proven to work in practice.
There is a third, smaller change: when deciding whether an argument
is packable, we take into account not just equivalence type
definitions, but the definitions of notag types as well.
This takes advantage of the fact that if a notag type is abstract
exported, its representation is put into the relevant .int3 file
even though its definition isn't. (This is why du_type_layout.m
couldn't "see through" notag types: it couldn't depend on knowing
which types were notags.)
compiler/prog_item.m:
Change the types we use for type representation information.
Their previous definitions baked in the assumption that the only
distinction between platforms that mattered was the 64 vs 32 bit
distinction, which is not the case.
Use a more consistent naming scheme for the types we use
to represent type representation information.
Include the "dereferenced" types of the arguments in functors'
representations. (I use "dereferencing" here to mean expanding
equivalence types and throwing away any notag wrappers.).
We don't need it when generating C code using the low level
data representation, but we do need it to create constructors
when generating e.g. Java code that uses the high level data
representation.
compiler/parse_type_repn.m:
Rewrite most of this module due to the changes in prog_item.m.
compiler/parse_tree_out_type_repn.m:
A new module containing the code for writing out type representations.
The original code used to be in parse_tree_out.m, but it has been
mostly rewritten. Partly this is due the changes in prog_item.m,
but partly it is to provide much more structured output for humans,
since this makes debugging so much easier.
compiler/parse_tree.m:
Add the new module to the parse_tree package.
compiler/parse_tree_out.m:
Delete the code moved to parse_tree_out_type_repn.m.
compiler/parse_tree_out_info.m:
Provide a mechanism for selecting between output for machines
(the default) and output for humans.
compiler/hlds_data.m:
compiler/prog_data.m:
Move the ptag type from hlds_data.m to prog_data.m, to make it
accessible in prog_item.m.
Add some documentation in prog_data.m.
compiler/comp_unit_interface.m:
If the experiment1 option is enabled, invoke decide_type_repn.m
to decide what type_repn items to put into the .int file we are
generating. Otherwise, maintain the status quo.
compiler/write_module_interface_files.m:
Pass the globals to comp_unit_interface.m so it can look up experiment1.
compiler/equiv_type.m:
Add a predicate for expanding equivalence types for use by
decide_type_repn.m. This predicate expands just one type,
but reports any use of circular equivalence types in that type.
Improve the error message for circular equivalence types by *naming*
the type constructors involved. To make this possible, pass around
sets of such type constructors instead of just a boolean saying
*whether* we have found *some* circular equivalence type.
Replace bools used as changed/unchanged flag with a bespoke type.
Standardize some variable names.
compiler/options.m:
Add the developer-only option --pack-everything, which, if set,
tells decide_type_repn.m to turn on all the packing options
that currently work. This is to allow the testing of decide_type_repn.m
in the eventual intended mode of operation, even if the various
allow-packing-... options used by du_type_layout.m are set to "no".
compiler/disj_gen.m:
compiler/equiv_type_hlds.m:
compiler/llds_out_data.m:
compiler/lookup_util.m:
compiler/ml_call_gen.m:
compiler/ml_closure_gen.m:
compiler/mlds_to_c_data.m:
compiler/rtti.m:
compiler/rtti_out.m:
compiler/rtti_to_mlds.m:
compiler/tag_switch.m:
Conform to the changes above (mostly the move of ptag to prog_data.m.)
compiler/parse_pragma.m:
Improve indentation.
tests/valid_make_int/test_repn.m:
tests/valid_make_int/test_repn_sub.m:
A fairly comprehensive test case of the new functionality.
test_repn_sub.m defines one ore more simple type constructors
of each possible kind, and test_repn.m uses them to define types
that use each possible kind of complex type representation.
tests/valid_make_int/Mmakefile:
tests/valid_make_int/Mercury.options:
Enable the new test case.
compiler/comp_unit_interface.m:
We used to put type_repn items for the simple types defined in a module
into the module's .int3 file if the option --experiment1 was given.
Change this to *always* do this. (What makes a type "simple" is defined
in du_type_layout.m; roughly a type is simple if its representation
does not depend on the representation of any other type.) It is
from these type_repn items in .int3 files that we should be able to
compute type representations for *all* types when creating a module's
.int file.
Don't take a globals parameter, as it is no longer needed.
compiler/write_module_interface_files.m:
Don't pass a globals parameter.
compiler/options.m:
Add a synonym for --compiler-sufficiently-recent, so a later diff
to configure.ac can test for the presence of this functionality
in the installed compiler.
It constructs the data it returns in maps (keyed on type_ctors),
but used to turn those maps into lists before returning them.
This was wasteful, as comp_unit_interface.m promptly turned them back
into the maps that decide_type_repn.m constructed those lists from.
compiler/decide_type_repn.m:
compiler/comp_unit_interface.m:
Get the data from decide_type_repn.m to comp_unit_interface.m
in the form of a map.
For a while now, we have been generating interface files into
file-kind-specific parse trees, but we then converted those into
the generic parse_tree_int type for further processing, because
the old code for doing that further processing operated on
parse_tree_ints. This diff replaces most of that "further processing"
code with code that operates on int-file-kind-specific parse trees,
thus avoiding the cost of conversion. The main cost is not performance
(the effect of this change on compiler speed is negligible), but loss
of code clarity through the loss of invariants.
compiler/comp_unit_interface.m:
After generating a parse_tree_intN for N in {0,1,2,3}, do NOT convert it
to a generic parse_tree_int.
When constructing a parse_tree_int3 from a raw_compilation_unit,
guard against a situation where we could unintentionally create
a duplicate of an abstract type definition. The solution here
is not the most elegant possible, but a better one requires other
changes that don't really fit in this diff.
compiler/canonicalize_interface.m:
Put two large blocks of code (dealing with the canonicalization
of pred and mode declarations respectively) into their own predicates.
Expose a mechanism to standardize not the contents of a whole
interface file, but just the predicate and mode declarations in it.
(These are the tricky kinds of items to canonicalize, because sorting
a set of mode declarations can change the meaning of the program.)
This mechanism is just a specialized version of the existing machinery.
Since this diff leaves the existing machinery unused, the next diff
will delete it, and since that will probably leave a too-small module,
delete the whole module, after moving the remaining small piece of code
to the only module that uses it, parse_tree_out.m.
compiler/parse_module.m:
Instead of implementing actually_read_module_intN in terms of
actually_read_module_int, do the reverse, which avoids the unnecessary
conversion from parse_tree_intN to parse_tree_int.
compiler/parse_tree_out.m:
Provide predicates, output_parse_tree_intN, to write out parse_tree_intN,
not just a generic parse_tree_int.
Change the existing service routines, mercury_output_parse_tree_intN,
that do all the work of the above new predicates to write out the contents
of each parse tree in the canonical order, using the newly exposed
functionality in canonicalize_interface.m where needed. (Previously,
write_module_interface_files.m invoked canonicalize_interface.m to do this
before calling parse_tree_out.m.)
These service routines were originally written for debugging; make them
suitable for creating proper .intN files by removing unneeded output.
compiler/write_module_interface_files.m:
Call the newly provided predicates in parse_tree_out.m to output
interface files. To make this possible, break up the old predicate
actually_write_interface_file into smaller predicates, so that the
new predicates actually_write_interface_fileN can each call whichever
of these are relevant for the given interface-file-kind. (Some pieces
are relevant to only a subset of these kinds.)
compiler/convert_parse_tree.m:
Note the reason why this diff is not deleting the functions that convert
parse_tree_intN to parse_tree_int.
compiler/read_modules.m:
Note the reason why this diff is not deleting the functions that read in
generic parse_tree_ints.
Replacing item blocks file-kind-specific kinds of section markers with
file-kind-specific parse trees has several benefits.
- It allows us to encode the structural invariants of each kind of file
we read in within the type of its representation. This makes the detection
of any accidental violations of those invariants trivial.
- Since each file-kind-specific parse tree has separate lists for separate
kinds of items, code that wants to operate on one or a few kinds of items
can just operate on those kinds of items, without having to traverse
item blocks containing many other kinds of items as well. The most
important consequence of this is not the improved efficiency, though
that is nice, but the increased clarity of the code.
- The new design is much more flexible. For example, it should be possible
to record that e.g. an interface file we read in as a indirect dependency
(i.e. a file we read not because its module was imported by the module
we are compiling, but because its module was imported by *another* imported
module) should be used *only* for the purpose it was read in for. This should
avoid situations where deleting an import of A from a module, because it
is not needed anymore, leads the compiler to generate an error message
about a missing import of module B. This can happen if (a) module B
always *should* have been imported, since it is used, but (b) module A's
import of module B lead to module B's interface being available *without*
an import of B.
Specifically, this flexibility should enable us to establish each module's
.int file as the single source of truth about how values of each type
defined in that module should be represented. When compiling each source
file, this approach requires the compiler to read in that module's .int file
but using only the type_repn items from that .int file, and nothing else.
- By recording a single parse tree for each file we have read, instead of
a varying number of item blocks, it should be significantly easier to
derive the contents of .d files directly from the records of those
parse trees, *without* having to maintain a separate set of fields
in the module_and_imports structure for that purpose. We could also
trivially avoid any possibility of inconsistencies between these two
different sources of truth. (We currently fill in the fields used to
drive the generation of .d files using two different pieces of code,
one used for --generate-dependencies and one used for all other invocations,
and these two *definitely* generate inconsistent results, as the significant
differences in .d files between (a) just after an invocation of
--generate-dependencies and (b) just after any other compiler invocation
can witness.)
This change is big and therefore hard to review. Therefore in many files,
this change adds "XXX CLEANUP" comments to draw attention to places that
have issues that should be fixed, but whose fixes should come later, in
separate diffs.
compiler/module_imports.m:
The compiler uses the module_and_imports structure defined here
to go from a raw compilation unit (essentially a module to be compiled)
to an augmented compilation unit (a raw compilation unit together
with all the interface and optimization files its compilation needs).
We used to store the contents of both the source file and of
the interface and optimization files in the module_and_imports structure
as item blocks. This diff replaces all those item blocks with
file-kind-specific parse trees, for the reasons mentioned above.
Separate out the .int0 files of ancestors modules from the .intN
files for N>0 of directly imported modules. (Their item blocks
used to be stored in the same list.)
Maintain a database of the source, interface and optimization files
we have read in so far. We use it to avoid reading in interface files
if we have already read in a file for the same module that contains
strictly more information (either an interface file with a smaller
number as a suffix, or the source file itself).
Shorten some field names.
compiler/prog_item.m:
Define data structures for storing information about include_module,
import_module and use_module declarations, both in a form that allows
the representation of possibly erroneous code in actual source files,
and in checked-and-cleaned-up form which is guaranteed to be free
of the relevant kinds of errors. Add a block comment at the start
of the module about the need for this distinction.
Define parse_tree_module_src, a data structure for representing
the source code of a single module. This is different from the existing
parse_tree_src type, which represents the contents of a single source file
but which may contain *more* than one module, and also different from
a raw_compilation_unit, which is based on item blocks and is thus
unable to express to invariants such as "no clauses in the interface".
Modify the existing parse_tree_intN types to express the distinction
mentioned just above, and to unify them "culturally", i.e. if they
store the same information, make them store it using the same types.
Fix a mistake by allowing promises to appear in .opt files.
I originally ruled them out because the code that generates .opt files
does not have any code to write out promises, but some of the predicates
whose clauses it writes out have goal_type_promise, which means that
they originated as promises, and get written out as promises.
Split the existing pragma item kind into three item kinds, which have
different invariants applying to them.
- The decl (short for declarative) pragmas give the compiler some
information, such as that a predicate is obsolete or that we
want to type specialize some predicate or function, that is in effect
part of the module's interface. Decl pragmas may appear in module
interfaces, and the compiler may put them into interface files;
neither statement is true of the other two kinds of pragmas.
- The impl (short for implementation) pragmas are named so
precisely because they may appear only in implementation sections.
They give the compiler information that is private to that module.
Examples include foreign_decls, foreign_codes, foreign_procs,
and promises of clause equivalence, and requests for inlining,
tabling etc. These will never be put into interface files,
though some of them can affect the compilation of other modules
by being included in .opt files.
- The gen (short for generated) pragmas can never (legally) appear
in source files at all. They record the results of compiler
analyses e.g. about which arguments of a predicate are unused,
or what exceptions a function can throw, and accordingly they
should only ever occur in compiler-generated interface files.
Use the new type differences between the three kinds of pragmas
to encode the above invariants about which kinds of pragmas can appear
where into the various kinds of parse trees.
Make the augmented compilation unit, which is computed from
the final module_and_imports structure, likewise switch from
storing item blocks to storing the whole parse trees of the
files that went into its construction. With each such parse tree,
record *why* we read it, since this controls what permissions
the source module being compiled has for access to the entities
in the parse tree.
Simplify the contains_foreign_code type, since one of three
function symbols was equivalent to one possible use of another
function symbol.
Provide a way to record which method of which class a compiler-generated
predicate is for. (See hlds_pred.m below.)
Move the code of almost all utility operations to item_util.m
(which is imported by many fewer modules than prog_item.m),
keeping just the most "popular" ones.
compiler/item_util.m:
Move most of the previously-existing utility operations here from
prog_item.m, most in a pretty heavily modified form.
Add a whole bunch of other utility operations that are needed
in more than one other module.
compiler/convert_parse_tree.m:
Provide predicates to convert from raw compilation units to
parse_tree_module_srcs, and vice versa (though the reverse
shouldn't be needed much longer).
Update the conversion operations between the general parse_tree_int
and the specific parse_tree_intN forms for the changes in prog_item.m
mentioned above. In doing so, use a consistent approach, based on
new operations in item_util.m, to detect errors such as duplicate
include_module and import/use_module declarations in all kinds
of parse trees.
Enforce the invariants that the types of parse trees of various kinds
can now express in types, generating error messages for their violations.
Delete some utility operations that have been moved to item_util.m
because now they are also needed by other modules.
compiler/grab_modules.m:
Delete code that did tests on raw compilation units that are now done
when that raw compilation unit is converted to a parse_tree_module_src.
Use the results of the checks done during that conversion to decide
which modules are imported/used and in which module section.
Record a single reason for why we reading in each interface and
optimization file. The code of make_hlds_separate_items.m will use
this reason to set up the appropriate permissions for each item
in those files.
Use separate code for handling different kinds of interface and
optimization files. Using generic traversal code was acceptable economy
when we used the same data structure for every kind of interface file,
but now that we *can* express different invariants for different kinds
of interface and optimization file, we want to execute not just different
code for each kind of file, but the data structures we want to work on
are also of different types. Using file-kind-specific code is a bit
longer, but it is significantly simpler and more robust, and it is
*much* easier to read and understand.
Delete the code that separates the parts of the implementation section
that are exported to submodules, and the part that isn't, since that task
is now done in make_hlds_separate_items.m.
Pass a database of the files we have read through the relevant predicates.
Give some predicates more meaningful names.
compiler/notes/interface_files.html:
Note a problem with the current operation of grab_modules.
compiler/get_dependencies.m:
Add operations to gather implicit references to builtin modules
(which have to be made available even without an explicit import_module
or use_module declaration) in all kinds of parse trees. These have
more code overall, but will be at runtime, since we need only look at
the item kinds that may *have* such implicit references.
Add a mechanism to record the result of these gathering operations
in import_and_or_use_maps.
Give some types, function symbols, predicates and variables
more meaningful names.
compiler/make_hlds_separate_items.m:
When we stored the contents of the source module and the
interface and optimization files we read in to augment it
in the module_and_imports structure as a bunch of item blocks,
the job of this module was to separate out the different kinds of items
in the item blocks, returning a single list of each kind of item,
with each such item being packaged up with its status (which encodes
a set of permissions saying what the source module is allowed
to do with it).
Now that the module_and_imports structure stores this info in
file-kind-specific parse trees, all of which have separate lists
for each kind of item and none of which contain item blocks,
the job of this module has changed. Now its job is to convert
the reason why each file was read in into the (one or more) statuses
that apply to the different kinds of items stored in it, wrap up
each item with its status, and return the resulting overall list
of status/item pairs for each kind of item.
compiler/read_modules.m:
Add predicates that, when reading an interface file, return its contents
in the tightest possible file-kind-specific parse tree.
Refine the database of files we have read to allow us to store
more file-kind-specific parse trees.
Don't require that files in the database have associated timestamps,
since in some cases, we read files we can put into the database
*without* getting their timestamps.
Allow the database to record that an attempt to read a file failed.
compiler/split_parse_tree_src.m:
Rearchitect how this module separates out nested submodules from within
the main module in a file.
Another of the jobs of this module is to generate error messages for
when module A includes module B twice, whether via nesting or via
include_module declarations, with one special exception for the case
where A's interface contains nested submodule A.B's interface,
and A's implementation contains nested submodule A.B's implementation.
The problem ironically was that while it reported duplicate include_module
declarations as errors, split_parse_tree_src.m also *generated*
duplicate include_module declarations. Since it replaced each nested
submodule occurrence with an include_module declaration, in the scenario
above, it generated two include_module declarations for A.B. Even worse,
the interface incarnation of submodule A.B could contain
(the interface of) its own nested submodule A.B.C, while its
implementation incarnation could contain (the implementation section of)
A.B.C. Each occurrence of A.B.C would be its only occurrence in the
including part of its parent A.B, which means local tests for duplicates
do not work. (I found this out the hard way.)
The solution we now adopt adds include_module declarations to the
parents of any submodule only once the parse tree of the entire
file has been processed, since only then do we know all the
includer/included relationships among nested modules. Until then,
we just record such relationships in a database as we discover them,
reporting duplicates when needed (e.g. when A includes B twice
*in the same section*), but not reporting duplicates when not needed
(e.g. when A.B includes A.B.C in *different* sections).
compiler/prog_data.m:
Add a new type, pf_sym_name_and_arity, that exactly specifies
a predicate or function. It is a clone of the existing simple_call_id
type, but its name does NOT imply that the predicate or function
is being called.
Add XXXs that call for some other improvements in type names.
compiler/prog_data_foreign.m:
Give a type, and the operations on that type, a more specific name.
compiler/error_util.m:
Add an id field to all error_specs, which by convention should be
filled in with $pred. Print out the value in this field if the compiler
is invoked with the developer-only option --print-error-spec-id.
This allows a person debugging the compiler find out where in the code
an undesired error message is coming from significantly easier
than was previously possible.
Most of the modules that have changes only "to conform to the changes
above" will be for this change. In many cases, the updated code
will also simplify the creation of the affected error_specs.
Fix a bug that looked for a phase in only one kind of error_spec.
Add some utility operations needed by other parts of this change.
Delete a previously internal function that has been moved to
mdbcomp/prim_data.m to make it accessible in other modules as well.
compiler/Mercury.options:
Ask the compiler to warn about dead predicates in every module
touched by this change (at least in one its earlier versions).
compiler/add_foreign_enum.m:
Replace a check for an inappropriately placed foreign_enum declaration
with a sanity check, since with this diff, the error should be caught
earlier.
compiler/add_mutable_aux_preds.m:
Delete a check for an inappropriately placed mutable declaration,
since with this diff, the error should be caught earlier.
compiler/add_pragma.m:
Instead of adding pass2 and pass3 pragmas, add decl and impl and
generated pragmas.
Delete the tests for generated pragma occurring anywhere except
.opt files, since those tests are now done earlier.
Shorten some too-long predicate names.
compiler/comp_unit_interface.m:
Operate on as specific kinds of parse trees as the interface of this
module will allow. (We could operate on more specific parse trees
if we changed the interface, but that is future work).
Use the same predicates for handling duplicate include_module,
import_module and use_module declarations as everywhere else.
Delete the code of an experiment that shouldn't be needed anymore.
compiler/equiv_type.m:
Replace code that operated on item blocks with code that operates
on various kinds of parse trees.
Move a giant block of comments to the front, where it belongs.
compiler/hlds_module.m:
Add a field to the module_info that lets us avoid generating
misleading error messages above missing definitions of predicates
or functions when those definitions were present but were not
added to the HLDS because they had errors.
Give a field and its access predicates a more specific name.
Mark a spot where an existing type cannot express everything
it is supposed to.
compiler/hlds_pred.m:
For predicates which the compiler creates to represent a class method
(the virtual function, in OOP terms), record not just this fact,
but the id of the class and of the method. Using this extra info
in progress messages (with mmc -V) prevents the compiler from printing e.g.
% Checking typeclass constraints on class method
% Checking typeclass constraints on class method
% Checking typeclass constraints on class method
when checking three such predicates.
compiler/make.m:
Provide a slot in the make_info structure to allow the database
of the files we have read in to be passed around.
compiler/make_hlds_error.m:
Delete predicates that are needed in just one other module,
and have therefore been moved there.
compiler/make_hlds_passes.m:
Add decl, impl and generated pragma separately, instead of adding
pass2 and pass3 pragmas separately.
Do not generate error messages for clauses, initialises or finalises
in module interfaces, since with this diff, such errors should be
caught earlier.
compiler/mercury_compile_main.m:
compiler/recompilation.check.m:
Explicitly pass around the expanded database of parse trees
of files that have been read in.
compiler/module_qual.collect_mq_info.m:
compiler/module_qual.m:
compiler/module_qual.qualify_items.m:
Collect module qualification information, and do module qualification
respectively on parse trees of various kinds, not item blocks.
Take information about what the module may do with the contents
of each interface or optimization file from the record of why
we read that file, not from the section markers in item blocks.
Break up some too-large predicates by carving smaller ones out of them.
compiler/options.m:
Add an option to control whether errors and/or warnings detecting
when deciding what should go into a .intN file be printed,
thus (potentially) preventing the creation of that file.
Add commented-out documentation for a previously totally undocumented
option.
doc/user_guide.texi:
Document the new option.
NEWS:
Announce the new option.
Mention that we now generate warnings for unused import_module and
use_module declarations in the interface even if the module has
submodules.
compiler/write_module_interface_files.m:
Let the new option control whether we filter out any messages generated
when deciding what should go into a .intN file.
compiler/parse_item.m:
Delete actually_read_module_opt, since it is no longer needed;
its callers now call actually_read_module_{plain,trans}_opt instead.
Delete unneeded arguments from some predicates.
compiler/parse_module.m:
Delete some long unused predicates.
compiler/parse_pragma.m:
When parsing pragmas, wrap them up in the new decl, impl or generated
pragma kinds.
compiler/parse_tree_out.m:
Add predicates to write out each of the file-kind-specific parse trees.
compiler/parse_tree_out_pragma.m:
Add predicates to write out decl, impl and generated pragmas.
compiler/polymorphism.m:
Add a conditionally-enabled progress message, which can be useful
in tracking down problems.
compiler/prog_item_stats.m:
Conform NOT to the changes above beyond what is needed to let this module
compile. Let that work be done the next time the functionality of
this module is needed, by which time the affected data structures
maybe have changed further.
compiler/typecheck.m:
Fix a performance problem. With intermodule optimization, we read in
.opt files, some of which (e.g. list.opt and int.opt) contain promises.
These promises are read in as predicates with goal_type_promise,
but they do not have declarations of the types of their arguments
(since promises do not have declarations as such). Those argument types
therefore have to be inferred. That inference replaces the original
"I don't know" argument types with their actual types.
The performance problem is that when we change the recorded argument types
of a predicate, we require another loop over all the predicates in the
module, so that any calls to this predicate can be checked against
the updated types. This is as it should be for callable predicates,
but promises are not callable. So if all the *only* predicates whose
recorded argument types change during the first iteration to fixpoint
are promises, then a second iteration is not needed, yet we used to do it.
The fix is to replace the "Have the recorded types of this predicate
changed?" boolean flag with a bespoke enum that says "Did the checking
of this predicate discover a need for another iteration", and not
setting it when processing predicates whose type is goal_type_promise.
compiler/typecheck_errors.m:
Do not generate an error message for a predicate missing its clauses
is the clauses existed but were not added to the HLDS because they were
in the interface section.
When reporting on ambiguities (when a call can match more than one
predicate or function), sort the possible matches before reporting
them.
compiler/accumulator.m:
compiler/add_class.m:
compiler/add_clause.m:
compiler/add_foreign_proc.m:
compiler/add_mode.m:
compiler/add_pragma_tabling.m:
compiler/add_pragma_type_spec.m:
compiler/add_pred.m:
compiler/add_type.m:
compiler/canonicalize_interface.m:
compiler/check_for_missing_type_defns.m:
compiler/check_parse_tree_type_defns.m:
compiler/check_promise.m:
compiler/check_raw_comp_unit.m:
compiler/check_typeclass.m:
compiler/common.m:
compiler/compile_target_code.m:
compiler/compiler_util.m:
compiler/dead_proc_elim.m:
compiler/deps_map.m:
compiler/det_analysis.m:
compiler/det_report.m:
compiler/du_type_layout.m:
compiler/field_access.m:
compiler/find_module.m:
compiler/float_regs.m:
compiler/format_call.m:
compiler/goal_expr_to_goal.m:
compiler/handle_options.m:
compiler/hlds_out_module.m:
compiler/hlds_out_pred.m:
compiler/hlds_out_util.m:
compiler/inst_check.m:
compiler/intermod.m:
compiler/introduce_parallelism.m:
compiler/layout_out.m:
compiler/make.dependencies.m:
compiler/make.module_dep_file.m:
compiler/make_hlds_warn.m:
compiler/mark_tail_calls.m:
compiler/mercury_compile_llds_back_end.m:
compiler/ml_top_gen.m:
compiler/mmakefiles.m:
compiler/mode_errors.m:
compiler/mode_robdd.equiv_vars.m:
compiler/modes.m:
compiler/module_qual.qual_errors.m:
compiler/oisu_check.m:
compiler/old_type_constraints.m:
compiler/options_file.m:
compiler/parse_class.m:
compiler/parse_dcg_goal.m:
compiler/parse_goal.m:
compiler/parse_inst_mode_defn.m:
compiler/parse_inst_mode_name.m:
compiler/parse_mutable.m:
compiler/parse_sym_name.m:
compiler/parse_type_defn.m:
compiler/parse_type_name.m:
compiler/parse_type_repn.m:
compiler/parse_types.m:
compiler/parse_util.m:
compiler/parse_vars.m:
compiler/post_term_analysis.m:
compiler/post_typecheck.m:
compiler/prog_event.m:
compiler/prog_mode.m:
compiler/purity.m:
compiler/qual_info.m:
compiler/recompilation.version.m:
compiler/resolve_unify_functor.m:
compiler/simplify_goal.m:
compiler/simplify_goal_call.m:
compiler/simplify_goal_disj.m:
compiler/simplify_goal_ite.m:
compiler/simplify_proc.m:
compiler/state_var.m:
compiler/stratify.m:
compiler/style_checks.m:
compiler/superhomogeneous.m:
compiler/table_gen.m:
compiler/term_constr_errors.m:
compiler/term_errors.m:
compiler/termination.m:
compiler/trace_params.m:
compiler/unused_args.m:
compiler/unused_imports.m:
compiler/write_deps_file.m:
compiler/xml_documentation.m:
Conform to the changes above.
mdbcomp/prim_data.m:
Move a utility function on pred_or_funcs here from a compiler module,
to make it available to other compiler modules as well.
scripts/compare_s1s2_lib:
A new script that helped debug this diff, and may help debug
similar diffs the future. It can compare (a) .int* files, (b) .*opt
files, (c) .mh/.mih files or (d) .c files between the stage 1 and
stage 2 library directories. The reason for the restriction
to the library directory is that any problems affecting the
generation of any of these kinds of files are likely to manifest
themselves in the library directory, and if they do, the bootcheck
won't go on to compile any of the other stage 2 directories.
tests/debugger/breakpoints.a.m:
tests/debugger/breakpoints.b.m:
Move import_module declarations to the implementation section
when they are not used in the interface. Until now, the compiler
has ignored this, but this diff causes the compiler to generate
a warning for such misplaced import_module declarations even modules
that have submodules. The testing of such warnings is not the point
of the breakpoints test.
tests/invalid/Mercury.options:
Since the missing_interface_import test case tests error messages
generated during an invocation of mmc --make-interface, add the
new option that *allows* that invocation to generate error messages.
tests/invalid/ambiguous_overloading_error.err_exp:
tests/invalid/max_error_line_width.err_exp:
tests/warnings/ambiguous_overloading.exp:
Expect the updated error messages for ambiguity, in which
the possible matches are sorted.
tests/invalid/bad_finalise_decl.m:
tests/invalid/bad_initialise_decl.m:
Fix programming style.
tests/invalid/bad_item_in_interface.err_exp:
Expect an error message for a foreign_export_enum item in the interface,
where it should not be.
tests/invalid/errors.err_exp:
Expect the expanded wording of a warning message.
tests/invalid/foreign_enum_invalid.err_exp:
Expect a different wording for an error message. It is more "standard"
but slightly less informative.
tests/invalid_submodules/children2.m:
Move a badly placed import_module declaration, to avoid having
the message the compiler now generates for it from affecting the test.
tests/submodules/parent2.m:
Move a badly placed import_module declaration, to avoid having
the message the compiler now generates for it from affecting the test.
Update programming style.
library/one_or_more.m:
We used to have a type named one_or_more in the list module representing
nonempty lists. It had literally just two predicates and two functions
defined on it, three of which did conversions to and from lists, which
limited their usefulness.
This new module is the new home of the one_or_more type, together with
a vastly expanded set of utility predicates and functions. Specifically,
it implements every operation in list.m which makes sense for nonempty
lists.
library/list.m:
Delete the code moved over to one_or_more.m.
library/one_or_more_map.m:
This new module is a near copy of multi_map.m, with the difference being
that while the multi_map type defined in multi_map.m maps each key
to a list(V) of values (a list that happens to always be nonempty),
the one_or_more_map type defined in one_or_more_map.m maps each key
to a one_or_more(V) of values (which enforces the presence of at least
one value for each key in the type).
library/map.m:
Mention the existence of one_or_more_map.m as well as multi_map.m.
library/MODULES_DOC:
library/library.m:
List the new modules as belonging to the standard library.
NEWS:
Mention the new modules, and the non-backwards-compatible changes to
list.m.
compiler/*.m:
Import the one_or_more module when needed.
tests/hard_coded/test_one_or_more_chunk.{m,exp}:
Test the one predicate in one_or_more.m that is non-trivially different
from the corresponding predicate in list.m: the chunk predicate.
tests/hard_coded/Mmakefile:
Enable the new test case.
compiler/prog_item.m:
Fix a bug: provide a slot for foreign_procs in parse_tree_plain_opts.
Eliminate unnecessary differences between parse_tree_{plain,trans}_opt
and parse_tree_int[0123].
compiler/parse_module.m:
As a temporary measure for testing,
- convert every .opt and .trans_opt file read in from its generic
parse_tree_opt representation to its specific parse_tree_plain_opt
or parse_tree_trans_opt representation, in order to check for
items that should not occur in the purpose-specific representations,
- and then convert it back.
Provide (not yet used) predicates for reading in optimization files
into their specific parse tree formats. These do the first conversion
but not the second.
compiler/parse_tree_out.m:
Add predicates for writing out parse_tree_{plain,trans}_opt directly,
without conversion to a generic opt file.
compiler/convert_parse_tree.m:
Make the above possible by adding conversions to and from
parse_tree_{plain,trans}_opt.
Give exported predicates more expressive names.
compiler/comp_unit_interface.m:
compiler/intermod.m:
Conform to the changes above.
compiler/add_pred.m:
compiler/grab_modules.m:
Minor style improvements.
compiler/add_foreign_proc.m:
Fix the wording of an error message.
tests/invalid/fp_dup_bug.err_exp:
Expect the updated wording.
compiler/convert_interface.m:
As above. I am about to add code to convert optimization files as well.
compiler/parse_tree.m:
Include the module under its new name.
compiler/notes/compiler_design.html:
Document the module under its new name.
compiler/comp_unit_interface.m:
compiler/intermod.m:
compiler/parse_module.m:
Import the module under its new name.
compiler/prog_item.m:
Add types for representing .opt and .trans_opt files that specify
exactly what kinds of items may appear in them.
Provide a mechanism for representing just the kinds of pragmas
that we may want to put into .opt files to represent a predicate marker.
To make the above possible, generalize the item_pragma_info type.
Do not store the "maybe attributes" field in all pragmas; store it
in just the one pragma for which it had pragma-specific code (which code
is dubious anyway). Its only use is to suppress error messages about
incorrect pragmas if that pragma was created by the compiler, on the
theory that the user cannot do anything about any such error messages.
However, if such errors are never reported to anyone, then they won't
be fixed. I think it is better to allow such problems to be discovered,
even if they cause a bit of annoyance to the discoverer. The default
content of the field as set by the parser, item_origin_user, can be
misleading anway; it is correct when the pragma is read in from a .m file
or from a .int* file, but it is wrong when read in from a .*opt file,
since the contents of those are decided by the compiler.
Store a varset and tvarset in structure sharing and reuse pragmas,
since without this, one cannot print them out properly.
compiler/intermod.m:
Change the predicates that write out .opt and .trans_opt files
to return as large a fraction of the parse trees of those files
as possible, as a step towards generating those files not directly,
but by building and then writing out those parse trees. For now,
we cannot do this fully for .opt files, because for a few item kinds,
it is far from obvious how to represent as a item what we write out.
Leave the opening and closing of the file streams for writing out
.opt and .trans_opt files to our caller, because for .opt files,
this allows us to avoid having to open the file *twice*.
Put the output of result-of-analysis pragmas into a standard order.
Factor out as common code the process for deciding what should go into
.opt files.
Give a field of the intermod_info structure a more precise name.
compiler/mercury_compile_front_end.m:
Hold the stream of the .opt file open between the two different pieces
of code that write out the two different parts of .opt files.
If --experiment5 is set, write out the parse tree of the .opt file
to the .optx file, to enable comparison with the .opt file.
compiler/mercury_compile_middle_passes.m:
If --experiment5 is set, write out the parse tree of the .trans_opt file
to the .trans_optx file, to enable comparison with the .trans_opt file.
Reset a memo table for structure_{sharing,reuse}.analysis.
compiler/structure_reuse.analysis.m:
compiler/structure_sharing.analysis.m:
Don't take an I/O state pair as arguments, since we needed them *only*
for that reset, and for progress messages.
Give the main predicates more descriptive names.
compiler/trailing_analysis.m:
Give the main predicate a more descriptive names.
compiler/closure_analysis.m:
Don't take an I/O state pair as arguments, since we needed them *only*
for progress messages.
compiler/add_pragma.m:
Don't ignore an error, since one of the other changes in this diff
could have fixed its cause.
compiler/convert_interface.m:
Export utility functions needed by code added by this diff.
ompiler/lp_rational.m:
Tighten the inst of an output argument for use by intermod.m.
Bring programming style up to date.
compiler/parse_pragma.m:
Don't put a maybe attributes field into item_pragma_infos.
Include the varset in structure sharing and reuse pragmas.
Use simplest_spec where possible.
compiler/parse_tree_out.m:
Add predicates for writing out the new parse trees of .opt and
.trans_opt files.
compiler/parse_tree_out_pragma.m:
Add predicates needed by the new code in parse_tree_out.m.
compiler/add_mutable_aux_preds.m:
compiler/canonicalize_interface.m:
compiler/comp_unit_interface.m:
compiler/equiv_type.m:
compiler/get_dependencies.m:
compiler/grab_modules.m:
compiler/item_util.m:
compiler/make_hlds_error.m:
compiler/make_hlds_passes.m:
compiler/make_hlds_separate_items.m:
compiler/module_qual.qualify_items.m:
compiler/prog_item_stats.m:
compiler/recompilation.version.m:
Conform to the changes above.
compiler/check_parse_tree_type_defns.m:
Change the definition of a checked type to distinguish direct_dummy
and enum types on the one hand, which may have foreign enum definitions,
from other du types, which may not.
Change the definition of a checked type to record the names of the
function symbols in direct_dummy and enum types, since type representation
items need this information, we have it, and requiring decide_type_repn.m
to compute it again from scratch is wasteful.
Do not explicitly record the number of function symbols in direct_dummy
% and enum types, and the number bits needed to represent values of
such types. We now store the list of function symbols in such types,
and this information is trivially computable from that.
Check foreign enum definitions for correctness. At the moment,
this sort-of duplicates related functionality in add_foreign_enum.m,
but the intention is that eventually, these checks will be done only here,
with the sort-of duplicated code in add_foreign_enum.m being deleted there.
compiler/prog_item.m:
Change the data structures we use to record type representation information
in items to encode all the invariants that the updated checked type
definition map encodes. This should allow code that reads such items
from interface files to avoid having to check for violations of those
invariants.
Document the relationship between checked type definitions and
type representation items, including why neither can subsume the other.
compiler/decide_type_repn.m:
Rewrite this module almost completely. The old version gathered up
all the type and enum definitions for each type constructor, and then
did a limited, purpose-specific job of checking their consistency,
before using the information in them to decide on the representations
of simple types. The new version takes a checked type definition map
as input, and so its task is limited to deciding type representations.
The switch has two main benefits.
- The consistency checks we now do in check_parse_tree_type_defns.m
can replace not just the consistency checks we used to do in
decide_type_repn.m, but in the future, it can also replace
the consistency checks we now do in add_type.m and add_foreign_enum.m.
Having a *consistent* set of consistency checks is a good idea,
even though it is a bit too meta :-)
- The consistency checks in check_parse_tree_type_defns.m
are significantly more thorough than both the ones in the version
of decide_type_repn.m, and the current version distributed through
add_type.m and add_foreign_enum.m.
The changes in check_parse_tree_type_defns.m and prog_item.m to bring
the two representations as close together as possible should significantly
simplify the code we will need to add to decide_type_repn.m to decide
the representations of complex types.
compiler/comp_unit_interface.m:
Pass the checked type definition map to decide_type_repn.m.
compiler/parse_type_repn.m:
Parse the updated type representations.
compiler/parse_tree_out.m:
Output the updated type representations.
compiler/du_type_layout.m:
compiler/prog_data.m:
Move a predicate from du_type_layout.m to prog_data.m (split into
two predicates), since decide_type_repn.m now wants access to its
functionality as well.
compiler/prog_type.m:
Export a utility predicate.
compiler/add_foreign_enum.m:
Export some functionality to check_parse_tree_type_defns.m.
That functionality should in fact be moved to check_parse_tree_type_defns,
but that would make this diff harder to review, so that will be done later
in a separate commit.
compiler/equiv_type.m:
compiler/module_qual.qualify_items.m:
Conform to the changes above.
compiler/Mercury.options:
Specify --warn-dead-preds for the modules touched by this change.
Since several of them had large parts replaced by new code, this was
needed to catch left-over parts of their old and now obsolete contents.
This fixes Mantis bug #485.
compiler/check_parse_tree_type_defns.m:
Generalize the representation of du types in checked type definitions
to give special representation to dummy types, as well as enum types.
Allow foreign enum definitions for types whose du definitions are dummy,
as well as those whose du definitions are enum.
compiler/prog_type.m:
Have the predicate that tests whether a du type is an enum type
to return the number of function symbols as well as the number of bits
needed to represent them. This is the most direct differentiator between
dummy types and enum types.
compiler/comp_unit_interface.m:
Conform to the change in prog_type.m.
tests/valid/bug485.m:
A regression test for the bug.
tests/valid/Mmakefile:
Enable the new test case.
compiler/convert_interface.m:
compiler/prog_item.m:
As above.
compiler/parse_tree.m:
Include the new module.
compiler/comp_unit_interface.m:
compiler/parse_module.m:
Import the new module.
... when generating interface files.
compiler/check_parse_tree_type_defns.m:
A new module for doing the checking.
compiler/parse_tree.m:
Add the new module.
compiler/comp_unit_interface.m:
When computing what should go into a .intN file, use the new module
to check for inconsistencies in the source code of the module.
Making the above possible requires retaining some information
(such as contexts in foreign enum definitions, and the presence
of definitions, as opposed to declarations, for some types)
for longer than was needed until now.
Give some predicates more descriptive names.
Move the record_foreign_enum_spec predicate near where it is used.
compiler/write_module_interface_files.m:
If comp_unit_interface.m reports any errors when generating the
would-be contents of an interface file, do NOT create that
interface file.
The errors comp_unit_interface.m and check_parse_tree_type_defns.m
now generate are the kinds of inconsistencies whose resolution requires
everything depending on this module to be recompiled anyway,
so stopping the compilation process *without* producing
the would-be-erroneous interface file should be a net win
almost all the time.
compiler/prog_item.m:
Factor out some commonalities in data structures.
When converting the generic parse_tree_int we get from reading
in a .intN file to the parse tree type specific to that N,
check the definitions we read in for consistency.
As in comp_unit_interface, making the above possible requires
retaining some information for longer than was needed until now.
Never output two or more declarations of the same type_ctor
in the same section of an interface file, since the code
*reading* interface files now reports such redundancies
(but see the change to options.m).
compiler/options.m:
Disable the printing of error messages for problems found in
interface files, since the new code in check_parse_tree_type_defns.m
now finds problems in interface files generated by currently
installed compilers. Specifically, many contain duplicate
declarations for Mercury types. One declaration is written
by the programmer, the other is the type's actual definition
turned into the redundant declaration by the compiler.
compiler/parse_pragma.m:
When parsing foreign enum pragmas, implicitly qualify the
name of the type constructor they are for with the current module name.
This is semantically sound, since foreign_enum pragmas *must* be
for a type constructor defined in the same module. It is desirable
because type_ctors in type definitions are module qualified.
The code of check_parse_tree_type_defns.m needs to process
the type definitions and foreign enums for a type_ctor at the
same time, and doing so would be more needlessly complicated
if the type ctor keys in the "type_ctor to type definitions"
and "type_ctor to foreign enums" maps were incompatible due to
the difference in qualification.
The type_ctor in a foreign enum definition may still be manually
module qualified by programmers; it just happens that any qualification
other than the default is a semantic error.
compiler/module_qual.qual_errors.m:
When printing error messages about undefined type_ctors in
foreign enum definitions, do not print this implicitly
added qualification, since it adds only clutter.
compiler/error_util.m:
Provide a mechanism for printing type_ctors directly,
i.e. without converting them to a sym_name/arity pair.
Put a qual_ prefix in front of top_ctor_of_type, since it
always prints module qualification. (This is to remind people
who may use this component about that qualification.)
Factor out some common code.
compiler/det_analysis.m:
Conform to the change in error_util.m.
compiler/parse_module.m:
Fix too-long lines.
compiler/Mercury.options:
Require some of the modules above to have no dead predicates.
library/erlang_rtti_implementation.m:
library/io.m:
Delete declarations of types that are unneeded because the types
also have definitions.
tests/typeclasses/unqualified_method2.m:
Move an import out of the interface, since we now print the warning
generated for its improper location.
compiler/prog_data_foreign.m:
compiler/prog_item.m:
Replace it with the fim_spec type, which contains the exact same information.
compiler/comp_unit_interface.m:
compiler/compile_target_code.m:
compiler/make.module_dep_file.m:
compiler/mercury_compile_llds_back_end.m:
compiler/ml_top_gen.m:
compiler/mlds.m:
compiler/mlds_to_c_file.m:
compiler/module_imports.m:
compiler/parse_tree_out.m:
compiler/prog_foreign.m:
compiler/write_deps_file.m:
Conform to the change above.
Using lists to store type-, inst- and mode definitions, foreign enum
definitions and type representation items does nothing to enforce
uniqueness requirements, such as "every inst constructor should have
at most one one non-abstract definition in the interface of a module".
However, one can encode such requirements using maps.
This diff therefore changes the representation of these kinds of items
inside the interface-kind-specific parse trees of interface files to use
these kinds of maps. It does not take advantage of the possilities
offered by this diff; that is for a later change.
compiler/prog_item.m:
Make the change described above.
Since after this diff, the info in foreign_enum pragmas will be in a map,
while the info in other kinds of pragmas will still be in a list, avoid
the possibility of being able to represent invalid states by giving
foreign enums their own item kind, separate from that of pragmas.
Since foreign export enums are handled very similarly to foreign enum
pragmas in many places, given them their own item kind as well.
Update the predicates converting generic interface file parse trees
to interface-kind-specific parse trees, and vice versa, to conform
to the changes above.
compiler/comp_unit_interface.m:
Convert lists to maps before putting them into interface-kind-specific
parse trees where the above change requires it.
compiler/hlds_module.m:
Delete types that contain almost exactly the same info as the item
information we now keep for foreign enum and foreign export enum items.
Include the item status next to foreign enum items, since that is the
one piece of info the deleted types had that we need.
compiler/parse_pragma.m:
compiler/parse_tree_out.m:
compiler/parse_tree_out_pragma.m:
Parse and print out foreign enums and foreign export enums as their
own item kinds, not as kinds of pragmas.
compiler/add_foreign_enum.m:
compiler/canonicalize_interface.m:
compiler/equiv_type.m:
compiler/get_dependencies.m:
compiler/grab_modules.m:
compiler/intermod.m:
compiler/item_util.m:
compiler/make_hlds.m:
compiler/make_hlds_separate_items.m:
compiler/module_qual.collect_mq_info.m:
compiler/module_qual.qual_errors.m:
compiler/module_qual.qualify_items.m:
compiler/prog_item_stats.m:
compiler/recompilation.check.m:
compiler/recompilation.version.m:
Conform to the changes above.
We used to read all kinds of interface files (.int0, .int, .int2 and .int3)
into the same representation, parse_tree_int. This diff makes the first step
towards returning the contents of each kind of interface file in its own
bespoke parse tree type, parse_tree_intN for N in {0,1,2,3}.
compiler/parse_module.m:
Read in the contents of interface files using a cut-down grammar.
The main changes are:
- the file must start with a ":- module" declaration;
- the start of each section must be an explicit section marker;
- there can be at most two sections, the first an interface section
and the second an implementation section, in that order,
either (or both) of which can be missing;
- version number maps are expected to be *before* the first section;
- there cannot be any submodules;
- there cannot be any source_file pragmas anywhere.
Each of these changes enforces an invariant that is observed by the code
constructing and writing out the automatically generated interface files.
The change makes many invalid interface file contents unrepresentable.
Add predicates for reading each kind interface file to its own
bespoke parse tree type. These new predicates are not yet used.
Change the generic predicate for reading in interface files
to convert the generic parse_tree_int it generates to the given interface
file's own specific parse tree type, and back. This *should* be an
identity operation, but it generates an error message whenever it finds
an entity of a kind that shouldn't be there. Print these messages
unless inhibited by an option.
This back-and-forth is intended to be temporary. Once we have sufficient
experience with the system to be confident it works, i.e. it does not
miscompile anything and never generates any of these diagnostics,
we should be able to switch to using the kind-specific parse tree types
exclusively.
compiler/options.m:
Add that option, halt-at-invalid-interface, for now, enabled by default.
We should be able to delete the option, making its effects permanent,
when we make the switch mentioned above.
compiler/prog_item.m:
Modify the representation of import_module and use_module declarations
in .int0 files a bit to match their representations in other kinds of
interface files.
Add predicates to convert a parse_tree_int to each parse_tree_intN
for N in {0,1,2,3}, generating error messages if they contain
any kind of entity that they shouldn't contain.
Add functions for describing such entities in the error messages.
Add some other auxiliary functions, and field names, needed by
the rest of this change.
compiler/file_kind.m:
Replace ifk_int with ifk_int1, because we consistently use numeric
suffixes after interface file kinds everywhere else. (Partly this is
to disambiguate "int" as short for "interface file", not for
"interface section of a module".
compiler/parse_types.m:
Provide a mechanism to describe all entities read in during parsing,
not just those that end up in parse trees, for use in error messages
in parse_module.m.
compiler/comp_unit_interface.m:
compiler/deps_map.m:
compiler/grab_modules.m:
compiler/make_hlds_error.m:
compiler/module_qual.qual_errors.m:
Conform to the changes above.
The implementation of a mutable requires several predicates, some user visible,
some not. Exactly what set of predicates a mutable needs depends both on its
attributes and on the target platform. Previously, we computed that set of
predicates three times:
- when adding their declarations to the HLDS,
- when adding their definitions to the HLDS, and
- when adding (a subset of) their declarations to a .int0 file.
This diff tries to centralizes these decisions in one place. It does not
quite get there, because getting there would require compromises that are
undesirable for other reasons, but it gets close.
compiler/prog_data.m:
Document the uses of the various kinds of predicates we can generate
for a mutable.
compiler/prog_mutable.m:
Add a predicate that decides which public auxiliary predicates
a mutable needs, for use by both add_mutable_aux_preds.m and by
comp_unit_interface.m.
Add a predicate that constructs the pred_decl item of any given
mutable auxiliary predicate, again for use by add_mutable_aux_preds.m
and by comp_unit_interface.m.
Move predicates that are used only by add_mutable_aux_preds.m
to add_mutable_aux_preds.m.
Delete predicates that are no longer needed.
compiler/add_mutable_aux_preds.m:
Add a predicate that decides which private auxiliary predicates
a mutable needs,
Use the new mechanisms above to declare a mutable's auxiliary procedures.
Use the new mechanisms above to define a mutable's auxiliary procedures,
mostly. There is still some code that repeats the logic of the decision
predicate for public auxiliary predicates, but now a sanity check
ensures that the two decision procedures arrived at the same result.
Move predicates that are used only by add_mutable_aux_preds.m
here from prog_mutable.m.
Delete predicates that are no longer needed.
Give several predicates more descriptive names. Standardize
argument orders.
compiler/add_pred.m:
Add a mechanism for adding a pred_decl item as a whole to the HLDS.
(Previously, we could a pred_decl to the HLDS by first breaking it down
to its components.) The code is mostly copied from make_hlds_passes.m.
compiler/comp_unit_interface.m:
Use the new mechanisms above to declare a mutable's auxiliary procedures.
The new code does not need any knowledge of what auxiliary predicates
a mutable needs.
compiler/make_hlds_passes.m:
Use the new mechanism in add_pred.m, in order to avoid code duplication.
library/set.m:
Add a new predicate for add_mutable_aux_preds.m.
compiler/comp_unit_interface.m:
If the experiment1 option is set, we used to put type_repn items
for simple types into .int3 files.
With this diff, under the same conditions, we also put type_repn items
for simple types into .int2 files, which are mostly module qualified
versions of .int3 files.
compiler/equiv_type.m:
Do not abort when asked to expand equivalence types in type_repn items,
since such items from .int2 files *do* end up being passed here.
compiler/prog_item.m:
Define a type that represents the possible contents of .int0 files.
Provide a way to convert from this new representation to the generic
interface file representation.
compiler/comp_unit_interface.m:
Generate the contents of .int0 files using the new bespoke representation.
When deciding the contents of interface files, we used to generate
messages for these three kinds of errors:
- clauses in the module interface
- not-allowed-in-the-interface pragmas in the module interface
- empty module interface
None of these affected the interface file we were building, and their
reporting can easily be delayed until we are compiling the affected module
to target language code. At that time, the error messages get put into
.err files, whereas any messages printed when creating interface files
tends to get lost in the overall sea of compiler invocation messages
when running mmake.
compiler/comp_unit_interface.m:
Do not generate error messages when building the contents of interface
files.
compiler/write_module_interface_files.m:
Do not print the error messages that are no longer being generated.
We *do* still print any of the other kinds of error messages we have
been printing, such as for those when a needed .int3 file cannot be read,
or when ambiguity prevents full module qualification. (The contents of
.int and .int2 files must be fully module qualified to do their jobs.)
compiler/add_pragma.m:
Report an error when processing a pass 3 pragma that occurs in the
interface but is not allowed in the interface.
add_pragma.m has already been doing this for all other kinds of pragmas,
and in general, compiler invocations that generate target language code
do generate the messages for all the conditions whose messages we are
no longer printing when generating interface files.
tests/invalid/bad_item_in_interface.{m,err_exp}:
tests/invalid/ft_examples:
A new test case to test the above assertion with respect to
inappropriate items in the interface of a module.
tests/invalid/empty_interface.{m,err_exp}:
A new test case to test the above assertion with respect to
the interface of a module being empty.
tests/invalid/Mercury.options:
Specify the right options for the empty_interface test case.
tests/invalid/Mmakefile:
Enable the new test cases.
compiler/comp_unit_interface.m:
The original form of the affected code was there to allow a potential
future change. That change turns out not to work.
For now, we construct the contents of those files using the new bespoke types,
but convert them to the general parse_tree_int type when that is done.
Later changes should extend the use of the bespoke types to the later
processing of interface file contents (computation of version number maps,
standardization, and writing out) as well as their processing when
being read in.
compiler/prog_item.m:
Create the bespoke types parse_tree_int1 and parse_tree_int2
for the parse trees of .int and .int2 files respectively.
These types make explicit significantly more of the structural invariants
required of the contents of these interface files than the old general
parse_tre_int type.
Add conversion functions from the new bespoke types to that general type.
Create a bespoke type for representing version number fields,
mainly to provide a central place for documenting how such fields
are filled in.
Make predicate definition order match predicate declaration order.
compiler/comp_unit_interface.m:
When generating .int and .int2 files, create their contents as values
of the new bespoke types, converting them to the general parse_tree_int
type when that is done.
compiler/module_imports.m:
compiler/parse_module.m:
compiler/parse_tree_out.m:
compiler/recompilation.check.m:
compiler/recompilation.version.m:
compiler/write_module_interface_files.m:
Conform to the change in version number representation.
compiler/prog_item.m:
Even though we express foreign import module declarations syntactically
as pragmas, semantically, they are much closer to import_module
declarations. This means that the treatment they require in most places
in the compiler is similar to the treatment of import_module declarations,
and quite different from the treatment of other kinds of items.
Therefore this diff takes foreign_import_module declarations (FIMs
for short) out of item type. From now on, in parse trees and their
components, FIMs are stored in data structures of their own, next to
import_module declarations.
compiler/parse_types.m:
Provide a mechanism for the parser to return FIMs as an entity kind
of its own, not as an item.
compiler/comp_unit_interface.m:
Conform to the changes above, and give a predicate a more specific name.
compiler/module_qual.m:
Conform to the changes above, and require .int3 files to contain no FIMs.
compiler/canonicalize_interface.m:
compiler/check_raw_comp_unit.m:
compiler/equiv_type.m:
compiler/get_dependencies.m:
compiler/grab_modules.m:
compiler/hlds_module.m:
compiler/item_util.m:
compiler/make_hlds_passes.m:
compiler/make_hlds_separate_items.m:
compiler/module_imports.m:
compiler/module_qual.collect_mq_info.m:
compiler/module_qual.qualify_items.m:
compiler/parse_module.m:
compiler/parse_pragma.m:
compiler/parse_tree_out.m:
compiler/prog_item_stats.m:
compiler/read_modules.m:
compiler/recompilation.check.m:
compiler/recompilation.version.m:
compiler/split_parse_tree_src.m:
Conform to the changes above.
Like the first, it aims to eliminate all the items from .int2 files
that do not need to be there.
compiler/comp_unit_interface.m:
Modify the algorithm we use to generate .int2 files if the option
experiment4 is set to "yes" (which it is by default). The modification
is to include a use_module declaration for a module in the interface
of the .int2 file only if an item we are including in that interface
refers to that module.
This change, and later changes, aim to eliminate all the items
from .int2 files that do not need to be there.
compiler/comp_unit_interface.m:
If the option experiment4 is set to "yes", switch to a modified algorithm
for computing the contents of .int2 files. As a first step, this eliminates
from implementation sections all items other than type definitions,
and all import_module and use_module declarations. The interface section
is left as is, for now.
compiler/options.m:
Set the default value of experiment4 to "yes".
compiler/prog_item.m:
Define parse_tree_int3, a type that represents the parse trees
of .int3 files. It enforces several structural invariants that
the old parse_tree_int type (which was designed to represent the
contents of *all* interface files) does not: the absence of anything
in the implementation section, the absence of use_module declarations,
the absence of repeated include_module or import_module declarations,
and the absence of any items other than the (a) the definitions of
types, insts, modes, typeclasses and instances, and later
(b) type_repn items.
Define a function to convert from parse_tree_int3s to parse_tree_ints.
Define auxiliaty predicates and functions now needed by
comp_unit_interface.m, and similar predicates and functions
for related types, that may be needed later.
compiler/comp_unit_interface.m:
Compute the contents of .int3 files into parse_tree_int3s.
Translate these to parse_tree_ints for the rest of the compiler,
which does use the new type yet.
compiler/decide_type_repn.m:
Return type representation information for .int3 files
in the format required by the updated code in comp_unit_interface.m.
compiler/write_module_interface_files.m:
Ignore the new parse_tree_int3 returned by comp_unit_interface.m
until the rest of this module has been taught to use it.
compiler/prog_item.m:
Rename the tc_constraints field of typeclass items to tc_superclasses.
compiler/comp_unit_interface.m:
compiler/parse_class.m:
Conform to this change.
compiler/comp_unit_interface.m:
If the option experiment3 is set, then replace all import_module
declarations in .int files with use_module declarations.
compiler/options.m:
Set experiment3 to true by default.
compiler/notes/interface_files.html:
Document that we do this, and why.
compiler/write_module_interface_files.m:
If the right experiment option is set, when we generate m.int2,
generate m.int2.via3 as well, using the same algorithm we use
to generate .int3 files. This new file is not used anywhere.
It is purely an experiment, one that allows us to compare the
advertised nature of .int2 files (as fully module qualified versions
of .int3 files) with the reality.
Modify a service predicate to make the above possible, and to delete
a long-unused argument.
compiler/options.m:
Add a bunch of boolean experiment options, to control this and other
(upcoming) experiments. Update the documentation of the experiment
options.
compiler/comp_unit_interface.m:
Add a predicate for generating .int2 files using the algorithm we use
for .int3 files.
Switch to using one the new, bool experiment options for an existing
experiment.
compiler/comp_unit_interface.m:
Instead of computing (a) a set of modules we should import in the interface
and (b) *whether* we need to import *any* modules in the interface,
switch to using just (a). Part (b) was flawed anyway. Besides saying
that items that added modules to (a) needed imported modules, it also
said that e.g. definitions of dummy and enum types needed imported modules,
which they do not.
Switch from handling imports and uses as lists of items
to handling them as sets of the names of imported and used modules.
Given the four sets
imported in interface
used in interface
imported in implementation
used in implementation
we use this to ensure that
(a) no module can appear more than once in a set, and
(b) no module can appear in more than one set.
Add XXXs documenting possible further improvements.
Separate the two jobs of make_imp_type_abstract.
compiler/notes/interface_files.html:
Describe what import_module and use_module declarations
we put into .int files.
compiler/comp_unit_interface.m:
Decide what foreign_import_modules (FIMs) items we need to put
into .int files when we decide to put into the .int file
the item (e.g. the definition of a foreign type) that *needs* the FIM.
This means gathering information about which FIMs are needed
during the pass that processes all the items in the .m file,
instead of separate later passes over all items.
Switch from handling these FIMs as lists to handling them as sets,
with respect to both FIMs that are explicit in the .m file, and those
that are implicitly needed. This eliminates the possibility of
including the same FIM in the .int file twice.
Put both explicit and implicit FIMs from each section of the .m file
into the corresponding section of the .int file.
Do not put into the implementation section of the .int file
a FIM item that we put into its interface section.
Clarify in variable names which section we are processing.
Disambiguate the func vs pred versions of some calls to init.
compiler/comp_unit_interface.m:
We used to compute the set of foreign_import_modules implicitly needed
by a module, and add them to the module's items, both *before* we grabbed
the imported modules and used them to module qualify the current module,
and *after*.
There was never any need to do any of this work pre-grab, because
neither the grabbing process nor module qualification pays any attention
to foreign_import_modules: they neither modify them nor do they base
any decisions on their presence or absence.
This diff just moves the pre-grab code to run post-grab. Unifying
this moved code with the original post-grab code for adding implicit
foreign_import_modules is the next step.
compiler/comp_unit_interface.m:
Include a foreign_enum pragma in the .int file if *any* of its
exported definitions is non-abstract. Previously, we required it
to have *exactly one* exported definition, which does not allow
for a redundant declaration as well as a du definition.
Abort if we find a solver type definition in the interface
*after* we are supposed to have converted all such definitions
to abstract types.
Inline a function in its only caller.
Give some variables and predicates more descriptive names.
Clarify some comments. Add some XXXs where relevant.