Skip to content

Conversation

@tanishiking
Copy link
Member

@tanishiking tanishiking commented Nov 13, 2025

Fixes #24074

When resolving overridden methods from Java interfaces, treat arrays as covariant.
This fixes incorrect method selection when multiple Java interfaces override methods with array return types.

Previously, in the example below, we get the compilation error:

value foo is not a member of org.test.Test2.A
    lvl3.foo.head.foo()
public class JavaPart {
    public interface A { }
    public interface B extends A {
        int onlyInB();
    }
    public interface Lvl1 {
        A[] getData();
    }
    public interface Lvl2 extends Lvl1 {
        @OverRide
        B[] getData();
    }
    public interface Lvl3 extends Lvl2, Lvl1 { }
}
def test(lvl3: JavaPart.Lvl3): Unit =
  lvl3.getData.head.onlyInB()

because Denotations#mergeSingleDenot creates a JointRefDenotation for Lvl1.getData: A[] and Lvl2.getData: B[], with a return type of JArray[A] & JArray[B]
(since the compiler doesn't recognize that Lvl2.getData overrides Lvl1.getData).

And because JArray isn't recognized as covariant, JArray[A & B] <: JArray[A] & JArray[B] cannot be derived.

Consequently, lvl3.getData.head returns a value typed as A instead of neither B nor A & B, which fails to resolve the method onlyInB.


It seems in Scala2, we use Types#matches (which does match in Scala3 as well), so Scala3 is more strict?

https://github.com/scala/scala/blob/ae6ae4dd59cb90af62093cde52a292e5bd8bb7a8/src/reflect/scala/reflect/internal/Symbols.scala#L2461

https://github.com/scala/scala/blob/ae6ae4dd59cb90af62093cde52a292e5bd8bb7a8/src/reflect/scala/reflect/internal/Symbols.scala#L2445-L2452

https://github.com/scala/scala/blob/ae6ae4dd59cb90af62093cde52a292e5bd8bb7a8/src/reflect/scala/reflect/internal/Types.scala#L862-L874


Previous try #24377

Fixes scala#24074

When resolving overloads from Java interfaces, treat arrays as
covariant.
This fixes incorrect method selection when multiple Java interfaces override methods with array return types.

Previously, in the example below, we get the compilation error:

```
value foo is not a member of org.test.Test2.A
    lvl3.foo.head.foo()
```

```java
public class JavaPart {
    public interface A { }
    public interface B extends A {
        int onlyInB();
    }
    public interface Lvl1 {
        A[] getData();
    }
    public interface Lvl2 extends Lvl1 {
        @OverRide
        B[] getData();
    }
    public interface Lvl3 extends Lvl2, Lvl1 { }
}
```

```scala
def test(lvl3: JavaPart.Lvl3): Unit =
  lvl3.getData.head.onlyInB()
```

because `Denotations#mergeSingleDenot` creates a `JointRefDenotation`
for `Lvl1.getData: A[]` and `Lvl2.getData: B[]`,
with a return type of `JArray[A] & JArray[B]`
(since the compiler doesn't recognize that `Lvl2.getData` overrides `Lvl1.getData`).

And because `JArray` isn't recognized as covariant,
`JArray[A & B] <: JArray[A] & JArray[B]` cannot be derived.

Consequently, `lvl3.getData.head` returns a value typed as
`A` instead of neither `B` nor `A & B`,
which fails to resolve the method `onlyInB`.
@tanishiking tanishiking changed the title [WIP] Fix override checking for Java methods with covariant array Fix override checking for Java methods with covariant array Nov 13, 2025
if symScore <= 0 && info2.overrides(info1, matchLoosely, checkClassInfo = false) then
val compareCtx =
if sym1.is(JavaDefined) && sym2.is(JavaDefined) then
ctx.withProperty(TypeComparer.ComparingJavaMethods, Some(()))
Copy link
Member Author

@tanishiking tanishiking Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let TypeComparer know we're comparing Java defined methods, since TypeComparer alone can't tell the symbols are defined in Java.
Not sure this is legid usage of Context#property?

@tanishiking tanishiking marked this pull request as ready for review November 13, 2025 10:21
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

Successfully merging this pull request may close these issues.

Unexpected overload selection involving covariant Array returned by java class

1 participant