scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.81k stars 1.05k forks source link

Compiler plugins cause REPL to crash #18381

Closed mrdziuban closed 1 year ago

mrdziuban commented 1 year ago

Compiler version

3.3.0, also with the latest 3.3.1-RC4 and nightly 3.4.0-RC1-bin-20230809-c5adafc-NIGHTLY

Minimized code

https://github.com/mrdziuban/dotty-compiler-plugin-issue

After cloning that repo and running sbt package, you can reproduce the issue by running scala with the plugin:

scala -Xplugin:target/scala-3.3.0/example-compiler-plugin_3-0.1.0-SNAPSHOT.jar

and then by evaluating two statements:

scala> val x = 1
********* running example compiler plugin
val x: Int = 1

scala> val y = 2

Output

When evaluating the second statement, the REPL crashes:

Exception in thread "main" java.lang.AssertionError: assertion failed: phase example has already been used once; cannot be reused
    at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:8)
    at dotty.tools.dotc.core.Phases$Phase.init(Phases.scala:403)
    at dotty.tools.dotc.core.Phases$Phase.init(Phases.scala:417)
    at dotty.tools.dotc.core.Phases$PhasesBase.usePhases(Phases.scala:168)
    at dotty.tools.dotc.core.Phases$PhasesBase.usePhases$(Phases.scala:37)
    at dotty.tools.dotc.core.Contexts$ContextBase.usePhases(Contexts.scala:836)
    at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:232)
    at dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:280)
    at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:67)
    at dotty.tools.dotc.Run.compileUnits(Run.scala:280)
    at dotty.tools.dotc.Run.compileUnits(Run.scala:201)
    at dotty.tools.repl.ReplCompiler.compile(ReplCompiler.scala:87)
    at dotty.tools.repl.ReplDriver.compile(ReplDriver.scala:307)
    at dotty.tools.repl.ReplDriver.interpret(ReplDriver.scala:269)
    at dotty.tools.repl.ReplDriver.loop$1(ReplDriver.scala:169)
    at dotty.tools.repl.ReplDriver.runUntilQuit$$anonfun$1(ReplDriver.scala:172)
    at dotty.tools.repl.ReplDriver.withRedirectedOutput(ReplDriver.scala:211)
    at dotty.tools.repl.ReplDriver.runBody$$anonfun$1(ReplDriver.scala:185)
    at dotty.tools.runner.ScalaClassLoader$.asContext(ScalaClassLoader.scala:80)
    at dotty.tools.repl.ReplDriver.runBody(ReplDriver.scala:185)
    at dotty.tools.repl.ReplDriver.runUntilQuit(ReplDriver.scala:172)
    at dotty.tools.repl.ReplDriver.tryRunning(ReplDriver.scala:134)
    at dotty.tools.repl.Main$.main(Main.scala:7)
    at dotty.tools.MainGenericRunner$.run$1(MainGenericRunner.scala:193)
    at dotty.tools.MainGenericRunner$.process(MainGenericRunner.scala:270)
    at dotty.tools.MainGenericRunner$.main(MainGenericRunner.scala:281)
    at dotty.tools.MainGenericRunner.main(MainGenericRunner.scala)

Expectation

The REPL should handle compiler plugins and not crash

SethTisue commented 1 year ago

nice catch. compared to Scala 2, I think Scala 3 compiler plugins in are in much less wide use, so I'm not too surprised that something quite basic like this is broken (but it's dismaying nonetheless, of course)

som-snytt commented 1 year ago

I'll take a quick look. At a glance, maybe the Run re-adds the plugin when the Context is built.

som-snytt commented 1 year ago

I took a look, and Scala 3 expects to instantiate phase objects per Run. The example incorrectly supplies the same phase object in init, where init allows a phase to optionally participate in the Run. It works correctly as:

  override def init(opts: List[String]): List[PluginPhase] = List(ExamplePhase())

  class ExamplePhase extends PluginPhase {

There is no trivial means to subvert this requirement (by overriding behavior).

Not closing in case there is documentation follow-up.

mrdziuban commented 1 year ago

Oooh, very good to know, thank you @som-snytt! ~Feel free to close this if you'd like, or reuse it for documenting the behavior if you think it's worthwhile~ sorry I see you already said this 😄

SethTisue commented 1 year ago

@mrdziuban was your sample plugin code copied from some upstream source where the bug could be corrected?

I am the author of (closed source) Scala 3 compiler plugin and I see that my plugin has this bug, so I'm glad to learn of it, thanks.

I see no indication of the requirement in the Scaladoc of StandardPlugin (or ResearchPlugin). We should document it.

mrdziuban commented 1 year ago

Yeah I maintain a couple open source plugins at work, I just updated both of them a bit ago

SethTisue commented 1 year ago

doc PR: #18394