Skip to content

Commit

Permalink
Value classes: eliminated half-boxing
Browse files Browse the repository at this point in the history
We now apply erasure of value classes everywhere. previously,
erasure was disabled in the value class itself. This led to
irregegularities and bugs. See test run/valueclasses-pavlov.scala
for something that led to a ClassCastException before.
  • Loading branch information
odersky authored and paulp committed Sep 20, 2012
1 parent b94ae90 commit e171d6d
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 18 deletions.
10 changes: 9 additions & 1 deletion src/compiler/scala/tools/nsc/transform/Erasure.scala
Expand Up @@ -429,6 +429,14 @@ abstract class Erasure extends AddInterfaces
|both have erased type ${afterPostErasure(bridge.tpe)}""".stripMargin)
}
for (bc <- root.baseClasses) {
if (settings.debug.value)
afterPostErasure(println(
s"""check bridge overrides in $bc
${bc.info.nonPrivateDecl(bridge.name)}
${site.memberType(bridge)}
${site.memberType(bc.info.nonPrivateDecl(bridge.name) orElse IntClass)}
${(bridge.matchingSymbol(bc, site))}""".stripMargin))

def overriddenBy(sym: Symbol) =
sym.matchingSymbol(bc, site).alternatives filter (sym => !sym.isBridge)
for (overBridge <- afterPostErasure(overriddenBy(bridge))) {
Expand Down Expand Up @@ -1046,7 +1054,7 @@ abstract class Erasure extends AddInterfaces
preEraseIsInstanceOf
} else if (fn.symbol.owner.isRefinementClass && !fn.symbol.isOverridingSymbol) {
ApplyDynamic(qualifier, args) setSymbol fn.symbol setPos tree.pos
} else if (fn.symbol.isMethodWithExtension) {
} else if (fn.symbol.isMethodWithExtension && !fn.symbol.tpe.isErroneous) {
Apply(gen.mkAttributedRef(extensionMethods.extensionMethod(fn.symbol)), qualifier :: args)
} else {
tree
Expand Down
Expand Up @@ -70,7 +70,8 @@ abstract class ExtensionMethods extends Transform with TypingTransformers {
val companionInfo = imeth.owner.companionModule.info
val candidates = extensionNames(imeth) map (companionInfo.decl(_))
val matching = candidates filter (alt => normalize(alt.tpe, imeth.owner) matches imeth.tpe)
assert(matching.nonEmpty, "no extension method found for "+imeth+" among "+candidates+"/"+extensionNames(imeth))
assert(matching.nonEmpty,
s"no extension method found for $imeth:${imeth.tpe}+among ${candidates map (c => c.name+":"+c.tpe)} / ${extensionNames(imeth)}")
matching.head
}

Expand Down
22 changes: 10 additions & 12 deletions src/reflect/scala/reflect/internal/transform/Erasure.scala
Expand Up @@ -203,28 +203,26 @@ trait Erasure {
def specialErasure(sym: Symbol)(tp: Type): Type =
if (sym != NoSymbol && sym.enclClass.isJavaDefined)
erasure(sym)(tp)
else if (sym.isTerm && sym.owner.isDerivedValueClass)
specialErasureAvoiding(sym.owner, tp)
else if (sym.isValue && sym.owner.isMethodWithExtension)
specialErasureAvoiding(sym.owner.owner, tp)
else if (sym.isClassConstructor)
specialConstructorErasure(sym.owner, tp)
else
specialScalaErasure(tp)

def specialErasureAvoiding(clazz: Symbol, tpe: Type): Type = {
def specialConstructorErasure(clazz: Symbol, tpe: Type): Type = {
tpe match {
case PolyType(tparams, restpe) =>
specialErasureAvoiding(clazz, restpe)
specialConstructorErasure(clazz, restpe)
case ExistentialType(tparams, restpe) =>
specialErasureAvoiding(clazz, restpe)
specialConstructorErasure(clazz, restpe)
case mt @ MethodType(params, restpe) =>
MethodType(
cloneSymbolsAndModify(params, specialErasureAvoiding(clazz, _)),
if (restpe.typeSymbol == UnitClass) erasedTypeRef(UnitClass)
else specialErasureAvoiding(clazz, (mt.resultType(mt.paramTypes))))
cloneSymbolsAndModify(params, specialScalaErasure),
specialConstructorErasure(clazz, restpe))
case TypeRef(pre, `clazz`, args) =>
typeRef(pre, clazz, List())
case _ =>
specialScalaErasure(tpe)
case tp =>
assert(clazz == ArrayClass || tp.isError, s"unexpected constructor erasure $tp for $clazz")
specialScalaErasure(tp)
}
}

Expand Down
7 changes: 7 additions & 0 deletions test/files/neg/valueclasses-pavlov.check
@@ -0,0 +1,7 @@
valueclasses-pavlov.scala:8: error: double definition:
method foo:(x: Box2)String and
method foo:(x: String)String at line 7
have same type after erasure: (x: String)String
def foo(x: Box2) = "foo(Box2): ok"
^
one error found
23 changes: 23 additions & 0 deletions test/files/neg/valueclasses-pavlov.scala
@@ -0,0 +1,23 @@
trait Foo[T <: AnyVal] extends Any {
def foo(x: String): String
def foo(x: T): String
}

class Box1(val value: String) extends AnyVal with Foo[Box2] {
def foo(x: String) = "foo(String): ok"
def foo(x: Box2) = "foo(Box2): ok"
}

class Box2(val value: String) extends AnyVal


object test2a {

def main(args: Array[String]) {
val b1 = new Box1(null)
val b2 = new Box2(null)
val f: Foo[Box2] = b1
println(f.foo(""))
println(f.foo(b2))
}
}
6 changes: 4 additions & 2 deletions test/files/run/Meter.scala
Expand Up @@ -2,7 +2,7 @@ package a {
class Meter(val underlying: Double) extends AnyVal with _root_.b.Printable {
def + (other: Meter): Meter =
new Meter(this.underlying + other.underlying)
def / (other: Meter): Double = this.underlying / other.underlying
def / (other: Meter)(implicit dummy: Meter.MeterArg = null): Double = this.underlying / other.underlying
def / (factor: Double): Meter = new Meter(this.underlying / factor)
def < (other: Meter): Boolean = this.underlying < other.underlying
def toFoot: Foot = new Foot(this.underlying * 0.3048)
Expand All @@ -12,6 +12,8 @@ package a {

object Meter extends (Double => Meter) {

private[a] trait MeterArg

def apply(x: Double): Meter = new Meter(x)

implicit val boxings = new BoxingConversions[Meter, Double] {
Expand Down Expand Up @@ -80,7 +82,7 @@ object Test extends App {
println(m)
foo(arr)
}
//
//
// { println("testing wrapped arrays")
// import collection.mutable.FlatArray
// val arr = FlatArray(x, y + x)
Expand Down
6 changes: 4 additions & 2 deletions test/files/run/MeterCaseClass.scala
Expand Up @@ -2,7 +2,7 @@ package a {
case class Meter(underlying: Double) extends AnyVal with _root_.b.Printable {
def + (other: Meter): Meter =
new Meter(this.underlying + other.underlying)
def / (other: Meter): Double = this.underlying / other.underlying
def / (other: Meter)(implicit dummy: Meter.MeterArg = null): Double = this.underlying / other.underlying
def / (factor: Double): Meter = new Meter(this.underlying / factor)
def < (other: Meter): Boolean = this.underlying < other.underlying
def toFoot: Foot = new Foot(this.underlying * 0.3048)
Expand All @@ -11,6 +11,8 @@ package a {

object Meter extends (Double => Meter) {

private[a] trait MeterArg

implicit val boxings = new BoxingConversions[Meter, Double] {
def box(x: Double) = new Meter(x)
def unbox(m: Meter) = m.underlying
Expand Down Expand Up @@ -77,7 +79,7 @@ object Test extends App {
println(m)
foo(arr)
}
//
//
// { println("testing wrapped arrays")
// import collection.mutable.FlatArray
// val arr = FlatArray(x, y + x)
Expand Down
2 changes: 2 additions & 0 deletions test/files/run/valueclasses-pavlov.check
@@ -0,0 +1,2 @@
box1: ok
box2: ok
26 changes: 26 additions & 0 deletions test/files/run/valueclasses-pavlov.scala
@@ -0,0 +1,26 @@
trait Foo extends Any {
def box1(x: Box1): String
def box2(x: Box2): String
}

class Box1(val value: String) extends AnyVal

class Box2(val value: String) extends AnyVal with Foo {
def box1(x: Box1) = "box1: ok"
def box2(x: Box2) = "box2: ok"
}

class C(x: String) {
def this() = this("")
}

object Test {

def main(args: Array[String]) {
val b1 = new Box1("")
val b2 = new Box2("")
val f: Foo = b2
println(f.box1(b1))
println(f.box2(b2))
}
}

0 comments on commit e171d6d

Please sign in to comment.