Compiling Java 9 (take 2)

Alex Buckley alex.buckley at oracle.com
Sat Jan 7 00:37:12 UTC 2017


On 1/4/2017 12:44 PM, Stephan Herrmann wrote:
> I have started to dig through the JLS changes (2016-12-16) to figure out
> requirements for a Java 9 compiler without consulting javac, but I'm
> having some difficulties understanding the interplay of scope,
> visibility, importing, accessibility and observability at this level.
>
> For instance, this already starts when re-reading unchanged parts from
> 6.3 and 6.4.1:
> 6.3:
>      The scope of a declaration is the region of the program within
> which the entity declared by the declaration can be referred to using a
> simple name, provided it is visible (§6.4.1).
> 6.4.1:
>      A declaration d is said to be visible at point p in a program if
> the scope of d includes p, and d is not shadowed by any other
> declaration at p.
> => For a declaration to be in scope it must be visible, and for being
> visible it must be in scope. This is beyond me.

Yes, the historic wording around scope et al is often troublesome. (As 
an example, see JDK-4420532.) The precise "layering" of one concept on 
top of another is hard to see.

> Is it correct (and intended) that "visibility of a declaration" (6.3) is
> a different concept than "visibility of a compilation unit" (7.3)? (Are
> we so entangled with overloading that we need to overload the terms in
> our spec, too? :) ).

Yes, and I owe you some apologies for this. The "visibility of a 
declaration" is an obscure corner of the scope rules, and I intend to 
rename it. Visibility will unambiguously be "of a compilation unit", 
since that's key to how the Java Language supports the JPMS.

> I'm also surprised that some requirements are indeed specified with
> regard to compilation units. Modules export packages, and types are
> declared in packages. Isn't it possible to define requirements due to
> JPMS solely in those terms (modules, packages, types), and avoid to
> mention compilation units in these rules? What do compilation units add
> conceptually to JPMS (besides adding complexity)?

Per 7.3: "The ordinary compilation units that are visible to M drive the 
packages that are visible to M (§7.4.3), which in turn drives the top 
level packages in scope for code in compilation units associated with M 
(§6.3)."

That is, the effect of 'requires' statements is to move from a flat 
space of observable compilation units to a module-keyed space of visible 
compilation units. If a compiler is compiling module M that requires N, 
and module O that requires P, then the compiler has decided that 
compilation units in M and N and O and P are observable, but when 
processing a compilation unit in (say) M, only the compilation units in 
N (and not O or P) are visible.

> As I already mentioned overloading, here's my example of the day:
>
> //--- Base/module-info.java
> module Base {
>      exports base;
> }
> //--- Base/other/Other.java
> package other;
>
> /** I'm not exported */
> public class Other {
>      public void test() {
>          System.out.println("Other.test");
>      }
> }
> //--- Base/base/Base.java
> package base;
>
> import other.Other;
>
> /** I'm exported */
> public class Base {
>      public void test(Object other) {
>          System.out.println("Object");
>      }
>      public void test(Other other) {
>          System.out.println("Other");
>          other.test();
>      }
> }
> //---
>
> //--- Test/module-info.java
> module Test {
>      requires Base;
> }
> //--- Test/other/Other.java
> package other;
>
> public class Other {
> }
> //--- Test/test/Test.java
> package test;
>
> import base.Base;
> import other.Other;
>
> public class Test {
>      void test(Base b, Other other, Object o) {
>          b.test(o); // OK
>          b.test(other); // ??
>      }
>      public static void main(String[] args) {
>          new Test().test(new Base(), new Other(), new Object());
>      }
> }
> //---
>
>
>
> Firstly, I'm surprised that javac (ea-149) compiles this fine but trying
> to run the program under JPMS crashes with
> Error occurred during initialization of VM
> java.lang.reflect.LayerInstantiationException: Package other in both
> module Base and module Test
>          at java.lang.reflect.Layer.fail(java.base at 9-ea/Layer.java:716)
>          at
> java.lang.reflect.Layer.checkBootModulesForDuplicatePkgs(java.base at 9-ea/Layer.java:674)
>
>          at
> java.lang.reflect.Layer.defineModules(java.base at 9-ea/Layer.java:610)
>          at
> java.lang.reflect.Layer.defineModules(java.base at 9-ea/Layer.java:383)
>          at
> jdk.internal.module.ModuleBootstrap.boot(java.base at 9-ea/ModuleBootstrap.java:307)
>
>          at java.lang.System.initPhase2(java.base at 9-ea/System.java:1927)
>
> Is that because the compiler knows nothing about layers?
> Is there any tool that statically checks whether a given modular program
> can possibly run in the default Layer implementation?

How did you invoke javac? Which mode of javac (see "Compile time" in JEP 
261) are you in?

> Secondly, I'm confused how overload resolution for b.test(..) should
> happen.
> We have two candidates, where one parameter type is accessible (Object)
> and the other is not (other.Other).
> In all of JLS chapter 15 (2016-12-16) I cannot find anything that would
> allow the compiler to consider accessibility of parameter types during
> overload resolution.

Right, nothing has changed in this respect.

> Hence, I see two possible answers by a compiler:
> (1) resolve b.test(other) to test(Other) because both types other.Other
> appear to be the same type, and hence test(Other) is applicable and more
> specific than test(Object).
> (2) reject the program because of a name clash on other.Other.
>
> Javac (ea-149) does neither, but resolves b.test(other) to test(Object).
> Apparently, javac "knows" that Base/other.Other and Test/other.Other are
> distinct types.

It sounds like you're compiling in multi-module mode, where javac does 
indeed know this.

> Given that types are identified by qualified names that
> do not contain the module name, this distinction doesn't seem to be
> possible per JLS.

Per 7.3, javac is associating a first other.Other type with module Base, 
and a second other.Other type with module Test. The first and second 
types have no relationship to each other, so there are no conversions 
between them, so Base's test(other.Other) method is not applicable to 
the invocation b.test(other).

It's true that 7.3 also says that all the compilation units of Base are 
visible to Other, since Other reads Base. javac is effectively hiding 
Base's other.Other type from code in Other, since Other already has an 
other.Other type. It's possible we need to specify this explicitly.

Alex


More information about the jigsaw-dev mailing list