scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
230 stars 21 forks source link

"Error while emitting" from scalac on wrong mock usage #12924

Closed evis closed 5 months ago

evis commented 6 months ago

Reproduction steps

Scala version: 2.13.12

Dependencies in build.sbt:

libraryDependencies ++= Seq(
  "org.scalamock" %% "scalamock" % "5.2.0",
  "org.scalatest" %% "scalatest" % "3.2.17",
)

Code:

trait A1
trait A2

trait A {
  def f(x: Int)(implicit a1: A1, a2: A2 = new A2 {})
}

import org.scalamock.scalatest.MockFactory

trait Test extends MockFactory {
  val aMock = mock[A]
  (aMock.f(_: Int)(_: A1)).expects(*, *) // here is a bug, A2 should be passed too
}

Problem

Expected: compilation fails with message, that A2 should be passed too

Actual: compilation fails with unhelpful message:

scalac: Error while emitting Main.scala
value x$1
som-snytt commented 6 months ago

The error is in the backend; I don't remember offhand why the error message is necessarily unhelpful. Maybe a symbol is missing?

Probably a macro is emitting a bad tree.

at -Vprint:typer, the reflective invocation is wrong

Test.this.aMock.getClass().getMethod("mock$f$0").invoke(Test.this.aMock).asInstanceOf[function.this.MockFunction2[scala.this.Int,<empty>.this.A1,scala.this.Unit]]

where the mock implementation is correctly

def mock$f$0: function.this.MockFunction3[scala.this.Int,<empty>.this.A1,<empty>.this.A2,scala.this.Unit] = $anon.this.mock$f$0

It does not pas -Ycheck:typer:

[warn]  currentOwner chain: value <local Test> -> trait Test -> package class <empty>
[warn]        symbol chain: value x$1 -> value $anonfun -> value <local Test> -> trait Test -> package class <empty> -> package class <root>ValDef / <artifact> val x$1: scala.this.Int = (x$1: scala.this.Int)
[warn]   (aMock.f(_: Int)(_: A1)).expects(*, *) // here is a bug, A2 should be passed too
[warn]             ^

The expansion requires locals, which are probably not handled correctly by the mock framework.

  <artifact> val x$1: scala.this.Int = (x$1: scala.this.Int);
  <artifact> val x$2: <empty>.this.A1 = (x$2: <empty>.this.A1);
  <artifact> val x$3: <empty>.this.A2 = Test.this.aMock.f$default$3(x$1);
  Test.this.aMock.getClass().getMethod("mock$f$0").invoke(Test.this.aMock)

I was about to say that -Vprint should show before/after macro expansion, but -Vmacro-lite says:

performing macro expansion Test.this.toMockFunction2[scala.this.Int, <empty>.this.A1, scala.this.Unit](((x$1: scala.this.Int, x$2: <empty>.this.A1) => {
  <artifact> val x$1: scala.this.Int = (x$1: scala.this.Int);
  <artifact> val x$2: <empty>.this.A1 = (x$2: <empty>.this.A1);
  <artifact> val x$3: <empty>.this.A2 = Test.this.aMock.f$default$3(x$1);
  Test.this.aMock.f(x$1)(x$2, x$3)
}))(util.this.Defaultable.defaultUnit)

which is before, with after as shown above.

som-snytt commented 6 months ago

Eager to use scala-cli, but I accidentally omitted a colon. I wound up using sbt.

Here is the corrected prologue, but I don't see the debug output.

//> using scala 2.13.12
//> using test.dep org.scalamock::scalamock:5.2.0
//> using test.dep org.scalatest::scalatest:3.2.17
//> using options -Vprint:typer

The "command" must be test not compile.

$ scala-cli test t12924.test.scala
Downloading 3 dependencies and one internal dependency
Compiling project (test, Scala 2.13.12, JVM (21))
Error: Error while emitting t12924.test.scala
value x$1
Error compiling project (test, Scala 2.13.12, JVM (21))
Compilation failed

Edit: someone mentioned there is an option for scala-cli not use a server process, so that you see compiler verbiage, but at the moment (before coffee), I've misplaced it in the mind.

lrytz commented 5 months ago

So this should be reported at scalamock, @evis can you create a ticket there?