4

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.

Community
  • 1
  • 1
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Changing `args` to `method.paramLists.map(_.map(_.asTerm.name))` should work, although I'm not sure I can make a good case about why at the moment. – Travis Brown Apr 13 '16 at 13:11
  • @TravisBrown It does work for this case! Unfortunately, it breaks a different test (when parameters are shadowed, I want to use the original parameters). Hm, maybe I should throw an error, or at least a warning, in this case instead. – Alexey Romanov Apr 13 '16 at 13:17

1 Answers1

0

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.

psp
  • 12,138
  • 1
  • 41
  • 51
  • See http://scastie.org/16282 for an example where not supplying the type parameters fails to compile. – Alexey Romanov Apr 15 '16 at 17:05
  • How can I provide the type parameters other than by name? If I pass `TypeSymbol`s, I get "Can't unquote List[SuperImpl.this.c.universe.TypeSymbol]". – Alexey Romanov Apr 15 '16 at 17:25
  • Re the gist, oh yeah, I forgot for a millisecond there are so many other bugs which underlie any given effort. Type arguments are types, not symbols. Try .asType.toType. The awfulness of that .asType.toType reminds me of what madness it all is. Good luck! – psp Apr 15 '16 at 18:34
  • I tried, unfortunately it brings the type mismatch back (doesn't matter whether I use `asTerm.name` as Travis Brown suggested or `q"$sym"` as I originally had in `args`). – Alexey Romanov Apr 15 '16 at 19:12