Macro implicite. Valeur implicite par défaut. Comment?

Sep 29 2020

Je ne sais même pas comment poser la question.

J'ai une macro qui crée une instance de IsEnum[T]pour un type T.

Je fais des tests pour cela, et je veux m'assurer que l'implicite n'est pas trouvé pour les types qui ne sont pas scellés ou qui, en général, ne répondent pas aux exigences d'une énumération.

J'ai donc créé cette méthode pour tester

    def enumOf[T](implicit isEnum:IsEnum[T] = null) = isEnum

Et puis je m'assure que enumOf[NotAnEnum] == null

Mais au lieu de cela, il échoue au moment de la compilation.

Une chose est l'erreur de macro. Une autre chose est que la macro ne s'applique tout simplement pas à un cas donné. Comment faire cette distinction lors de la création de macros?

Edit: j'ai utilisé c.abortet c.error, les deux me donnant les mêmes résultats.

Réponses

2 DmytroMitin Sep 29 2020 at 23:43

On dirait que vous n'avez pas IsEnumcréé votre boîte blanche de classe de type de matérialisation de macro . Normalement, les macros implicites doivent être des boîtes blanches.

import scala.language.experimental.macros
import scala.reflect.macros.whitebox

trait IsEnum[T]
object IsEnum {
  implicit def mkIsEnum[T]: IsEnum[T] = macro mkIsEnumImpl[T]
  def mkIsEnumImpl[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._
    val typ = weakTypeOf[T]
    val classSymbol = typ.typeSymbol.asClass
    if (!classSymbol.isTrait || !classSymbol.isSealed) c.abort(c.enclosingPosition, s"$typ must be sealed trait") val symbols = classSymbol.knownDirectSubclasses symbols.collectFirst { case symbol if !symbol.isModuleClass || !symbol.asClass.isCaseClass => c.abort(c.enclosingPosition, s"${symbol.asClass.toType} must be case object")
    }
    q"new IsEnum[$typ] {}"
  }
}

def enumOf[T](implicit isEnum: IsEnum[T] = null) = isEnum

Usage:

sealed trait A
object A {
  case object B extends A
  case object C extends A
  case class D() extends A
}
enumOf[A] //null

sealed trait A
object A {
  case object B extends A
  case object C extends A
  class D extends A
}
enumOf[A] //null

sealed trait A
object A {
  case object B extends A
  object C extends A
}
enumOf[A] //null

trait A
object A {
  case object B extends A
  case object C extends A
}
enumOf[A] //null

sealed trait A
object A {
  case object B extends A
  case object C extends A
}
enumOf[A] //App$$anon$1@47f37ef1

Le temps d'exécution des macros correspond à la compilation du code principal. Si une macro boîte noire (même une macro boîte noire implicite) lève une exception, ce sera une erreur de compilation lors de la compilation du code principal. Si une macro implicite de boîte blanche lève une exception, pendant la compilation du code principal, l'implicite sera supprimée silencieusement des candidats.

https://docs.scala-lang.org/overviews/macros/blackbox-whitebox.html