Skip to content

Commit 839a9fc

Browse files
committed
Merge pull request #109 from DarkDimius/erasure-bridges
Bridge generation in erasure implemented.
2 parents 75e30d9 + 0b50b29 commit 839a9fc

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)