modula3 / cm3

Critical Mass Modula-3
http://modula3.github.io/cm3/
Other
139 stars 25 forks source link

Compiler not finding revelations from imported packages #744

Open ajcz opened 3 years ago

ajcz commented 3 years ago

One way to avoid recursive imports among interdependent ADT interfaces is to declare their principal types as opaque types in a common interface.

Trouble is, if you import this this common interface from another package, you might get an “opaque type never revealed” error at link-time.

Here’s a small test. If you build “testlib” and then “testpgm”, you’ll see this error; but if you build “testlib” as a program (cm3 -Dstdalone), it links just fine.

RodneyBates commented 3 years ago

Nowhere is Adtl imported, which doesn't get its module (where the revelation is) into the import closure.  If I remember right, PM3 would bring it in solely because it's named in the m3makefile, but CM3 will not.  I may have a note to this effect somewhere, and will look.

The question in my mind is why it does link when testlib is a program.

Add a IMPORT Adtl somewhere, and I think all will work as expected.

On 8/15/21 8:25 AM, jcchu wrote:

One way to avoid recursive imports among interdependent ADT interfaces is to declare their principal types as opaque types in a common interface.

Trouble is, if you import this this common interface from another package, you might get an “opaque type never revealed” error at link-time.

Here’s a small test https://github.com/modula3/cm3/files/6988182/test.zip. If you build “testlib” and then “testpgm”, you’ll see this error; but if you build “testlib” as a program (|cm3 -Dstdalone|), it links just fine.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/modula3/cm3/issues/744, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABSVZNHQ2VRYLH253NPY4PTT46545ANCNFSM5CGFYEHA.

ajcz commented 3 years ago

Yes, I know I can import Adt1 manually (either in the importer or in a module implementing the Common interface) to get around this error, but I think that’s where the problem is.

The importer may not care about the representation of the imported opaque types; it certainly shouldn’t have to know where they are revealed (and have code that depends on this information), and it would be a violation of modularity principle otherwise.

In any case, it’s quite useful (if uncommon) to reveal an opaque type in a module that doesn’t export its home interface. This doesn’t seem to violate the language definition’s requirements about revelations:

  1. In any scope, all partially revealed supertypes of the same opaque type are linearly ordered by <:. (The compiler demands more than this, though. For example, you can’t say T <: REF INTEGER and then reveal T <: REFANY, even though REF INTEGER <: REFANY and REFANY </: REF INTEGER.)
  2. The concrete type of an opaque type is a subtype of its supertypes, revealed anywhere.
  3. Concrete types are branded types, which are distinct from each other.

A possibly related bug (which I believe is a bug anyway) here is that requirement 3 (no multiple revelations) isn’t enforced when you’re linking a library. Actually you can reveal Common.Adt1 in “Main.m3” and you’ll no longer get the “opaque type never revealed” message.

RodneyBates commented 3 years ago

Adding a use of Common.Adt1 to Main in testpgm shows behavior I expect.  Without the IMPORT Adt1, compiling testpgm reports a never revealed type.  With the IMPORT, All compiles and works as expected.

But compiling testlib as a program has a problem.  With a use of Common.Adt1 and without an IMPORT of Adt1 (Note different meanings of Adt1) does not give a compile time error, but on startup, gives a runtime error: "A compile-time type is missing."  If it's missing, for good reason or bad, that should at least be reported at link time.

There is a 2x2x2 cartesian product of cases here: [not]imported x [not]used x [not]in a library, and I don't think I have tried them all.  It's getting confusing to keep track.

On 8/15/21 8:25 AM, jcchu wrote:

One way to avoid recursive imports among interdependent ADT interfaces is to declare their principal types as opaque types in a common interface.

Trouble is, if you import this this common interface from another package, you might get an “opaque type never revealed” error at link-time.

Here’s a small test https://github.com/modula3/cm3/files/6988182/test.zip. If you build “testlib” and then “testpgm”, you’ll see this error; but if you build “testlib” as a program (|cm3 -Dstdalone|), it links just fine.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/modula3/cm3/issues/744, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABSVZNHQ2VRYLH253NPY4PTT46545ANCNFSM5CGFYEHA.

RodneyBates commented 3 years ago

Hmm, there are some interesting questions here, starting with how should the compiler know what files should be included in the import/export closure.  The programmer is going to have to specify this somehow, directly or indirectly, either through the M3 code itself or through separate compiler input such as the m3makefile.

The language says nothing, as far as I can recall about compiler input other than code itself.

In any case, in the example, the programmer can't avoid specifying somehow that Adt1.i3 and Adt1.m3 are included, one way or the other. Even IMPORT Adt1 gives it no clue that Adt1.m3 exists, since a revelation could be in any file with any name.  So an m3makefile is needed regardless, to bring in Adt1.m3.

This case is unusual in that the revelation refers not to the original opaque type declaration (Common.Adt1), but to a rename of it (Adt1.T). If the rename is not in the set of files the compiler knows about, then we certainly should not expect it to go looking for an additional unknown (to it) file for the revelation.  But the fact that it must, and does, routinely look at modules named in the m3makefile, surely should mean, for consistency, that it would do the same for interfaces named in the m3makefile, even if not imported.

In the absence of a m3makefile, it does just look at all the .i3 and .m3 files in the src directory, but it would be crazy for it to look more widely.

On 8/15/21 9:25 PM, jcchu wrote:

Yes, I know I can import Adt1 manually (either in the importer or in a module implementing the Common interface) to get around this error, but I think that’s where the problem is.

The importer may not care about the representation of the imported opaque types; it certainly shouldn’t have to know where they are revealed (and have code that depends on this information), and it would be a violation of modularity principle otherwise.

In any case, it’s quite useful (if uncommon) to reveal an opaque type in a module that doesn’t export its home interface. This doesn’t seem to violate the language definition’s requirements about revelations:

  1. In any scope, all partially revealed supertypes of the same opaque type are linearly ordered by <:. (The compiler demands more than this, though. For example, you can’t say T <: REF INTEGER and then reveal T <: REFANY, even though REF INTEGER <: REFANY and REFANY </: REF INTEGER.)
  2. The concrete type of an opaque type is a subtype of its supertypes, revealed anywhere.
  3. Concrete types are branded types, which are distinct from each other.

A possibly related bug (which I believe is a bug anyway) here is that requirement 3 (no multiple revelations) isn’t enforced when you’re linking a library https://github.com/modula3/cm3/files/6989392/test2.zip. Actually you can reveal Common.Adt1 in “Main.m3” https://github.com/modula3/cm3/files/6989384/test3.zip and you’ll no longer get the “opaque type never revealed” message.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/modula3/cm3/issues/744#issuecomment-899170079, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABSVZNFJ3IQOA4KNGMVYJFLT5BZH5ANCNFSM5CGFYEHA.