Skip to content

Commit d9ea0a0

Browse files
committed
New bridge implementation
This one is based on the way things are done in scalac. As yet missing: Systematic treatment of errors. Test i1240a was moved to pending. It caused a merge error before but this was likely accidental. We have to check whether systematic bridge error checking will discover a problem. For the moment the test behaves exactly as for scalac: it compiles and the result is the same.
1 parent ba21f73 commit d9ea0a0

File tree

5 files changed

+113
-5
lines changed

5 files changed

+113
-5
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package dotty.tools
2+
package dotc
3+
package transform
4+
5+
import core._
6+
import Symbols._, Types._, Contexts._, Decorators._, SymDenotations._, Flags._, Scopes._
7+
import DenotTransformers._
8+
import ast.untpd
9+
import collection.{mutable, immutable}
10+
import TypeErasure._
11+
import ValueClasses.isDerivedValueClass
12+
13+
/** A helper class for generating bridge methods in class `root`. */
14+
class Bridges(root: ClassSymbol)(implicit ctx: Context) {
15+
import ast.tpd._
16+
17+
assert(ctx.phase == ctx.erasurePhase.next)
18+
private val preErasureCtx = ctx.withPhase(ctx.erasurePhase)
19+
20+
private class BridgesCursor(implicit ctx: Context) extends OverridingPairs.Cursor(root) {
21+
22+
/** Only use the superclass of `root` as a parent class. This means
23+
* overriding pairs that have a common implementation in a trait parent
24+
* are also counted. This is necessary because we generate bridge methods
25+
* only in classes, never in traits.
26+
*/
27+
override def parents = Array(root.superClass)
28+
override def exclude(sym: Symbol) = !sym.is(Method) || super.exclude(sym)
29+
}
30+
31+
//val site = root.thisType
32+
33+
private var toBeRemoved = immutable.Set[Symbol]()
34+
private val bridges = mutable.ListBuffer[Tree]()
35+
private val bridgesScope = newScope
36+
private val bridgeTarget = mutable.HashMap[Symbol, Symbol]()
37+
38+
/** Add a bridge between `member` and `other`, where `member` overrides `other`
39+
* before erasure, if the following conditions are satisfied.
40+
*
41+
* - `member` and other have different signatures
42+
* - `member` is not inline
43+
* - there is not yet a bridge with the same name and signature in `root`
44+
*
45+
* The bridge has the erased info of `other` and forwards to `member`.
46+
*/
47+
private def addBridgeIfNeeded(member: Symbol, other: Symbol) = {
48+
val otherInfo = erasure(other.info)
49+
def bridgeExists =
50+
bridgesScope.lookupAll(member.name).exists(bridge =>
51+
bridgeTarget(bridge) == member && bridge.info =:= otherInfo)
52+
if (!(member.is(Inline) || other.info =:= member.info || bridgeExists))
53+
addBridge(member, other)
54+
}
55+
56+
/** Generate bridge between `member` and `other`
57+
*/
58+
private def addBridge(member: Symbol, other: Symbol) = {
59+
val bridgePos = if (member.owner == root && member.pos.exists) member.pos else root.pos
60+
val bridge = other.copy(
61+
owner = root,
62+
flags = (member.flags | Method | Bridge | Artifact) &~
63+
(Accessor | ParamAccessor | CaseAccessor | Deferred | Lazy | Module),
64+
coord = bridgePos).enteredAfter(ctx.erasurePhase.asInstanceOf[DenotTransformer]).asTerm
65+
66+
println(
67+
i"""generating bridge from ${other.showLocated}: ${other.info}
68+
|to ${member.showLocated}: ${member.info} @ ${member.pos}
69+
|bridge: ${bridge.showLocated} with flags: ${bridge.flags}""")
70+
71+
bridgeTarget(bridge) = member
72+
bridgesScope.enter(bridge)
73+
74+
if (other.owner == root) {
75+
root.delete(other)
76+
toBeRemoved += other
77+
}
78+
79+
bridges +=
80+
DefDef(bridge, This(root).select(member).appliedToArgss(_)).withPos(bridge.pos)
81+
}
82+
83+
/** Add all necessary bridges to template statements `stats`, and remove at the same
84+
* time deferred methods in `stats` that are replaced by a bridge with the same signature.
85+
*/
86+
def add(stats: List[untpd.Tree]): List[untpd.Tree] =
87+
if (root.is(Trait)) stats
88+
else {
89+
val opc = new BridgesCursor()(preErasureCtx)
90+
while (opc.hasNext) {
91+
if (!opc.overriding.is(Deferred)) addBridgeIfNeeded(opc.overriding, opc.overridden)
92+
opc.next()
93+
}
94+
if (bridges.isEmpty) stats
95+
else stats.filterNot(stat => toBeRemoved contains stat.symbol) ::: bridges.toList
96+
}
97+
}

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class Erasure extends Phase with DenotTransformer { thisTransformer =>
6767
val oldOwner = ref.owner
6868
val newOwner = if (oldOwner eq defn.AnyClass) defn.ObjectClass else oldOwner
6969
val oldInfo = ref.info
70-
val newInfo = transformInfo(ref.symbol, oldInfo)
70+
val newInfo = transformInfo(oldSymbol, oldInfo)
7171
val oldFlags = ref.flags
7272
val newFlags =
7373
if (oldSymbol.is(Flags.TermParam) && isCompacted(oldSymbol.owner)) oldFlags &~ Flags.Param
@@ -594,8 +594,10 @@ object Erasure extends TypeTestsCasts{
594594
EmptyTree
595595

596596
override def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = {
597-
val stats1 = Trees.flatten(super.typedStats(stats, exprOwner))
598-
if (ctx.owner.isClass) stats1 ::: addBridges(stats, stats1)(ctx) else stats1
597+
val stats1 =
598+
if (takesBridges(ctx.owner)) new Bridges(ctx.owner.asClass).add(stats)
599+
else stats
600+
super.typedStats(stats1, exprOwner)
599601
}
600602

601603
// this implementation doesn't check for bridge clashes with value types!
@@ -721,4 +723,7 @@ object Erasure extends TypeTestsCasts{
721723
else adaptToType(tree, pt)
722724
}
723725
}
726+
727+
def takesBridges(sym: Symbol)(implicit ctx: Context) =
728+
sym.isClass && !sym.is(Flags.Trait | Flags.Package)
724729
}

compiler/src/dotty/tools/dotc/transform/OverridingPairs.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ object OverridingPairs {
3131
*/
3232
protected def exclude(sym: Symbol): Boolean = !sym.memberCanMatchInheritedSymbols
3333

34-
/** The parents of base (may also be refined).
34+
/** The parents of base that are checked when deciding whether an overriding
35+
* pair has already been treated in a parent class.
36+
* This may be refined in subclasses. @see Bridges for a use case.
3537
*/
3638
protected def parents: Array[Symbol] = base.info.parents.toArray map (_.typeSymbol)
3739

tests/neg/i1240a.scala renamed to tests/pending/neg/i1240a.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ class B extends A {
1212
override def give[X] = Nil
1313
override def foo[B](x: C[B]): C[B] = {println("B.C"); x} // error: merge error during erasure
1414
val a: A = this
15-
a.foo(a.give[Int]) // what method should be called here in runtime?
15+
}
16+
17+
object Test extends B {
18+
def main(args: Array[String]): Unit =
19+
a.foo(a.give[Int]) // what method should be called here in runtime?
1620
}
1721

File renamed without changes.

0 commit comments

Comments
 (0)