Skip to content

Overload that differs only in parameter type is mistaken as override, crashes with a ClassCastException at runtime #12828

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

Closed
julienrf opened this issue Jun 15, 2021 · 1 comment · Fixed by #12982
Assignees
Milestone

Comments

@julienrf
Copy link
Contributor

julienrf commented Jun 15, 2021

Compiler version

3.0.1-RC1

Minimized code

trait Foo[A]:
  def foo(x: A): Unit

trait Bar[A] extends Foo[A]:
  def foo(x: A & String): Unit = println(x.toUpperCase)

object Baz extends Bar[Int]

@main def run() = Baz.foo(42)

Output

The code compiles, but fails at run-time:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at Baz$.foo(main.scala:7)

Expectation

The code should not compile. It should produce the following error:

object creation impossible, since def foo(x: A): Unit in trait Foo is not defined
(Note that
parameter A in def foo(x: A): Unit in trait Foo does not match
parameter A & String in def foo(x: A & String): Unit in trait Bar
type A & String is a subtype of trait A, but method parameter types must match exactly.)

Note: adding the override keyword raises a compile-time error:

trait Foo[A]:
  def foo(x: A): Unit

trait Bar[A] extends Foo[A]:
  override def foo(x: A & String): Unit = println(x.toUpperCase)
//                    ^^^^^^^^^^
// method foo has a different signature than the overridden declaration
@smarter smarter changed the title Generic method parameter types can be refined covariantly Overload that differs only in parameter type is mistaken as override, crashes with a ClassCastException at runtime Jun 15, 2021
@smarter smarter added this to the 3.0.2-RC1 milestone Jun 15, 2021
@odersky odersky assigned odersky and smarter and unassigned odersky Jun 16, 2021
@smarter
Copy link
Member

smarter commented Jun 16, 2021

The override check is supposed to happen in https://github.com/lampepfl/dotty/blob/4ad0c8f2a73f12e58750bcd4570616cbf50a3011/compiler/src/dotty/tools/dotc/typer/RefChecks.scala#L499-L521
But even though the opc.matches method returns true on the symbols we'd like to compare, ops.hasNext returns false, so this seems to be an issue in how OverridingPairs.Cursor works and I have to admit that I don't fully understand that class (but I know we're missing some fixes from scala 2 there: #7551), do you think you could have a look @odersky ?

@smarter smarter assigned odersky and unassigned smarter Jun 16, 2021
odersky added a commit to dotty-staging/dotty that referenced this issue Jun 29, 2021
We now exclude a pair of overriding / overridden symbols that
have a common subclass parent of the base class only under the
additional condition that their signatures also match.

scala#12828 shows a case where this matters:
```scala
trait Foo[A]:
  def foo(x: A): Unit

trait Bar[A] extends Foo[A]:
  def foo(x: A & String): Unit = println(x.toUpperCase)

object Baz extends Bar[Int] // error overriding foo: incompatible type

@main def run() = Baz.foo(42)
```
When checking `Baz`, there is a common subclass parent (namely `Bar`),
but the signatures of the two `foo` definitions as seen from `Bar` are
different, so we cannot assume that the pair has already been checked.

Fixes scala#12828
BarkingBad pushed a commit to BarkingBad/dotty that referenced this issue Jul 23, 2021
We now exclude a pair of overriding / overridden symbols that
have a common subclass parent of the base class only under the
additional condition that their signatures also match.

scala#12828 shows a case where this matters:
```scala
trait Foo[A]:
  def foo(x: A): Unit

trait Bar[A] extends Foo[A]:
  def foo(x: A & String): Unit = println(x.toUpperCase)

object Baz extends Bar[Int] // error overriding foo: incompatible type

@main def run() = Baz.foo(42)
```
When checking `Baz`, there is a common subclass parent (namely `Bar`),
but the signatures of the two `foo` definitions as seen from `Bar` are
different, so we cannot assume that the pair has already been checked.

Fixes scala#12828
@Kordyjan Kordyjan modified the milestones: 3.0.2-RC1, 3.0.2 Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants