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
|
package scala.reflect.reify
import scala.reflect.macros.{ReificationException, UnexpectedReificationException, TypecheckException}
import scala.reflect.macros.runtime.Context
abstract class Taggers {
val c: Context
import c.universe._
import definitions._
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,
AnyValTpe -> nme.AnyVal,
AnyRefTpe -> nme.AnyRef,
ObjectTpe -> nme.Object,
NothingTpe -> nme.Nothing,
NullTpe -> nme.Null)
def materializeClassTag(tpe: Type): Tree = {
val tagModule = ClassTagModule
materializeTag(EmptyTree, 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 = {
val tagType = if (concrete) TypeTagClass else WeakTypeTagClass
// what we need here is to compose a type Universe # TypeTag[$tpe]
// to look for an implicit that conforms to this type
// that's why neither appliedType(tagType, List(tpe)) aka TypeRef(TypeTagsClass.thisType, tagType, List(tpe))
// nor TypeRef(ApiUniverseClass.thisType, tagType, List(tpe)) won't fit here
// scala> :type -v def foo: scala.reflect.api.Universe#TypeTag[Int] = ???
// NullaryMethodType(TypeRef(pre = TypeRef(TypeSymbol(Universe)), TypeSymbol(TypeTag), args = List($tpe))))
val unaffiliatedTagTpe = TypeRef(ApiUniverseClass.typeConstructor, 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 WeakTypeTagModule
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.isTopLevel) Ident(tagModule) else Select(prefix, tagModule.name)
Select(ref, coreTags(coreTpe))
case _ =>
translatingReificationErrors(materializer)
}
try c.typeCheck(result)
catch { case terr @ TypecheckException(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 @ TypecheckException(pos, msg) => failExpr(result, terr) }
}
private def translatingReificationErrors(materializer: => Tree): Tree = {
try materializer
catch {
case ReificationException(pos, msg) =>
c.error(pos.asInstanceOf[c.Position], msg) // this cast is a very small price for the sanity of exception handling
EmptyTree
case UnexpectedReificationException(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")
}
}
|