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

dotty.tools.dotc.transform.Memoize.scala Maven / Gradle / Ivy

The newest version!
package dotty.tools.dotc
package transform

import core._
import DenotTransformers._
import Contexts.Context
import SymDenotations.SymDenotation
import Denotations._
import Symbols._
import SymUtils._
import Constants._
import ast.Trees._
import MegaPhase._
import NameOps._
import Flags._
import Decorators._

object Memoize {
  val name: String = "memoize"
}

/** Provides the implementations of all getters and setters, introducing
 *  fields to hold the value accessed by them.
 *  TODO: Make LazyVals a part of this phase?
 *
 *       def x(): T = e
 *      -->  private val x: T = e
 *              def x(): T = x
 *
 *      def x(): T = e
 *      -->  private[this] var x: T = e
 *             def x(): T = x
 *
 *      def x_=(y: T): Unit = ()
 *      -->   def x_=(y: T): Unit = x = y
 */
class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase =>
  import ast.tpd._

  override def phaseName: String = Memoize.name

  /* Makes sure that, after getters and constructors gen, there doesn't
   * exist non-deferred definitions that are not implemented. */
  override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
    def errorLackImplementation(t: Tree) = {
      val firstPhaseId = t.symbol.initial.validFor.firstPhaseId
      val definingPhase = ctx.withPhase(firstPhaseId).phase.prev
      throw new AssertionError(
        i"Non-deferred definition introduced by $definingPhase lacks implementation: $t")
    }
    tree match {
      case ddef: DefDef
        if !ddef.symbol.is(Deferred) &&
           !ddef.symbol.isConstructor && // constructors bodies are added later at phase Constructors
           ddef.rhs == EmptyTree =>
        errorLackImplementation(ddef)
      case tdef: TypeDef
        if tdef.symbol.isClass && !tdef.symbol.is(Deferred) && tdef.rhs == EmptyTree =>
        errorLackImplementation(tdef)
      case _ =>
    }
    super.checkPostCondition(tree)
  }

  /** Should run after mixin so that fields get generated in the
   *  class that contains the concrete getter rather than the trait
   *  that defines it.
   */
  override def runsAfter: Set[String] = Set(Mixin.name)

  override def transformDefDef(tree: DefDef)(implicit ctx: Context): Tree = {
    val sym = tree.symbol

    def newField = {
      assert(!sym.hasAnnotation(defn.ScalaStaticAnnot))
      val fieldType =
        if (sym.isGetter) sym.info.resultType
        else /*sym.isSetter*/ sym.info.firstParamTypes.head

      ctx.newSymbol(
        owner = ctx.owner,
        name  = sym.name.asTermName.fieldName,
        flags = Private | (if (sym.is(StableRealizable)) EmptyFlags else Mutable),
        info  = fieldType,
        coord = tree.span
      ).withAnnotationsCarrying(sym, defn.FieldMetaAnnot)
       .enteredAfter(thisPhase)
    }

    def addAnnotations(denot: Denotation): Unit =
      denot match {
        case fieldDenot: SymDenotation if sym.annotations.nonEmpty =>
          val cpy = fieldDenot.copySymDenotation()
          cpy.annotations = sym.annotations
          cpy.installAfter(thisPhase)
        case _ => ()
      }

    def removeAnnotations(denot: SymDenotation): Unit =
      if (sym.annotations.nonEmpty) {
        val cpy = sym.copySymDenotation()
        cpy.annotations = Nil
        cpy.installAfter(thisPhase)
      }

    val NoFieldNeeded = Lazy | Deferred | JavaDefined | (if (ctx.settings.YnoInline.value) EmptyFlags else Inline)

    def erasedBottomTree(sym: Symbol) = {
      if (sym eq defn.NothingClass) Throw(nullLiteral)
      else if (sym eq defn.NullClass) nullLiteral
      else if (sym eq defn.BoxedUnitClass) ref(defn.BoxedUnit_UNIT)
      else {
        assert(false, sym + " has no erased bottom tree")
        EmptyTree
      }
    }

    if (sym.is(Accessor, butNot = NoFieldNeeded)) {
      val field = sym.field.orElse(newField).asTerm

      def adaptToField(tree: Tree): Tree =
        if (tree.isEmpty) tree else tree.ensureConforms(field.info.widen)

      def isErasableBottomField(cls: Symbol): Boolean =
        !field.isVolatile && ((cls eq defn.NothingClass) || (cls eq defn.NullClass) || (cls eq defn.BoxedUnitClass))

      if (sym.isGetter) {
        var rhs = tree.rhs.changeOwnerAfter(sym, field, thisPhase)
        if (isWildcardArg(rhs)) rhs = EmptyTree
        val fieldDef = transformFollowing(ValDef(field, adaptToField(rhs)))
        val rhsClass = tree.tpt.tpe.widenDealias.classSymbol
        val getterRhs =
          if (isErasableBottomField(rhsClass)) erasedBottomTree(rhsClass)
          else transformFollowingDeep(ref(field))(ctx.withOwner(sym))
        val getterDef = cpy.DefDef(tree)(rhs = getterRhs)
        addAnnotations(fieldDef.denot)
        removeAnnotations(sym)
        Thicket(fieldDef, getterDef)
      } else if (sym.isSetter) {
        if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs } // This is intended as an assertion
        field.setFlag(Mutable) // Necessary for vals mixed in from Scala2 traits
        val initializer =
          if (isErasableBottomField(tree.vparamss.head.head.tpt.tpe.classSymbol)) Literal(Constant(()))
          else Assign(ref(field), adaptToField(ref(tree.vparamss.head.head.symbol)))
        val setterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)(ctx.withOwner(sym)))
        removeAnnotations(sym)
        setterDef
      }
      else tree // curiously, some accessors from Scala2 have ' ' suffixes. They count as
                // neither getters nor setters
    }
    else tree
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy