All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.tools.nsc.ast.Trees.scala Maven / Gradle / Ivy

The newest version!
/*
 * Scala (https://www.scala-lang.org)
 *
 * Copyright EPFL and Lightbend, Inc.
 *
 * Licensed under Apache License 2.0
 * (http://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package scala.tools.nsc
package ast

import scala.reflect.ClassTag
import java.lang.System.lineSeparator

import scala.annotation.nowarn

trait Trees extends scala.reflect.internal.Trees { self: Global =>
  // --- additional cases --------------------------------------------------------
  /** Only used during parsing */
  case class Parens(args: List[Tree]) extends Tree {
    override def traverse(traverser: Traverser): Unit = {
      traverser.traverseTrees(args)
    }
  }

  /** Documented definition, eliminated by analyzer */
  case class DocDef(comment: DocComment, definition: Tree)
       extends Tree {
    override def symbol: Symbol = definition.symbol
    override def symbol_=(sym: Symbol): Unit = { definition.symbol = sym }
    override def isDef = definition.isDef
    override def isTerm = definition.isTerm
    override def isType = definition.isType
    override def transform(transformer: ApiTransformer): Tree =
      transformer.treeCopy.DocDef(this, comment, transformer.transform(definition))
    override def traverse(traverser: Traverser): Unit = {
      traverser.traverse(definition)
    }
  }

 /** Array selection ` . ` only used during erasure */
  case class SelectFromArray(qualifier: Tree, name: Name, erasure: Type)
       extends RefTree with TermTree {
   override def transform(transformer: ApiTransformer): Tree =
     transformer.treeCopy.SelectFromArray(
       this, transformer.transform(qualifier), name, erasure)
   override def traverse(traverser: Traverser): Unit = {
     traverser.traverse(qualifier)
   }
 }

  /** Derived value class injection (equivalent to: `new C(arg)` after erasure); only used during erasure.
   *  The class `C` is stored as a tree attachment.
   */
  case class InjectDerivedValue(arg: Tree)
       extends SymTree with TermTree {
    override def transform(transformer: ApiTransformer): Tree =
      transformer.treeCopy.InjectDerivedValue(this, transformer.transform(arg))
    override def traverse(traverser: Traverser): Unit = {
      traverser.traverse(arg)
    }
  }

  /** emitted by typer, eliminated by refchecks */
  case class TypeTreeWithDeferredRefCheck(precheck: TypeTree)(val check: () => TypeTree) extends TypTree {
    override def transform(transformer: ApiTransformer): Tree =
      transformer.treeCopy.TypeTreeWithDeferredRefCheck(this)
    override def traverse(traverser: Traverser): Unit = {
      // (and rewrap the result? how to update the deferred check? would need to store wrapped tree instead of returning it from check)
    }
  }

  // --- factory methods ----------------------------------------------------------

  /** Factory method for a primary constructor super call `super.(args_1)...(args_n)`
   */
  def PrimarySuperCall(argss: List[List[Tree]]): Tree = argss match {
    case Nil        => Apply(gen.mkSuperInitCall, Nil)
    case xs :: rest => rest.foldLeft(Apply(gen.mkSuperInitCall, xs): Tree)(Apply.apply)
  }

  /** Construct class definition with given class symbol, value parameters,
   *  supercall arguments and template body.
   *
   *  @param sym        the class symbol
   *  @param constrMods the modifiers for the class constructor, i.e. as in `class C private (...)`
   *  @param vparamss   the value parameters -- if they have symbols they
   *                    should be owned by `sym`
   *  @param body       the template statements without primary constructor
   *                    and value parameter fields.
   */
  def ClassDef(sym: Symbol, constrMods: Modifiers, vparamss: List[List[ValDef]], body: List[Tree], superPos: Position): ClassDef = {
    // "if they have symbols they should be owned by `sym`"
    assert(mforall(vparamss)(_.symbol.owner == sym), (mmap(vparamss)(_.symbol), sym))

    ClassDef(sym,
      gen.mkTemplate(sym.info.parents map TypeTree,
                    if (sym.thisSym == sym || phase.erasedTypes) noSelfType else ValDef(sym.thisSym),
                    constrMods, vparamss, body, superPos))
  }

 // --- subcomponents --------------------------------------------------

  object treeInfo extends {
    val global: Trees.this.type = self
  } with TreeInfo
  import treeInfo._

  // --- additional cases in operations ----------------------------------

  trait TreeCopier extends InternalTreeCopierOps {
    def DocDef(tree: Tree, comment: DocComment, definition: Tree): DocDef
    def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type): SelectFromArray
    def InjectDerivedValue(tree: Tree, arg: Tree): InjectDerivedValue
    def TypeTreeWithDeferredRefCheck(tree: Tree): TypeTreeWithDeferredRefCheck
  }
  implicit val TreeCopierTag: ClassTag[TreeCopier] = ClassTag[TreeCopier](classOf[TreeCopier])

  def newStrictTreeCopier: TreeCopier = new StrictAstTreeCopier
  def newLazyTreeCopier: TreeCopier = new LazyAstTreeCopier

  @nowarn("""cat=deprecation&origin=scala\.tools\.nsc\.ast\.Trees\.StrictTreeCopier""")
  final type StrictAstTreeCopier = StrictTreeCopier

  @nowarn("msg=shadowing a nested class of a parent is deprecated")
  @deprecated("use StrictAstTreeCopier instead", since = "2.13.4")
  class StrictTreeCopier extends super.StrictTreeCopier with TreeCopier {
    def DocDef(tree: Tree, comment: DocComment, definition: Tree) =
      new DocDef(comment, definition).copyAttrs(tree)
    def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type) =
      new SelectFromArray(qualifier, selector, erasure).copyAttrs(tree)
    def InjectDerivedValue(tree: Tree, arg: Tree) =
      new InjectDerivedValue(arg).copyAttrs(tree)
    def TypeTreeWithDeferredRefCheck(tree: Tree) = tree match {
      case dc@TypeTreeWithDeferredRefCheck(prechk) => new TypeTreeWithDeferredRefCheck(prechk)(dc.check).copyAttrs(tree)
      case x => throw new MatchError(x)
    }
  }

  @nowarn("""cat=deprecation&origin=scala\.tools\.nsc\.ast\.Trees\.LazyTreeCopier""")
  final type LazyAstTreeCopier = LazyTreeCopier

  @nowarn("msg=shadowing a nested class of a parent is deprecated")
  @deprecated("use LazyAstTreeCopier instead", since = "2.13.4")
  class LazyTreeCopier extends super.LazyTreeCopier with TreeCopier {
    def DocDef(tree: Tree, comment: DocComment, definition: Tree) = tree match {
      case t @ DocDef(comment0, definition0)
      if (comment0 == comment) && (definition0 == definition) => t
      case _ => this.treeCopy.DocDef(tree, comment, definition)
    }
    def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type) = tree match {
      case t @ SelectFromArray(qualifier0, selector0, _)
      if (qualifier0 == qualifier) && (selector0 == selector) => t
      case _ => this.treeCopy.SelectFromArray(tree, qualifier, selector, erasure)
    }
    def InjectDerivedValue(tree: Tree, arg: Tree) = tree match {
      case t @ InjectDerivedValue(arg0)
      if (arg0 == arg) => t
      case _ => this.treeCopy.InjectDerivedValue(tree, arg)
    }
    def TypeTreeWithDeferredRefCheck(tree: Tree) = tree match {
      case t: TypeTreeWithDeferredRefCheck => t
      case _ => this.treeCopy.TypeTreeWithDeferredRefCheck(tree)
    }
  }

  type ApiTransformer = super.Transformer

  // TODO: uncomment when deprecating the below
  // @nowarn("""cat=deprecation&origin=scala\.tools\.nsc\.ast\.Trees\.Transformer""")
  final type AstTransformer = Transformer

  // TODO: deprecate when we can cleanly cross-compile without warnings
  // @deprecated("use AstTransformer instead", since = "2.13.4")
  @nowarn("msg=shadowing a nested class of a parent is deprecated")
  class Transformer extends InternalTransformer {
    def transformUnit(unit: CompilationUnit): Unit = {
      try unit.body = transform(unit.body)
      catch {
        case ex: Exception =>
          log(supplementErrorMessage("unhandled exception while transforming "+unit))
          throw ex
      }
    }
  }

  // used when a phase is disabled
  object noopTransformer extends AstTransformer {
    override def transformUnit(unit: CompilationUnit): Unit = {}
  }

  override protected def xtransform(transformer: super.Transformer, tree: Tree): Tree = tree match {
    case DocDef(comment, definition) =>
      transformer.treeCopy.DocDef(tree, comment, transformer.transform(definition))
    case SelectFromArray(qualifier, selector, erasure) =>
      transformer.treeCopy.SelectFromArray(
        tree, transformer.transform(qualifier), selector, erasure)
    case InjectDerivedValue(arg) =>
      transformer.treeCopy.InjectDerivedValue(
        tree, transformer.transform(arg))
    case _: TypeTreeWithDeferredRefCheck =>
      transformer.treeCopy.TypeTreeWithDeferredRefCheck(tree)
    case _ => super.xtransform(transformer, tree)
  }

  // Finally, no one uses resetAllAttrs anymore, so I'm removing it from the compiler.
  // Even though it's with great pleasure I'm doing that, I'll leave its body here to warn future generations about what happened in the past.
  //
  // So what actually happened in the past is that we used to have two flavors of resetAttrs: resetAllAttrs and resetLocalAttrs.
  // resetAllAttrs destroyed all symbols and types in the tree in order to reset its state to something suitable for retypechecking
  // and/or embedding into bigger trees / different lexical scopes. (Btw here's some background on why people would want to use
  // reset attrs in the first place: https://groups.google.com/forum/#!topic/scala-internals/TtCTPlj_qcQ).
  //
  // However resetAllAttrs was more of a poison than of a treatment, because along with locally defined symbols that are the cause
  // for almost every or maybe even every case of tree corruption, it erased external bindings that sometimes could not be restored.
  // This is how we came up with resetLocalAttrs that left external bindings alone, and that was a big step forward.
  // Then slowly but steadily we've evicted all usages of resetAllAttrs from our codebase in favor of resetLocalAttrs
  // and have been living happily ever after.
  //
  // def resetAllAttrs(x: Tree, leaveAlone: Tree => Boolean = null): Tree = new ResetAttrs(localOnly = false, leaveAlone).transform(x)

  // upd. Unfortunately this didn't work out quite as we expected. The last two users of resetAllAttrs:
  // reification and typedLabelDef broke in very weird ways when we replaced resetAllAttrs with resetLocalAttrs
  // (see scala/bug#8316 change from resetAllAttrs to resetLocalAttrs in reifiers broke Slick and
  // scala/bug#8318 NPE in mixin in scala-continuations for more information).
  // Given that we're supposed to release 2.11.0-RC1 in less than a week, I'm temporarily reinstating resetAllAttrs
  // until we have time to better understand what's going on. In order to dissuade people from using it,
  // it now comes with a new, ridiculous name.
  /** @see ResetAttrs */
  def brutallyResetAttrs(x: Tree, leaveAlone: Tree => Boolean = null): Tree = new ResetAttrs(brutally = true, leaveAlone).transform(x)

  /** @see ResetAttrs */
  def resetAttrs(x: Tree): Tree = new ResetAttrs(brutally = false, leaveAlone = null).transform(x)

  /** A transformer which resets symbol and tpe fields of all nodes in a given tree,
   *  with special treatment of:
   *    TypeTree nodes: are replaced by their original if it exists, otherwise tpe field is reset
   *                    to empty if it started out empty or refers to local symbols (which are erased).
   *    TypeApply nodes: are deleted if type arguments end up reverted to empty
   *    This(pkg) nodes where pkg is a package: these are kept.
   *
   *  (bq:) This transformer has mutable state and should be discarded after use
   */
  private class ResetAttrs(brutally: Boolean, leaveAlone: Tree => Boolean) {
    // this used to be based on -Ydebug, but the need for logging in this code is so situational
    // that I've reverted to a hard-coded constant here.
    val debug = false
    val trace = scala.tools.nsc.util.trace when debug

    val locals = util.HashSet[Symbol](8)
    val orderedLocals = scala.collection.mutable.ListBuffer[Symbol]()
    def registerLocal(sym: Symbol): Unit = {
      if (sym != null && sym != NoSymbol) {
        if (debug && !(locals contains sym)) orderedLocals += sym
        locals addEntry sym
      }
    }

    class MarkLocals extends self.InternalTraverser {
      def markLocal(tree: Tree): Unit = {
        if (tree.symbol != null && tree.symbol != NoSymbol) {
          val sym = tree.symbol
          registerLocal(sym)
          registerLocal(sym.sourceModule)
          registerLocal(sym.moduleClass)
          registerLocal(sym.companionClass)
          registerLocal(sym.companionModule)
          registerLocal(sym.deSkolemize)
          sym match {
            case sym: TermSymbol => registerLocal(sym.referenced)
            case _ => ;
          }
        }
      }

      override def traverse(tree: Tree) = {
        tree match {
          case _: DefTree | Function(_, _) | Template(_, _, _) => markLocal(tree)
          case _ =>
        }
        tree.traverse(this)
      }
    }

    class ResetTransformer extends AstTransformer {
      override def transform(tree: Tree): Tree = {
        if (leaveAlone != null && leaveAlone(tree))
          tree
        else {
          val tree1 = {
            tree match {
              case tree if !tree.canHaveAttrs =>
                tree
              case tpt: TypeTree =>
                if (tpt.original != null)
                  transform(tpt.original)
                else {
                  val refersToLocalSymbols = tpt.tpe != null && (tpt.tpe exists (tp => locals contains tp.typeSymbol))
                  val isInferred = tpt.wasEmpty
                  if (refersToLocalSymbols || isInferred) {
                    tpt.duplicate.clearType()
                  } else {
                    tpt
                  }
                }
              // If one of the type arguments of a TypeApply gets reset to an empty TypeTree, then this means that:
              // 1) It isn't empty now (tpt.tpe != null), but it was empty before (tpt.wasEmpty).
              // 2) Thus, its argument got inferred during a preceding typecheck.
              // 3) Thus, all its arguments were inferred (because scalac can only infer all or nothing).
              // Therefore, we can safely erase the TypeApply altogether and have it inferred once again in a subsequent typecheck.
              // UPD: Actually there's another reason for erasing a type behind the TypeTree
              // is when this type refers to symbols defined in the tree being processed.
              // These symbols will be erased, because we can't leave alive a type referring to them.
              // Here we can only hope that everything will work fine afterwards.
              case TypeApply(fn, args) if args map transform exists (_.isEmpty) =>
                transform(fn)
              case EmptyTree =>
                tree
              // The typer does not accept UnApply. Replace it with Apply, which can be retyped.
              case UnApply(Unapplied(Applied(Select(fun, nme.unapply | nme.unapplySeq), _, _)), args) =>
                Apply(transform(fun), transformTrees(args))
              case _ =>
                val dupl = tree.duplicate
                // Typically the resetAttrs transformer cleans both symbols and types.
                // However there are exceptions when we cannot erase symbols due to idiosyncrasies of the typer.
                // vetoXXX local variables declared below describe the conditions under which we cannot erase symbols.
                //
                // The first reason to not erase symbols is the threat of non-idempotency (scala/bug#5464).
                // Here we take care of references to package classes (scala/bug#5705).
                // There are other non-idempotencies, but they are not worked around yet.
                //
                // The second reason has to do with the fact that resetAttrs needs to be less destructive.
                // Erasing locally-defined symbols is useful to prevent tree corruption, but erasing external bindings is not,
                // therefore we want to retain those bindings, especially given that restoring them can be impossible
                // if we move these trees into lexical contexts different from their original locations.
                if (dupl.hasSymbolField) {
                  val sym = dupl.symbol
                  val vetoScope = !brutally && !(locals contains sym) && !(locals contains sym.deSkolemize)
                  val vetoThis = dupl.isInstanceOf[This] && sym.isPackageClass
                  if (!(vetoScope || vetoThis)) dupl.symbol = NoSymbol
                }
                dupl.clearType()
            }
          }
          tree1.transform(this)
        }
      }
    }

    def transform(x: Tree): Tree = {
      new MarkLocals().traverse(x)

      if (debug) {
        assert(locals.size == orderedLocals.size, "Incongruent ordered locals")
        val msg = orderedLocals.toList.filter{_ != NoSymbol}
          .map("  " + _)
          .mkString(lineSeparator)
        trace("locals (%d total): %n".format(orderedLocals.size))(msg)
      }

      new ResetTransformer().transform(x)
    }
  }

  /* New pattern matching cases:

   case Parens(expr)                                               (only used during parsing)
   case DocDef(comment, defn) =>                                   (eliminated by typer)
   case TypeTreeWithDeferredRefCheck(prechk) =>                    (created by typer and eliminated by refchecks)
   case SelectFromArray(_, _, _) =>                                (created and eliminated by erasure)
   case InjectDerivedValue(_) =>                                   (created and eliminated by erasure)

  */

 }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy