[m-rev.] for review/discussion: change submodule visibility rules

Zoltan Somogyi zoltan.somogyi at runbox.com
Thu Jul 16 03:49:02 AEST 2026


On Wed, 15 Jul 2026 15:31:03 +1000, Peter Wang <novalazy at gmail.com> wrote:

> Change submodule visibility rules.
> 
> The test case in Mantis bug #584 contains a module with this structure:
> 
>     :- module mod_a.
>     :- interface.
>     :- include_module sub1.
> 
>     :- implementation.
>     :- include_module mod_a.sub2.
>     :- import_module mod_a.sub2.
> 
>     :- end_module mod_a.
> 
> where the interface of sub1 refers to a type defined in sub2,
> but sub1 does not import sub2 itself (which is valid under the existing
> visibility rules). Without an explicit import of sub2, we generated a
> mod_a.sub1.int file that also did not contain a `:- use_module' declaration
> for sub2. If an unrelated module then imported mod_a.sub1, it could encounter
> the type defined in sub2 which it knows nothing about, leading to a compiler
> abort.
> 
> Zoltan's suggestion in the Mantis bug discussion was to make it an error
> for sub1 to reference the type in sub2 when generating its .int file.

The rule I had in my then was:

Given a module mod_a with a submodules, mod_a.sub1 in mod_a.m's interface
section, the interface of mod_a.sub1 may NOT refer to any entities (types, insts etc)
defined in any NON-PUBLIC submodule of mod_a.

I started to implement this at the start of april, but then I realized that
to make this work in general, you would have to generalize it to situations
where the most recent common ancestor (MRCA) of the two modules in question
is NOT the parent of both modules. Basically, if you had

- a.b as the MRCA,
- a.b.c.d as the module whose interface's contents we are talking about, while
- a.b.e.f.g is the module that defines a type used in a.b.c.d's interface,

then IF a.b.c.d is visible outside a.b, then this is allowed ONLY if a.b.e.f.g
is ALSO visible outside a.b.

Implementing that test requires nontrivial new machinery, because until now,
the compiler did not CARE about WHICH SECTION of an ancestor an import
occurred in.

> To that end, I propose:
> 
> Visibility rule change #1:
> 
>   - An `:- import_module` or `:- use_module` declaration for module `M`
>     in the implementation section of a module will make the declarations
>     in the interface section of `M` visible in the implementation sections
>     of its submodules only. Previously, the declarations would also be also
>     visible in the submodules' interface sections.
> 
> However, after implementing that, it revealed a different problem.
> If a submodule inherits visibility of M's exports in its implementation
> section, it should still be possible to import M in its interface section.
> But when compiling the submodule, the compiler would read the interface file
> for M, due to M being imported in an ancestor's implementation section.
> The compiler would not read M's interface file again even though M is also
> imported in the submodule's interface file. Under the new rules, the
> submodule would only have visibility to M's exports in the implementation
> section.
> 
> Visibility rule change #2:
> 
>   - If a submodule contains `:- import_module` or `:- use_module` declarations
>     that import module `M`, visibility of M's exports within the submodule will
>     be determined only by those declarations, not by import or use declarations
>     in any ancestor module.

I am not sure what *exactly* you are proposing here. Would this rephrasing
express what you are proposing?

"If a module a.b.c has in :- import_module or :- use_module declaration for
any module D, then the visibility of D's exports inside a.b.c will be governed
ONLY by the position of that declaration, WHETHER OR NOT D has any of a.b.c's
ancestors (meaning a and a.b in this case) as its parent module."

(In this case, a.b.c is an example module name, and D is a metasyntactic
variable.)

If that is indeed what you are proposing, then I would say that
that is a simple and amply justifiable rule.

However, I still do not see a correctness argument of exactly HOW these
two rules together guarantee the absence of this bug, especially in
the generalized case I mentioned at the top. (I am not saying there is
no possible correctness argument; I just don't see it.)

> That only requires fairly simple changes to the compiler, and also seems to
> me a decent language design. As Zoltan mentioned in bug #584, we may want to
> require submodules to do all their own imports in the future anyway;
> this rule change would be consistent with that direction.

Yes, it would. However, any such change would have to be done in at least
two steps. The first would generate warnings for the use of implicit imports
via ancestors, and only the second would prohibit them. This would give time
for people to adjust to the new rule at a time of their choosing.

If you confirm my interpretation of your proposed second rule, then I
intend to review your diff, but

- can you please attach it as a file, since my mail user agent is not well suited
  to editing long emails, and

- my laptop's keyboard is not working (I am typing this on an external keyboard),
  and if the diagnostics end up saying it is hardware problem, I may end up
  without my work machine while it is repaired.

Zoltan.


More information about the reviews mailing list