Skip to content

AssertionError in dotty.tools.dotc.transform.ResolveSuper #14415

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
dgruntz opened this issue Feb 5, 2022 · 5 comments · Fixed by #14527
Closed

AssertionError in dotty.tools.dotc.transform.ResolveSuper #14415

dgruntz opened this issue Feb 5, 2022 · 5 comments · Fixed by #14527
Assignees
Milestone

Comments

@dgruntz
Copy link

dgruntz commented Feb 5, 2022

Compiler version

Welcome to Scala 3.1.1 (17, Java Java HotSpot(TM) 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.

Minimized code and output

I executed the following lines in the Scala REPL:

trait A {
    def m(a:Int): Int
}

trait B extends A {
    override def m(a:Int): Int = { return a; }
}

trait C extends A {
    abstract override def m(a:Int):Int = { return super.m(a); }
}

trait D extends B with C {
    override def m(a:Int):Int = { return super.m(a); }
}

trait E extends C with B {
    abstract override def m(a:Int):Int = { return super.m(a); }
}

class X extends E with D

and with entering the last line I see the following stack trace (and the shell exits).

Click to expand!
Exception in thread "main" java.lang.AssertionError: assertion failed: cannot rebind method repl$$rs$line$3$C$$super$m, repl$$rs$line$3$C$$super$m m
    at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
    at dotty.tools.dotc.transform.ResolveSuper$.rebindSuper(ResolveSuper.scala:115)
    at dotty.tools.dotc.transform.ResolveSuper.superAccessors$2$$anonfun$2(ResolveSuper.scala:52)
    at scala.collection.immutable.List.map(List.scala:246)
    at dotty.tools.dotc.transform.ResolveSuper.superAccessors$3(ResolveSuper.scala:53)
    at dotty.tools.dotc.transform.ResolveSuper.$anonfun$1(ResolveSuper.scala:55)
    at scala.collection.immutable.List.flatMap(List.scala:293)
    at dotty.tools.dotc.transform.ResolveSuper.transformTemplate(ResolveSuper.scala:55)
    at dotty.tools.dotc.transform.ResolveSuper.transformTemplate(ResolveSuper.scala:43)
    at dotty.tools.dotc.transform.MegaPhase.goTemplate(MegaPhase.scala:1004)
    at dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:363)
    at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:429)
    at dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:256)
    at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:427)
    at dotty.tools.dotc.transform.MegaPhase.transformStat$2(MegaPhase.scala:437)
    at dotty.tools.dotc.transform.MegaPhase.recur$1(MegaPhase.scala:442)
    at dotty.tools.dotc.transform.MegaPhase.recur$1(MegaPhase.scala:442)
    at dotty.tools.dotc.transform.MegaPhase.transformStats(MegaPhase.scala:442)
    at dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:362)
    at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:429)
    at dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:256)
    at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:427)
    at dotty.tools.dotc.transform.MegaPhase.transformStat$2(MegaPhase.scala:437)
    at dotty.tools.dotc.transform.MegaPhase.recur$1(MegaPhase.scala:442)
    at dotty.tools.dotc.transform.MegaPhase.recur$1(MegaPhase.scala:442)
    at dotty.tools.dotc.transform.MegaPhase.transformStats(MegaPhase.scala:442)
    at dotty.tools.dotc.transform.MegaPhase.mapPackage$1(MegaPhase.scala:382)
    at dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:385)
    at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:429)
    at dotty.tools.dotc.transform.MegaPhase.transformUnit(MegaPhase.scala:448)
    at dotty.tools.dotc.transform.MegaPhase.run(MegaPhase.scala:460)
    at dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:308)
    at scala.collection.immutable.List.map(List.scala:246)
    at dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:309)
    at dotty.tools.dotc.Run.runPhases$4$$anonfun$4(Run.scala:261)
    at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
    at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
    at scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1323)
    at dotty.tools.dotc.Run.runPhases$5(Run.scala:272)
    at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:280)
    at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
    at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:68)
    at dotty.tools.dotc.Run.compileUnits(Run.scala:289)
    at dotty.tools.dotc.Run.compileUnits(Run.scala:228)
    at dotty.tools.repl.ReplCompiler.runCompilationUnit(ReplCompiler.scala:155)
    at dotty.tools.repl.ReplCompiler.compile(ReplCompiler.scala:165)
    at dotty.tools.repl.ReplDriver.compile(ReplDriver.scala:250)
    at dotty.tools.repl.ReplDriver.interpret(ReplDriver.scala:218)
    at dotty.tools.repl.ReplDriver.loop$1(ReplDriver.scala:152)
    at dotty.tools.repl.ReplDriver.runUntilQuit$$anonfun$1(ReplDriver.scala:155)
    at dotty.tools.repl.ReplDriver.withRedirectedOutput(ReplDriver.scala:174)
    at dotty.tools.repl.ReplDriver.runUntilQuit(ReplDriver.scala:155)
    at dotty.tools.repl.ReplDriver.tryRunning(ReplDriver.scala:117)
    at dotty.tools.repl.Main$.main(Main.scala:6)
    at dotty.tools.repl.Main.main(Main.scala)

Expectation

Well, the definition of class X is not correct and therefore I expected an error message. The linearization of X is X D E B C A i.e. class C is not mixed in correctly as method C.m is declared abstract (i.e. needs to be mixed in in such a way that a concrete implementation follows class C in the linearization). The problem seems to be the definition of method D.m which should have been declared as abstract override.

Scala 2 reports an error on definition of method D.m:

`abstract override` modifiers required to override:
absoverride def m(a: Int): Int (defined in trait C)
  with override def m(a: Int): Int (defined in trait D)

If method D.m were declared as abstract override, then the Scala compiler would report the following error on creation of class X without crashing:

scala> class X extends E with D
-- Error: ----------------------------------------------------------------------
1 |class X extends E with D
  |      ^
  |class X needs to be abstract, since def m(a: Int): Int in trait A is not defined
  |(The class implements abstract override def m(a: Int): Int in trait D but that definition still needs an implementation)
1 error found
@smarter
Copy link
Member

smarter commented Feb 5, 2022

The abstract override check is skipped because of the use of inLinearizationOrder in:
https://github.com/lampepfl/dotty/blob/4c7ec9b7411b6d8e979e1e5409b72fd6d90c0914/compiler/src/dotty/tools/dotc/typer/RefChecks.scala#L211-L219

More precisely, when we check as seen from class X if method m in D is a valid override of method m in C, the check is skipped because C is a parent of D, but it turns out that for abstract override checks this isn't enough: the linearization order of D is D C B A whereas in the linearization order of X ends with B C A as you noted. So we need a stricter check at least when sym2.is(AbsOverride), but I don't know if that's the only special case here /cc @odersky

@dgruntz
Copy link
Author

dgruntz commented Feb 7, 2022

I think the problem is not the check triggered by the definition of class X, but rather the fact that the Scala3 compiler does not report an error on the definition of method D.m. If D.m were declared as an abstract override, then the Scala3 compiler would report an error on the definition of class X (similar to Scala2):

scala> class X extends E with D
-- Error: ----------------------------------------------------------------------
1 |class X extends E with D
  |      ^
  |class X needs to be abstract, since def m(a: Int): Int in trait A is not defined
  |(The class implements abstract override def m(a: Int): Int in trait D but that definition still needs an implementation)
1 error found

The difference to Scala2 is, that the Scala3 compiler does not report an error on the definition of method D.m which should be declared as abstract override. This then leads to the java.lang.AssertionError on the definition of class X.

@smarter
Copy link
Member

smarter commented Feb 7, 2022

The difference to Scala2 is, that the Scala3 compiler does not report an error on the definition of method D.m

Not sure what you mean, Scala 2 does not report an error if I try to typecheck D and its parent classes but not X:

trait A {
    def m(a:Int): Int
}

trait B extends A {
    override def m(a:Int): Int = { return a; }
}

trait C extends A {
    abstract override def m(a:Int):Int = { return super.m(a); }
}

trait D extends B with C {
    override def m(a:Int):Int = { return super.m(a); }
}

The error you mention is only reported in X, because it's only when clazz=X, other=C.m that other.isIncompleteIn(clazz) returns true, this check is identical in both Scala 2 and 3, the only difference is that we skip it in Scala 3 because of the reasons I mentioned above, see:

@dgruntz
Copy link
Author

dgruntz commented Feb 8, 2022

Sorry for the confusion. You are right. Scala 2 also only reports an error on the definition on X (as you wrote) and not on the definition of method D.m:

Scala 2 (2.13.8) shows the following behavior:

  • In case that D.m is defined as an override:

      scala> class X extends E with D
                   ^
             error: `abstract override` modifiers required to override:
             absoverride def m(a: Int): Int (defined in trait C)
               with override def m(a: Int): Int (defined in trait D)
    
  • In case that D.m is defined as an abstract override:

      scala> class X extends E with D
                   ^
             error: Member method m of mixin trait iw$C is missing a concrete super implementation.
    

Scala 3 (3.1.1) shows the following behavior:

  • In case that D.m is defined as an override:

    scala> class X extends E with D
    Exception in thread "main" java.lang.AssertionError: assertion failed: cannot rebind method repl$$rs$line$3$C$$super$m, repl$$rs$line$3$C$$super$m m
            at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
            at dotty.tools.dotc.transform.ResolveSuper$.rebindSuper(ResolveSuper.scala:122)
            ...
    
  • In case that D.m is defined as an abstract override:

    scala> class X extends E with D
    -- Error: ----------------------------------------------------------------------
    1 |class X extends E with D
      |      ^
      |class X needs to be abstract, since def m(a: Int): Int in trait A is not defined
      |(The class implements abstract override def m(a: Int): Int in trait D but that definition still needs an implementation)
    1 error found
    

@odersky
Copy link
Contributor

odersky commented Feb 21, 2022

@smarter Your analysis is spot on.

olsdavis pushed a commit to olsdavis/dotty that referenced this issue Apr 4, 2022
@Kordyjan Kordyjan added this to the 3.1.3 milestone Aug 1, 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