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

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

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

import core._
import Symbols._, Types._, Contexts._, Decorators._, Flags._, Scopes._
import DenotTransformers._
import ast.untpd
import collection.{mutable, immutable}
import ShortcutImplicits._
import util.Spans.Span
import util.SourcePosition

/** A helper class for generating bridge methods in class `root`. */
class Bridges(root: ClassSymbol, thisPhase: DenotTransformer)(implicit ctx: Context) {
  import ast.tpd._

  assert(ctx.phase == ctx.erasurePhase.next)
  private val preErasureCtx = ctx.withPhase(ctx.erasurePhase)
  private val elimErasedCtx = ctx.withPhase(ctx.elimErasedValueTypePhase.next)

  private class BridgesCursor(implicit ctx: Context) extends OverridingPairs.Cursor(root) {

    /** Only use the superclass of `root` as a parent class. This means
     *  overriding pairs that have a common implementation in a trait parent
     *  are also counted. This is necessary because we generate bridge methods
     *  only in classes, never in traits.
     */
    override def parents = Array(root.superClass)

    override def exclude(sym: Symbol) =
      !sym.isOneOf(MethodOrModule) ||
      isImplicitShortcut(sym) ||
      super.exclude(sym)
  }

  //val site = root.thisType

  private[this] var toBeRemoved = immutable.Set[Symbol]()
  private val bridges = mutable.ListBuffer[Tree]()
  private val bridgesScope = newScope
  private val bridgeTarget = newMutableSymbolMap[Symbol]

  def bridgePosFor(member: Symbol): SourcePosition =
    (if (member.owner == root && member.span.exists) member else root).sourcePos

  /** Add a bridge between `member` and `other`, where `member` overrides `other`
   *  before erasure, if the following conditions are satisfied.
   *
   *   - `member` and `other` have different signatures
   *   - there is not yet a bridge with the same name and signature in `root`.
   *
   *  The bridge has the erased info of `other` and forwards to `member`.
   *  Additionally, if `member` and `other` do have the same signature,
   *  but not the same type after erasure and before elimErasedValueTypes
   *  issue an error: A bridge would be needed yet it would clash with the member itself.
   *  See neg/i1905.scala
   */
  private def addBridgeIfNeeded(member: Symbol, other: Symbol) = {
    def bridgeExists =
      bridgesScope.lookupAll(member.name).exists(bridge =>
        bridgeTarget(bridge) == member && bridge.signature == other.signature)
    def info(sym: Symbol)(implicit ctx: Context) = sym.info
    def desc(sym: Symbol)= {
      val infoStr = info(sym)(preErasureCtx) match {
        case ExprType(info) => i": $info"
        case info => info.show
      }
      i"$sym$infoStr in ${sym.owner}"
    }
    if (member.signature == other.signature) {
      if (!member.info.matches(other.info))
        ctx.error(em"""bridge generated for member ${desc(member)}
                      |which overrides ${desc(other)}
                      |clashes with definition of the member itself; both have erased type ${info(member)(elimErasedCtx)}."""",
                  bridgePosFor(member))
    }
    else if (!bridgeExists)
      addBridge(member, other)
  }

  /** Generate bridge between `member` and `other`
   */
  private def addBridge(member: Symbol, other: Symbol) = {
    val bridge = other.copy(
      owner = root,
      flags = (member.flags | Method | Bridge | Artifact) &~
        (Accessor | ParamAccessor | CaseAccessor | Deferred | Lazy | Module),
      coord = bridgePosFor(member).span).enteredAfter(thisPhase).asTerm

    ctx.debuglog(
      i"""generating bridge from ${other.showLocated}: ${other.info}
             |to ${member.showLocated}: ${member.info} @ ${member.span}
             |bridge: ${bridge.showLocated} with flags: ${bridge.flagsString}""")

    bridgeTarget(bridge) = member
    bridgesScope.enter(bridge)

    if (other.owner == root) {
      root.delete(other)
      toBeRemoved += other
    }

    def bridgeRhs(argss: List[List[Tree]]) = {
      val ref = This(root).select(member)
      if (member.info.isParameterless) ref // can happen if `member` is a module
      else ref.appliedToArgss(argss)
    }

    bridges += DefDef(bridge, bridgeRhs(_).withSpan(bridge.span))
  }

  /** Add all necessary bridges to template statements `stats`, and remove at the same
   *  time deferred methods in `stats` that are replaced by a bridge with the same signature.
   */
  def add(stats: List[untpd.Tree]): List[untpd.Tree] = {
    val opc = new BridgesCursor()(preErasureCtx)
    val ectx = ctx.withPhase(thisPhase)
    while (opc.hasNext) {
      if (!opc.overriding.is(Deferred)) {
        addBridgeIfNeeded(opc.overriding, opc.overridden)

        if (needsImplicitShortcut(opc.overriding)(ectx) && needsImplicitShortcut(opc.overridden)(ectx))
          // implicit shortcuts do not show up in the Bridges cursor, since they
          // are created only when referenced. Therefore we need to generate a bridge
          // for them specifically, if one is needed for the original methods.
          addBridgeIfNeeded(
            shortcutMethod(opc.overriding, thisPhase)(ectx),
            shortcutMethod(opc.overridden, thisPhase)(ectx))
      }
      opc.next()
    }
    if (bridges.isEmpty) stats
    else stats.filterNot(stat => toBeRemoved contains stat.symbol) ::: bridges.toList
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy