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

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

package dotty.tools.dotc
package transform

import core._
import MegaPhase._
import Contexts.Context
import Flags._
import SymUtils._
import Symbols._
import SymDenotations._
import Types._
import Decorators._
import DenotTransformers._
import StdNames._
import NameOps._
import Phases._
import ast.untpd
import ast.Trees._
import NameKinds.ImplMethName
import collection.mutable

/** Rewrite calls
 *
 *    super[M].f(args)
 *
 *  where M is a Scala 2.11 trait implemented by the current class to
 *
 *    M$class.f(this, args)
 *
 *  provided the implementation class M$class defines a corresponding function `f`.
 *  If M is a Scala 2.12 or newer trait, rewrite to
 *
 *    M.f(this, args)
 *
 *  where f is a static member of M.
 */
class LinkScala2Impls extends MiniPhase with IdentityDenotTransformer { thisPhase =>
  import ast.tpd._

  override def phaseName: String = "linkScala2Impls"
  override def changesMembers = true

  override def runsAfterGroupsOf = Set(Mixin.name)
    // Adds as a side effect static members to traits which can confuse Mixin,
    // that's why it is runsAfterGroupOf

  /** Copy definitions from implementation class to trait itself */
  private def augmentScala_2_12_Trait(mixin: ClassSymbol)(implicit ctx: Context): Unit = {
    def info_2_12(sym: Symbol) = sym.info match {
      case mt @ MethodType(paramNames @ nme.SELF :: _) =>
        // 2.12 seems to always assume the enclsing mixin class as self type parameter,
        // whereas 2.11 used the self type of this class instead.
        val selfType :: otherParamTypes = mt.paramInfos
        MethodType(paramNames, mixin.typeRef :: otherParamTypes, mt.resType)
      case info => info
    }
    def newImpl(sym: TermSymbol): Symbol = sym.copy(
      owner = mixin,
      name = if (sym.isConstructor) sym.name else ImplMethName(sym.name),
      info = info_2_12(sym)
    )
    for (sym <- mixin.implClass.info.decls)
      newImpl(sym.asTerm).enteredAfter(thisPhase)
  }

  override def prepareForTemplate(impl: Template)(implicit ctx: Context) = {
    val cls = impl.symbol.owner.asClass
    for (mixin <- cls.mixins)
      if (mixin.is(Scala_2_12_Trait, butNot = Scala_2_12_Augmented)) {
        augmentScala_2_12_Trait(mixin)
        mixin.setFlag(Scala_2_12_Augmented)
      }
    ctx
  }

  override def transformApply(app: Apply)(implicit ctx: Context) = {
    def currentClass = ctx.owner.enclosingClass.asClass
    app match {
      case Apply(sel @ Select(Super(_, _), _), args)
      if sel.symbol.owner.is(Scala2xTrait) && currentClass.mixins.contains(sel.symbol.owner) =>
        val impl = implMethod(sel.symbol)
        if (impl.exists) Apply(ref(impl), This(currentClass) :: args).withPos(app.pos)
        else app // could have been an abstract method in a trait linked to from a super constructor
      case Apply(sel, args)
      if sel.symbol.maybeOwner.is(ImplClass) && sel.symbol.owner.traitOfImplClass.is(Scala_2_12_Trait) =>
        val impl = implMethod(sel.symbol)
        cpy.Apply(app)(ref(impl), args)
      case _ =>
        app
    }
  }

  /** The 2.12 implementation method of a super call or implementation class target */
  private def implMethod(meth: Symbol)(implicit ctx: Context): Symbol = {
    val implName = ImplMethName(meth.name.asTermName)
    val cls = meth.owner
    if (cls.is(ImplClass))
      cls.traitOfImplClass.info.decl(implName).atSignature(meth.signature).symbol
    else if (cls.is(Scala_2_12_Trait))
      if (meth.isConstructor)
        cls.info.decl(nme.TRAIT_CONSTRUCTOR).symbol
      else
        cls.info.decl(implName)
          .suchThat(c => FullParameterization.memberSignature(c.info) == meth.signature)
          .symbol
    else throw new AssertionError(i"no impl method for $meth")
  }

  private val Scala2xTrait = allOf(Scala2x, Trait)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy