Странное несоответствие типа с макросом: найдено: одноэлементный тип с базовым типом A, требуется: A

у меня есть

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
}

где superCall макрос белого ящика должен расширяться до super.foo[B](x, y), и это то, что показывает -Ymacro-debug-lite. Проблема в том, что он не компилируется со следующей ошибкой:

[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]                                               ^

Что для меня не имеет смысла: y.type более узкий, чем B, поэтому, если он найден, когда требуется B, это не должно быть ошибкой. Еще более странно, если я заменю superCall его расширением super.foo[B](x, y), ошибка исчезнет!

superCall реализация (слегка упрощена за счет удаления ненужных условных выражений, полную версию можно посмотреть в 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)"
}

РЕДАКТИРОВАТЬ: добавление -uniqid показывает

[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

что объясняет, как возможна ошибка, но не объясняет, чем отличаются 2 B. Я попытался переименовать B в C на случай, если один из них ссылается на B пользователя Foo.foo, но он по-прежнему показывает C с двумя разными идентификаторами, хотя в программе есть только один C.

РЕДАКТИРОВАТЬ 2: см. На этапе ввода Scala указано два варианта использования параметра типа отличаются. Здесь древовидный тип (с super.foo[B](x, y) вместо superCall) равен

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)
};

поэтому кажется, что superCall вместо этого расширяется до Bar#26820.super.foo#26823[B#26833](x#26836, y#26837).


person Alexey Romanov    schedule 13.04.2016    source источник
comment
Изменение args на method.paramLists.map(_.map(_.asTerm.name)) должно сработать, хотя я не уверен, что могу объяснить, почему на данный момент.   -  person Travis Brown    schedule 13.04.2016
comment
@TravisBrown В этом случае это работает! К сожалению, это нарушает другой тест (когда параметры затенены, я хочу использовать исходные параметры). Хм, может быть, я должен выдать ошибку или, по крайней мере, предупреждение в этом случае.   -  person Alexey Romanov    schedule 13.04.2016


Ответы (1)


Вы усложняете себе жизнь, пытаясь направить информацию о типе обратно через имена параметров типа — простые имена — это средство обмена с очень большими потерями — но зачем вы вообще предоставляете аргументы типа? При отсутствии доказательств обратного вы должны написать q"super.$methodName(...$args)", и они будут выведены.

person psp    schedule 15.04.2016
comment
См. scastie.org/16282 для примера, когда без указания параметров типа не удается выполнить компиляцию. - person Alexey Romanov; 15.04.2016
comment
Как я могу указать параметры типа, отличные от имени? Если я передаю TypeSymbols, я получаю Can't unquote List[SuperImpl.this.c.universe.TypeSymbol]. - person Alexey Romanov; 15.04.2016
comment
Что касается сути, о да, я забыл на миллисекунду, что есть так много других ошибок, которые лежат в основе любых усилий. Аргументы типа — это типы, а не символы. Попробуйте .asType.toType. Ужасность этого .asType.toType напоминает мне, какое все это безумие. Удачи! - person psp; 15.04.2016
comment
Я пробовал, к сожалению, это возвращает несоответствие типов (не имеет значения, использую ли я asTerm.name, как предложил Трэвис Браун, или q"$sym", как я изначально использовал в args). - person Alexey Romanov; 15.04.2016