summaryrefslogtreecommitdiff
path: root/src/compiler/scala/reflect/reify/Taggers.scala
blob: d8da7e74ff7d00f6be38a2453d1e19dbab83e17a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package scala.reflect.reify

import scala.reflect.makro.{ReificationError, UnexpectedReificationError}
import scala.reflect.makro.runtime.Context

abstract class Taggers {
  val c: Context

  import c.universe._
  import definitions._

  val coreTags = Map(
    ByteClass.asType -> newTermName("Byte"),
    ShortClass.asType -> newTermName("Short"),
    CharClass.asType -> newTermName("Char"),
    IntClass.asType -> newTermName("Int"),
    LongClass.asType -> newTermName("Long"),
    FloatClass.asType -> newTermName("Float"),
    DoubleClass.asType -> newTermName("Double"),
    BooleanClass.asType -> newTermName("Boolean"),
    UnitClass.asType -> newTermName("Unit"),
    AnyClass.asType -> newTermName("Any"),
    ObjectClass.asType -> newTermName("Object"),
    NothingClass.asType -> newTermName("Nothing"),
    NullClass.asType -> newTermName("Null"),
    StringClass.asType -> newTermName("String"))

  // todo. the following two methods won't be necessary once we implement implicit macro generators for tags

  def materializeArrayTag(prefix: Tree, tpe: Type): Tree =
    materializeClassTag(prefix, tpe)

  def materializeClassTag(prefix: Tree, tpe: Type): Tree =
    materializeTag(prefix, tpe, ClassTagModule, {
      val runtimeClass = c.reifyRuntimeClass(tpe, concrete = true)
      val factory = TypeApply(Select(Ident(ClassTagModule), "apply"), List(TypeTree(tpe)))
      Apply(factory, List(runtimeClass))
    })

  def materializeTypeTag(prefix: Tree, tpe: Type, concrete: Boolean): Tree = {
    val tagModule = if (concrete) ConcreteTypeTagModule else TypeTagModule
    materializeTag(prefix, tpe, tagModule, c.reifyType(prefix, tpe, dontSpliceAtTopLevel = true, concrete = concrete))
  }

  private def materializeTag(prefix: Tree, tpe: Type, tagModule: Symbol, materializer: => Tree): Tree = {
    val result =
      tpe match {
        case coreTpe if coreTags contains coreTpe =>
          val ref = if (tagModule.owner.isPackageClass) Ident(tagModule) else Select(prefix, tagModule.name)
          Select(ref, coreTags(coreTpe))
        case _ =>
          translatingReificationErrors(materializer)
      }
    try c.typeCheck(result)
    catch { case terr @ c.TypeError(pos, msg) => failTag(result, terr) }
  }

  def materializeExpr(prefix: Tree, expr: Tree): Tree = {
    val result = translatingReificationErrors(c.reifyTree(prefix, expr))
    try c.typeCheck(result)
    catch { case terr @ c.TypeError(pos, msg) => failExpr(result, terr) }
  }

  private def translatingReificationErrors(materializer: => Tree): Tree = {
    try materializer
    catch {
      case ReificationError(pos, msg) =>
        c.error(pos.asInstanceOf[c.Position], msg) // this cast is a very small price for the sanity of exception handling
        EmptyTree
      case UnexpectedReificationError(pos, err, cause) if cause != null =>
        throw cause
    }
  }

  private def failTag(result: Tree, reason: Any): Nothing = {
    val Apply(TypeApply(fun, List(tpeTree)), _) = c.macroApplication
    val tpe = tpeTree.tpe
    val PolyType(_, MethodType(_, tagTpe)) = fun.tpe
    val tagModule = tagTpe.typeSymbol.companionSymbol
    if (c.compilerSettings.contains("-Xlog-implicits"))
      c.echo(c.enclosingPosition, s"cannot materialize ${tagModule.name}[$tpe] as $result because:\n$reason")
    c.abort(c.enclosingPosition, "No %s available for %s".format(tagModule.name, tpe))
  }

  private def failExpr(result: Tree, reason: Any): Nothing = {
    val Apply(_, expr :: Nil) = c.macroApplication
    c.abort(c.enclosingPosition, s"Cannot materialize $expr as $result because:\n$reason")
  }
}