From 2d3562373699dec9c0e9f1fd6e21ef98954170ac Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 26 May 2015 00:16:16 +0200 Subject: [PATCH 01/12] SyntheticMethods: don't generate hashCode for value classes It will be added by the VCXPrototype parent class --- src/dotty/tools/dotc/transform/SyntheticMethods.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala index fa931a3794b1..7ae6d6db59c9 100644 --- a/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -31,7 +31,6 @@ import scala.language.postfixOps * implementation already exists: * * def equals(other: Any): Boolean - * def hashCode(): Int */ class SyntheticMethods(thisTransformer: DenotTransformer) { import ast.tpd._ @@ -41,7 +40,7 @@ class SyntheticMethods(thisTransformer: DenotTransformer) { private def initSymbols(implicit ctx: Context) = if (myValueSymbols.isEmpty) { - myValueSymbols = List(defn.Any_hashCode, defn.Any_equals) + myValueSymbols = List(defn.Any_equals) myCaseSymbols = myValueSymbols ++ List(defn.Any_toString, defn.Product_canEqual, defn.Product_productArity, defn.Product_productPrefix) } From 9e80b483061838ee58699ed7b2d7a8c1f44c3cb5 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 1 Jul 2015 13:59:04 +0200 Subject: [PATCH 02/12] VCXCompanion and VCXCasePrototype: don't disallow overriding toString --- src/dotty/runtime/vc/VCPrototype.scala | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/dotty/runtime/vc/VCPrototype.scala b/src/dotty/runtime/vc/VCPrototype.scala index 0a3abe5a25e6..eeee6e3ca17c 100644 --- a/src/dotty/runtime/vc/VCPrototype.scala +++ b/src/dotty/runtime/vc/VCPrototype.scala @@ -40,7 +40,7 @@ abstract class VCFloatCompanion[T <: VCFloatPrototype] extends ClassTag[T] { final def _1$extension(underlying: Float) = underlying final def hashCode$extension(underlying: Float) = underlying.hashCode() - final def toString$extension(underlying: Float) = s"${productPrefix$extension(underlying)}($underlying)" + def toString$extension(underlying: Float) = s"${productPrefix$extension(underlying)}($underlying)" def productPrefix$extension(underlying: Float): String } @@ -75,7 +75,7 @@ abstract class VCObjectCasePrototype(underlying: Object) extends VCObjectPrototy underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } @@ -91,7 +91,7 @@ abstract class VCObjectCompanion[T <: VCObjectPrototype] extends ClassTag[T] { final def _1$extension(underlying: Object) = underlying final def hashCode$extension(underlying: Object) = underlying.hashCode() - final def toString$extension(underlying: Object) = s"${productPrefix$extension(underlying)}($underlying)" + def toString$extension(underlying: Object) = s"${productPrefix$extension(underlying)}($underlying)" def productPrefix$extension(underlying: Object): String } @@ -128,7 +128,7 @@ abstract class VCShortCasePrototype(underlying: Short) extends VCShortPrototype( underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } @@ -144,7 +144,7 @@ abstract class VCShortCompanion[T <: VCShortPrototype] extends ClassTag[T] { final def _1$extension(underlying: Short) = underlying final def hashCode$extension(underlying: Short) = underlying.hashCode() - final def toString$extension(underlying: Short) = s"${productPrefix$extension(underlying)}($underlying)" + def toString$extension(underlying: Short) = s"${productPrefix$extension(underlying)}($underlying)" def productPrefix$extension(underlying: Short): String } @@ -182,7 +182,7 @@ abstract class VCLongCasePrototype(underlying: Long) extends VCLongPrototype(und underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } @@ -198,7 +198,7 @@ abstract class VCLongCompanion[T <: VCLongPrototype] extends ClassTag[T] { final def _1$extension(underlying: Long) = underlying final def hashCode$extension(underlying: Long) = underlying.hashCode() - final def toString$extension(underlying: Long) = s"${productPrefix$extension(underlying)}($underlying)" + def toString$extension(underlying: Long) = s"${productPrefix$extension(underlying)}($underlying)" def productPrefix$extension(underlying: Long): String } @@ -235,7 +235,7 @@ abstract class VCIntCasePrototype(underlying: Int) extends VCIntPrototype(underl underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } @@ -251,7 +251,7 @@ abstract class VCIntCompanion[T <: VCIntPrototype] extends ClassTag[T] { final def _1$extension(underlying: Int) = underlying final def hashCode$extension(underlying: Int) = underlying.hashCode() - final def toString$extension(underlying: Int) = s"${productPrefix$extension(underlying)}($underlying)" + def toString$extension(underlying: Int) = s"${productPrefix$extension(underlying)}($underlying)" def productPrefix$extension(underlying: Int): String } @@ -286,7 +286,7 @@ abstract class VCDoubleCasePrototype(underlying: Double) extends VCDoublePrototy underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } @@ -302,7 +302,7 @@ abstract class VCDoubleCompanion[T <: VCDoublePrototype] extends ClassTag[T] { final def _1$extension(underlying: Double) = underlying final def hashCode$extension(underlying: Double) = underlying.hashCode() - final def toString$extension(underlying: Double) = s"${productPrefix$extension(underlying)}($underlying)" + def toString$extension(underlying: Double) = s"${productPrefix$extension(underlying)}($underlying)" def productPrefix$extension(underlying: Double): String } @@ -337,7 +337,7 @@ abstract class VCBooleanCasePrototype(underlying: Boolean) extends VCBooleanProt underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } @@ -353,7 +353,7 @@ abstract class VCBooleanCompanion[T <: VCBooleanPrototype] extends ClassTag[T] { final def _1$extension(underlying: Boolean) = underlying final def hashCode$extension(underlying: Boolean) = underlying.hashCode() - final def toString$extension(underlying: Boolean) = s"${productPrefix$extension(underlying)}($underlying)" + def toString$extension(underlying: Boolean) = s"${productPrefix$extension(underlying)}($underlying)" def productPrefix$extension(underlying: Boolean): String } @@ -390,7 +390,7 @@ abstract class VCCharCasePrototype(underlying: Char) extends VCCharPrototype(und underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } @@ -408,7 +408,7 @@ abstract class VCCharCompanion[T <: VCCharPrototype] extends ClassTag[T] { final def _1$extension(underlying: Char) = underlying final def hashCode$extension(underlying: Char) = underlying.hashCode() - final def toString$extension(underlying: Char) = s"${productPrefix$extension(underlying)}($underlying)" + def toString$extension(underlying: Char) = s"${productPrefix$extension(underlying)}($underlying)" def productPrefix$extension(underlying: Char): String } @@ -443,7 +443,7 @@ abstract class VCByteCasePrototype(underlying: Byte) extends VCBytePrototype(und underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } @@ -459,7 +459,7 @@ abstract class VCByteCompanion[T <: VCBytePrototype] extends ClassTag[T] { final def _1$extension(underlying: Byte) = underlying final def hashCode$extension(underlying: Byte) = underlying.hashCode() - final def toString$extension(underlying: Byte) = s"${productPrefix$extension(underlying)}($underlying)" + def toString$extension(underlying: Byte) = s"${productPrefix$extension(underlying)}($underlying)" def productPrefix$extension(underlying: Byte): String } From 7613a017ff65ba58c5ac19af8aa15bcbeb5d46ae Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 1 Jul 2015 13:59:51 +0200 Subject: [PATCH 03/12] VCXArray: don't make the constructor private It will be useful to wrap SeqLiterals --- src/dotty/runtime/vc/VCPrototype.scala | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dotty/runtime/vc/VCPrototype.scala b/src/dotty/runtime/vc/VCPrototype.scala index eeee6e3ca17c..6d5ca9770df6 100644 --- a/src/dotty/runtime/vc/VCPrototype.scala +++ b/src/dotty/runtime/vc/VCPrototype.scala @@ -44,7 +44,7 @@ abstract class VCFloatCompanion[T <: VCFloatPrototype] extends ClassTag[T] { def productPrefix$extension(underlying: Float): String } -final class VCFloatArray[T <: VCFloatPrototype] private (val arr: Array[Float], val ct: VCFloatCompanion[T]) +final class VCFloatArray[T <: VCFloatPrototype] (val arr: Array[Float], val ct: VCFloatCompanion[T]) extends VCArrayPrototype[T] { def this(ct: VCFloatCompanion[T], sz: Int) = this(new Array[Float](sz), ct) @@ -95,7 +95,7 @@ abstract class VCObjectCompanion[T <: VCObjectPrototype] extends ClassTag[T] { def productPrefix$extension(underlying: Object): String } -final class VCObjectArray[T <: VCObjectPrototype] private (val arr: Array[Object], val ct: VCObjectCompanion[T]) +final class VCObjectArray[T <: VCObjectPrototype] (val arr: Array[Object], val ct: VCObjectCompanion[T]) extends VCArrayPrototype[T] { def this(ct: VCObjectCompanion[T], sz: Int) = this(new Array[Object](sz), ct) @@ -148,7 +148,7 @@ abstract class VCShortCompanion[T <: VCShortPrototype] extends ClassTag[T] { def productPrefix$extension(underlying: Short): String } -final class VCShortArray[T <: VCShortPrototype] private (val arr: Array[Short], val ct: VCShortCompanion[T]) +final class VCShortArray[T <: VCShortPrototype] (val arr: Array[Short], val ct: VCShortCompanion[T]) extends VCArrayPrototype[T] { def this(ct: VCShortCompanion[T], sz: Int) = this(new Array[Short](sz), ct) @@ -202,7 +202,7 @@ abstract class VCLongCompanion[T <: VCLongPrototype] extends ClassTag[T] { def productPrefix$extension(underlying: Long): String } -final class VCLongArray[T <: VCLongPrototype] private (val arr: Array[Long], val ct: VCLongCompanion[T]) +final class VCLongArray[T <: VCLongPrototype] (val arr: Array[Long], val ct: VCLongCompanion[T]) extends VCArrayPrototype[T] { def this(ct: VCLongCompanion[T], sz: Int) = this(new Array[Long](sz), ct) @@ -255,7 +255,7 @@ abstract class VCIntCompanion[T <: VCIntPrototype] extends ClassTag[T] { def productPrefix$extension(underlying: Int): String } -final class VCIntArray[T <: VCIntPrototype] private (val arr: Array[Int], val ct: VCIntCompanion[T]) +final class VCIntArray[T <: VCIntPrototype] (val arr: Array[Int], val ct: VCIntCompanion[T]) extends VCArrayPrototype[T] { def this(ct: VCIntCompanion[T], sz: Int) = this(new Array[Int](sz), ct) @@ -306,7 +306,7 @@ abstract class VCDoubleCompanion[T <: VCDoublePrototype] extends ClassTag[T] { def productPrefix$extension(underlying: Double): String } -final class VCDoubleArray[T <: VCDoublePrototype] private (val arr: Array[Double], val ct: VCDoubleCompanion[T]) +final class VCDoubleArray[T <: VCDoublePrototype] (val arr: Array[Double], val ct: VCDoubleCompanion[T]) extends VCArrayPrototype[T] { def this(ct: VCDoubleCompanion[T], sz: Int) = this(new Array[Double](sz), ct) @@ -357,7 +357,7 @@ abstract class VCBooleanCompanion[T <: VCBooleanPrototype] extends ClassTag[T] { def productPrefix$extension(underlying: Boolean): String } -final class VCBooleanArray[T <: VCBooleanPrototype] private (val arr: Array[Boolean], val ct: VCBooleanCompanion[T]) +final class VCBooleanArray[T <: VCBooleanPrototype] (val arr: Array[Boolean], val ct: VCBooleanCompanion[T]) extends VCArrayPrototype[T] { def this(ct: VCBooleanCompanion[T], sz: Int) = this(new Array[Boolean](sz), ct) @@ -412,7 +412,7 @@ abstract class VCCharCompanion[T <: VCCharPrototype] extends ClassTag[T] { def productPrefix$extension(underlying: Char): String } -final class VCCharArray[T <: VCCharPrototype] private (val arr: Array[Char], val ct: VCCharCompanion[T]) +final class VCCharArray[T <: VCCharPrototype] (val arr: Array[Char], val ct: VCCharCompanion[T]) extends VCArrayPrototype[T] { def this(ct: VCCharCompanion[T], sz: Int) = this(new Array[Char](sz), ct) @@ -463,7 +463,7 @@ abstract class VCByteCompanion[T <: VCBytePrototype] extends ClassTag[T] { def productPrefix$extension(underlying: Byte): String } -final class VCByteArray[T <: VCBytePrototype] private (val arr: Array[Byte], val ct: VCByteCompanion[T]) +final class VCByteArray[T <: VCBytePrototype] (val arr: Array[Byte], val ct: VCByteCompanion[T]) extends VCArrayPrototype[T] { def this(ct: VCByteCompanion[T], sz: Int) = this(new Array[Byte](sz), ct) From 7e494619d36ee461e0035b7cef1e96c33aa58c82 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 1 Jul 2015 14:16:24 +0200 Subject: [PATCH 04/12] VCXPrototype: remove bound on type variable This is needed so that Array[T] erases to Object, see the note in the code. --- src/dotty/runtime/vc/VCPrototype.scala | 61 ++++++++++++++------------ 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/src/dotty/runtime/vc/VCPrototype.scala b/src/dotty/runtime/vc/VCPrototype.scala index 6d5ca9770df6..1f41b46171d8 100644 --- a/src/dotty/runtime/vc/VCPrototype.scala +++ b/src/dotty/runtime/vc/VCPrototype.scala @@ -29,13 +29,20 @@ abstract class VCFloatCasePrototype(underlying: Float) extends VCFloatPrototype( } } -abstract class VCFloatCompanion[T <: VCFloatPrototype] extends ClassTag[T] { +// NOTE for all VCXCompanion: The type parameter T should be bounded like this: +// abstract class VCXCompanion[T <: VCXPrototype] extends ClassTag[T] +// But this affects erasure: it means that Array[T] is erased to [VCIntPrototype; +// instead of Object, but we really need it to erase to Object if we want +// VCXArray to be a valid array. We work around this by adding casts where +// we need to assume that T is a subtype of VCXPrototype. + +abstract class VCFloatCompanion[T /*<: VCFloatPrototype*/] extends ClassTag[T] { def box(underlying: Float): T - final def unbox(boxed: T) = boxed.underlying + final def unbox(boxed: T) = boxed.asInstanceOf[VCFloatPrototype].underlying implicit def classTag: this.type = this override def newArray(len: Int): Array[T] = - new VCFloatArray(this, len).asInstanceOf[Array[T]] + new VCFloatArray(this.asInstanceOf[VCFloatCompanion[VCFloatPrototype]], len).asInstanceOf[Array[T]] final def _1$extension(underlying: Float) = underlying @@ -80,13 +87,13 @@ abstract class VCObjectCasePrototype(underlying: Object) extends VCObjectPrototy } } -abstract class VCObjectCompanion[T <: VCObjectPrototype] extends ClassTag[T] { +abstract class VCObjectCompanion[T /*<: VCObjectPrototype*/] extends ClassTag[T] { def box(underlying: Object): T - final def unbox(boxed: T) = boxed.underlying + final def unbox(boxed: T) = boxed.asInstanceOf[VCObjectPrototype].underlying implicit def classTag: this.type = this override def newArray(len: Int): Array[T] = - new VCObjectArray(this, len).asInstanceOf[Array[T]] + new VCObjectArray(this.asInstanceOf[VCObjectCompanion[VCObjectPrototype]], len).asInstanceOf[Array[T]] final def _1$extension(underlying: Object) = underlying @@ -133,13 +140,13 @@ abstract class VCShortCasePrototype(underlying: Short) extends VCShortPrototype( } } -abstract class VCShortCompanion[T <: VCShortPrototype] extends ClassTag[T] { +abstract class VCShortCompanion[T /*<: VCShortPrototype*/] extends ClassTag[T] { def box(underlying: Short): T - final def unbox(boxed: T) = boxed.underlying + final def unbox(boxed: T) = boxed.asInstanceOf[VCShortPrototype].underlying implicit def classTag: this.type = this override def newArray(len: Int): Array[T] = - new VCShortArray(this, len).asInstanceOf[Array[T]] + new VCShortArray(this.asInstanceOf[VCShortCompanion[VCShortPrototype]], len).asInstanceOf[Array[T]] final def _1$extension(underlying: Short) = underlying @@ -187,13 +194,13 @@ abstract class VCLongCasePrototype(underlying: Long) extends VCLongPrototype(und } } -abstract class VCLongCompanion[T <: VCLongPrototype] extends ClassTag[T] { +abstract class VCLongCompanion[T /*<: VCLongPrototype*/] extends ClassTag[T] { def box(underlying: Long): T - final def unbox(boxed: T) = boxed.underlying + final def unbox(boxed: T) = boxed.asInstanceOf[VCLongPrototype].underlying implicit def classTag: this.type = this override def newArray(len: Int): Array[T] = - new VCLongArray(this, len).asInstanceOf[Array[T]] + new VCLongArray(this.asInstanceOf[VCLongCompanion[VCLongPrototype]], len).asInstanceOf[Array[T]] final def _1$extension(underlying: Long) = underlying @@ -240,13 +247,13 @@ abstract class VCIntCasePrototype(underlying: Int) extends VCIntPrototype(underl } } -abstract class VCIntCompanion[T <: VCIntPrototype] extends ClassTag[T] { +abstract class VCIntCompanion[T /*<: VCIntPrototype*/] extends ClassTag[T] { def box(underlying: Int): T - final def unbox(boxed: T) = boxed.underlying + final def unbox(boxed: T) = boxed.asInstanceOf[VCIntPrototype].underlying implicit def classTag: this.type = this override def newArray(len: Int): Array[T] = - new VCIntArray(this, len).asInstanceOf[Array[T]] + new VCIntArray(this.asInstanceOf[VCIntCompanion[VCIntPrototype]], len).asInstanceOf[Array[T]] final def _1$extension(underlying: Int) = underlying @@ -291,13 +298,13 @@ abstract class VCDoubleCasePrototype(underlying: Double) extends VCDoublePrototy } } -abstract class VCDoubleCompanion[T <: VCDoublePrototype] extends ClassTag[T] { +abstract class VCDoubleCompanion[T /*<: VCDoublePrototype*/] extends ClassTag[T] { def box(underlying: Double): T - final def unbox(boxed: T) = boxed.underlying + final def unbox(boxed: T) = boxed.asInstanceOf[VCDoublePrototype].underlying implicit def classTag: this.type = this override def newArray(len: Int): Array[T] = - new VCDoubleArray(this, len).asInstanceOf[Array[T]] + new VCDoubleArray(this.asInstanceOf[VCDoubleCompanion[VCDoublePrototype]], len).asInstanceOf[Array[T]] final def _1$extension(underlying: Double) = underlying @@ -342,13 +349,13 @@ abstract class VCBooleanCasePrototype(underlying: Boolean) extends VCBooleanProt } } -abstract class VCBooleanCompanion[T <: VCBooleanPrototype] extends ClassTag[T] { +abstract class VCBooleanCompanion[T /*<: VCBooleanPrototype*/] extends ClassTag[T] { def box(underlying: Boolean): T - final def unbox(boxed: T) = boxed.underlying + final def unbox(boxed: T) = boxed.asInstanceOf[VCBooleanPrototype].underlying implicit def classTag: this.type = this override def newArray(len: Int): Array[T] = - new VCBooleanArray(this, len).asInstanceOf[Array[T]] + new VCBooleanArray(this.asInstanceOf[VCBooleanCompanion[VCBooleanPrototype]], len).asInstanceOf[Array[T]] final def _1$extension(underlying: Boolean) = underlying @@ -397,13 +404,13 @@ abstract class VCCharCasePrototype(underlying: Char) extends VCCharPrototype(und // subclasses are expected to implement equals, productPrefix, and canEqual } -abstract class VCCharCompanion[T <: VCCharPrototype] extends ClassTag[T] { +abstract class VCCharCompanion[T /*<: VCCharPrototype*/] extends ClassTag[T] { def box(underlying: Char): T - final def unbox(boxed: T) = boxed.underlying + final def unbox(boxed: T) = boxed.asInstanceOf[VCCharPrototype].underlying implicit def classTag: this.type = this override def newArray(len: Int): Array[T] = - new VCCharArray(this, len).asInstanceOf[Array[T]] + new VCCharArray(this.asInstanceOf[VCCharCompanion[VCCharPrototype]], len).asInstanceOf[Array[T]] final def _1$extension(underlying: Char) = underlying @@ -448,13 +455,13 @@ abstract class VCByteCasePrototype(underlying: Byte) extends VCBytePrototype(und } } -abstract class VCByteCompanion[T <: VCBytePrototype] extends ClassTag[T] { +abstract class VCByteCompanion[T /*<: VCBytePrototype*/] extends ClassTag[T] { def box(underlying: Byte): T - final def unbox(boxed: T) = boxed.underlying + final def unbox(boxed: T) = boxed.asInstanceOf[VCBytePrototype].underlying implicit def classTag: this.type = this override def newArray(len: Int): Array[T] = - new VCByteArray(this, len).asInstanceOf[Array[T]] + new VCByteArray(this.asInstanceOf[VCByteCompanion[VCBytePrototype]], len).asInstanceOf[Array[T]] final def _1$extension(underlying: Byte) = underlying From bb1d746f4bdb7601dcd60a7f4601d8c0da789585 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 1 Jul 2015 14:52:09 +0200 Subject: [PATCH 05/12] VCPrototype: fix the toString of VCXArray It should be "[Foo" not "[class Foo" --- src/dotty/runtime/vc/VCPrototype.scala | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dotty/runtime/vc/VCPrototype.scala b/src/dotty/runtime/vc/VCPrototype.scala index 1f41b46171d8..937ba7853056 100644 --- a/src/dotty/runtime/vc/VCPrototype.scala +++ b/src/dotty/runtime/vc/VCPrototype.scala @@ -67,7 +67,7 @@ final class VCFloatArray[T <: VCFloatPrototype] (val arr: Array[Float], val ct: } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -120,7 +120,7 @@ final class VCObjectArray[T <: VCObjectPrototype] (val arr: Array[Object], val c } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -173,7 +173,7 @@ final class VCShortArray[T <: VCShortPrototype] (val arr: Array[Short], val ct: } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -227,7 +227,7 @@ final class VCLongArray[T <: VCLongPrototype] (val arr: Array[Long], val ct: VCL } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -278,7 +278,7 @@ final class VCIntArray[T <: VCIntPrototype] (val arr: Array[Int], val ct: VCIntC } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -329,7 +329,7 @@ final class VCDoubleArray[T <: VCDoublePrototype] (val arr: Array[Double], val c } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -382,7 +382,7 @@ final class VCBooleanArray[T <: VCBooleanPrototype] (val arr: Array[Boolean], va } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -435,7 +435,7 @@ final class VCCharArray[T <: VCCharPrototype] (val arr: Array[Char], val ct: VCC } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -486,7 +486,7 @@ final class VCByteArray[T <: VCBytePrototype] (val arr: Array[Byte], val ct: VCB } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } From b1fa658d4789c82d32ca2cdbcc4c0e0a4c1612e4 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 1 Jul 2015 14:19:57 +0200 Subject: [PATCH 06/12] Wrap SeqLiteral of value classes using custom wrapper instead of wrapRefArray wrapRefArray is defined as: def wrapRefArray[T <: AnyRef](xs: Array[T]): WrappedArray[T] After erasure this becomes: def wrapRefArray(xs: Object[]): WrappedArray This is fine for value classes as long as arrays are boxed, but once we change the representation this will result in ClassCastException. Therefore we introduce a wrapper just for value classes: def wrapVCArray[T <: AnyVal](xs: Array[T]): WrappedArray[T] Which is erased to: def wrapVCArray(xs: Object): WrappedArray Which lets us represent arrays of value classes using any reference type. --- src/dotty/DottyPredef.scala | 13 ++++++++++ .../tools/dotc/transform/SeqLiterals.scala | 25 ++++++++++++------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/dotty/DottyPredef.scala b/src/dotty/DottyPredef.scala index 0a5d4d7f3492..76a11e39f0d2 100644 --- a/src/dotty/DottyPredef.scala +++ b/src/dotty/DottyPredef.scala @@ -1,7 +1,9 @@ package dotty +import scala.collection.mutable.WrappedArray import scala.reflect.ClassTag import scala.reflect.runtime.universe.TypeTag +import scala.runtime.ScalaRunTime.arrayElementClass import scala.Predef.??? // this is currently ineffective, because of #530 abstract class I1 { @@ -34,4 +36,15 @@ object DottyPredef extends I6 { /** ClassTags for final classes */ implicit val NothingClassTag: ClassTag[Nothing] = ClassTag.Nothing + + // This implicit will never be used for arrays of primitives because + // the wrap*Array in scala.Predef have a higher priority. + implicit def wrapVCArray[T <: AnyVal](xs: Array[T]): WrappedArray[T] = + new WrappedArray[T] { + val array = xs + lazy val elemTag = ClassTag[T](arrayElementClass(array.getClass)) + def length: Int = array.length + def apply(index: Int): T = array(index) + def update(index: Int, elem: T) { array(index) = elem } + } } diff --git a/src/dotty/tools/dotc/transform/SeqLiterals.scala b/src/dotty/tools/dotc/transform/SeqLiterals.scala index 6e29d7e092df..94b3b5a63306 100644 --- a/src/dotty/tools/dotc/transform/SeqLiterals.scala +++ b/src/dotty/tools/dotc/transform/SeqLiterals.scala @@ -7,7 +7,7 @@ import dotty.tools.dotc.transform.TreeTransforms._ import Contexts.Context import Symbols._ import Phases._ -import Decorators._ +import Decorators._, ValueClasses._ /** A transformer that eliminates SeqLiteral's, transforming `SeqLiteral(elems)` to an operation * equivalent to @@ -36,13 +36,20 @@ class SeqLiterals extends MiniPhaseTransform { //println(i"trans seq $tree, arr = $arr: ${arr.tpe} ${arr.tpe.elemType}") val elemtp = arr.tpe.elemType.bounds.hi val elemCls = elemtp.classSymbol - val (wrapMethStr, targs) = - if (elemCls.isPrimitiveValueClass) (s"wrap${elemCls.name}Array", Nil) - else if (elemtp derivesFrom defn.ObjectClass) ("wrapRefArray", elemtp :: Nil) - else ("genericWrapArray", elemtp :: Nil) - ref(defn.ScalaPredefModule) - .select(wrapMethStr.toTermName) - .appliedToTypes(targs) - .appliedTo(arr) + if (isDerivedValueClass(elemCls)) { + ref(defn.DottyPredefModule) + .select("wrapVCArray".toTermName) + .appliedToTypes(List(elemtp)) + .appliedTo(arr) + } else { + val (wrapMethStr, targs) = + if (elemCls.isPrimitiveValueClass) (s"wrap${elemCls.name}Array", Nil) + else if (elemtp derivesFrom defn.ObjectClass) ("wrapRefArray", elemtp :: Nil) + else ("genericWrapArray", elemtp :: Nil) + ref(defn.ScalaPredefModule) + .select(wrapMethStr.toTermName) + .appliedToTypes(targs) + .appliedTo(arr) + } } } From 2051769b8bc00834018df4866c7029df09ccd615 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 1 Jul 2015 14:33:49 +0200 Subject: [PATCH 07/12] New phase VCParents: add parents to value classes and their companions Note that this mean that the following does not pass Ycheck after VCParents: def foo[T <: AnyVal](...) foo[Meter](...) Ycheck will complain that Meter does not conform to the upper bound AnyVal, which is true. I'm not sure what the best way to deal with this is. --- src/dotty/tools/dotc/Compiler.scala | 1 + src/dotty/tools/dotc/core/Definitions.scala | 26 ++++++ .../dotc/transform/ExtensionMethods.scala | 20 +---- .../tools/dotc/transform/VCParents.scala | 84 +++++++++++++++++++ 4 files changed, 113 insertions(+), 18 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/VCParents.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 827134e847b3..7619c3d5bf70 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -46,6 +46,7 @@ class Compiler { new ElimRepeated, new NormalizeFlags, new ExtensionMethods, + new VCParents, new ExpandSAMs, new TailRec, new ClassOf), diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 7ed0a26e018c..0df1601464e1 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -4,6 +4,7 @@ package core import Types._, Contexts._, Symbols._, Denotations._, SymDenotations._, StdNames._, Names._ import Flags._, Scopes._, Decorators._, NameOps._, util.Positions._ +import transform.ValueClasses import unpickleScala2.Scala2Unpickler.ensureConstructor import scala.annotation.{ switch, meta } import scala.collection.{ mutable, immutable } @@ -599,6 +600,31 @@ class Definitions { lazy val volatileRefClass: Map[Symbol, Symbol] = refClasses.map(rc => rc -> ctx.requiredClass(s"scala.runtime.Volatile${rc.name}Ref")).toMap + lazy val vcPrototype: Map[Symbol, Symbol] = + refClasses.map(vc => vc -> ctx.requiredClass(s"dotty.runtime.vc.VC${vc.name}Prototype")).toMap + lazy val vcCompanion: Map[Symbol, Symbol] = + refClasses.map(vc => vc -> ctx.requiredClass(s"dotty.runtime.vc.VC${vc.name}Companion")).toMap + lazy val vcArray: Map[Symbol, Symbol] = + refClasses.map(vc => vc -> ctx.requiredClass(s"dotty.runtime.vc.VC${vc.name}Array")).toMap + + lazy val VCArrayPrototypeClass = ctx.requiredClass(s"dotty.runtime.vc.VCArrayPrototype") + def VCArrayPrototypeType = VCArrayPrototypeClass.typeRef + + def vcPrototypeOf(vc: ClassDenotation) = { + val underlying = ValueClasses.valueClassUnbox(vc).info.classSymbol + vcPrototype.getOrElse(underlying, vcPrototype(defn.ObjectClass)) + } + def vcCompanionOf(vc: ClassDenotation) = { + val underlying = ValueClasses.valueClassUnbox(vc).info.classSymbol + vcCompanion.getOrElse(underlying, vcCompanion(defn.ObjectClass)) + } + def vcArrayOf(vc: ClassDenotation) = { + val underlying = ValueClasses.valueClassUnbox(vc).info.classSymbol + vcArray.getOrElse(underlying, vcArray(defn.ObjectClass)) + } + + + def wrapArrayMethodName(elemtp: Type): TermName = { val cls = elemtp.classSymbol if (cls.isPrimitiveValueClass) nme.wrapXArray(cls.name) diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 087e15c71685..255df845bda7 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -72,28 +72,12 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful val evt2uSym = ctx.newSymbol(moduleSym, nme.EVT2U, Synthetic | Method, MethodType(List(nme.x_0), List(evt), underlying)) - val defn = ctx.definitions - - val underlyingCls = underlying.classSymbol - val underlyingClsName = - if (defn.ScalaNumericValueClasses.contains(underlyingCls) || - underlyingCls == defn.BooleanClass) underlyingCls.name else nme.Object - - - val syp = ctx.requiredClass(s"dotty.runtime.vc.VC${underlyingClsName}Companion").asClass - - newSuperClass = tpd.ref(syp).select(nme.CONSTRUCTOR).appliedToType(valueClass.typeRef).tpe.resultType - decls1.enter(u2evtSym) decls1.enter(evt2uSym) } - // Add the extension methods, the cast methods u2evt$ and evt2u$, and a VC*Companion superclass - moduleClassSym.copySymDenotation(info = - cinfo.derivedClassInfo( - // FIXME: use of VC*Companion superclasses is disabled until the conflicts with SyntheticMethods are solved. - //classParents = ctx.normalizeToClassRefs(List(newSuperClass), moduleSym, decls1), - decls = decls1)) + // Add the extension methods, the cast methods u2evt$ and evt2u$ + moduleClassSym.copySymDenotation(info = cinfo.derivedClassInfo(decls = decls1)) case _ => moduleClassSym } diff --git a/src/dotty/tools/dotc/transform/VCParents.scala b/src/dotty/tools/dotc/transform/VCParents.scala new file mode 100644 index 000000000000..ebabf63e61c7 --- /dev/null +++ b/src/dotty/tools/dotc/transform/VCParents.scala @@ -0,0 +1,84 @@ +package dotty.tools.dotc +package transform + +import ast.{Trees, tpd} +import core._, core.Decorators._ +import Contexts._, Flags._, Trees._, Types._, StdNames._, Symbols._ +import Denotations._, SymDenotations._ +import DenotTransformers._, TreeTransforms._, Phases.Phase +import ValueClasses._ + +/** This phase makes value classes extend VCXPrototype and make their companions extend VCXCompanion. + * + * For a value class class V whose erased underlying type is U, X is "U" if U is a primitive + * type and is "Object" otherwise. + * + */ +class VCParents extends MiniPhaseTransform with DenotTransformer { + import tpd._ + + override def phaseName: String = "vcParents" + + override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { + case moduleClass: ClassDenotation if moduleClass.is(ModuleClass) => + moduleClass.linkedClass match { + case valueClass: ClassSymbol if isDerivedValueClass(valueClass) => + val moduleSym = moduleClass.symbol.asClass + val cinfo = moduleClass.classInfo + + val superType = tpd.ref(defn.vcCompanionOf(valueClass)) + .select(nme.CONSTRUCTOR) + .appliedToType(valueClass.typeRef) + .tpe + .resultType + + moduleClass.copySymDenotation(info = + cinfo.derivedClassInfo(classParents = + ctx.normalizeToClassRefs(List(superType), moduleSym, cinfo.decls))) + case _ => + moduleClass + } + case valueClass: ClassDenotation if isDerivedValueClass(valueClass) => + val cinfo = valueClass.classInfo + val superType = defn.vcPrototypeOf(valueClass).typeRef + + val (p :: ps) = cinfo.classParents + assert(p.isRef(defn.AnyValClass)) + val parents = superType :: ps + valueClass.copySymDenotation(info = cinfo.derivedClassInfo(classParents = parents)) + case _ => + ref + } + + override def transformTemplate(tree: tpd.Template)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = + ctx.owner.denot match { + case moduleClass: ClassDenotation if moduleClass.is(ModuleClass) => + moduleClass.linkedClass match { + case valueClass: ClassSymbol if isDerivedValueClass(valueClass) => + val superCall = New(defn.vcCompanionOf(valueClass).typeRef) + .select(nme.CONSTRUCTOR) + .appliedToType(valueClass.typeRef) + .appliedToNone + + val (p :: ps) = tree.parents + // TODO: We shouldn't disallow extending companion objects of value classes + // with classes other than AnyRef + assert(p.tpe.isRef(defn.ObjectClass)) + cpy.Template(tree)(parents = superCall :: ps) + case _ => + tree + } + case valueClass: ClassDenotation if isDerivedValueClass(valueClass) => + val prototype = defn.vcPrototypeOf(valueClass).typeRef + val underlyingSym = valueClassUnbox(valueClass) + val superCall = New(prototype, List(ref(underlyingSym))) + // TODO: manually do parameter forwarding: the prototype has a local field + // so we don't need a field inside the value class + + val (p :: ps) = tree.parents + assert(p.tpe.isRef(defn.AnyValClass)) + cpy.Template(tree)(parents = superCall :: ps) + case _ => + tree + } +} From 63243ab36063a59a9784d952f99c12b0c0060a4e Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 1 Jul 2015 14:34:18 +0200 Subject: [PATCH 08/12] New representation for arrays of value classes For a value class V whose underlying type is U, instead of representing an array of V as V[] on the JVM, we use our own class VCXArray where X is "U" if U is a primitive type and is "Object" otherwise. This avoids boxing when creating arrays but we still need to box and unbox when using such an array in a generic position, this also breaks reflection and makes it pretty hard to use an array of a value class from Java or Scala 2. See also the FIXMEs in tests/run/valueclasses-array.scala for the current implementation restrictions. --- src/dotty/tools/dotc/Compiler.scala | 1 + src/dotty/tools/dotc/core/StdNames.scala | 1 + src/dotty/tools/dotc/core/TypeErasure.scala | 4 +- src/dotty/tools/dotc/transform/VCArrays.scala | 112 ++++++++++++++++++ tests/run/valueclasses-array.check | 9 ++ tests/run/valueclasses-array.scala | 48 ++++++++ 6 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 src/dotty/tools/dotc/transform/VCArrays.scala create mode 100644 tests/run/valueclasses-array.check create mode 100644 tests/run/valueclasses-array.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 7619c3d5bf70..ccca26233309 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -62,6 +62,7 @@ class Compiler { new AugmentScala2Traits, new ResolveSuper), List(new Erasure), + List(new VCArrays), // Separate group for easier debugging for now List(new ElimErasedValueType, new VCInline, new Mixin, diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index eb1a73625381..a17e599c6123 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -227,6 +227,7 @@ object StdNames { // Compiler-internal val ANYname: N = "" + val ARR: N = "arr" val CONSTRUCTOR: N = Names.CONSTRUCTOR.toString val DEFAULT_CASE: N = "defaultCase$" val EVT2U: N = "evt2u$" diff --git a/src/dotty/tools/dotc/core/TypeErasure.scala b/src/dotty/tools/dotc/core/TypeErasure.scala index 0ef31015c2de..a8645879c51f 100644 --- a/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/src/dotty/tools/dotc/core/TypeErasure.scala @@ -372,8 +372,10 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean private def eraseArray(tp: RefinedType)(implicit ctx: Context) = { val defn.ArrayType(elemtp) = tp + // Arrays are semi-erased to ErasedValueType(V, U)[] and fully erased in + // VCArrays def arrayErasure(tpToErase: Type) = - erasureFn(isJava, semiEraseVCs = false, isConstructor, wildcardOK)(tpToErase) + erasureFn(isJava, semiEraseVCs = true, isConstructor, wildcardOK)(tpToErase) if (elemtp derivesFrom defn.NullClass) JavaArrayType(defn.ObjectType) else if (isUnboundedGeneric(elemtp)) defn.ObjectType else JavaArrayType(arrayErasure(elemtp)) diff --git a/src/dotty/tools/dotc/transform/VCArrays.scala b/src/dotty/tools/dotc/transform/VCArrays.scala new file mode 100644 index 000000000000..123608b4bc1f --- /dev/null +++ b/src/dotty/tools/dotc/transform/VCArrays.scala @@ -0,0 +1,112 @@ +package dotty.tools.dotc +package transform + +import ast.{Trees, tpd} +import core._, core.Decorators._ +import Contexts._, Trees._, Types._, StdNames._, Symbols._ +import DenotTransformers._, TreeTransforms._, Phases.Phase +import TypeErasure.ErasedValueType, ValueClasses._ + +/** This phase erases arrays of value classes to their runtime representation. + * + * For a value class V whose erased underlying type is U, an array of V has type + * Array[V] before Erasure and Array[ErasedValueType(V, U)] afterwards. This phase + * replaces this type by VCXArray where X is "U" if U is a primitive type and is "Object" + * otherwise. + */ +class VCArrays extends MiniPhaseTransform with InfoTransformer { + import tpd._ + + override def phaseName: String = "vcArrays" + + override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = + eraseVCArrays(tp) + + private def eraseVCArrays(tp: Type)(implicit ctx: Context): Type = tp match { + case JavaArrayType(ErasedValueType(cls, _)) => + defn.vcArrayOf(cls).typeRef + case tp: MethodType => + val paramTypes = tp.paramTypes.mapConserve(eraseVCArrays) + val retType = eraseVCArrays(tp.resultType) + tp.derivedMethodType(tp.paramNames, paramTypes, retType) + case _ => + tp + } + + private def transformTypeOfTree(tree: Tree)(implicit ctx: Context): Tree = + tree.withType(eraseVCArrays(tree.tpe)) + + override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val tpt1 = transformTypeOfTree(tree.tpt) + cpy.ValDef(tree)(tpt = tpt1) + } + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val tpt1 = transformTypeOfTree(tree.tpt) + cpy.DefDef(tree)(tpt = tpt1) + } + + override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = + tree match { + case TypeApply(sel @ Select(_, _), _) if (sel.symbol == defn.newRefArrayMethod) => + // Preserve the semi-erased type of the array so that we can properly transform + // it in transformApply + tree + case TypeApply(fun, args) => + val tree1 = cpy.TypeApply(tree)(fun, args.map(transformTypeOfTree(_))) + transformTypeOfTree(tree1) + } + + override def transformSeqLiteral(tree: SeqLiteral)(implicit ctx: Context, info: TransformerInfo): Tree = + tree.tpe match { + // [arg1, arg2, ...] => new VCXArray([V.evt2u$(arg1), V.evt2u$(arg2), ...]) + case JavaArrayType(ErasedValueType(cls, _)) => + val evt2uMethod = ref(evt2u(cls)) + val underlyingArray = JavaSeqLiteral(tree.elems.map(evt2uMethod.appliedTo(_))) + val mod = cls.companionModule + New(defn.vcArrayOf(cls).typeRef, List(underlyingArray, ref(mod))) + case _ => + tree + } + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = { + tree match { + // newRefArray[ErasedValueType(V, U)[]](args) => New VCXArray(newXArray(args), V) + case Apply(ta @ TypeApply(sel @ Select(_,_), List(targ)), args) + if (sel.symbol == defn.newRefArrayMethod) => + targ.tpe match { + case JavaArrayType(ErasedValueType(cls, underlying)) => + val mod = cls.companionModule + New(defn.vcArrayOf(cls).typeRef, + List(newArray(TypeTree(underlying), tree.pos).appliedToArgs(args), + ref(mod))) + case _ => + tree + } + // array.[]update(idx, elem) => array.arr().[]update(idx, elem) + case Apply(Select(array, nme.primitive.arrayUpdate), List(idx, elem)) => + elem.tpe.widen match { + case ErasedValueType(cls, _) => + array.select(nme.ARR).appliedToNone + .select(nme.primitive.arrayUpdate).appliedTo(idx, ref(evt2u(cls)).appliedTo(elem)) + case _ => + tree + } + // array.[]apply(idx) => array.arr().[]apply(idx) + case Apply(Select(array, nme.primitive.arrayApply), List(idx)) => + tree.tpe.widen match { + case ErasedValueType(cls, _) => + ref(u2evt(cls)).appliedTo(array.select(nme.ARR).appliedToNone + .select(nme.primitive.arrayApply).appliedTo(idx)) + case _ => + tree + } + // array.[]length() => array.arr().[]length() + case Apply(Select(array, nme.primitive.arrayLength), Nil) + if (array.tpe <:< defn.VCArrayPrototypeType) => + array.select(nme.ARR).appliedToNone + .select(nme.primitive.arrayLength).appliedToNone + case _ => + tree + } + } +} diff --git a/tests/run/valueclasses-array.check b/tests/run/valueclasses-array.check new file mode 100644 index 000000000000..14140012b68c --- /dev/null +++ b/tests/run/valueclasses-array.check @@ -0,0 +1,9 @@ +[Meter +Meter(1) +1 +Meter(2) +2 +Meter(3) +1 +Meter(4) +2 diff --git a/tests/run/valueclasses-array.scala b/tests/run/valueclasses-array.scala new file mode 100644 index 000000000000..e761f497dcb1 --- /dev/null +++ b/tests/run/valueclasses-array.scala @@ -0,0 +1,48 @@ +class Meter(val underlying: Int) extends AnyVal { + def plus(other: Meter): Meter = + new Meter(underlying + other.underlying) + + override def toString = "Meter(" + underlying + ")" +} + +object Meter { + // FIXME: generate these methods in VCParents + def box(underlying: Int) = new Meter(underlying) + def runtimeClass = classOf[Meter] +} + +object Test { + def genAccess[T](genArray: Array[T]) = genArray(0) + def genUpdated[T](genArray: Array[T], elem: T) = genArray(0) = elem + def genLength[T](genArray: Array[T]) = genArray.length + + def foo(myArray: Array[Meter], elem: Meter) = { + val update = myArray(0) = elem + val access = myArray(0) + val length: Int = myArray.length + + println(access) + println(length) + } + + def genFoo[T](genArray: Array[T], elem: T) = { + val update = genArray(0) = elem + val access = genArray(0) + val length: Int = genArray.length + + println(access) + println(length) + } + + def main(args: Array[String]) = { + val myArray = new Array[Meter](1) + val myArray2 = Array[Meter](new Meter(1), new Meter(2)) + assert(myArray.getClass eq myArray2.getClass) + + println(myArray.toString) + foo(myArray, new Meter(1)) + foo(myArray2, new Meter(2)) + genFoo(myArray, new Meter(3)) + genFoo(myArray2, new Meter(4)) + } +} From 019d57049d7106360e871aa2bc93e1c1d675cb9c Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Mon, 13 Jul 2015 20:07:02 +0200 Subject: [PATCH 09/12] VCParents: Make VCPrototype extend AnyVal to fix -Ycheck --- src/dotty/tools/dotc/core/Definitions.scala | 3 +++ .../tools/dotc/transform/VCParents.scala | 22 +++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 0df1601464e1..bc95817f3491 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -602,11 +602,14 @@ class Definitions { lazy val vcPrototype: Map[Symbol, Symbol] = refClasses.map(vc => vc -> ctx.requiredClass(s"dotty.runtime.vc.VC${vc.name}Prototype")).toMap + lazy val vcPrototypeValues = vcPrototype.values.toSet lazy val vcCompanion: Map[Symbol, Symbol] = refClasses.map(vc => vc -> ctx.requiredClass(s"dotty.runtime.vc.VC${vc.name}Companion")).toMap lazy val vcArray: Map[Symbol, Symbol] = refClasses.map(vc => vc -> ctx.requiredClass(s"dotty.runtime.vc.VC${vc.name}Array")).toMap + lazy val VCPrototypeClass = ctx.requiredClass(s"dotty.runtime.vc.VCPrototype") + def VCPrototypeType = VCPrototypeClass.typeRef lazy val VCArrayPrototypeClass = ctx.requiredClass(s"dotty.runtime.vc.VCArrayPrototype") def VCArrayPrototypeType = VCArrayPrototypeClass.typeRef diff --git a/src/dotty/tools/dotc/transform/VCParents.scala b/src/dotty/tools/dotc/transform/VCParents.scala index ebabf63e61c7..6a8b93756c5e 100644 --- a/src/dotty/tools/dotc/transform/VCParents.scala +++ b/src/dotty/tools/dotc/transform/VCParents.scala @@ -10,9 +10,11 @@ import ValueClasses._ /** This phase makes value classes extend VCXPrototype and make their companions extend VCXCompanion. * - * For a value class class V whose erased underlying type is U, X is "U" if U is a primitive - * type and is "Object" otherwise. + * (For a value class class V whose erased underlying type is U, X is "U" if U is a primitive + * type and is "Object" otherwise). * + * Furthermore, this phase also make VCPrototype extend AnyVal instead of AnyRef to preserve the + * invariant that value classes should always extend AnyVal. */ class VCParents extends MiniPhaseTransform with DenotTransformer { import tpd._ @@ -46,6 +48,22 @@ class VCParents extends MiniPhaseTransform with DenotTransformer { assert(p.isRef(defn.AnyValClass)) val parents = superType :: ps valueClass.copySymDenotation(info = cinfo.derivedClassInfo(classParents = parents)) + case proto: ClassDenotation if proto.symbol eq defn.VCPrototypeClass => + // After this phase, value classes extend VCXPrototype which extends VCPrototype, + // so we make VCPrototype extend AnyVal to preserve existing subtyping relations. + // We could make VCPrototype extend AnyVal earlier than this phase, but then we + // would need to be careful to not treat it like a real value class. + val cinfo = proto.classInfo + val (p :: ps) = cinfo.classParents + assert(p.isRef(defn.ObjectClass)) + proto.copySymDenotation(info = + cinfo.derivedClassInfo(classParents = defn.AnyValClass.typeRef :: ps)) + case proto: ClassDenotation if defn.vcPrototypeValues.contains(proto.symbol) => + // We need to copy the ClassDenotations of the VCXPrototype classes to reset + // their cache of base classes, the cache is no longer valid because these + // classes extend VCPrototype and we changed the superclass of VCPrototype + // in this phase. + proto.copySymDenotation(info = proto.info) case _ => ref } From 09867eebc5e08eb35576c72be3b5ccf4c02b1215 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 14 Jul 2015 15:24:26 +0200 Subject: [PATCH 10/12] VCParents: generate box and runtimeClass methods Note that this will crash if these methods are present in the source code, we either needs to disallow them in the source code or find an alternative design. --- .../tools/dotc/transform/VCParents.scala | 37 +++++++++++++++++-- tests/run/valueclasses-array.scala | 6 --- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/dotty/tools/dotc/transform/VCParents.scala b/src/dotty/tools/dotc/transform/VCParents.scala index 6a8b93756c5e..778749d60d65 100644 --- a/src/dotty/tools/dotc/transform/VCParents.scala +++ b/src/dotty/tools/dotc/transform/VCParents.scala @@ -4,6 +4,7 @@ package transform import ast.{Trees, tpd} import core._, core.Decorators._ import Contexts._, Flags._, Trees._, Types._, StdNames._, Symbols._ +import Constants.Constant import Denotations._, SymDenotations._ import DenotTransformers._, TreeTransforms._, Phases.Phase import ValueClasses._ @@ -27,6 +28,16 @@ class VCParents extends MiniPhaseTransform with DenotTransformer { case valueClass: ClassSymbol if isDerivedValueClass(valueClass) => val moduleSym = moduleClass.symbol.asClass val cinfo = moduleClass.classInfo + val decls1 = cinfo.decls.cloneScope + + val underlying = underlyingOfValueClass(valueClass) + // FIXME: what should we do if these symbols already exist? + val boxSym = ctx.newSymbol(moduleClass.symbol, nme.box, + Synthetic | Override | Method, MethodType(List(nme.x_0), List(underlying), valueClass.typeRef)) + val runtimeClassSym = ctx.newSymbol(moduleClass.symbol, nme.runtimeClass, + Synthetic | Override | Method, MethodType(Nil, defn.ClassClass.typeRef)) + decls1.enter(boxSym) + decls1.enter(runtimeClassSym) val superType = tpd.ref(defn.vcCompanionOf(valueClass)) .select(nme.CONSTRUCTOR) @@ -35,8 +46,8 @@ class VCParents extends MiniPhaseTransform with DenotTransformer { .resultType moduleClass.copySymDenotation(info = - cinfo.derivedClassInfo(classParents = - ctx.normalizeToClassRefs(List(superType), moduleSym, cinfo.decls))) + cinfo.derivedClassInfo(decls = decls1, classParents = + ctx.normalizeToClassRefs(List(superType), moduleSym, decls1))) case _ => moduleClass } @@ -68,11 +79,31 @@ class VCParents extends MiniPhaseTransform with DenotTransformer { ref } + private def boxDefDef(vc: ClassDenotation)(implicit ctx: Context) = { + val vctp = vc.typeRef + val sym = vc.linkedClass.info.decl(nme.box).symbol.asTerm + DefDef(sym, { paramss => + val List(params) = paramss + New(vctp, params) + }) + } + + private def runtimeClassDefDef(vc: ClassDenotation)(implicit ctx: Context) = { + val vctp = vc.typeRef + val sym = vc.linkedClass.info.decl(nme.runtimeClass).symbol.asTerm + DefDef(sym, { _ => + Literal(Constant(TypeErasure.erasure(vctp))) + }) + } + override def transformTemplate(tree: tpd.Template)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = ctx.owner.denot match { case moduleClass: ClassDenotation if moduleClass.is(ModuleClass) => moduleClass.linkedClass match { case valueClass: ClassSymbol if isDerivedValueClass(valueClass) => + val rcDef = runtimeClassDefDef(valueClass) + val boxDef = boxDefDef(valueClass) + val superCall = New(defn.vcCompanionOf(valueClass).typeRef) .select(nme.CONSTRUCTOR) .appliedToType(valueClass.typeRef) @@ -82,7 +113,7 @@ class VCParents extends MiniPhaseTransform with DenotTransformer { // TODO: We shouldn't disallow extending companion objects of value classes // with classes other than AnyRef assert(p.tpe.isRef(defn.ObjectClass)) - cpy.Template(tree)(parents = superCall :: ps) + cpy.Template(tree)(parents = superCall :: ps, body = boxDef :: rcDef :: tree.body) case _ => tree } diff --git a/tests/run/valueclasses-array.scala b/tests/run/valueclasses-array.scala index e761f497dcb1..cefe75496e1c 100644 --- a/tests/run/valueclasses-array.scala +++ b/tests/run/valueclasses-array.scala @@ -5,12 +5,6 @@ class Meter(val underlying: Int) extends AnyVal { override def toString = "Meter(" + underlying + ")" } -object Meter { - // FIXME: generate these methods in VCParents - def box(underlying: Int) = new Meter(underlying) - def runtimeClass = classOf[Meter] -} - object Test { def genAccess[T](genArray: Array[T]) = genArray(0) def genUpdated[T](genArray: Array[T], elem: T) = genArray(0) = elem From 1c74191883132d9f52e6bfbef95f0a6ac8482d18 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 14 Jul 2015 16:18:30 +0200 Subject: [PATCH 11/12] Put VCArrays in the same group as ElimErasedValueType This required adding a new magic method Arrays#vcArray to remember the value class type of a SeqLiteral. --- src/dotty/runtime/Arrays.scala | 5 ++ src/dotty/tools/dotc/Compiler.scala | 4 +- src/dotty/tools/dotc/core/Definitions.scala | 2 + .../tools/dotc/transform/SeqLiterals.scala | 9 +++- src/dotty/tools/dotc/transform/VCArrays.scala | 53 ++++++++----------- 5 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/dotty/runtime/Arrays.scala b/src/dotty/runtime/Arrays.scala index 4469dced7ff1..1296232154f1 100644 --- a/src/dotty/runtime/Arrays.scala +++ b/src/dotty/runtime/Arrays.scala @@ -22,6 +22,11 @@ object Arrays { arr } + /** Method used only to remember the unerased type of an array of + * value class after erasure. + */ + def vcArray[T <: AnyVal](xs: Array[T], clazz: Class[_]): Array[T] = ??? + /** Create an array of type T. T must be of form Array[E], with * E being a reference type. */ diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index ccca26233309..8ce6fe785fd3 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -62,8 +62,8 @@ class Compiler { new AugmentScala2Traits, new ResolveSuper), List(new Erasure), - List(new VCArrays), // Separate group for easier debugging for now - List(new ElimErasedValueType, + List(new VCArrays, + new ElimErasedValueType, new VCInline, new Mixin, new LazyVals, diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index bc95817f3491..813047c62a4f 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -209,6 +209,7 @@ class Definitions { lazy val DottyArraysModule = ctx.requiredModule("dotty.runtime.Arrays") def newRefArrayMethod = ctx.requiredMethod(DottyArraysModule.moduleClass.asClass, "newRefArray") + def vcArrayMethod = ctx.requiredMethod(DottyArraysModule.moduleClass.asClass, "vcArray") lazy val NilModule = ctx.requiredModule("scala.collection.immutable.Nil") lazy val PredefConformsClass = ctx.requiredClass("scala.Predef." + tpnme.Conforms) @@ -607,6 +608,7 @@ class Definitions { refClasses.map(vc => vc -> ctx.requiredClass(s"dotty.runtime.vc.VC${vc.name}Companion")).toMap lazy val vcArray: Map[Symbol, Symbol] = refClasses.map(vc => vc -> ctx.requiredClass(s"dotty.runtime.vc.VC${vc.name}Array")).toMap + lazy val vcArrayValues = vcArray.values.toSet lazy val VCPrototypeClass = ctx.requiredClass(s"dotty.runtime.vc.VCPrototype") def VCPrototypeType = VCPrototypeClass.typeRef diff --git a/src/dotty/tools/dotc/transform/SeqLiterals.scala b/src/dotty/tools/dotc/transform/SeqLiterals.scala index 94b3b5a63306..1914e46569be 100644 --- a/src/dotty/tools/dotc/transform/SeqLiterals.scala +++ b/src/dotty/tools/dotc/transform/SeqLiterals.scala @@ -4,6 +4,7 @@ package transform import core._ import Types._ import dotty.tools.dotc.transform.TreeTransforms._ +import Constants.Constant import Contexts.Context import Symbols._ import Phases._ @@ -37,10 +38,16 @@ class SeqLiterals extends MiniPhaseTransform { val elemtp = arr.tpe.elemType.bounds.hi val elemCls = elemtp.classSymbol if (isDerivedValueClass(elemCls)) { + val vcArr = + ref(defn.DottyArraysModule) + .select(defn.vcArrayMethod) + .appliedToTypes(List(elemtp)) + .appliedTo(arr, Literal(Constant(TypeErasure.erasure(elemtp)))) + ref(defn.DottyPredefModule) .select("wrapVCArray".toTermName) .appliedToTypes(List(elemtp)) - .appliedTo(arr) + .appliedTo(vcArr) } else { val (wrapMethStr, targs) = if (elemCls.isPrimitiveValueClass) (s"wrap${elemCls.name}Array", Nil) diff --git a/src/dotty/tools/dotc/transform/VCArrays.scala b/src/dotty/tools/dotc/transform/VCArrays.scala index 123608b4bc1f..e6c8d445c7e6 100644 --- a/src/dotty/tools/dotc/transform/VCArrays.scala +++ b/src/dotty/tools/dotc/transform/VCArrays.scala @@ -4,6 +4,7 @@ package transform import ast.{Trees, tpd} import core._, core.Decorators._ import Contexts._, Trees._, Types._, StdNames._, Symbols._ +import Constants.Constant import DenotTransformers._, TreeTransforms._, Phases.Phase import TypeErasure.ErasedValueType, ValueClasses._ @@ -56,20 +57,15 @@ class VCArrays extends MiniPhaseTransform with InfoTransformer { transformTypeOfTree(tree1) } - override def transformSeqLiteral(tree: SeqLiteral)(implicit ctx: Context, info: TransformerInfo): Tree = - tree.tpe match { - // [arg1, arg2, ...] => new VCXArray([V.evt2u$(arg1), V.evt2u$(arg2), ...]) - case JavaArrayType(ErasedValueType(cls, _)) => - val evt2uMethod = ref(evt2u(cls)) - val underlyingArray = JavaSeqLiteral(tree.elems.map(evt2uMethod.appliedTo(_))) - val mod = cls.companionModule - New(defn.vcArrayOf(cls).typeRef, List(underlyingArray, ref(mod))) - case _ => - tree - } - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = { tree match { + // vcArray(xs, clsTp) => new VCXArray(xs, mod) + case Apply(qual, List(xs, Literal(Constant(clsTp: Type)))) + if qual.symbol == defn.vcArrayMethod => + val cls = clsTp.classSymbol.asClass + val mod = cls.companionModule + New(defn.vcArrayOf(cls).typeRef, List(xs, ref(mod))) + // newRefArray[ErasedValueType(V, U)[]](args) => New VCXArray(newXArray(args), V) case Apply(ta @ TypeApply(sel @ Select(_,_), List(targ)), args) if (sel.symbol == defn.newRefArrayMethod) => @@ -84,27 +80,22 @@ class VCArrays extends MiniPhaseTransform with InfoTransformer { } // array.[]update(idx, elem) => array.arr().[]update(idx, elem) case Apply(Select(array, nme.primitive.arrayUpdate), List(idx, elem)) => - elem.tpe.widen match { - case ErasedValueType(cls, _) => - array.select(nme.ARR).appliedToNone - .select(nme.primitive.arrayUpdate).appliedTo(idx, ref(evt2u(cls)).appliedTo(elem)) - case _ => - tree - } + if (defn.vcArrayValues.contains(array.tpe.widen.classSymbol)) { + array.select(nme.ARR).appliedToNone + .select(nme.primitive.arrayUpdate).appliedTo(idx, elem) + } else tree // array.[]apply(idx) => array.arr().[]apply(idx) - case Apply(Select(array, nme.primitive.arrayApply), List(idx)) => - tree.tpe.widen match { - case ErasedValueType(cls, _) => - ref(u2evt(cls)).appliedTo(array.select(nme.ARR).appliedToNone - .select(nme.primitive.arrayApply).appliedTo(idx)) - case _ => - tree - } + case Apply(sel @ Select(array, nme.primitive.arrayApply), List(idx)) => + if (defn.vcArrayValues.contains(array.tpe.widen.classSymbol)) { + array.select(nme.ARR).appliedToNone + .select(nme.primitive.arrayApply).appliedTo(idx) + } else tree // array.[]length() => array.arr().[]length() - case Apply(Select(array, nme.primitive.arrayLength), Nil) - if (array.tpe <:< defn.VCArrayPrototypeType) => - array.select(nme.ARR).appliedToNone - .select(nme.primitive.arrayLength).appliedToNone + case Apply(Select(array, nme.primitive.arrayLength), Nil) => + if (defn.vcArrayValues.contains(array.tpe.widen.classSymbol)) { + array.select(nme.ARR).appliedToNone + .select(nme.primitive.arrayLength).appliedToNone + } else tree case _ => tree } From b581b17817307660ca05f9c6b028641ada89ecae Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 15 Jul 2015 17:31:43 +0200 Subject: [PATCH 12/12] tpd#New: avoid double binding error I have no idea why this works better. --- src/dotty/tools/dotc/ast/tpd.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index a35e0e523da2..b87359d8ae5f 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -384,7 +384,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def New(tp: Type, constr: TermSymbol, args: List[Tree])(implicit ctx: Context): Apply = { val targs = tp.argTypes New(tp withoutArgs targs) - .select(TermRef.withSig(tp.normalizedPrefix, constr)) + .select(constr) .appliedToTypes(targs) .appliedToArgs(args) }