Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sealed] ECJ rejects casting from a sealed interface that permits a non-sealed class #3184

Closed
jarthana opened this issue Oct 29, 2024 · 7 comments
Assignees
Milestone

Comments

@jarthana
Copy link
Member

This code produces compilation errors:

sealed interface SealedInterface permits NonSealedClass {}
non-sealed class NonSealedClass implements SealedInterface {}
class AnotherClass {}
public class X {
	static SealedInterface si;
	public static void main(String[] args) {
		 AnotherClass ac = (AnotherClass) si; // Cannot cast from SealedInterface to AnotherClass
		 System.out.println(ac == si); // Incompatible operand types
	}
}

This cast and comparison should be allowed since NonSealedClass is non-sealed. This code is accepted by Javac.

If I alter the class definition to this, then both compilers report error:
final class NonSealedClass implements SealedInterface {}

@srikanth-sankaran
Copy link
Contributor

Upto 4.32 we compile this code. 4.33 rejects it. Likely change agent is #2595

See also #2709 where I seem to have concluded perhaps in a thoroughly overworked state that javac has a bug in this area :)

I'll revalidate this and that one too.

@srikanth-sankaran
Copy link
Contributor

5.1.6.1 

A class or interface is disjoint from another class or interface if it can be determined
statically that they have no instances in common (other than the null value).

For the cast from SealedInterface to AnotherClass to be viable there must exist some common instance.

Only instances possible for SealedInterface must be through NonSealedClass as it is the only edge emanating from SealedInterface but NonSealedClass and AnotherClass class are disjoint since they have no relationship.

Given Java does not allow multiple inheritance for super classes, there cannot be a common descendent for NonSealedClass and AnotherClass that implements SealedInterface and extends AnotherClass - ergo, the cast is bad.

@stephan-herrmann - do you disagree ? See also #2709 (comment)

If the analysis is correct, this needs to be raised with Oracle for clarification.

@jarthana - FYI. Key question is: Can there be a type that is a subtype of AnotherClass that ALSO implements SealedInterface - I don't think so - do you see a hole in my reasoning ?? Perhaps some obvious mixup ?

@stephan-herrmann
Copy link
Contributor

@stephan-herrmann - do you disagree ? See also #2709 (comment)

Question raised here: https://mail.openjdk.org/pipermail/compiler-dev/2024-October/028344.html

Let's wait for an answer.

@jarthana
Copy link
Member Author

@jarthana - FYI. Key question is: Can there be a type that is a subtype of AnotherClass that ALSO implements SealedInterface

No, I don't see that being allowed.

@srikanth-sankaran
Copy link
Contributor

We have a confirmation that this is a javac bug - see https://mail.openjdk.org/pipermail/compiler-dev/2024-October/028367.html

@srikanth-sankaran srikanth-sankaran closed this as not planned Won't fix, can't repro, duplicate, stale Oct 29, 2024
@davidnussio
Copy link

@srikanth-sankaran This case could be related to this issue?

public class Main {
    class C {}

    sealed interface I<T extends C> permits C1, C2 {
        T op();
    }

    final class C1 extends C implements I<C1> {
        @Override
        public C1 op() {
            return new C1();
        }
    }
    
    final class C2 extends C implements I<C2> {

        @Override
        public C2 op() {
            return new C2();
        }
    }

    C getC() {
        return new C2();
    }

    C1 getC1() {
        return new C1();
    }

    public void test() {
        // javac: no error
        // eclipse: Incompatible conditional operand types Main.C and Main.I<?>Java(16777232)
        var a = getC();
        if (a instanceof I<?> i) {
            i.op();
        }

        // javac: Pattern type 'I<?>' is a supertype of expression type 'Main. C1'
        // eclipse: no error
        var b = getC1();
        if (b instanceof I<?> i) {
            i.op();
        }
    }
}

@srikanth-sankaran
Copy link
Contributor

Hello @davidnussio - you haven't mentioned which version of Eclipse you are on and which version of javac you are on.

Both must be very old.

JDK23 and Eclipse master/HEAD both compile this program

The javac error javac: Pattern type 'I<?>' is a supertype of expression type 'Main. C1' is withdrawn by a spec change as of JLS19 we are on JLS23 now. So you are at least 4 versions behind javac wise

I think you could be using JDK18 or earlier ?

Likewise Eclipse compiles this code as of at least 4.29 and upto top of trunk.

Since ECJ master HEAD agrees with JDK23 I don't think there is any action called for other than for you to consider an upgrade

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants