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/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/runtime/vc/VCPrototype.scala b/src/dotty/runtime/vc/VCPrototype.scala index 0a3abe5a25e6..937ba7853056 100644 --- a/src/dotty/runtime/vc/VCPrototype.scala +++ b/src/dotty/runtime/vc/VCPrototype.scala @@ -29,22 +29,29 @@ 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 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 } -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) @@ -60,7 +67,7 @@ final class VCFloatArray[T <: VCFloatPrototype] private (val arr: Array[Float], } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -75,27 +82,27 @@ abstract class VCObjectCasePrototype(underlying: Object) extends VCObjectPrototy underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } -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 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 } -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) @@ -113,7 +120,7 @@ final class VCObjectArray[T <: VCObjectPrototype] private (val arr: Array[Object } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -128,27 +135,27 @@ abstract class VCShortCasePrototype(underlying: Short) extends VCShortPrototype( underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } -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 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 } -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) @@ -166,7 +173,7 @@ final class VCShortArray[T <: VCShortPrototype] private (val arr: Array[Short], } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -182,27 +189,27 @@ abstract class VCLongCasePrototype(underlying: Long) extends VCLongPrototype(und underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } -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 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 } -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) @@ -220,7 +227,7 @@ final class VCLongArray[T <: VCLongPrototype] private (val arr: Array[Long], val } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -235,27 +242,27 @@ abstract class VCIntCasePrototype(underlying: Int) extends VCIntPrototype(underl underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } -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 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 } -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) @@ -271,7 +278,7 @@ final class VCIntArray[T <: VCIntPrototype] private (val arr: Array[Int], val ct } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -286,27 +293,27 @@ abstract class VCDoubleCasePrototype(underlying: Double) extends VCDoublePrototy underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } -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 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 } -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) @@ -322,7 +329,7 @@ final class VCDoubleArray[T <: VCDoublePrototype] private (val arr: Array[Double } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -337,27 +344,27 @@ abstract class VCBooleanCasePrototype(underlying: Boolean) extends VCBooleanProt underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } -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 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 } -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) @@ -375,7 +382,7 @@ final class VCBooleanArray[T <: VCBooleanPrototype] private (val arr: Array[Bool } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -390,29 +397,29 @@ abstract class VCCharCasePrototype(underlying: Char) extends VCCharPrototype(und underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } // 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 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 } -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) @@ -428,7 +435,7 @@ final class VCCharArray[T <: VCCharPrototype] private (val arr: Array[Char], val } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } @@ -443,27 +450,27 @@ abstract class VCByteCasePrototype(underlying: Byte) extends VCBytePrototype(und underlying.hashCode() } - override final def toString: String = { + override def toString: String = { s"$productPrefix($underlying)" } } -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 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 } -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) @@ -479,7 +486,7 @@ final class VCByteArray[T <: VCBytePrototype] private (val arr: Array[Byte], val } override def toString: String = { - "[" + ct.runtimeClass + "[" + ct.toString } } diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 827134e847b3..8ce6fe785fd3 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), @@ -61,7 +62,8 @@ class Compiler { new AugmentScala2Traits, new ResolveSuper), List(new Erasure), - List(new ElimErasedValueType, + List(new VCArrays, + new ElimErasedValueType, new VCInline, new Mixin, new LazyVals, 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) } diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 7ed0a26e018c..813047c62a4f 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 } @@ -208,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) @@ -599,6 +601,35 @@ 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 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 vcArrayValues = vcArray.values.toSet + + 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 + + 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/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/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/SeqLiterals.scala b/src/dotty/tools/dotc/transform/SeqLiterals.scala index 6e29d7e092df..1914e46569be 100644 --- a/src/dotty/tools/dotc/transform/SeqLiterals.scala +++ b/src/dotty/tools/dotc/transform/SeqLiterals.scala @@ -4,10 +4,11 @@ package transform import core._ import Types._ import dotty.tools.dotc.transform.TreeTransforms._ +import Constants.Constant 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 +37,26 @@ 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)) { + 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(vcArr) + } 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) + } } } 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) } diff --git a/src/dotty/tools/dotc/transform/VCArrays.scala b/src/dotty/tools/dotc/transform/VCArrays.scala new file mode 100644 index 000000000000..e6c8d445c7e6 --- /dev/null +++ b/src/dotty/tools/dotc/transform/VCArrays.scala @@ -0,0 +1,103 @@ +package dotty.tools.dotc +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._ + +/** 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 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) => + 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)) => + 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(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 (defn.vcArrayValues.contains(array.tpe.widen.classSymbol)) { + array.select(nme.ARR).appliedToNone + .select(nme.primitive.arrayLength).appliedToNone + } else tree + case _ => + tree + } + } +} diff --git a/src/dotty/tools/dotc/transform/VCParents.scala b/src/dotty/tools/dotc/transform/VCParents.scala new file mode 100644 index 000000000000..778749d60d65 --- /dev/null +++ b/src/dotty/tools/dotc/transform/VCParents.scala @@ -0,0 +1,133 @@ +package dotty.tools.dotc +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._ + +/** 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). + * + * 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._ + + 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 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) + .appliedToType(valueClass.typeRef) + .tpe + .resultType + + moduleClass.copySymDenotation(info = + cinfo.derivedClassInfo(decls = decls1, classParents = + ctx.normalizeToClassRefs(List(superType), moduleSym, decls1))) + 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 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 + } + + 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) + .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, body = boxDef :: rcDef :: tree.body) + 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 + } +} 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..cefe75496e1c --- /dev/null +++ b/tests/run/valueclasses-array.scala @@ -0,0 +1,42 @@ +class Meter(val underlying: Int) extends AnyVal { + def plus(other: Meter): Meter = + new Meter(underlying + other.underlying) + + override def toString = "Meter(" + underlying + ")" +} + +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)) + } +}