summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/ast/Reifiers.scala
blob: 04468a096dba7cb3be336760aaf395906b1575f3 (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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
/* NSC -- new Scala compiler
 * Copyright 2005-2011 LAMP/EPFL
 * @author Gilles Dubochet
 */

package scala.tools.nsc
package ast

import symtab._
import Flags._
import scala.reflect.api.Modifier._
import scala.collection.{ mutable, immutable }
import scala.collection.mutable.ListBuffer
import scala.tools.nsc.util.FreshNameCreator
import scala.runtime.ScalaRunTime.{ isAnyVal, isTuple }

/** Given a tree or 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.macro.Context.
 *
 *  @author Martin Odersky
 *  @version 2.10
 */
trait Reifiers { self: Global =>

  def reify(tree: Tree): Tree = {
    class Reifier {
      import definitions._
      import Reifier._

      final val scalaPrefix = "scala."
      final val localPrefix = "$local"
      final val memoizerName = "$memo"

      val reifyDebug = settings.Yreifydebug.value

      private val reifiableSyms = mutable.ArrayBuffer[Symbol]() // the symbols that are reified with the tree
      private val symIndex = mutable.HashMap[Symbol, Int]() // the index of a reifiable symbol in `reifiableSyms`
      private var boundSyms = Set[Symbol]() // set of all symbols that are bound in tree to be reified

      private def definedInLiftedCode(tpe: Type) =
        tpe exists (tp => boundSyms contains tp.typeSymbol)

      private def definedInLiftedCode(sym: Symbol) =
        boundSyms contains sym

      /**
       * Generate tree of the form
       *
       *    { val $mr = scala.reflect.runtime.Mirror
       *      $local1 = new TypeSymbol(owner1, NoPosition, name1)
       *      ...
       *      $localN = new TermSymbol(ownerN, NoPositiion, nameN)
       *      $local1.setInfo(tpe1)
       *      ...
       *      $localN.setInfo(tpeN)
       *      $localN.setAnnotations(annotsN)
       *      rtree
       *    }
       *
       *  where
       *
       *   - `$localI` are free type symbols in the environment, as well as local symbols
       *      of refinement types.
       *   - `tpeI` are the info's of `symI`
       *   - `rtree` is code that generates `data` at runtime, maintaining all attributes.
       *   - `data` is typically a tree or a type.
       */
      def reifyTopLevel(data: Any): Tree = {
        val rtree = reify(data)
        Block(mirrorAlias :: reifySymbolTableSetup, rtree)
      }

      private def isLocatable(sym: Symbol) =
        sym.isPackageClass || sym.owner.isClass || sym.isTypeParameter && sym.paramPos >= 0

      private def registerReifiableSymbol(sym: Symbol): Unit =
        if (!(symIndex contains sym)) {
          sym.owner.ownersIterator find (x => !isLocatable(x)) foreach registerReifiableSymbol
          symIndex(sym) = reifiableSyms.length
          reifiableSyms += sym
        }

      // helper methods

      private def localName(sym: Symbol): TermName =
        newTermName(localPrefix + symIndex(sym))

      private def call(fname: String, args: Tree*): Tree =
        Apply(termPath(fname), args.toList)

      private def mirrorSelect(name: String): Tree =
        termPath(nme.MIRROR_PREFIX + name)

      private def mirrorCall(name: TermName, args: Tree*): Tree =
        call("" + (nme.MIRROR_PREFIX append name), args: _*)

      private def mirrorCall(name: String, args: Tree*): Tree =
        call(nme.MIRROR_PREFIX + name, args: _*)

      private def mirrorFactoryCall(value: Product, args: Tree*): Tree =
        mirrorFactoryCall(value.productPrefix, args: _*)

      private def mirrorFactoryCall(prefix: String, args: Tree*): Tree =
        mirrorCall(prefix, args: _*)

      private def scalaFactoryCall(name: String, args: Tree*): Tree =
        call(scalaPrefix + name + ".apply", args: _*)

      private def mkList(args: List[Tree]): Tree =
        scalaFactoryCall("collection.immutable.List", args: _*)

      private def reifyModifiers(m: Modifiers) =
        mirrorCall("modifiersFromInternalFlags", reify(m.flags), reify(m.privateWithin), reify(m.annotations))

      private def reifyAggregate(name: String, args: Any*) =
        scalaFactoryCall(name, (args map reify).toList: _*)

      /**
       * Reify a list
       */
      private def reifyList(xs: List[Any]): Tree =
        mkList(xs map reify)

      /**
       * Reify an array
       */
      private def reifyArray(xs: Array[_]): Tree =
        // @xeno.by: doesn't work for Array(LiteralAnnotArg(...))
        // because we cannot generate manifests for path-dependent types
        scalaFactoryCall(nme.Array, xs map reify: _*)

      /** Reify a name */
      private def reifyName(name: Name) =
        mirrorCall(if (name.isTypeName) "newTypeName" else "newTermName", Literal(Constant(name.toString)))

      private def isFree(sym: Symbol) =
        !(symIndex contains sym)

      /**
       * Reify a reference to a symbol
       */
      private def reifySymRef(sym: Symbol): Tree = {
        symIndex get sym match {
          case Some(idx) =>
            Ident(localName(sym))
          case None =>
            if (sym == NoSymbol)
              mirrorSelect("NoSymbol")
            else if (sym == RootPackage)
              mirrorSelect("definitions.RootPackage")
            else if (sym == RootClass)
              mirrorSelect("definitions.RootClass")
            else if (sym == EmptyPackage)
              mirrorSelect("definitions.EmptyPackage")
            else if (sym.isModuleClass)
              Select(reifySymRef(sym.sourceModule), "moduleClass")
            else if (sym.isStatic && sym.isClass)
              mirrorCall("staticClass", reify(sym.fullName))
            else if (sym.isStatic && sym.isModule)
              mirrorCall("staticModule", reify(sym.fullName))
            else if (isLocatable(sym))
              if (sym.isTypeParameter)
                mirrorCall("selectParam", reify(sym.owner), reify(sym.paramPos))
              else {
                if (reifyDebug) println("locatable: " + sym + " " + sym.isPackageClass + " " + sym.owner + " " + sym.isTypeParameter)
                val rowner = reify(sym.owner)
                val rname = reify(sym.name.toString)
                if (sym.isType)
                  mirrorCall("selectType", rowner, rname)
                else if (sym.isMethod && sym.owner.isClass && sym.owner.info.decl(sym.name).isOverloaded) {
                  val index = sym.owner.info.decl(sym.name).alternatives indexOf sym
                  assert(index >= 0, sym)
                  mirrorCall("selectOverloadedMethod", rowner, rname, reify(index))
                } else
                  mirrorCall("selectTerm", rowner, rname)
              }
            else {
              if (sym.isTerm) {
                if (reifyDebug) println("Free: " + sym)
                val symtpe = lambdaLift.boxIfCaptured(sym, sym.tpe, erasedTypes = false)
                def markIfCaptured(arg: Ident): Tree =
                  if (sym.isCapturedVariable) referenceCapturedVariable(arg) else arg
                mirrorCall("newFreeVar", reify(sym.name.toString), reify(symtpe), markIfCaptured(Ident(sym)))
              } else {
                if (reifyDebug) println("Late local: " + sym)
                registerReifiableSymbol(sym)
                reifySymRef(sym)
              }
            }
        }
      }

      /**
       * reify the creation of a symbol
       */
      private def reifySymbolDef(sym: Symbol): Tree = {
        if (reifyDebug) println("reify sym def " + sym)

        ValDef(NoMods, localName(sym), TypeTree(),
          Apply(
            Select(reify(sym.owner), "newNestedSymbol"),
            List(reify(sym.name), reify(sym.pos), Literal(Constant(sym.flags)), Literal(Constant(sym.isClass)))
          )
        )
      }

      /**
       * Generate code to add type and annotation info to a reified symbol
       */
      private def fillInSymbol(sym: Symbol): Tree = {
        val rset = Apply(Select(reifySymRef(sym), nme.setTypeSignature), List(reifyType(sym.info)))
        if (sym.annotations.isEmpty) rset
        else Apply(Select(rset, nme.setAnnotations), List(reify(sym.annotations)))
      }

      /** Reify a scope */
      private def reifyScope(scope: Scope): Tree = {
        scope foreach registerReifiableSymbol
        mirrorCall(nme.newScopeWith, scope.toList map reifySymRef: _*)
      }

      /** Reify a list of symbols that need to be created */
      private def reifySymbols(syms: List[Symbol]): Tree = {
        syms foreach registerReifiableSymbol
        mkList(syms map reifySymRef)
      }

      /** Reify a type that defines some symbols */
      private def reifyTypeBinder(value: Product, bound: List[Symbol], underlying: Type): Tree =
        mirrorFactoryCall(value, reifySymbols(bound), reify(underlying))

      /** Reify a type */
      private def reifyType(tpe0: Type): Tree = {
        val tpe = tpe0.normalize

        if (tpe.isErroneous)
          CannotReifyErroneousType(tpe)
        if (definedInLiftedCode(tpe))
          CannotReifyTypeInvolvingBoundType(tpe)

        val tsym = tpe.typeSymbol
        if (tsym.isClass && tpe == tsym.typeConstructor && tsym.isStatic)
          Select(reifySymRef(tpe.typeSymbol), nme.asTypeConstructor)
        else tpe match {
          case t @ NoType =>
            reifyMirrorObject(t)
          case t @ NoPrefix =>
            reifyMirrorObject(t)
          case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic =>
            mirrorCall(nme.thisModuleType, reify(clazz.fullName))
          case t @ RefinedType(parents, decls) =>
            registerReifiableSymbol(tpe.typeSymbol)
            mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol))
          case t @ ClassInfoType(parents, decls, clazz) =>
            registerReifiableSymbol(clazz)
            mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol))
          case t @ ExistentialType(tparams, underlying) =>
            reifyTypeBinder(t, tparams, underlying)
          case t @ PolyType(tparams, underlying) =>
            reifyTypeBinder(t, tparams, underlying)
          case t @ MethodType(params, restpe) =>
            reifyTypeBinder(t, params, restpe)
          case t @ AnnotatedType(anns, underlying, selfsym) =>
            val saved1 = reifySymbols
            val saved2 = reifyTypes

            try {
              // one more quirk of reifying annotations
              //
              // when reifying AnnotatedTypes we need to reify all the types and symbols of inner ASTs
              // that's because a lot of logic expects post-typer trees to have non-null tpes
              //
              // Q: reified trees are pre-typer, so there's shouldn't be a problem.
              //    reflective typechecker will fill in missing symbols and types, right?
              // A: actually, no. annotation ASTs live inside AnnotatedTypes,
              //    and insides of the types is the place where typechecker doesn't look.
              reifySymbols = true
              reifyTypes = true
              if (reifyDebug) println("reify AnnotatedType: " + tpe)
              reifyProductUnsafe(tpe)
            } finally {
              reifySymbols = saved1
              reifyTypes = saved2
            }
          case _ =>
            reifyProductUnsafe(tpe)
        }
      }

      var reifySymbols = false
      var reifyTypes = false

      /** Preprocess a tree before reification */
      private def trimTree(tree: Tree): Tree = {
        def trimSyntheticCaseClassMembers(deff: Tree, stats: List[Tree]) = {
          var stats1 = stats filterNot (stat => stat.isDef && {
            if (stat.symbol.isCaseAccessorMethod && reifyDebug) println("discarding case accessor method: " + stat)
            stat.symbol.isCaseAccessorMethod
          })
          stats1 = stats1 filterNot (memberDef => memberDef.isDef && {
            val isSynthetic = memberDef.symbol.isSynthetic
            // @xeno.by: this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass)
            // that's why I replace the check with an assumption that all synthetic members are, in fact, generated of case classes
//            val isCaseMember = deff.symbol.isCaseClass || deff.symbol.companionClass.isCaseClass
            val isCaseMember = true
            if (isSynthetic && isCaseMember && reifyDebug) println("discarding case class synthetic def: " + memberDef)
            isSynthetic && isCaseMember
          })
          stats1 = stats1 map {
            case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isCaseAccessor =>
              if (reifyDebug) println("resetting visibility of case accessor field: " + valdef)
              val Modifiers(flags, privateWithin, annotations) = mods
              val flags1 = flags & ~Flags.LOCAL & ~Flags.PRIVATE
              val mods1 = Modifiers(flags1, privateWithin, annotations)
              ValDef(mods1, name, tpt, rhs).copyAttrs(valdef)
            case stat =>
              stat
          }
          stats1
        }

        def trimSyntheticCaseClassCompanions(stats: List[Tree]) =
          stats diff (stats collect { case moddef: ModuleDef => moddef } filter (moddef => {
            val isSynthetic = moddef.symbol.isSynthetic
            // @xeno.by: this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass)
            // that's why I replace the check with an assumption that all synthetic modules are, in fact, companions of case classes
//            val isCaseCompanion = moddef.symbol.companionClass.isCaseClass
            val isCaseCompanion = true
            // @xeno.by: we also have to do this ugly hack for the very same reason described above
            // normally this sort of stuff is performed in reifyTree, which binds related symbols, however, local companions will be out of its reach
            if (reifyDebug) println("boundSym: "+  moddef.symbol)
            boundSyms += moddef.symbol
            if (isSynthetic && isCaseCompanion && reifyDebug) println("discarding synthetic case class companion: " + moddef)
            isSynthetic && isCaseCompanion
          }))

        tree match {
          case tree if tree.isErroneous =>
            tree
          case ta @ TypeApply(hk, ts) =>
            def isErased(tt: TypeTree) = tt.tpe != null && definedInLiftedCode(tt.tpe) && tt.original == null
            val discard = ts collect { case tt: TypeTree => tt } exists isErased
            if (reifyDebug && discard) println("discarding TypeApply: " + tree)
            if (discard) hk else ta
          case classDef @ ClassDef(mods, name, params, impl) =>
            val Template(parents, self, body) = impl
            val body1 = trimSyntheticCaseClassMembers(classDef, body)
            var impl1 = Template(parents, self, body1).copyAttrs(impl)
            ClassDef(mods, name, params, impl1).copyAttrs(classDef)
          case moduledef @ ModuleDef(mods, name, impl) =>
            val Template(parents, self, body) = impl
            val body1 = trimSyntheticCaseClassMembers(moduledef, body)
            var impl1 = Template(parents, self, body1).copyAttrs(impl)
            ModuleDef(mods, name, impl1).copyAttrs(moduledef)
          case template @ Template(parents, self, body) =>
            val body1 = trimSyntheticCaseClassCompanions(body)
            Template(parents, self, body1).copyAttrs(template)
          case block @ Block(stats, expr) =>
            val stats1 = trimSyntheticCaseClassCompanions(stats)
            Block(stats1, expr).copyAttrs(block)
          case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isLazy =>
            if (reifyDebug) println("dropping $lzy in lazy val's name: " + tree)
            val name1 = if (name endsWith nme.LAZY_LOCAL) name dropRight nme.LAZY_LOCAL.length else name
            ValDef(mods, name1, tpt, rhs).copyAttrs(valdef)
          case unapply @ UnApply(fun, args) =>
            def extractExtractor(tree: Tree): Tree = {
              val Apply(fun, args) = tree
              args match {
                case List(Ident(special)) if special == nme.SELECTOR_DUMMY =>
                  val Select(extractor, flavor) = fun
                  assert(flavor == nme.unapply || flavor == nme.unapplySeq)
                  extractor
                case _ =>
                  extractExtractor(fun)
              }
            }

            if (reifyDebug) println("unapplying unapply: " + tree)
            val fun1 = extractExtractor(fun)
            Apply(fun1, args).copyAttrs(unapply)
          case _ =>
            tree
        }
      }

      /** Reify a tree */
      private def reifyTree(tree0: Tree): Tree = {
        val tree = trimTree(tree0)

        var rtree = tree match {
          case tree if tree.isErroneous =>
            CannotReifyErroneousTree(tree)
          case self.EmptyTree =>
            reifyMirrorObject(EmptyTree)
          case self.emptyValDef =>
            mirrorSelect(nme.emptyValDef)
          case This(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) =>
            reifyFree(tree)
          case Ident(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) =>
            if (tree.symbol.isVariable && tree.symbol.owner.isTerm) {
              if (reifyDebug) println("captured variable: " + tree.symbol)
              captureVariable(tree.symbol) // Note order dependency: captureVariable needs to come before reifyTree here.
              mirrorCall("Select", reifyFree(tree), reifyName(nme.elem))
            } else reifyFree(tree)
          case tt: TypeTree if (tt.tpe != null) =>
            reifyTypeTree(tt)
          case Literal(constant @ Constant(tpe: Type)) if boundSyms exists (tpe contains _) =>
            CannotReifyClassOfBoundType(tree, tpe)
          case Literal(constant @ Constant(sym: Symbol)) if boundSyms contains sym =>
            CannotReifyClassOfBoundEnum(tree, constant.tpe)
          case tree if tree.isDef =>
            if (reifyDebug) println("boundSym: %s of type %s".format(tree.symbol, (tree.productIterator.toList collect { case tt: TypeTree => tt } headOption).getOrElse(TypeTree(tree.tpe))))
            boundSyms += tree.symbol

            bindRelatedSymbol(tree.symbol.sourceModule, "sourceModule")
            bindRelatedSymbol(tree.symbol.moduleClass, "moduleClass")
            bindRelatedSymbol(tree.symbol.companionClass, "companionClass")
            bindRelatedSymbol(tree.symbol.companionModule, "companionModule")
            Some(tree.symbol) collect { case termSymbol: TermSymbol => bindRelatedSymbol(termSymbol.referenced, "referenced") }
            def bindRelatedSymbol(related: Symbol, name: String): Unit =
              if (related != null && related != NoSymbol) {
                if (reifyDebug) println("boundSym (" + name + "): " + related)
                boundSyms += related
              }

            val prefix = tree.productPrefix
            val elements = (tree.productIterator map {
              // annotations exist in two flavors:
              // 1) pre-typer ones that populate: a) Modifiers, b) Annotated nodes (irrelevant in this context)
              // 2) post-typer ones that dwell inside: a) sym.annotations, b) AnnotatedTypes (irrelevant in this context)
              //
              // here we process Modifiers that are involved in deftrees
              // AnnotatedTypes get reified elsewhere (currently, in ``reifyTypeTree'')
              case Modifiers(flags, privateWithin, annotations) =>
                assert(annotations.isEmpty) // should've been eliminated by the typer
                val postTyper = tree.symbol.annotations filter (_.original != EmptyTree)
                if (reifyDebug && !postTyper.isEmpty) println("reify symbol annotations for %s: %s".format(tree.symbol, tree.symbol.annotations))
                val preTyper = postTyper map toPreTyperAnnotation
                Modifiers(flags, privateWithin, preTyper)
              case x =>
                x
            }).toList
            reifyProduct(prefix, elements)
          case _ =>
            reifyProduct(tree)
        }

        // usually we don't reify symbols/types, because they can be re-inferred during subsequent reflective compilation
        // however, reification of AnnotatedTypes is special. see ``reifyType'' to find out why.
        if (reifySymbols && tree.hasSymbol) {
          if (reifyDebug) println("reifying symbol %s for tree %s".format(tree.symbol, tree))
          rtree = Apply(Select(rtree, nme.setSymbol), List(reifySymRef(tree.symbol)))
        }
        if (reifyTypes && tree.tpe != null) {
          if (reifyDebug) println("reifying type %s for tree %s".format(tree.tpe, tree))
          rtree = Apply(Select(rtree, nme.setType), List(reifyType(tree.tpe)))
        }

        rtree
      }

      /** Reify pre-typer representation of a type.
       *
       *  NB: This is the trickiest part of reification!
       *
       *  In most cases, we're perfectly fine to reify a Type itself (see ``reifyType'').
       *  However if the type involves a symbol declared inside the quasiquote (i.e. registered in ``boundSyms''),
       *  then we cannot reify it, or otherwise subsequent reflective compilation will fail.
       *
       *  Why will it fail? Because reified deftrees (e.g. ClassDef(...)) will generate fresh symbols during that compilation,
       *  so naively reified symbols will become out of sync, which brings really funny compilation errors and/or crashes, e.g.:
       *  https://issues.scala-lang.org/browse/SI-5230
       *
       *  To deal with this unpleasant fact, we need to fall back from types to equivalent trees (after all, parser trees don't contain any types, just trees, so it should be possible).
       *  Luckily, these original trees get preserved for us in the ``original'' field when Trees get transformed into TypeTrees.
       *  And if an original of a type tree is empty, we can safely assume that this type is non-essential (e.g. was inferred/generated by the compiler).
       *  In that case the type can be omitted (e.g. reified as an empty TypeTree), since it will be inferred again later on.
       *
       *  An important property of the original is that it isn't just a pre-typer tree.
       *  It's actually kind of a post-typer tree with symbols assigned to its Idents (e.g. Ident("List") will contain a symbol that points to immutable.this.List).
       *  This is very important, since subsequent reflective compilation won't have to resolve these symbols.
       *  In general case, such resolution cannot be performed, since reification doesn't preserve lexical context,
       *  which means that reflective compilation won't be aware of, say, imports that were provided when the reifee has been compiled.
       *
       *  This workaround worked surprisingly well and allowed me to fix several important reification bugs, until the abstraction has leaked.
       *  Suddenly I found out that in certain contexts original trees do not contain symbols, but are just parser trees.
       *  To the moment I know only one such situation: typedAnnotations does not typecheck the annotation in-place, but rather creates new trees and typechecks them, so the original remains symless.
       *  This is laboriously worked around in the code below. I hope this will be the only workaround in this department.
       */
      private def reifyTypeTree(tt: TypeTree): Tree = {
        if (definedInLiftedCode(tt.tpe)) {
          if (reifyDebug) println("reifyTypeTree, defined in lifted code: " + tt.tpe)
          if (tt.original != null) {
            val annotations = tt.tpe filter { _.isInstanceOf[AnnotatedType] } collect { case atp: AnnotatedType => atp.annotations } flatten
            val annmap = annotations map { ann => (ann.original, ann) } toMap

            // annotations exist in two flavors:
            // 1) pre-typer ones that populate: a) Modifiers (irrelevant in this context), b) Annotated nodes
            // 2) post-typer ones that dwell inside: a) sym.annotations (irrelevant in this context), b) AnnotatedTypes
            //
            // here we process AnnotatedTypes, since only they can be involved in TypeTrees
            // Modifiers get reified elsewhere (currently, in the "isDef" case of ``reifyTree'')
            //
            // the problem with annotations is that their originals don't preserve any symbols at all
            // read the comment to this method to find out why it's bad
            // that's why we transplant typechecked, i.e. symful, annotations onto original trees
            class AnnotationFixup extends self.Transformer {
              override def transform(tree: Tree) = tree match {
                case Annotated(ann0, args) =>
                  assert(annmap contains ann0)
                  val ann1 = annmap(ann0)
                  val ann = toPreTyperAnnotation(ann1)
                  Annotated(ann, transform(args))
                case _ =>
                  tree
              }
            }

            if (reifyDebug) println("verdict: essential, reify as original")
            val patchedOriginal = new AnnotationFixup().transform(tt.original)
            reifyTree(patchedOriginal)
          } else {
            // type is deemed to be non-essential
            // erase it and hope that subsequent reflective compilation will be able to recreate it again
            if (reifyDebug) println("verdict: non-essential, discard")
            mirrorCall("TypeTree")
          }
        } else {
          var rtt = mirrorCall(nme.TypeTree, reifyType(tt.tpe))
          // @xeno.by: temporarily disabling reification of originals
          // subsequent reflective compilation will try to typecheck them
          // and this means that the reifier has to do additional efforts to ensure that this will succeed
          // additional efforts + no clear benefit = will be implemented later
//          if (tt.original != null) {
//            val setOriginal = Select(rtt, newTermName("setOriginal"))
//            val reifiedOriginal = reify(tt.original)
//            rtt = Apply(setOriginal, List(reifiedOriginal))
//          }
          rtt
        }
      }

      /** Reify post-typer representation of an annotation */
      private def reifyAnnotation(ann: AnnotationInfo): Tree =
        // @xeno.by: if you reify originals, you get SO when trying to reify AnnotatedTypes, so screw it - after all, it's not that important
        mirrorFactoryCall("AnnotationInfo", reifyType(ann.atp), reifyList(ann.args), reify(ann.assocs))

      /** Reify pre-typer representation of an annotation.
       *  The trick here is to retain the symbols that have been populated during typechecking of the annotation.
       *  If we do not do that, subsequent reflective compilation will fail.
       */
      private def toPreTyperAnnotation(ann: AnnotationInfo): Tree = {
        if (definedInLiftedCode(ann.atp)) {
          // todo. deconstruct reifiable tree from ann.original and ann.args+ann.assocs
          //
          // keep in mind that we can't simply use ann.original, because its args are symless
          // which means that any imported symbol (e.g. List) will crash subsequent reflective compilation
          // hint: if I had enough time, I'd try to extract reifiable annotation type from ann.original
          // and to apply its constructor to ann.args (that are symful, i.e. suitable for reification)
          //
          // also, if we pursue the route of reifying annotations defined in lifted code
          // we should think about how to provide types for all nodes of the return value
          // this will be necessary for reifying AnnotatedTypes, since ASTs inside ATs must all have non-null tpes
          // an alternative would be downgrading ATs to Annotated nodes, but this needs careful thinking
          // for now I just leave this as an implementation restriction
          CannotReifyAnnotationInvolvingBoundType(ann)
        } else {
          val args = if (ann.assocs.isEmpty) {
            ann.args
          } else {
            def toScalaAnnotation(jann: ClassfileAnnotArg): Tree = jann match {
              case LiteralAnnotArg(const) =>
                Literal(const)
              case ArrayAnnotArg(arr) =>
                Apply(Ident(definitions.ArrayModule), arr.toList map toScalaAnnotation)
              case NestedAnnotArg(ann) =>
                toPreTyperAnnotation(ann)
            }

            ann.assocs map { case (nme, arg) => AssignOrNamedArg(Ident(nme), toScalaAnnotation(arg)) }
          }

          New(ann.atp, args: _*)
        }
      }

      /**
       * Reify a free reference. The result will be either a mirror reference
       *  to a global value, or else a mirror Literal.
       */
      private def reifyFree(tree: Tree): Tree = tree match {
        case This(_) if tree.symbol.isClass && !tree.symbol.isModuleClass =>
          val sym = tree.symbol
          if (reifyDebug) println("This for %s, reified as freeVar".format(sym))
          if (reifyDebug) println("Free: " + sym)
          val freeVar = mirrorCall("newFreeVar", reify(sym.name.toString), reify(sym.tpe), This(sym))
          mirrorCall(nme.Ident, freeVar)
        case This(_) =>
          if (reifyDebug) println("This for %s, reified as This".format(tree.symbol))
          mirrorCall(nme.This, reifySymRef(tree.symbol))
        case _ =>
          mirrorCall(nme.Ident, reifySymRef(tree.symbol))
      }

      // todo: consider whether we should also reify positions
      private def reifyPosition(pos: Position): Tree =
        reifyMirrorObject(NoPosition)

      // !!! we must eliminate these casts.
      private def reifyProductUnsafe(x: Any): Tree =
        if (x.isInstanceOf[Product]) reifyProduct(x.asInstanceOf[Product])
        else throw new Exception("%s of type %s cannot be cast to Product".format(x, x.getClass))
      private def reifyProduct(x: Product): Tree =
        reifyProduct(x.productPrefix, x.productIterator.toList)
      private def reifyProduct(prefix: String, elements: List[Any]): Tree = {
        // @xeno.by: reflection would be more robust, but, hey, this is a hot path
        if (prefix.startsWith("Tuple")) reifyAggregate(prefix, elements: _*)
        else mirrorCall(prefix, (elements map reify): _*)
      }

      /**
       * Reify a case object defined in Mirror
       */
      private def reifyMirrorObject(name: String): Tree = mirrorSelect(name)
      private def reifyMirrorObject(x: Product): Tree   = reifyMirrorObject(x.productPrefix)

      private def isReifiableConstant(value: Any) = value match {
        case null      => true  // seems pretty reifable to me?
        case _: String => true
        case _         => isAnyVal(value)
      }

      /** Reify an arbitary value */
      private def reify(value: Any): Tree = value match {
        case tree: Tree         => reifyTree(tree)
        case sym: Symbol        => reifySymRef(sym)
        case tpe: Type          => reifyType(tpe)
        case xs: List[_]        => reifyList(xs)
        case xs: Array[_]       => reifyArray(xs)
        case scope: Scope       => reifyScope(scope)
        case x: Name            => reifyName(x)
        case x: Position        => reifyPosition(x)
        case x: Modifiers       => reifyModifiers(x)
        case x: AnnotationInfo  => reifyAnnotation(x)
        case _ =>
          if (isReifiableConstant(value)) Literal(Constant(value))
          else reifyProductUnsafe(value)
      }

      /**
       * An (unreified) path that refers to definition with given fully qualified name
       *  @param mkName   Creator for last portion of name (either TermName or TypeName)
       */
      private def path(fullname: String, mkName: String => Name): Tree = {
        val parts = fullname split "\\."
        val prefixParts = parts.init
        val lastName = mkName(parts.last)
        if (prefixParts.isEmpty) Ident(lastName)
        else {
          val prefixTree = ((Ident(prefixParts.head): Tree) /: prefixParts.tail)(Select(_, _))
          Select(prefixTree, lastName)
        }
      }

      /** An (unreified) path that refers to term definition with given fully qualified name */
      private def termPath(fullname: String): Tree = path(fullname, newTermName)

      /** An (unreified) path that refers to type definition with given fully qualified name */
      private def typePath(fullname: String): Tree = path(fullname, newTypeName)

      private def mirrorAlias =
        ValDef(NoMods, nme.MIRROR_SHORT, SingletonTypeTree(termPath(fullnme.MirrorPackage)), termPath(fullnme.MirrorPackage))

      /**
       * Generate code that generates a symbol table of all symbols registered in `reifiableSyms`
       */
      private def reifySymbolTableSetup: List[Tree] = {
        val symDefs, fillIns = new mutable.ArrayBuffer[Tree]
        var i = 0
        while (i < reifiableSyms.length) {
          // fillInSymbol might create new reifiableSyms, that's why this is done iteratively
          symDefs += reifySymbolDef(reifiableSyms(i))
          fillIns += fillInSymbol(reifiableSyms(i))
          i += 1
        }

        symDefs.toList ++ fillIns.toList
      }
    } // end of Reifier

    object Reifier {
      def CannotReifyPreTyperTree(tree: Tree) = {
        val msg = "pre-typer trees are not supported, consider typechecking the tree before passing it to the reifier"
        throw new ReifierError(tree.pos, msg)
      }

      def CannotReifyErroneousTree(tree: Tree) = {
        val msg = "erroneous trees are not supported, make sure that your tree typechecks successfully before passing it to the reifier"
        throw new ReifierError(tree.pos, msg)
      }

      def CannotReifyErroneousType(tpe: Type) = {
        val msg = "erroneous types are not supported, make sure that your tree typechecks successfully before passing it to the reifier"
        throw new ReifierError(NoPosition, msg)
      }

      def CannotReifyClassOfBoundType(tree: Tree, tpe: Type) = {
        val msg = "implementation restriction: cannot reify classOf[%s] which refers to a type declared inside the block being reified".format(tpe)
        throw new ReifierError(tree.pos, msg)
      }

      def CannotReifyClassOfBoundEnum(tree: Tree, tpe: Type) = {
        val msg = "implementation restriction: cannot reify classOf[%s] which refers to an enum declared inside the block being reified".format(tpe)
        throw new ReifierError(tree.pos, msg)
      }

      def CannotReifyTypeInvolvingBoundType(tpe: Type) = {
        val msg = "implementation restriction: cannot reify type %s which involves a symbol declared inside the block being reified".format(tpe)
        throw new ReifierError(NoPosition, msg)
      }

      def CannotReifyAnnotationInvolvingBoundType(ann: AnnotationInfo) = {
        val msg = "implementation restriction: cannot reify annotation @%s which involves a symbol declared inside the block being reified".format(ann)
        throw new ReifierError(ann.original.pos, msg)
      }
    } // end of Reifier

    // begin reify
    import Reifier._
    if (tree.tpe != null) {
      val saved = printTypings
      try {
        val reifyDebug = settings.Yreifydebug.value
        val debugTrace = util.trace when reifyDebug
        debugTrace("transforming = ")(if (settings.Xshowtrees.value) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)
        debugTrace("transformed = ") {
          val reifier = new Reifier()
          val untyped = reifier.reifyTopLevel(tree)

          val reifyCopypaste = settings.Yreifycopypaste.value
          if (reifyCopypaste) {
            if (reifyDebug) println("=======================")
            println(reifiedNodeToString(untyped))
            if (reifyDebug) println("=======================")
          }

          untyped
        }
      } finally {
        printTypings = saved
      }
    } else {
      CannotReifyPreTyperTree(tree)
    }
  }

  /** A throwable signalling a reification error */
  class ReifierError(var pos: Position, val msg: String) extends Throwable(msg) {
    def this(msg: String) = this(NoPosition, msg)
  }
}