summaryrefslogtreecommitdiff
path: root/src/compiler/scala/reflect/reify/Taggers.scala
blob: e4c3d02f2259d1457b20abfa5ef9a67157fdf9ba (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
90
91
92
93
94
95
96
97
98
99
100
101
102
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._
  import treeBuild._

  val coreTags = Map(
    ByteTpe -> nme.Byte,
    ShortTpe -> nme.Short,
    CharTpe -> nme.Char,
    IntTpe -> nme.Int,
    LongTpe -> nme.Long,
    FloatTpe -> nme.Float,
    DoubleTpe -> nme.Double,
    BooleanTpe -> nme.Boolean,
    UnitTpe -> nme.Unit,
    AnyTpe -> nme.Any,
    ObjectTpe -> nme.Object,
    NothingTpe -> nme.Nothing,
    NullTpe -> nme.Null)

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

  def materializeTypeTag(universe: Tree, mirror: Tree, tpe: Type, concrete: Boolean): Tree = {
    if (universe.symbol == MacroContextUniverse && mirror == EmptyTree) {
      import scala.reflect.makro.runtime.ContextReifiers
      import language.implicitConversions
      implicit def context2contextreifiers(c0: Context) : ContextReifiers { val c: c0.type } = new { val c: c0.type = c0 } with ContextReifiers
      val Select(prefix, _) = universe
      c.materializeTypeTagForMacroContext(prefix, tpe, concrete)
    } else {
      val tagType = if (concrete) TypeTagClass else AbsTypeTagClass
      val unaffiliatedTagTpe = TypeRef(BaseUniverseClass.asTypeConstructor, tagType, List(tpe))
      val unaffiliatedTag = c.inferImplicitValue(unaffiliatedTagTpe, silent = true, withMacrosDisabled = true)
      unaffiliatedTag match {
        case success if !success.isEmpty =>
          Apply(Select(success, nme.in), List(mirror orElse mkDefaultMirrorRef(c.universe)(universe, c.callsiteTyper)))
        case _ =>
          val tagModule = if (concrete) TypeTagModule else AbsTypeTagModule
          materializeTag(universe, tpe, tagModule, c.reifyType(universe, mirror, tpe, 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(universe: Tree, mirror: Tree, expr: Tree): Tree = {
    val result = translatingReificationErrors(c.reifyTree(universe, mirror, 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")
  }
}