summaryrefslogtreecommitdiff
path: root/src/compiler/scala/reflect/reify/Reifier.scala
blob: a5b2fef18c16a769d8ed54f86ee6e80dee5f67e4 (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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package scala.reflect
package reify

import scala.tools.nsc.Global
import scala.reflect.makro.ReificationError
import scala.reflect.makro.UnexpectedReificationError
import scala.reflect.reify.utils.Utils

/** Given a tree or a type, generate a tree that when executed at runtime produces the original tree or type.
 *  See more info in the comments to ``reify'' in scala.reflect.api.Universe.
 *
 *  @author Martin Odersky
 *  @version 2.10
 */
abstract class Reifier extends States
                          with Phases
                          with Errors
                          with Utils {

  val global: Global
  import global._
  import definitions._

  val typer: global.analyzer.Typer
  val universe: Tree
  val mirror: Tree
  val reifee: Any
  val concrete: Boolean

  // needed to seamlessly integrate with standalone utils
  override def getReifier: Reifier { val global: Reifier.this.global.type } =
    this.asInstanceOf[Reifier { val global: Reifier.this.global.type }]
  override def hasReifier = true

  /**
   *  For ``reifee'' and other reification parameters, generate a tree of the form
   *
   *    {
   *      val $u: universe.type = <[ universe ]>
   *      val $m: $u.Mirror = <[ mirror ]>
   *      $u.Expr[T](rtree)       // if data is a Tree
   *      $u.TypeTag[T](rtree)    // if data is a Type
   *    }
   *
   *  where
   *
   *    - `universe` is the tree that represents the universe the result will be bound to
   *    - `mirror` is the tree that represents the mirror the result will be initially bound to
   *    - `rtree` is code that generates `reifee` at runtime.
   *    - `T` is the type that corresponds to `data`.
   *
   *  This is not a method, but a value to indicate the fact that Reifier instances are a one-off.
   */
  lazy val reification: Tree = {
    try {
      // [Eugene] conventional way of doing this?
      if (universe exists (_.isErroneous)) CannotReifyErroneousPrefix(universe)
      if (universe.tpe == null) CannotReifyUntypedPrefix(universe)

      val result = reifee match {
        case tree: Tree =>
          reifyTrace("reifying = ")(if (opt.showTrees) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)
          reifyTrace("reifee is located at: ")(tree.pos)
          reifyTrace("universe = ")(universe)
          reifyTrace("mirror = ")(mirror)
          // [Eugene] conventional way of doing this?
          if (tree exists (_.isErroneous)) CannotReifyErroneousReifee(tree)
          if (tree.tpe == null) CannotReifyUntypedReifee(tree)
          val pipeline = mkReificationPipeline
          val rtree = pipeline(tree)

          // consider the following code snippet
          //
          //   val x = reify { class C; new C }
          //
          // inferred type for x will be C
          // but C ceases to exist after reification so this type is clearly incorrect
          // however, reify is "just" a library function, so it cannot affect type inference
          //
          // hence we crash here even though the reification itself goes well
          // fortunately, all that it takes to fix the error is to cast "new C" to Object
          // so I'm not very much worried about introducing this restriction
          if (tree.tpe exists (sub => sub.typeSymbol.isLocalToReifee))
            CannotReifyReifeeThatHasTypeLocalToReifee(tree)

          val tpe = typer.packedType(tree, NoSymbol)
          val ReifiedType(_, _, tpeSymtab, _, rtpe, tpeReificationIsConcrete) = `package`.reifyType(global)(typer, universe, mirror, tpe, concrete = false)
          state.reificationIsConcrete &= tpeReificationIsConcrete
          state.symtab ++= tpeSymtab
          ReifiedTree(universe, mirror, symtab, rtree, tpe, rtpe, reificationIsConcrete)

        case tpe: Type =>
          reifyTrace("reifying = ")(tpe.toString)
          reifyTrace("universe = ")(universe)
          reifyTrace("mirror = ")(mirror)
          val rtree = reify(tpe)
          ReifiedType(universe, mirror, symtab, tpe, rtree, reificationIsConcrete)

        case _ =>
          throw new Error("reifee %s of type %s is not supported".format(reifee, if (reifee == null) "null" else reifee.getClass.toString))
      }

      // todo. why do we resetAllAttrs?
      //
      // typically we do some preprocessing before reification and
      // the code emitted/moved around during preprocessing is very hard to typecheck, so we leave it as it is
      // however this "as it is" sometimes doesn't make any sense
      //
      // ===example 1===
      // we move a freevar from a nested symbol table to a top-level symbol table,
      // and then the reference to $u becomes screwed up, because nested symbol tables are already typechecked,
      // so we have an $u symbol that points to the nested $u rather than to the top-level one.
      //
      // ===example 2===
      // we inline a freevar by replacing a reference to it, e.g. $u.Apply($u.Select($u.Ident($u.newTermName("$u")), $u.newTermName("Ident")), List($u.Ident($u.newTermName("free$x"))))
      // with its original binding (e.g. $u.Ident("x"))
      // we'd love to typecheck the result, but we cannot do this easily, because $u is external to this tree
      // what's even worse, sometimes $u can point to the top-level symbol table's $u, which doesn't have any symbol/type yet -
      // it's just a ValDef that will be emitted only after the reification is completed
      //
      // hence, the simplest solution is to erase all attrs so that invalid (as well as non-existent) bindings get rebound correctly
      // this is ugly, but it's the best we can do
      //
      // todo. this is a common problem with non-trivial macros in our current macro system
      // needs to be solved some day
      //
      // list of non-hygienic transformations:
      // todo. to be updated
      // [Eugene++] yeah, ugly and extremely brittle, but we do need to do resetAttrs. will be fixed later
      // todo. maybe try `resetLocalAttrs` once the dust settles
      var importantSymbols = Set[Symbol](
        NothingClass, AnyClass, SingletonClass, PredefModule, ScalaRunTimeModule, TypeCreatorClass, TreeCreatorClass, MirrorOfClass,
        BaseUniverseClass, ApiUniverseClass, JavaUniverseClass, ReflectRuntimePackage, ReflectRuntimeCurrentMirror)
      importantSymbols ++= importantSymbols map (_.companionSymbol)
      importantSymbols ++= importantSymbols map (_.moduleClass)
      importantSymbols ++= importantSymbols map (_.linkedClassOfClass)
      def isImportantSymbol(sym: Symbol): Boolean = sym != null && sym != NoSymbol && importantSymbols(sym)
      val untyped = resetAllAttrs(result, leaveAlone = {
        case ValDef(_, u, _, _) if u == nme.UNIVERSE_SHORT => true
        case ValDef(_, m, _, _) if m == nme.MIRROR_SHORT => true
        case tree if symtab.syms contains tree.symbol => true
        case tree if isImportantSymbol(tree.symbol) => true
        case _ => false
      })

      if (reifyCopypaste) {
        if (reifyDebug) println("=============================")
        println(reifiedNodeToString(untyped))
        if (reifyDebug) println("=============================")
      } else {
        reifyTrace("reification = ")(untyped)
      }

      untyped
    } catch {
      case ex: ReificationError =>
        throw ex
      case ex: UnexpectedReificationError =>
        throw ex
      case ex: Throwable =>
        throw new UnexpectedReificationError(defaultErrorPosition, "reification crashed", ex)
    }
  }
}