[sealed] Changes to type system

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Tue Feb 25 11:08:27 UTC 2020


Hi Gavin,
the proposal makes sense to me - it seems like enhancing "side-cast" 
conversion rules to take sealed-ness into account is a natural extension 
of what we have; I'm not too worried about compiler performances; cast 
conversion is already a pretty complex routine as far as type 
relationships go, I don't see much here which can make it much worse, 
esp. if we consider the nightmare-ish stuff that javac currently does to 
rule out casts between provably disjoint generic types - where the 
notion of disjoint-ness is different from the simple one used by the 
spec and leads to much deeper checks.

What I wanted to bring to your consideration is something that I've been 
pondering over the last few days - let's imagine something like this:

sealed interface A permits Impl
sealed interface B permits Impl

Let's imagine that:

* Both A and B are defined in exported packages of a module M
* Impl is defined in a non-exported package of M
* In other words, two separate interfaces share the same (hidden) 
implementation class

I guess that, if we enhance cast conversion as you describe, casting A 
into B (and vice-versa) would be allowed by javac (there is in fact one 
type which implements both A and B), right?

But if that's the case, I have to admit that I find it a bit awkward 
that I can use javac to probe sealed interfaces to see which might share 
a common implementation class, even if that implementation class is out 
of my reach and hidden behind module boundaries. In other words, while 
with the rules we have now, the user can always "see" why a cast has 
succeeded or fail, with these new rules, sometimes a cast can 
(statically) be rejected or not depending on details which might be 
unavailable to the site where the cast operation occurs. I wonder - 
should javac "stop" looking, and avoid descending into subtypes which 
are not visible from the use site (e.g. consider A and B as completely 
disjoint in the example above, if the cast occurs outside M?)

What do you think?

Maurizio


On 18/02/2020 15:47, Gavin Bierman wrote:
> Dear Experts:
>
> One aspect of sealed types that we have not discussed before, is that it
> suggests a change in the type system.
>
> This change regards conversions used in casting contexts. For example, in Java
> right now:
>
>     interface I {}
>     class C {} // does not implement I
>
>     void test (C c) {
>         if (c instanceof I)
>             System.out.println("It's an I");
>     }
>
> This compiles even though it is *currently* not possible for a C object to
> implement the interface I. Of course, it might be, for example:
>
>     class B extends C implements I {}
>
>     test(new B());
>     // Prints "It's an I"
>
> So, the conversion rules capture "open extensibility"; i.e. the Java type system
> does not assume a closed world, classes and interfaces could be extended, and
> casting conversions compile to runtime tests, so we can safely be flexible.
>
> However, at the other end of the spectrum the conversion rules do address
> the case where a class can *not* be extended, i.e. when it is a *final* class.
>
>     final class C {}
>
>     void test (C c) {
>         if (c instanceof I)
>             System.out.println("It's an I");
>     }
>
> The method test fails to compile, as the compiler knows that there can be no
> subclass of C, so as C does not implement I then it is never possible for a C
> value to implement I. This is a compile-time error.
>
> But what about if C is not final, but *sealed*? Given that its direct subclasses
> are explicitly enumerated, and - by the definition of being sealed - in the same
> module, we would surely expect the compiler to look to see if it can spot a
> similar compile-time error. Consider the following code:
>
>     interface I {}
>     sealed class C permits D {}
>     final class D extends C {}
>     
>     void test (C c) {
>         if (c instanceof I)
>             System.out.println("It's an I");
>     }
>
> Class C does not implement I, and is not final - so by the old rules we might
> conclude that there is a conversion - but it is sealed. There is one permitted
> direct subclass of C: D. By definition of sealed types, D must be either final,
> sealed or non-sealed. In this example, all the direct subclasses of C are final
> and do not implement I, so in fact we can safely reject this program, as there
> cannot be a subtype of C that implements I.
>
> In contrast, consider a similar program where one of the direct subclasses of
> the sealed class is non-sealed:
>
>     interface I {}
>     sealed class C permits D, E {}
>     non-sealed class D extends C {}
>     final class E extends C {}
>
>     void test (C c) {
>         if (c instanceof I)
>             System.out.println("It's an I");
>     }
>
> This is type correct as it is possible for a subtype of the non-sealed type D to
> implement I.
>
> The trick is to extend the notion of allowed narrowing reference conversion (JLS
> 5.1.6.1) to follow the sealed type declarations. I'll spare you the gory details
> in this email - they will appear in the JLS draft appearing soon (!) - but I
> wanted to check that this extension to the type system seems reasonable.
> Obviously it will be putting more strain on the type checker. Is everyone happy
> with this extension?
>
> Thanks,
> Gavin
>
>
> PS: Some further examples for your amusement:
>
> Example 1:
> ----------
>
> non-sealed interface I {}
> non-sealed interface J {}
>
> I i;
> if (i instanceof J) {} // Yes!
>
> Example 2:
> ----------
>
> sealed interface I permits C {}
> final class C implements I {}
> non-sealed interface J {}
>
> I i;
> if (i instanceof J) {} // Error, only instance of I is C
>                         // Which doesn’t implement J
>
> Example 3:
> ----------
>
> non-sealed interface I {}
> sealed interface J permits C {}
> final class C implements J {}
>
> I i;
> if (i instanceof J) {} // Error, only instance of J is C
>                         // which doesn’t implement I
>
> Example 4:
> ----------
>
> sealed interface I permits A {}
> sealed interface J permits B {}
> final class A implements I {}
> final class B implements J {}
>
> I i;
> if (i instanceof J) {} // Error, similar to above
>
>
> Example 5:
> ----------
>
> non-sealed class C {}
> non-sealed interface I {}
>
> C c;
> if (c instanceof I) {} // Yes!
>
> Example 6:
> ----------
>
> non-sealed class C {}
> sealed interface I permits A {}
> final class A implements I {}
>
> C c;
> if (c instanceof I) {} // Error
>
> Example 7:
> ----------
>
> final class C {}
> non-sealed interface I {}
>
> C c;
> if (c instanceof I) {} // Error
>
> Example 8:
> ----------
>
> final class C {}
> sealed interface I permits D {}
> final class D implements I {}
>
> C c;
> if (c instanceof I) {} // Error
>
> Example 9:
> ----------
>
> sealed class C permits D {}
> final class D extends C {}
> non-sealed interface I {}
>
> C c;
> if (c instanceof I) {} // Error
>
> Example 10:
> -----------
>
> sealed class C permits D {}
> final class D {}
> sealed interface I permits E {}
> final class E {}
>
> C c;
> if (c instanceof I) {} // Error
>
> Example 11:
> -----------
>
> non-sealed interface I {}
> non-sealed class C {}
>
> I i;
> if (i instanceof C) {} // Yes!
>
> Example 12:
> -----------
>
> non-sealed interface I {}
> final class C {}
>
> I i;
> if (i instanceof C) {} // Error
>
> Example 13:
> -----------
>
> non-sealed interface I {}
> sealed class C permits D {}
> final class D {}
>
> I i;
> if (i instanceof C) {} // Error
>
> Example 14:
> -----------
>
> sealed interface I permits D {}
> final class D {}
> non-sealed class C {}
>
> I i;
> if (i instanceof C) {} // Error
>
> Example 15:
> -----------
>
> sealed interface I permits D {}
> final class D implements I {}
> final class C {}
>
> I i;
> if (i instanceof C) {} // Error
>
> Example 16:
> -----------
>
> sealed interface I permits D {}
> final class D implements I {}
> sealed class C permits E {}
> final class E extends C {}
>
> I i;
> if (i instanceof C) {} // Error
>


More information about the amber-spec-experts mailing list