diff options
author | Martin Odersky <odersky@gmail.com> | 2012-09-19 16:19:56 +0200 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-09-20 09:41:34 -0700 |
commit | e171d6d969fe7ce0dd2fd7a7679d6edb08c7579c (patch) | |
tree | f54e7991ce2dcc601f65ed0191b4260d7e6b21ee | |
parent | b94ae90110412e8af1e65230053c0e8aa26cd2d7 (diff) | |
download | scala-e171d6d969fe7ce0dd2fd7a7679d6edb08c7579c.tar.gz scala-e171d6d969fe7ce0dd2fd7a7679d6edb08c7579c.tar.bz2 scala-e171d6d969fe7ce0dd2fd7a7679d6edb08c7579c.zip |
Value classes: eliminated half-boxing
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.
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Erasure.scala | 10 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala | 3 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/transform/Erasure.scala | 22 | ||||
-rw-r--r-- | test/files/neg/valueclasses-pavlov.check | 7 | ||||
-rw-r--r-- | test/files/neg/valueclasses-pavlov.scala | 23 | ||||
-rw-r--r-- | test/files/run/Meter.scala | 6 | ||||
-rw-r--r-- | test/files/run/MeterCaseClass.scala | 6 | ||||
-rw-r--r-- | test/files/run/valueclasses-pavlov.check | 2 | ||||
-rw-r--r-- | test/files/run/valueclasses-pavlov.scala | 26 |
9 files changed, 87 insertions, 18 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 9381d6432e..072d823c60 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -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))) { @@ -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 diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 0820d3e714..c72fd3681f 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -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 } diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index cc5b5bb406..977398909f 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -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) } } diff --git a/test/files/neg/valueclasses-pavlov.check b/test/files/neg/valueclasses-pavlov.check new file mode 100644 index 0000000000..031589edad --- /dev/null +++ b/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 diff --git a/test/files/neg/valueclasses-pavlov.scala b/test/files/neg/valueclasses-pavlov.scala new file mode 100644 index 0000000000..a5858b2cf0 --- /dev/null +++ b/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)) + } +} diff --git a/test/files/run/Meter.scala b/test/files/run/Meter.scala index d94f338ca9..a0c04cc2a7 100644 --- a/test/files/run/Meter.scala +++ b/test/files/run/Meter.scala @@ -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) @@ -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] { @@ -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) diff --git a/test/files/run/MeterCaseClass.scala b/test/files/run/MeterCaseClass.scala index e5979cf761..18f8e23f89 100644 --- a/test/files/run/MeterCaseClass.scala +++ b/test/files/run/MeterCaseClass.scala @@ -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) @@ -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 @@ -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) diff --git a/test/files/run/valueclasses-pavlov.check b/test/files/run/valueclasses-pavlov.check new file mode 100644 index 0000000000..b112e5507e --- /dev/null +++ b/test/files/run/valueclasses-pavlov.check @@ -0,0 +1,2 @@ +box1: ok +box2: ok diff --git a/test/files/run/valueclasses-pavlov.scala b/test/files/run/valueclasses-pavlov.scala new file mode 100644 index 0000000000..e73897f653 --- /dev/null +++ b/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)) + } +} |