summaryrefslogblamecommitdiff
path: root/src/compiler/scala/reflect/reify/codegen/GenTrees.scala
blob: b97bf6b0cd7efb28d326138f444a023df3e32c41 (plain) (tree)
1
2
3
4
5
6
7
8
9


                           
                

                  
                 
                      
 
                                                               



                                                                                                                      
 




























                                                                                                                        
                              
                                    

                                          
                                    






                                







                                                                               



                                                                                                                     
                                                                       


                                                                                    
                                                                  

     


         

                                                                                                                                      


                                              
                                 
                                                   


                                                                    







                                                                                                                           

                                                           


                                                                                                                    

                                                                                                             


                                                                                                                   
                        

                                                                                          

           




                 


                                                                                                        






                                                                                                               
                                                               

                                                                                 
                                                   









                                                                                                                     
                                                                                               
              
                                                      











                                                                                                                     





                                                                                                                   







                                                                                                                          
                               


                                                                   
                                                     






                                                                                     
                                                      




                                                                                       
                                                  

                                                                                                              
                                                     





















                                                                                                                       


                                                                        
   
 
package scala.reflect.reify
package codegen

trait GenTrees {
  self: Reifier =>

  import global._
  import definitions._

  // unfortunately, these are necessary to reify AnnotatedTypes
  // I'd gladly get rid of them, but I don't fancy making a metaprogramming API that doesn't work with annotated types
  // luckily for our sanity, these vars are mutated only within a very restricted code execution path
  def reifyTreeSymbols: Boolean = state.reifyTreeSymbols
  def reifyTreeTypes: Boolean = state.reifyTreeTypes

  /**
   *  Reify a tree.
   *  For internal use only, use ``reified'' instead.
   */
  def reifyTree(tree: Tree): Tree = {
    assert(tree != null, "tree is null")

    if (tree.isErroneous)
      CannotReifyErroneousReifee(tree)

    val splicedTree = spliceTree(tree)
    if (splicedTree != EmptyTree)
      return splicedTree

    // the idea behind the new reincarnation of reifier is a simple maxim:
    //
    //   never call ``reifyType'' to reify a tree
    //
    // this works because the stuff we are reifying was once represented with trees only
    // and lexical scope information can be fully captured by reifying symbols
    //
    // to enable this idyll, we work hard in the ``Reshape'' phase
    // which replaces all types with equivalent trees and works around non-idempotencies of the typechecker
    //
    // why bother? because this brings method to the madness
    // the first prototype of reification reified all types and symbols for all trees => this quickly became unyieldy
    // the second prototype reified external types, but avoided reifying local ones => this created an ugly irregularity
    // current approach is uniform and compact
    var rtree = tree match {
      case global.EmptyTree =>
        reifyMirrorObject(EmptyTree)
      case global.emptyValDef =>
        mirrorBuildSelect(nme.emptyValDef)
      case FreeDef(_, _, _, _, _) =>
        reifyNestedFreeDef(tree)
      case FreeRef(_, _) =>
        reifyNestedFreeRef(tree)
      case BoundTerm(tree) =>
        reifyBoundTerm(tree)
      case BoundType(tree) =>
        reifyBoundType(tree)
      case Literal(const @ Constant(_)) =>
        mirrorCall(nme.Literal, reifyProduct(const))
      case Import(expr, selectors) =>
        mirrorCall(nme.Import, reify(expr), mkList(selectors map reifyProduct))
      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 (reifyTreeSymbols && tree.hasSymbol) {
      if (reifyDebug) println("reifying symbol %s for tree %s".format(tree.symbol, tree))
      rtree = mirrorBuildCall(nme.setSymbol, rtree, reify(tree.symbol))
    }
    if (reifyTreeTypes && tree.tpe != null) {
      if (reifyDebug) println("reifying type %s for tree %s".format(tree.tpe, tree))
      rtree = mirrorBuildCall(nme.setType, rtree, reify(tree.tpe))
    }

    rtree
  }

  def reifyModifiers(m: global.Modifiers) =
    mirrorFactoryCall(nme.Modifiers, mirrorBuildCall(nme.flagsFromBits, reify(m.flags)), reify(m.privateWithin), reify(m.annotations))

  private def spliceTree(tree: Tree): Tree = {
    tree match {
      case TreeSplice(splicee) =>
        if (reifyDebug) println("splicing " + tree)

        // see ``Metalevels'' for more info about metalevel breaches
        // and about how we deal with splices that contain them
        val isMetalevelBreach = splicee exists (sub => sub.hasSymbol && sub.symbol != NoSymbol && sub.symbol.metalevel > 0)
        val isRuntimeEval = splicee exists (sub => sub.hasSymbol && sub.symbol == ExprSplice)
        if (isMetalevelBreach || isRuntimeEval) {
          // we used to convert dynamic splices into runtime evals transparently, but we no longer do that
          // why? see comments in ``Metalevels''
          // if (reifyDebug) println("splicing has failed: cannot splice when facing a metalevel breach")
          // EmptyTree
          CannotReifyRuntimeSplice(tree)
        } else {
          if (reifyDebug) println("splicing has succeeded")
          splicee match {
            // we intentionally don't care about the prefix (the first underscore in the `RefiedTree` pattern match)
            case ReifiedTree(_, _, inlinedSymtab, rtree, _, _, _) =>
              if (reifyDebug) println("inlining the splicee")
              // all free vars local to the enclosing reifee should've already been inlined by ``Metalevels''
              inlinedSymtab.syms foreach (sym => if (sym.isLocalToReifee) assert(false, inlinedSymtab.symDef(sym)))
              state.symtab ++= inlinedSymtab
              rtree
            case tree =>
              val migrated = Apply(Select(splicee, nme.in), List(Ident(nme.MIRROR_SHORT)))
              Select(migrated, nme.tree)
          }
        }
      case _ =>
        EmptyTree
    }
  }

  // unlike in `reifyBoundType` we can skip checking for `tpe` being local or not local w.r.t the reifee
  // a single check for a symbol of the bound term should be enough
  // that's because only Idents and Thises can be bound terms, and they cannot host complex types
  private def reifyBoundTerm(tree: Tree): Tree = tree match {
    case tree @ This(_) if tree.symbol == NoSymbol =>
      throw new Error("unexpected: bound term that doesn't have a symbol: " + showRaw(tree))
    case tree @ This(_) if tree.symbol.isClass && !tree.symbol.isModuleClass && !tree.symbol.isLocalToReifee =>
      val sym = tree.symbol
      if (reifyDebug) println("This for %s, reified as freeVar".format(sym))
      if (reifyDebug) println("Free: " + sym)
      mirrorBuildCall(nme.Ident, reifyFreeTerm(sym, This(sym)))
    case tree @ This(_) if !tree.symbol.isLocalToReifee =>
      if (reifyDebug) println("This for %s, reified as This".format(tree.symbol))
      mirrorBuildCall(nme.This, reify(tree.symbol))
    case tree @ This(_) if tree.symbol.isLocalToReifee =>
      mirrorCall(nme.This, reify(tree.qual))
    case tree @ Ident(_) if tree.symbol == NoSymbol =>
      // this sometimes happens, e.g. for binds that don't have a body
      // or for untyped code generated during previous phases
      // (see a comment in Reifiers about the latter, starting with "why do we resetAllAttrs?")
      mirrorCall(nme.Ident, reify(tree.name))
    case tree @ Ident(_) if !tree.symbol.isLocalToReifee =>
      if (tree.symbol.isVariable && tree.symbol.owner.isTerm) {
        captureVariable(tree.symbol) // Note order dependency: captureVariable needs to come before reification here.
        mirrorCall(nme.Select, mirrorBuildCall(nme.Ident, reify(tree.symbol)), reify(nme.elem))
      } else {
        mirrorBuildCall(nme.Ident, reify(tree.symbol))
      }
    case tree @ Ident(_) if tree.symbol.isLocalToReifee =>
      mirrorCall(nme.Ident, reify(tree.name))
    case _ =>
      throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass))
  }

  private def reifyBoundType(tree: Tree): Tree = {
    def reifyBoundType(tree: Tree): Tree = {
      if (tree.tpe == null)
        throw new Error("unexpected: bound type that doesn't have a tpe: " + showRaw(tree))

      // if a symbol or a type of the scrutinee are local to reifee
      // (e.g. point to a locally declared class or to a path-dependent thingie that depends on a local variable)
      // then we can reify the scrutinee as a symless AST and that will definitely be hygienic
      // why? because then typechecking of a scrutinee doesn't depend on the environment external to the quasiquote
      // otherwise we need to reify the corresponding type
      if (tree.symbol.isLocalToReifee || tree.tpe.isLocalToReifee)
        reifyProduct(tree)
      else {
        val sym0 = tree.symbol
        val sym = sym0.dealias
        val tpe0 = tree.tpe
        val tpe = tpe0.dealias
        if (reifyDebug) println("reifying bound type %s (underlying type is %s, dealiased is %s)".format(sym0, tpe0, tpe))

        if (tpe.isSpliceable) {
          val spliced = spliceType(tpe)
          if (spliced == EmptyTree) {
            if (reifyDebug) println("splicing failed: reify as is")
            mirrorBuildCall(nme.TypeTree, reify(tpe))
          } else {
            spliced match {
              case TypeRefToFreeType(freeType) =>
                if (reifyDebug) println("splicing returned a free type: " + freeType)
                Ident(freeType)
              case _ =>
                if (reifyDebug) println("splicing succeeded: " + spliced)
                mirrorBuildCall(nme.TypeTree, spliced)
            }
          }
        } else {
          if (sym.isLocatable) {
            if (reifyDebug) println("tpe is locatable: reify as Ident(%s)".format(sym))
            mirrorBuildCall(nme.Ident, reify(sym))
          } else {
            if (reifyDebug) println("tpe is an alias, but not a locatable: reify as TypeTree(%s)".format(tpe))
            mirrorBuildCall(nme.TypeTree, reify(tpe))
          }
        }
      }
    }

    tree match {
      case Select(_, _) =>
        reifyBoundType(tree)
      case SelectFromTypeTree(_, _) =>
        reifyBoundType(tree)
      case Ident(_) =>
        reifyBoundType(tree)
      case _ =>
        throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass))
    }
  }

  private def reifyNestedFreeDef(tree: Tree): Tree = {
    if (reifyDebug) println("nested free def: %s".format(showRaw(tree)))
    reifyProduct(tree)
  }

  private def reifyNestedFreeRef(tree: Tree): Tree = {
    if (reifyDebug) println("nested free ref: %s".format(showRaw(tree)))
    reifyProduct(tree)
  }
}