Skip to content

Commit 0b50b29

Browse files
committed
Bridge generation in erasure implemented.
Relies on meaning of "override" to see which bridges are required. Doesn't take in account value classes for now Uses 'adapt' used by erasure for converting arguments and return value.
1 parent 2033b56 commit 0b50b29

File tree

2 files changed

+102
-6
lines changed

2 files changed

+102
-6
lines changed

src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ import typer.ProtoTypes._
1818
import typer.ErrorReporting._
1919
import core.transform.Erasure._
2020
import core.Decorators._
21-
import ast.{tpd, untpd}
21+
import dotty.tools.dotc.ast.{Trees, tpd, untpd}
2222
import ast.Trees._
23+
import scala.collection.mutable.ListBuffer
24+
import dotty.tools.dotc.core.Flags
2325

2426
class Erasure extends Phase with DenotTransformer {
2527

@@ -257,12 +259,95 @@ object Erasure {
257259
override def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) =
258260
EmptyTree
259261

260-
/*
261-
override def transformStats(stats: List[Tree], exprOwner: Symbol)(implicit ctx: Context) = {
262-
val stats1 = super.transform(stats, exprOwner)
263-
if (ctx.owner.isClass) addBridges(stats1) else stats1
262+
override def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[tpd.Tree] = {
263+
val statsFlatten = Trees.flatten(stats)
264+
val stats1 = super.typedStats(statsFlatten, exprOwner)
265+
266+
if (ctx.owner.isClass) addBridges(statsFlatten, stats1)(ctx) else stats1
267+
}
268+
269+
// this implementation doesn't check for bridge clashes with value types!
270+
def addBridges(oldStats: List[untpd.Tree], newStats: List[tpd.Tree])(implicit ctx: Context): List[tpd.Tree] = {
271+
val beforeCtx = ctx.withPhase(ctx.erasurePhase)
272+
def traverse(after: List[Tree], before: List[untpd.Tree],
273+
emittedBridges: ListBuffer[tpd.DefDef] = ListBuffer[tpd.DefDef]()): List[tpd.DefDef] = {
274+
after match {
275+
case Nil => emittedBridges.toList
276+
case (member: DefDef) :: newTail =>
277+
before match {
278+
case Nil => emittedBridges.toList
279+
case (oldMember: untpd.DefDef) :: oldTail =>
280+
val oldSymbol = oldMember.symbol(beforeCtx)
281+
val newSymbol = member.symbol(ctx)
282+
assert(oldSymbol.name(beforeCtx) == newSymbol.name,
283+
s"${oldSymbol.name(beforeCtx)} bridging with ${newSymbol.name}")
284+
val newOverriden = oldSymbol.denot.allOverriddenSymbols.toSet
285+
val oldOverriden = newSymbol.allOverriddenSymbols(beforeCtx).toSet
286+
val neededBridges = oldOverriden -- newOverriden
287+
288+
var minimalSet = Set[Symbol]()
289+
// compute minimal set of bridges that are needed:
290+
for (bridge <- neededBridges) {
291+
val isRequired = minimalSet.forall(nxtBridge => !(bridge.info =:= nxtBridge.info))
292+
293+
if (isRequired) {
294+
// check for clashes
295+
val clash: Option[Symbol] = oldSymbol.owner.decls.lookupAll(bridge.name).find {
296+
sym =>
297+
(sym.name eq bridge.name) && sym.info.widen =:= bridge.info.widen
298+
}.orElse(
299+
emittedBridges.find(stat => (stat.name == bridge.name) && stat.tpe.widen =:= bridge.info.widen)
300+
.map(_.symbol)
301+
)
302+
clash match {
303+
case Some(cl) =>
304+
ctx.error(s"bridge for method ${newSymbol.show(beforeCtx)}\n" +
305+
s"clashes with ${cl.symbol.show(beforeCtx)}\n" +
306+
s"both have same type after erasure: ${bridge.symbol.info.show}")
307+
case None => minimalSet += bridge
308+
}
309+
}
310+
}
311+
312+
val bridgeImplementations = minimalSet.map {
313+
sym => makeBridgeDef(member, sym)(ctx)
314+
}
315+
emittedBridges ++= bridgeImplementations
316+
traverse(newTail, oldTail)
317+
case notADefDef :: oldTail =>
318+
traverse(after, oldTail)
319+
}
320+
case notADefDef :: newTail =>
321+
traverse(newTail, before)
322+
}
323+
}
324+
325+
traverse(newStats, oldStats)
326+
}
327+
328+
def makeBridgeDef(newDef: tpd.DefDef, parentSym: Symbol)(implicit ctx: Context): tpd.DefDef = {
329+
def error(reason: String) = {
330+
assert(false, s"failure creating bridge from ${newDef.symbol} to ${parentSym}, reason: $reason")
331+
???
332+
}
333+
val bridge = ctx.newSymbol(newDef.symbol.owner,
334+
parentSym.name, parentSym.flags | Flags.Bridge, parentSym.info, coord = newDef.symbol.owner.coord).asTerm
335+
bridge.entered // this should be safe, as we're executing in context of next phase
336+
ctx.debuglog(s"generating bridge from ${newDef.symbol} to $bridge")
337+
338+
val sel: Tree = tpd.Select(This(newDef.symbol.owner.asClass), newDef.symbol.termRef)
339+
340+
val resultType = bridge.info.widen.resultType
341+
tpd.DefDef(bridge, { paramss: List[List[tpd.Tree]] =>
342+
val rhs = paramss.foldLeft(sel)((fun, vparams) =>
343+
fun.tpe.widen match {
344+
case MethodType(names, types) => Apply(fun, (vparams, types).zipped.map(adapt))
345+
case a => error(s"can not resolve apply type $a")
346+
347+
})
348+
adapt(rhs, resultType)
349+
})
264350
}
265-
*/
266351

267352
override def adapt(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
268353
ctx.traceIndented(i"adapting ${tree.showSummary}: ${tree.tpe} to $pt", show = true) {

tests/pos/Bridges.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
abstract class X[T]{
2+
def go2(x:T)(y:T = x): T = y
3+
def go: T
4+
def go1(x: T) = x
5+
}
6+
7+
class Y extends X[Int] {
8+
override def go2(x: Int)(z: Int) = 2
9+
override def go = 0
10+
override def go1(x: Int) = x
11+
}

0 commit comments

Comments
 (0)