Ошибка вычисления выражения типа shapeless.Witness.Aux[T] в макросе

Я пытаюсь оценить неявный параметр типа shapeless.Witness.Aux[T] в макросе, чтобы использовать значение одноэлементного типа T. Это минимальный пример:

import shapeless.Witness
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context

object Macro {
  def foo[N](implicit aux: Witness.Aux[N]): Unit = macro fooImpl[N]

  def fooImpl[N: c.WeakTypeTag](c: Context)
                               (aux: c.Expr[Witness.Aux[N]]): c.Expr[Unit] = {
    import c.universe._

    val typechecked = aux.tree
    println("Typechecked tree:")
    println(show(typechecked))

    val untypechecked = c.untypecheck(typechecked)
    println("Untypechecked tree:")
    println(show(untypechecked))

    def eval = c.eval(c.Expr(untypechecked))
    val w = scala.util.Try(eval).getOrElse(eval)
    // now use w.value
    c.Expr[Unit](q"()")
  }
}

Но компиляция этого

val w = shapeless.Witness(true)
Macro.foo[w.T]

завершается со следующей ошибкой:

[error] overriding value value in trait Witness of type fresh$macro$2.this.T;
[error] value value has incompatible type

Выход println(show(typechecked)):

{
  final class fresh$macro$2 extends AnyRef with shapeless.Witness {
    def <init>(): fresh$macro$2 = {
      fresh$macro$2.super.<init>();
      ()
    };
    type T = Boolean(true);
    private[this] val value: Boolean(true) = true;
    <stable> <accessor> def value: Boolean(true) = true
  };
  new fresh$macro$2()
}

Выход println(show(untypechecked)):

{
  final class fresh$macro$2 extends AnyRef with _root_.shapeless.Witness {
    def <init>() = {
      super.<init>();
      ()
    };
    type T = Boolean(true);
    private[this] val value: Boolean(true) = true;
    <stable> <accessor> def value: Boolean = true
  };
  new fresh$macro$2()
}

Мне кажется, проблема в том, что в дереве без проверки типов метод value имеет тип Boolean, а поле value имеет тип Boolean(true), и компилятору нужно, чтобы оба типа были одинаковыми.

Любые идеи, как обойти это? Поддерживается ли это даже для оценки макроса в макросе?

Кстати: этот проект https://github.com/fthomas/scala-macro содержит минимальный проект для воспроизведения этого.


person Frank S. Thomas    schedule 25.05.2015    source источник
comment
Правильно ли я предполагаю, что то, что вы пытаетесь сделать здесь, - это переправить значение времени компиляции в реализацию макроса, чтобы его можно было использовать в вычислении времени компиляции?   -  person Miles Sabin    schedule 25.05.2015
comment
@MilesSabin Это звучит правильно. Это произошло в контексте библиотеки refined, где я хочу, чтобы этот refineLit[MatchesRegex[shapeless.Witness.`"[0-9]+"`.T], String]("123") проверял во время компиляции, что строка 123 соответствует регулярному выражению [0-9]+.   -  person Frank S. Thomas    schedule 25.05.2015
comment
Gotcha.Witness действительно предназначен для сопоставления одноэлементных типов со значениями runtime. Я думаю, вам, вероятно, было бы лучше работать напрямую с вашим аргументом типа N и его тегом типа, а затем повторно использовать логику Witness внутри. Довольно простым способом сделать это было бы разложить большую часть макроса Witness на черту, которую можно смешать с вашим собственным макросом.   -  person Miles Sabin    schedule 25.05.2015
comment
Спасибо @MilesSabin за ваш ответ, хотя это был не тот ответ, на который я надеялся. :-) Похоже, мне нужно углубиться в макроземлю. Что немного усложняет дело, так это то, что существует эквивалент времени выполнения refineLit (называемый refine), который основан на том же механизме проверки и для которого Witness отлично работает.   -  person Frank S. Thomas    schedule 26.05.2015
comment
Если вам также нужна версия среды выполнения, это может изменить ситуацию. Я подозреваю, что ваша проблема, описанная выше, заключается в том, что отмена проверки типов, за которой следует повторная проверка типов, гарантированно будет идентичностью. Можете ли вы оценить дерево в его проверенном виде? Кроме того, имейте в виду, что оценка произвольного кода во время компиляции может быть ... рискованной ;-)   -  person Miles Sabin    schedule 26.05.2015
comment
@MilesSabin Я рискну :-) Я пытаюсь продвигать идею проверки во время компиляции, насколько это возможно. Оценка дерева с проверкой типов не поддерживается и завершается с ошибкой scala.tools.reflect.ToolBoxError: reflective toolbox has failed: cannot operate on trees that are already typed. Явная повторная проверка дерева с помощью c.typecheck(untypechecked) снова дает мне дерево, в котором поле value имеет тип Boolean(true), а метод value имеет тип Boolean. В остальном он идентичен оригинальному aux.tree.   -  person Frank S. Thomas    schedule 27.05.2015
comment
Хорошо, вместо того, чтобы использовать eval для всего Witness, можете ли вы перемещаться по дереву и извлекать поддерево, соответствующее правой части определения val/def? Затем вы можете использовать это как дерево или оценить его самостоятельно (что, как я ожидаю, будет работать правильно).   -  person Miles Sabin    schedule 27.05.2015
comment
@MilesSabin Фактический параметр, который принимает мой макрос, может содержать сложную комбинацию выражений Witness. Например. MatchesRegex[Witness.`"[0-9]+"`.T] Or Equal[Witness.`"abc"`.T]] был бы предикатом, который я хотел бы поддерживать. Я не уверен, как навигация и извлечение поддеревьев помогут мне здесь. Возможно, я мог бы сделать более слабую версию Witness, где value имеет менее точный тип, чем T. Может быть, что-то вроде trait WeakWitness { type T; val value: AnyVal } может сработать. Или, если value будет иметь неодноэлементный тип T, это тоже должно работать.   -  person Frank S. Thomas    schedule 27.05.2015
comment
@MilesSabin Я только что попробовал идею WeakWitness в своей бесформенной вилке. И это работает! Я могу вычислить WeakWitness в макросе и использовать там value (value имеет тип Boolean, а T это Boolean(true). Думаю, мне следует продолжить обсуждение в бесформенном PR.   -  person Frank S. Thomas    schedule 27.05.2015


Ответы (1)


Эта проблема исчезла с shapeless 2.2.2 из-за изменений в https://github.com/milessabin/shapeless/pull/410.

person Frank S. Thomas    schedule 14.06.2015