StackruError при расширении макроса reify
У меня есть простой тестовый макрос, который использует reify. Это вызывает StackruError во время раскрытия макроса.
def test() = macro testimpl
def testimpl(c:Context)():c.Expr[Any] = {
import c.universe._
val o = reify { // StackruError here
object O
O
}
o
}
Почему это происходит? Можно ли этого как-то избежать?
РЕДАКТИРОВАТЬ: Это то, что происходит с M6. Я только что попробовал это с M7, и теперь он говорит
ограничение реализации: невозможно определить тип Object{def (): O.type} (ClassInfoType)
Так что это отвечает на вопрос "почему", но остается вопрос, есть ли способ обойти это.
2 ответа
В настоящее время reifier не знает, как преобразовывать типы, которые ссылаются на вещи, определенные внутри reified блока. Отсюда и ошибка.
Но какое это имеет отношение к вашему примеру? Вот как это работает.
Чтобы изменить ваш блок кода, компилятор использует def apply[T: AbsTypeTag](mirror: MirrorOf[self.type], treec: TreeCreator): Expr[T]
(обн. в 2.10.0-RC1 AbsTypeTag
был переименован в WeakTypeTag
) создать объект типа Expr, который преобразует выражение. Однако в контракте Expr подразумевается, что он также отражает тип жены, и это порождает проблему.
Поэтому вам нужен обходной путь. Самым простым было бы разыграть O
в последней строке фрагмента к чему-то reifiable, например, чтобы написать O.asInstanceOf[Object]
, Тогда вы можете вручную снять asInstanceOf
часть из результата.
scala> reify { object O; O }
<console>:26: error: implementation restriction: cannot reify type Object{def <init>(): O.type} (ClassInfoType)
reify { object O; O }
^
scala> reify { object O; O.asInstanceOf[Object] }
res1 @ 2d059fd6: reflect.runtime.universe.Expr[Object] =
Expr[java.lang.Object]({
object O extends AnyRef {
def <init>() = {
super.<init>();
()
}
};
O.asInstanceOf[Object]
})
Я столкнулся с той же проблемой в последнее время. Но я не мог позволить себе приводить тип объекта, так как я использовал тип синглтона в другом макросе, чтобы различать (переменные времени компиляции). Таким образом, если вам действительно нужно повторно определить объект, вы можете сделать следующее внутри макроса, поэтому reify возвращает объект вместо значения Unit.
def mkObject(c: Context) = {
import c.universe._
val objectO = reify { object O }
c.Expr(objectO.tree match {
case Block(stats, expr) => Block(stats, Ident(newTermName("O")))
})
}