From 96408154f46dce623d3b3c3fdc67f5ccc3779f8f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 10 Sep 2012 18:07:20 +0200 Subject: Fixes SI-6260 Guards against bridge methods that clash with other methods. Two tests: The neg test is the original ticket. The run test tweaks things slightly so that the generated bridge method does not clash, and tests that the necessary unboxings are indeed performed at runtime. --- test/files/neg/t6260.check | 13 +++++++++++++ test/files/neg/t6260.scala | 17 +++++++++++++++++ test/files/run/t6260.check | 1 + test/files/run/t6260.scala | 12 ++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 test/files/neg/t6260.check create mode 100644 test/files/neg/t6260.scala create mode 100644 test/files/run/t6260.check create mode 100644 test/files/run/t6260.scala (limited to 'test/files') diff --git a/test/files/neg/t6260.check b/test/files/neg/t6260.check new file mode 100644 index 0000000000..2b7f1a8bfb --- /dev/null +++ b/test/files/neg/t6260.check @@ -0,0 +1,13 @@ +t6260.scala:3: error: bridge generated for member method apply: (x$1: Box[X])Box[Y] in anonymous class $anonfun +which overrides method apply: (v1: T1)R in trait Function1 +clashes with definition of the member itself; +both have erased type (v1: Object)Object + ((bx: Box[X]) => new Box(f(bx.x)))(this) + ^ +t6260.scala:8: error: bridge generated for member method apply: (x$1: Box[X])Box[Y] in anonymous class $anonfun +which overrides method apply: (v1: T1)R in trait Function1 +clashes with definition of the member itself; +both have erased type (v1: Object)Object + ((bx: Box[X]) => new Box(f(bx.x)))(self) + ^ +two errors found diff --git a/test/files/neg/t6260.scala b/test/files/neg/t6260.scala new file mode 100644 index 0000000000..93b5448227 --- /dev/null +++ b/test/files/neg/t6260.scala @@ -0,0 +1,17 @@ +class Box[X](val x: X) extends AnyVal { + def map[Y](f: X => Y): Box[Y] = + ((bx: Box[X]) => new Box(f(bx.x)))(this) +} + +object Test { + def map2[X, Y](self: Box[X], f: X => Y): Box[Y] = + ((bx: Box[X]) => new Box(f(bx.x)))(self) + + def main(args: Array[String]) { + val f = (x: Int) => x + 1 + val g = (x: String) => x + x + + map2(new Box(42), f) + new Box("abc") map g + } +} diff --git a/test/files/run/t6260.check b/test/files/run/t6260.check new file mode 100644 index 0000000000..54f98a10f0 --- /dev/null +++ b/test/files/run/t6260.check @@ -0,0 +1 @@ +Box(abcabc) diff --git a/test/files/run/t6260.scala b/test/files/run/t6260.scala new file mode 100644 index 0000000000..cfe9e1e640 --- /dev/null +++ b/test/files/run/t6260.scala @@ -0,0 +1,12 @@ +class Box[X <: CharSequence](val x: X) extends AnyVal { + def map[Y <: CharSequence](f: X => Y): Box[Y] = + ((bx: Box[X]) => new Box(f(bx.x)))(this) + override def toString = s"Box($x)" +} + +object Test { + def main(args: Array[String]) { + val g = (x: String) => x + x + println(new Box("abc") map g) + } +} -- cgit v1.2.3 From e171d6d969fe7ce0dd2fd7a7679d6edb08c7579c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 19 Sep 2012 16:19:56 +0200 Subject: 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. --- .../scala/tools/nsc/transform/Erasure.scala | 10 ++++++++- .../tools/nsc/transform/ExtensionMethods.scala | 3 ++- .../scala/reflect/internal/transform/Erasure.scala | 22 +++++++++--------- test/files/neg/valueclasses-pavlov.check | 7 ++++++ test/files/neg/valueclasses-pavlov.scala | 23 +++++++++++++++++++ test/files/run/Meter.scala | 6 +++-- test/files/run/MeterCaseClass.scala | 6 +++-- test/files/run/valueclasses-pavlov.check | 2 ++ test/files/run/valueclasses-pavlov.scala | 26 ++++++++++++++++++++++ 9 files changed, 87 insertions(+), 18 deletions(-) create mode 100644 test/files/neg/valueclasses-pavlov.check create mode 100644 test/files/neg/valueclasses-pavlov.scala create mode 100644 test/files/run/valueclasses-pavlov.check create mode 100644 test/files/run/valueclasses-pavlov.scala (limited to 'test/files') 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)) + } +} -- cgit v1.2.3 From d435f72e5fb7fe6486c881e7dd1bdca3743f42d4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 19 Sep 2012 17:03:02 +0200 Subject: New test case for closing SI-6385 The fixes for SI-6260 + elimination of hlaf-boxing also solve SI-6385 --- test/files/neg/t6385.check | 7 +++++++ test/files/neg/t6385.scala | 13 +++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 test/files/neg/t6385.check create mode 100644 test/files/neg/t6385.scala (limited to 'test/files') diff --git a/test/files/neg/t6385.check b/test/files/neg/t6385.check new file mode 100644 index 0000000000..93e51e8927 --- /dev/null +++ b/test/files/neg/t6385.check @@ -0,0 +1,7 @@ +t6385.scala:12: error: bridge generated for member method x: ()C[T] in class C +which overrides method x: ()C[T] in trait AA +clashes with definition of the member itself; +both have erased type ()Object + def x = this + ^ +one error found diff --git a/test/files/neg/t6385.scala b/test/files/neg/t6385.scala new file mode 100644 index 0000000000..cec58eec9e --- /dev/null +++ b/test/files/neg/t6385.scala @@ -0,0 +1,13 @@ +object N { + def main(args: Array[String]) { + val y: AA[Int] = C(2) + val c: Int = y.x.y + println(c) + } +} +trait AA[T] extends Any { + def x: C[T] +} +case class C[T](val y: T) extends AnyVal with AA[T] { + def x = this +} -- cgit v1.2.3 From c7204787b48166b5dee6215525f69ae615535c5a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 19 Sep 2012 17:37:24 +0200 Subject: New test case for SI-6337 This test case shows that the variant in the comment of SI-6337 now compiles also. --- test/files/run/t6337a.scala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 test/files/run/t6337a.scala (limited to 'test/files') diff --git a/test/files/run/t6337a.scala b/test/files/run/t6337a.scala new file mode 100644 index 0000000000..f5490f5cf0 --- /dev/null +++ b/test/files/run/t6337a.scala @@ -0,0 +1,16 @@ +object Test { + def main(args: Array[String]) { + val x = X(XX(3)) + assert(x.q.x.x + 9 == 13) + } +} +trait Q extends Any { + def x: Int + def inc: XX +} +case class X(val x: Q) extends AnyVal { + def q = X(x.inc) +} +case class XX(val x: Int) extends AnyVal with Q { + def inc = XX(x + 1) +} -- cgit v1.2.3 From d87592da76eb555f0e3fc3732169e56b1852fba1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 19 Sep 2012 19:02:43 +0200 Subject: Disabled failing build manager tests. When the refined build manager computes its change sets it mixes up the types. It computes constructors of inner classes of the first compilation that point to types of the second compilation. This breaks a useful assertion in ExtensionMethods. The error you get for t4245 is java.lang.AssertionError: assertion failed: unexpected constructor erasure A#6956.this.B#20211 for class B#6963 What goes on here is that the primary constructor of inner class B#6963 points to the new version of that inner class A#6956.this.B#20211. This happens during the computation of change sets, not during normal compilation. Since it looks like the computation of change sets is broken I have disabled the tests, rather than disabling the assertion. It seems that during residential compilation, the result type of a constructor can be a different version of the enclosing class. I could not reproduce this --- test/files/buildmanager/overloaded_1/A.scala | 11 ----------- test/files/buildmanager/overloaded_1/overloaded_1.check | 6 ------ test/files/buildmanager/overloaded_1/overloaded_1.test | 2 -- test/files/buildmanager/t4245/A.scala | 3 --- test/files/buildmanager/t4245/t4245.check | 6 ------ test/files/buildmanager/t4245/t4245.test | 2 -- test/files/disabled/A.scala | 11 +++++++++++ test/files/disabled/overloaded_1.check | 6 ++++++ test/files/disabled/overloaded_1.test | 2 ++ test/files/disabled/t4245/A.scala | 3 +++ test/files/disabled/t4245/t4245.check | 6 ++++++ test/files/disabled/t4245/t4245.test | 2 ++ 12 files changed, 30 insertions(+), 30 deletions(-) delete mode 100644 test/files/buildmanager/overloaded_1/A.scala delete mode 100644 test/files/buildmanager/overloaded_1/overloaded_1.check delete mode 100644 test/files/buildmanager/overloaded_1/overloaded_1.test delete mode 100644 test/files/buildmanager/t4245/A.scala delete mode 100644 test/files/buildmanager/t4245/t4245.check delete mode 100644 test/files/buildmanager/t4245/t4245.test create mode 100644 test/files/disabled/A.scala create mode 100644 test/files/disabled/overloaded_1.check create mode 100644 test/files/disabled/overloaded_1.test create mode 100644 test/files/disabled/t4245/A.scala create mode 100644 test/files/disabled/t4245/t4245.check create mode 100644 test/files/disabled/t4245/t4245.test (limited to 'test/files') diff --git a/test/files/buildmanager/overloaded_1/A.scala b/test/files/buildmanager/overloaded_1/A.scala deleted file mode 100644 index 33b63b8006..0000000000 --- a/test/files/buildmanager/overloaded_1/A.scala +++ /dev/null @@ -1,11 +0,0 @@ -trait As { - trait C extends D { - override def foo = this /// Shouldn't cause the change - override def foo(act: List[D]) = this - } - - abstract class D{ - def foo: D = this - def foo(act: List[D]) = this - } -} diff --git a/test/files/buildmanager/overloaded_1/overloaded_1.check b/test/files/buildmanager/overloaded_1/overloaded_1.check deleted file mode 100644 index 4d643ce6b4..0000000000 --- a/test/files/buildmanager/overloaded_1/overloaded_1.check +++ /dev/null @@ -1,6 +0,0 @@ -builder > A.scala -compiling Set(A.scala) -Changes: Map() -builder > A.scala -compiling Set(A.scala) -Changes: Map(class As$D -> List(), object As$C$class -> List(), object As$class -> List(), trait As -> List(), trait As$C -> List()) diff --git a/test/files/buildmanager/overloaded_1/overloaded_1.test b/test/files/buildmanager/overloaded_1/overloaded_1.test deleted file mode 100644 index 392e0d365f..0000000000 --- a/test/files/buildmanager/overloaded_1/overloaded_1.test +++ /dev/null @@ -1,2 +0,0 @@ ->>compile A.scala ->>compile A.scala diff --git a/test/files/buildmanager/t4245/A.scala b/test/files/buildmanager/t4245/A.scala deleted file mode 100644 index 7c4efe1b4b..0000000000 --- a/test/files/buildmanager/t4245/A.scala +++ /dev/null @@ -1,3 +0,0 @@ -class A { - class B(val a: Int) -} diff --git a/test/files/buildmanager/t4245/t4245.check b/test/files/buildmanager/t4245/t4245.check deleted file mode 100644 index 3d3898c671..0000000000 --- a/test/files/buildmanager/t4245/t4245.check +++ /dev/null @@ -1,6 +0,0 @@ -builder > A.scala -compiling Set(A.scala) -Changes: Map() -builder > A.scala -compiling Set(A.scala) -Changes: Map(class A -> List(), class A$B -> List()) diff --git a/test/files/buildmanager/t4245/t4245.test b/test/files/buildmanager/t4245/t4245.test deleted file mode 100644 index 392e0d365f..0000000000 --- a/test/files/buildmanager/t4245/t4245.test +++ /dev/null @@ -1,2 +0,0 @@ ->>compile A.scala ->>compile A.scala diff --git a/test/files/disabled/A.scala b/test/files/disabled/A.scala new file mode 100644 index 0000000000..c070faf978 --- /dev/null +++ b/test/files/disabled/A.scala @@ -0,0 +1,11 @@ +trait As { + trait C extends D { + override def foo = this /// Shouldn't cause the change + override def foo(act: List[D]) = this + } + + abstract class D{ + def foo: D = this + def foo(act: List[D]) = this + } +} diff --git a/test/files/disabled/overloaded_1.check b/test/files/disabled/overloaded_1.check new file mode 100644 index 0000000000..4d643ce6b4 --- /dev/null +++ b/test/files/disabled/overloaded_1.check @@ -0,0 +1,6 @@ +builder > A.scala +compiling Set(A.scala) +Changes: Map() +builder > A.scala +compiling Set(A.scala) +Changes: Map(class As$D -> List(), object As$C$class -> List(), object As$class -> List(), trait As -> List(), trait As$C -> List()) diff --git a/test/files/disabled/overloaded_1.test b/test/files/disabled/overloaded_1.test new file mode 100644 index 0000000000..392e0d365f --- /dev/null +++ b/test/files/disabled/overloaded_1.test @@ -0,0 +1,2 @@ +>>compile A.scala +>>compile A.scala diff --git a/test/files/disabled/t4245/A.scala b/test/files/disabled/t4245/A.scala new file mode 100644 index 0000000000..7c4efe1b4b --- /dev/null +++ b/test/files/disabled/t4245/A.scala @@ -0,0 +1,3 @@ +class A { + class B(val a: Int) +} diff --git a/test/files/disabled/t4245/t4245.check b/test/files/disabled/t4245/t4245.check new file mode 100644 index 0000000000..3d3898c671 --- /dev/null +++ b/test/files/disabled/t4245/t4245.check @@ -0,0 +1,6 @@ +builder > A.scala +compiling Set(A.scala) +Changes: Map() +builder > A.scala +compiling Set(A.scala) +Changes: Map(class A -> List(), class A$B -> List()) diff --git a/test/files/disabled/t4245/t4245.test b/test/files/disabled/t4245/t4245.test new file mode 100644 index 0000000000..392e0d365f --- /dev/null +++ b/test/files/disabled/t4245/t4245.test @@ -0,0 +1,2 @@ +>>compile A.scala +>>compile A.scala -- cgit v1.2.3