diff options
author | Dmitry Petrashko <dark@d-d.me> | 2014-04-04 20:04:44 +0200 |
---|---|---|
committer | Dmitry Petrashko <dark@d-d.me> | 2014-04-04 20:04:44 +0200 |
commit | 839a9fc5ecb2371672f25c2c5f639d0952ad8bbc (patch) | |
tree | 6fb8153f945d8d411be05a35e6ec72594906ed17 | |
parent | 75e30d92d17615c84292c6ebb3df1f15060197db (diff) | |
parent | 0b50b29cb9e8b43fff1d012dd53561b5ac0a437c (diff) | |
download | dotty-839a9fc5ecb2371672f25c2c5f639d0952ad8bbc.tar.gz dotty-839a9fc5ecb2371672f25c2c5f639d0952ad8bbc.tar.bz2 dotty-839a9fc5ecb2371672f25c2c5f639d0952ad8bbc.zip |
Merge pull request #109 from DarkDimius/erasure-bridges
Bridge generation in erasure implemented.
-rw-r--r-- | src/dotty/tools/dotc/transform/Erasure.scala | 97 | ||||
-rw-r--r-- | tests/pos/Bridges.scala | 11 |
2 files changed, 102 insertions, 6 deletions
diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index d4156e1d7..1f618b9f6 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -18,8 +18,10 @@ import typer.ProtoTypes._ import typer.ErrorReporting._ import core.transform.Erasure._ import core.Decorators._ -import ast.{tpd, untpd} +import dotty.tools.dotc.ast.{Trees, tpd, untpd} import ast.Trees._ +import scala.collection.mutable.ListBuffer +import dotty.tools.dotc.core.Flags class Erasure extends Phase with DenotTransformer { @@ -257,12 +259,95 @@ object Erasure { override def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) = EmptyTree - /* - override def transformStats(stats: List[Tree], exprOwner: Symbol)(implicit ctx: Context) = { - val stats1 = super.transform(stats, exprOwner) - if (ctx.owner.isClass) addBridges(stats1) else stats1 + override def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[tpd.Tree] = { + val statsFlatten = Trees.flatten(stats) + val stats1 = super.typedStats(statsFlatten, exprOwner) + + if (ctx.owner.isClass) addBridges(statsFlatten, stats1)(ctx) else stats1 + } + + // this implementation doesn't check for bridge clashes with value types! + def addBridges(oldStats: List[untpd.Tree], newStats: List[tpd.Tree])(implicit ctx: Context): List[tpd.Tree] = { + val beforeCtx = ctx.withPhase(ctx.erasurePhase) + def traverse(after: List[Tree], before: List[untpd.Tree], + emittedBridges: ListBuffer[tpd.DefDef] = ListBuffer[tpd.DefDef]()): List[tpd.DefDef] = { + after match { + case Nil => emittedBridges.toList + case (member: DefDef) :: newTail => + before match { + case Nil => emittedBridges.toList + case (oldMember: untpd.DefDef) :: oldTail => + val oldSymbol = oldMember.symbol(beforeCtx) + val newSymbol = member.symbol(ctx) + assert(oldSymbol.name(beforeCtx) == newSymbol.name, + s"${oldSymbol.name(beforeCtx)} bridging with ${newSymbol.name}") + val newOverriden = oldSymbol.denot.allOverriddenSymbols.toSet + val oldOverriden = newSymbol.allOverriddenSymbols(beforeCtx).toSet + val neededBridges = oldOverriden -- newOverriden + + var minimalSet = Set[Symbol]() + // compute minimal set of bridges that are needed: + for (bridge <- neededBridges) { + val isRequired = minimalSet.forall(nxtBridge => !(bridge.info =:= nxtBridge.info)) + + if (isRequired) { + // check for clashes + val clash: Option[Symbol] = oldSymbol.owner.decls.lookupAll(bridge.name).find { + sym => + (sym.name eq bridge.name) && sym.info.widen =:= bridge.info.widen + }.orElse( + emittedBridges.find(stat => (stat.name == bridge.name) && stat.tpe.widen =:= bridge.info.widen) + .map(_.symbol) + ) + clash match { + case Some(cl) => + ctx.error(s"bridge for method ${newSymbol.show(beforeCtx)}\n" + + s"clashes with ${cl.symbol.show(beforeCtx)}\n" + + s"both have same type after erasure: ${bridge.symbol.info.show}") + case None => minimalSet += bridge + } + } + } + + val bridgeImplementations = minimalSet.map { + sym => makeBridgeDef(member, sym)(ctx) + } + emittedBridges ++= bridgeImplementations + traverse(newTail, oldTail) + case notADefDef :: oldTail => + traverse(after, oldTail) + } + case notADefDef :: newTail => + traverse(newTail, before) + } + } + + traverse(newStats, oldStats) + } + + def makeBridgeDef(newDef: tpd.DefDef, parentSym: Symbol)(implicit ctx: Context): tpd.DefDef = { + def error(reason: String) = { + assert(false, s"failure creating bridge from ${newDef.symbol} to ${parentSym}, reason: $reason") + ??? + } + val bridge = ctx.newSymbol(newDef.symbol.owner, + parentSym.name, parentSym.flags | Flags.Bridge, parentSym.info, coord = newDef.symbol.owner.coord).asTerm + bridge.entered // this should be safe, as we're executing in context of next phase + ctx.debuglog(s"generating bridge from ${newDef.symbol} to $bridge") + + val sel: Tree = tpd.Select(This(newDef.symbol.owner.asClass), newDef.symbol.termRef) + + val resultType = bridge.info.widen.resultType + tpd.DefDef(bridge, { paramss: List[List[tpd.Tree]] => + val rhs = paramss.foldLeft(sel)((fun, vparams) => + fun.tpe.widen match { + case MethodType(names, types) => Apply(fun, (vparams, types).zipped.map(adapt)) + case a => error(s"can not resolve apply type $a") + + }) + adapt(rhs, resultType) + }) } -*/ override def adapt(tree: Tree, pt: Type)(implicit ctx: Context): Tree = ctx.traceIndented(i"adapting ${tree.showSummary}: ${tree.tpe} to $pt", show = true) { diff --git a/tests/pos/Bridges.scala b/tests/pos/Bridges.scala new file mode 100644 index 000000000..a7350d785 --- /dev/null +++ b/tests/pos/Bridges.scala @@ -0,0 +1,11 @@ +abstract class X[T]{ + def go2(x:T)(y:T = x): T = y + def go: T + def go1(x: T) = x +} + +class Y extends X[Int] { + override def go2(x: Int)(z: Int) = 2 + override def go = 0 + override def go1(x: Int) = x +} |