[m-rev.] a question about accessibility rules and error messages
zoltan.somogyi at runbox.com
Wed Mar 20 16:53:22 AEDT 2019
I would like to change the rules for accessibility of ancestor modules.
If a module imports module x.y.z, then in principle we require that
it must also import x.y and x. We use those imports to check that
x does indeed include a submodule named x.y, and that x.y has
a submodule named x.y.z. (In the rest of this email, "import" includes
use_modules as well.)
The problem is our current method of enforcement. When we are
trying to enforce this rule, we use as input a database of import_module
and use_module declarations from the entire augmented compilation unit,
which includes, besides the source code of the module being compiled,
everything we have read from .int, .int2, .int0 and .int3 files, as well as
from .opt and .trans_opt files. This means that if w.m imports x.y.z
but does not import x.y, whether we get an error message depends on
whether any of these *other* files happens to import x.y. If one does,
the error goes undiagnosed, until such time, possibly much later, when
that other module deletes its import of x.y. In this way, w.m can go
from apparently not containing accessibility errors to visibly containing
them *without being modified*.
I can fix this one of two ways. One way is to fix the enforcement;
base the check on the contents of the source file only. In fact, the
check should to be done twice, once for the interface and once for
the implementation, because if w.m imports x.y.z in the interface,
it should import x and x.y in the interface as well. (The reverse is
not true; if w.m imports x.y.z in the implementation section, it does not
matter in which section it imports x.y.)
The other way is to drop the requirement for the *explicit* inclusion
of ancestor modules, and make the compiler import them implicitly.
The second approach has the benefit of requiring less typing.
The first approach has the benefit that it is more canonical:
given a set of module imports, there is only one way to make that set
ancestor-complete (i.e. to ensure that for every imported module,
all its ancestors are imported as well), and the first approach requires it,
while the second approach permits non-ancestor-complete sets of imports,
of which there are many.
I have a slight preference for the first fix, but could easily live with the second.
If we choose the first fix, then I would propose a change in the diagnostic
we generate for violations. At the moment, we generate a message of
this form for every import of a module that is inaccessible:
very_long_name.m:123: In module `very_long_name':
very_long_name.m:123: error in `import_module' declaration:
very_long_name.m:123: module `parent_module.sub_module' is inaccessible.
This means that if e.g. you import ten submodules of the parse_tree package,
you will get ten such messages, even though there is only one bug: the
absence of an import of parse_tree itself. I propose to change things
so that instead of generating one message per *inaccessible* module import,
we generate one message per *missing* module import. The message would
of course list the currently-inaccessible modules that the absence of
this import *makes* inaccessible.
More information about the reviews