Skip to content

Commit aa7ee91

Browse files
committed
Fix #1905: Detect case where bridge would clash with the member
With value classes it is possible that a bridge is needed because the semi erased types of a member and its overridden method differ, yet the fully erased types of the member and the bridge are the same. With this commit we flag this situation as an error. Before it generated a ClassCastException at runtime instead.
1 parent f4abdd8 commit aa7ee91

File tree

2 files changed

+39
-5
lines changed

2 files changed

+39
-5
lines changed

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

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class Bridges(root: ClassSymbol)(implicit ctx: Context) {
1616

1717
assert(ctx.phase == ctx.erasurePhase.next)
1818
private val preErasureCtx = ctx.withPhase(ctx.erasurePhase)
19+
private val elimErasedCtx = ctx.withPhase(ctx.elimErasedValueTypePhase.next)
1920

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

@@ -35,31 +36,47 @@ class Bridges(root: ClassSymbol)(implicit ctx: Context) {
3536
private val bridgesScope = newScope
3637
private val bridgeTarget = newMutableSymbolMap[Symbol]
3738

39+
def bridgePosFor(member: Symbol) =
40+
if (member.owner == root && member.pos.exists) member.pos else root.pos
41+
3842
/** Add a bridge between `member` and `other`, where `member` overrides `other`
3943
* before erasure, if the following conditions are satisfied.
4044
*
41-
* - `member` and other have different signatures
42-
* - there is not yet a bridge with the same name and signature in `root`
45+
* - `member` and `other` have different signatures
46+
* - there is not yet a bridge with the same name and signature in `root`.
4347
*
4448
* The bridge has the erased info of `other` and forwards to `member`.
49+
* Additionally, if `member` and `other` do have the same signature,
50+
* but not the same type after erasure and before elimErasedValueTypes
51+
* issue an error: A bridge would be needed yet it would clash with the member itself.
52+
* See neg/i1905.scala
4553
*/
4654
private def addBridgeIfNeeded(member: Symbol, other: Symbol) = {
4755
def bridgeExists =
4856
bridgesScope.lookupAll(member.name).exists(bridge =>
4957
bridgeTarget(bridge) == member && bridge.signature == other.signature)
50-
if (!(member.signature == other.signature || bridgeExists))
58+
def info(sym: Symbol)(implicit ctx: Context) = sym.info
59+
def desc(sym: Symbol)= i"$sym${info(sym)(preErasureCtx)} in ${sym.owner}"
60+
if (member.signature == other.signature) {
61+
if (!member.info.matches(other.info))
62+
ctx.error(em"""bridge generated for member ${desc(member)}
63+
|which overrides ${desc(other)}
64+
|clashes with definition of the member itself; both have erased type ${info(member)(elimErasedCtx)}."""",
65+
bridgePosFor(member))
66+
}
67+
else if (!bridgeExists)
5168
addBridge(member, other)
5269
}
5370

5471
/** Generate bridge between `member` and `other`
5572
*/
5673
private def addBridge(member: Symbol, other: Symbol) = {
57-
val bridgePos = if (member.owner == root && member.pos.exists) member.pos else root.pos
5874
val bridge = other.copy(
5975
owner = root,
6076
flags = (member.flags | Method | Bridge | Artifact) &~
6177
(Accessor | ParamAccessor | CaseAccessor | Deferred | Lazy | Module),
62-
coord = bridgePos).enteredAfter(ctx.erasurePhase.asInstanceOf[DenotTransformer]).asTerm
78+
coord = bridgePosFor(member))
79+
.enteredAfter(ctx.erasurePhase.asInstanceOf[DenotTransformer]).asTerm
6380

6481
ctx.debuglog(
6582
i"""generating bridge from ${other.showLocated}: ${other.info}

tests/neg/i1905.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class Arr[T](private val underlying: scala.Array[T]) extends AnyVal
2+
3+
abstract class SeqMonoTransforms[+A, +Repr] {
4+
protected[this] def fromIterableWithSameElemType(): Repr
5+
def getFIWSET: Repr = fromIterableWithSameElemType()
6+
}
7+
8+
class ArrOps[A](val xs: Arr[A]) extends SeqMonoTransforms[A, Arr[A]] {
9+
def fromIterableWithSameElemType(): Arr[A] = xs // error: bridge clashes with member
10+
}
11+
12+
object Test {
13+
def main(args: Array[String]) = {
14+
val t = new ArrOps(new Arr(Array(1, 2, 3)))
15+
val t2 = t.getFIWSET
16+
}
17+
}

0 commit comments

Comments
 (0)