Strange type mismatch with a macro: found: singlet

2019-06-28 02:19发布

问题:

I have

class Foo[A] {
  def foo[B](x: A, y: B) = y
}

class Bar[A] extends Foo[A] {
  override def foo[B](x: A, y: B) = superCall
}

where superCall whitebox macro should expand to super.foo[B](x, y), and that's what -Ymacro-debug-lite shows. The problem is that it fails to compile with following error:

[error] /home/aromanov/IdeaProjects/scala-dry/src/test/scala/com/github/alexeyr/scaladry/SuperTests.scala:80: type mismatch;
[error]  found   : y.type (with underlying type B)
[error]  required: B
[error]             override def foo[B](x: A, y: B) = superCall
[error]                                               ^

Which makes no sense to me: y.type is more narrow than B, so if it's found when B is required it shouldn't be an error. Even stranger, if I replace superCall with its expansion super.foo[B](x, y), the error goes away!

superCall implementation (slightly simplified by removing irrelevant conditionals, you can see the full version at Github):

def superCall: Tree = {
  val method = c.internal.enclosingOwner.asMethod
  val args = method.paramLists.map(_.map(sym => c.Expr(q"$sym")))
  val typeParams = method.typeParams.map(_.asType.name)
  q"super.${method.name.toTermName}[..$typeParams](...$args)"
}

EDIT: adding -uniqid shows

[error]  found   : y#26847.type (with underlying type B#26833)
[error]  required: B#26834
[error]             override def foo[B](x: A, y: B) = superCall
[error]                                               ^
[error] one error found

which explains how the error is possible, but doesn't explain how 2 B's are different. I tried renaming B to C in case one of them referred to Foo.foo's B, but it still shows C with 2 different ids even though there is only one C in the program.

EDIT 2: see Scala typer stage says two uses of type parameter are different. The tree typer produces here (with super.foo[B](x, y) instead of superCall) is

class Foo#26818[A#26819] extends scala#22.AnyRef#2757 {
  def <init>#26822(): Foo#26818[A#26819] = {
    Foo#26818.super.<init>#3104();
    ()
  };
  def foo#26823[B#26824](x#26827: A#26819, y#26828: B#26825): B#26824 = y#26828
};
class Bar#26820[A#26821] extends Foo#26818[A#26821] {
  def <init>#26831(): Bar#26820[A#26821] = {
    Bar#26820.super.<init>#26822();
    ()
  };
  override def foo#26832[B#26833](x#26836: A#26821, y#26837: B#26834): B#26833 = Bar#26820.super.foo#26823[B#26834](x#26836, y#26837)
};

so it seems superCall expands to Bar#26820.super.foo#26823[B#26833](x#26836, y#26837) instead.

回答1:

You're making your life harder by trying to funnel your type information back through the names of type parameters - simple names are a very lossy medium of exchange - but why are you supplying the type arguments at all? In the absence of evidence to the contrary, you should write q"super.$methodName(...$args)" and they will be inferred.