Skip to content

Commit

Permalink
Merge pull request #3710 from dotty-staging/fix-#2994
Browse files Browse the repository at this point in the history
Fix #1905: Detect case where bridge would clash with the member
  • Loading branch information
odersky authored Jan 13, 2018
2 parents 6d91ef2 + f653095 commit b862758
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 6 deletions.
8 changes: 7 additions & 1 deletion compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case tp: TypeRef =>
if (tp.symbol.isAnonymousClass && !ctx.settings.uniqid.value)
return toText(tp.info)
if (tp.symbol.is(Param))
tp.prefix match {
case pre: ThisType if pre.cls == tp.symbol.owner =>
return nameString(tp.symbol)
case _ =>
}
case ExprType(result) =>
return "=> " ~ toText(result)
case ErasedValueType(tycon, underlying) =>
Expand Down Expand Up @@ -501,7 +507,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
typeDefText(tparamsTxt, toText(rhs))
case LambdaTypeTree(tparams, body) =>
recur(body, tparamsText(tparams))
case rhs: TypeTree if rhs.tpe.isInstanceOf[TypeBounds] =>
case rhs: TypeTree if rhs.typeOpt.isInstanceOf[TypeBounds] =>
typeDefText(tparamsTxt, toText(rhs))
case rhs =>
typeDefText(tparamsTxt, optText(rhs)(" = " ~ _))
Expand Down
27 changes: 22 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/Bridges.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Bridges(root: ClassSymbol)(implicit ctx: Context) {

assert(ctx.phase == ctx.erasurePhase.next)
private val preErasureCtx = ctx.withPhase(ctx.erasurePhase)
private val elimErasedCtx = ctx.withPhase(ctx.elimErasedValueTypePhase.next)

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

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

def bridgePosFor(member: Symbol) =
if (member.owner == root && member.pos.exists) member.pos else root.pos

/** Add a bridge between `member` and `other`, where `member` overrides `other`
* before erasure, if the following conditions are satisfied.
*
* - `member` and other have different signatures
* - there is not yet a bridge with the same name and signature in `root`
* - `member` and `other` have different signatures
* - there is not yet a bridge with the same name and signature in `root`.
*
* The bridge has the erased info of `other` and forwards to `member`.
* Additionally, if `member` and `other` do have the same signature,
* but not the same type after erasure and before elimErasedValueTypes
* issue an error: A bridge would be needed yet it would clash with the member itself.
* See neg/i1905.scala
*/
private def addBridgeIfNeeded(member: Symbol, other: Symbol) = {
def bridgeExists =
bridgesScope.lookupAll(member.name).exists(bridge =>
bridgeTarget(bridge) == member && bridge.signature == other.signature)
if (!(member.signature == other.signature || bridgeExists))
def info(sym: Symbol)(implicit ctx: Context) = sym.info
def desc(sym: Symbol)= i"$sym${info(sym)(preErasureCtx)} in ${sym.owner}"
if (member.signature == other.signature) {
if (!member.info.matches(other.info))
ctx.error(em"""bridge generated for member ${desc(member)}
|which overrides ${desc(other)}
|clashes with definition of the member itself; both have erased type ${info(member)(elimErasedCtx)}."""",
bridgePosFor(member))
}
else if (!bridgeExists)
addBridge(member, other)
}

/** Generate bridge between `member` and `other`
*/
private def addBridge(member: Symbol, other: Symbol) = {
val bridgePos = if (member.owner == root && member.pos.exists) member.pos else root.pos
val bridge = other.copy(
owner = root,
flags = (member.flags | Method | Bridge | Artifact) &~
(Accessor | ParamAccessor | CaseAccessor | Deferred | Lazy | Module),
coord = bridgePos).enteredAfter(ctx.erasurePhase.asInstanceOf[DenotTransformer]).asTerm
coord = bridgePosFor(member))
.enteredAfter(ctx.erasurePhase.asInstanceOf[DenotTransformer]).asTerm

ctx.debuglog(
i"""generating bridge from ${other.showLocated}: ${other.info}
Expand Down
17 changes: 17 additions & 0 deletions tests/neg/i1905.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class Arr[T](private val underlying: scala.Array[T]) extends AnyVal

abstract class SeqMonoTransforms[+A, +Repr] {
protected[this] def fromIterableWithSameElemType(): Repr
def getFIWSET: Repr = fromIterableWithSameElemType()
}

class ArrOps[A](val xs: Arr[A]) extends SeqMonoTransforms[A, Arr[A]] {
def fromIterableWithSameElemType(): Arr[A] = xs // error: bridge clashes with member
}

object Test {
def main(args: Array[String]) = {
val t = new ArrOps(new Arr(Array(1, 2, 3)))
val t2 = t.getFIWSET
}
}

0 comments on commit b862758

Please sign in to comment.