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

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

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

import MegaPhase._
import core.DenotTransformers._
import core.Symbols._
import core.Contexts._
import ast.TreeTypeMap
import core.Types._
import core.Flags._
import core.Decorators._
import collection.mutable
import ast.Trees._
import core.NameKinds.SuperArgName
import SymUtils._

object HoistSuperArgs {
  val name: String = "hoistSuperArgs"
}

/** This phase hoists complex arguments of supercalls and this-calls out of the enclosing class.
 *  Example:
 *
 *      class B(y: Int) extends A({ def f(x: Int) = x * x; f(y)})
 *
 *  is translated to
 *
 *      class B(y: Int) extends A(B#B$superArg$1(this.y)) {
 *        private  def B$superArg$1(y: Int): Int = {
 *          def f(x: Int): Int = x.*(x); f(y)
 *        }
 *      }
 *
 *  An argument is complex if it contains a method or template definition, a this or a new,
 *  or it contains an identifier which needs a `this` prefix to be accessed. This is the case
 *  if the identifer neither a global reference nor a reference to a parameter of the enclosing class.
 *  @see needsHoist for an implementation.
 *
 *  A hoisted argument definition gets the parameters of the class it is hoisted from
 *  as method parameters. The definition is installed in the scope enclosing the class,
 *  or, if that is a package, it is made a static method of the class itself.
 */
class HoistSuperArgs extends MiniPhase with IdentityDenotTransformer { thisPhase =>
  import ast.tpd._

  def phaseName: String = HoistSuperArgs.name

  override def runsAfter: Set[String] = Set(ByNameClosures.name)
    // By name closures need to be introduced first in order to be hoisted out here.
    // There's an interaction with by name closures in that the  marker
    // application should not be hoisted, but be left at the point of call.

  /** Defines methods for hoisting complex supercall arguments out of
   *  parent super calls and constructor definitions.
   *  Hoisted superarg methods are collected in `superArgDefs`
   */
  class Hoister(cls: Symbol)(implicit ctx: Context) {
    val superArgDefs: mutable.ListBuffer[DefDef] = new mutable.ListBuffer

    /** If argument is complex, hoist it out into its own method and refer to the
     *  method instead.
     *  @param   arg   The argument that might be hoisted
     *  @param   cdef  The definition of the constructor from which the call is made
     *  @return  The argument after possible hoisting
     */
    private def hoistSuperArg(arg: Tree, cdef: DefDef): Tree = {
      val constr = cdef.symbol
      lazy val origParams = // The parameters that can be accessed in the supercall
        if (constr == cls.primaryConstructor)
          cls.info.decls.filter(d => d.is(TypeParam) || d.is(ParamAccessor))
        else
          (cdef.tparams ::: cdef.vparamss.flatten).map(_.symbol)

      /** The parameter references defined by the constructor info */
      def allParamRefs(tp: Type): List[ParamRef] = tp match {
        case tp: LambdaType => tp.paramRefs ++ allParamRefs(tp.resultType)
        case _              => Nil
      }

      /** Splice `restpe` in final result type position of `tp` */
      def replaceResult(tp: Type, restpe: Type): Type = tp match {
        case tp: LambdaType =>
          tp.derivedLambdaType(resType = replaceResult(tp.resultType, restpe))
        case _ => restpe
      }

      /** A method representing a hoisted supercall argument */
      def newSuperArgMethod(argType: Type) = {
        val (staticFlag, methOwner) =
          if (cls.owner.is(Package)) (JavaStatic, cls) else (EmptyFlags, cls.owner)
        val argTypeWrtConstr = argType.subst(origParams, allParamRefs(constr.info))
        // argType with references to paramRefs of the primary constructor instead of
        // local parameter accessors
        val meth = ctx.newSymbol(
          owner = methOwner,
          name = SuperArgName.fresh(cls.name.toTermName),
          flags = Synthetic | Private | Method | staticFlag,
          info = replaceResult(constr.info, argTypeWrtConstr),
          coord = constr.coord)
        if (methOwner.isClass) meth.enteredAfter(thisPhase) else meth
      }

      /** Type of a reference implies that it needs to be hoisted */
      def refNeedsHoist(tp: Type): Boolean = tp match {
        case tp: ThisType => !tp.cls.isStaticOwner && tp.cls != cls
        case tp: TermRef  => refNeedsHoist(tp.prefix)
        case _            => false
      }

      /** Super call argument is complex, needs to be hoisted */
      def needsHoist(tree: Tree) = tree match {
        case _: DefDef            => true
        case _: Template          => true
        case _: New               => !tree.tpe.typeSymbol.isStatic
        case _: RefTree | _: This => refNeedsHoist(tree.tpe)
        case _                    => false
      }

      // begin hoistSuperArg
      arg match {
        case Apply(fn, arg1 :: Nil) if fn.symbol == defn.cbnArg =>
          cpy.Apply(arg)(fn, hoistSuperArg(arg1, cdef) :: Nil)
        case _ if (arg.existsSubTree(needsHoist)) =>
          val superMeth = newSuperArgMethod(arg.tpe)
          val superArgDef = polyDefDef(superMeth, trefs => vrefss => {
            val paramSyms = trefs.map(_.typeSymbol) ::: vrefss.flatten.map(_.symbol)
            val tmap = new TreeTypeMap(
              typeMap = new TypeMap {
                lazy val origToParam = origParams.zip(paramSyms).toMap
                def apply(tp: Type) = tp match {
                  case tp: NamedType
                  if (tp.symbol.owner == cls || tp.symbol.owner == constr) &&
                     tp.symbol.isParamOrAccessor =>
                    origToParam.get(tp.symbol) match {
                      case Some(mappedSym) => if (tp.symbol.isType) mappedSym.typeRef else mappedSym.termRef
                      case None => mapOver(tp)
                    }
                  case _ =>
                    mapOver(tp)
                }
              },
              treeMap = {
                case tree: RefTree if paramSyms.contains(tree.symbol) =>
                  cpy.Ident(tree)(tree.name).withType(tree.tpe)
                case tree =>
                  tree
              })
            tmap(arg).changeOwnerAfter(constr, superMeth, thisPhase)
          })
          superArgDefs += superArgDef
          def termParamRefs(tp: Type, params: List[Symbol]): List[List[Tree]] = tp match {
            case tp: PolyType =>
              termParamRefs(tp.resultType, params)
            case tp: MethodType =>
              val (thisParams, otherParams) = params.splitAt(tp.paramNames.length)
              thisParams.map(ref) :: termParamRefs(tp.resultType, otherParams)
            case _ =>
              Nil
          }
          val (typeParams, termParams) = origParams.span(_.isType)
          val res = ref(superMeth)
            .appliedToTypes(typeParams.map(_.typeRef))
            .appliedToArgss(termParamRefs(constr.info, termParams))
          ctx.log(i"hoist $arg, cls = $cls = $res")
          res
        case _ => arg
      }
    }

    /** Hoist complex arguments in super call out of the class. */
    def hoistSuperArgsFromCall(superCall: Tree, cdef: DefDef): Tree = superCall match {
      case Apply(fn, args) =>
        cpy.Apply(superCall)(hoistSuperArgsFromCall(fn, cdef), args.mapconserve(hoistSuperArg(_, cdef)))
      case _ =>
        superCall
    }

    /** Hoist complex arguments in this-constructor call of secondary constructor out of the class. */
    def hoistSuperArgsFromConstr(stat: Tree): Tree = stat match {
      case stat: DefDef if stat.symbol.isClassConstructor =>
        cpy.DefDef(stat)(rhs =
          stat.rhs match {
            case Block(superCall :: stats, expr) =>
              val superCall1 = hoistSuperArgsFromCall(superCall, stat)
              if (superCall1 eq superCall) stat.rhs
              else cpy.Block(stat.rhs)(superCall1 :: stats, expr)
            case _ =>
              hoistSuperArgsFromCall(stat.rhs, stat)
          })
      case _ =>
        stat
    }
  }

  override def transformTypeDef(tdef: TypeDef)(implicit ctx: Context): Tree =
    tdef.rhs match {
      case impl @ Template(cdef, superCall :: others, _, _) =>
        val hoist = new Hoister(tdef.symbol)
        val hoistedSuperCall = hoist.hoistSuperArgsFromCall(superCall, cdef)
        val hoistedBody = impl.body.mapconserve(hoist.hoistSuperArgsFromConstr)
        if (hoist.superArgDefs.isEmpty) tdef
        else {
          val (staticSuperArgDefs, enclSuperArgDefs) =
            hoist.superArgDefs.toList.partition(_.symbol.is(JavaStatic))
          flatTree(
              cpy.TypeDef(tdef)(
                  rhs = cpy.Template(impl)(
                      parents = hoistedSuperCall :: others,
                      body = hoistedBody ++ staticSuperArgDefs)) ::
              enclSuperArgDefs)
        }
      case _ =>
        tdef
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy