aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala2
-rw-r--r--src/dotty/tools/dotc/transform/Erasure.scala15
-rw-r--r--src/dotty/tools/dotc/transform/ExtensionMethods.scala34
-rw-r--r--src/dotty/tools/dotc/transform/ValueClasses.scala14
4 files changed, 58 insertions, 7 deletions
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index 829ff8b8f..74a121b47 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -226,6 +226,7 @@ object StdNames {
val ANYname: N = "<anyname>"
val CONSTRUCTOR: N = Names.CONSTRUCTOR.toString
val DEFAULT_CASE: N = "defaultCase$"
+ val EVT2U: N = "evt2u$"
val EQEQ_LOCAL_VAR: N = "eqEqTemp$"
val FAKE_LOCAL_THIS: N = "this$"
val IMPLCLASS_CONSTRUCTOR: N = "$init$"
@@ -257,6 +258,7 @@ object StdNames {
val SKOLEM: N = "<skolem>"
val SPECIALIZED_INSTANCE: N = "specInstance$"
val THIS: N = "_$this"
+ val U2EVT: N = "u2evt$"
final val Nil: N = "Nil"
final val Predef: N = "Predef"
diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala
index 65e508159..01f86915d 100644
--- a/src/dotty/tools/dotc/transform/Erasure.scala
+++ b/src/dotty/tools/dotc/transform/Erasure.scala
@@ -192,6 +192,8 @@ object Erasure extends TypeTestsCasts{
/** Generate a synthetic cast operation from tree.tpe to pt.
* Does not do any boxing/unboxing (this is handled upstream).
+ * Casts from and to ErasedValueType are special, see the explanation
+ * in ExtensionMethods#transform.
*/
def cast(tree: Tree, pt: Type)(implicit ctx: Context): Tree = {
// TODO: The commented out assertion fails for tailcall/t6574.scala
@@ -203,9 +205,18 @@ object Erasure extends TypeTestsCasts{
if treeElem.widen.isPrimitiveValueType && !ptElem.isPrimitiveValueType =>
// See SI-2386 for one example of when this might be necessary.
cast(ref(defn.runtimeMethod(nme.toObjectArray)).appliedTo(tree), pt)
+ case (_, ErasedValueType(cls, _)) =>
+ ref(u2evt(cls)).appliedTo(tree)
case _ =>
- if (pt.isPrimitiveValueType) primitiveConversion(tree, pt.classSymbol)
- else tree.asInstance(pt)
+ tree.tpe.widen match {
+ case ErasedValueType(cls, _) =>
+ ref(evt2u(cls)).appliedTo(tree)
+ case _ =>
+ if (pt.isPrimitiveValueType)
+ primitiveConversion(tree, pt.classSymbol)
+ else
+ tree.asInstance(pt)
+ }
}
}
diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala
index e6260bde2..724f3fc64 100644
--- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala
+++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala
@@ -14,13 +14,24 @@ import core._
import Phases.Phase
import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._
import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._
+import TypeErasure.{ erasure, ErasedValueType }
import TypeUtils._
import util.Positions._
import Decorators._
+import SymUtils._
/**
* Perform Step 1 in the inline classes SIP: Creates extension methods for all
* methods in a value class, except parameter or super accessors, or constructors.
+ *
+ * Additionally, for a value class V, let U be the underlying type after erasure. We add
+ * to the companion module of V two cast methods:
+ * def u2evt$(x0: U): ErasedValueType(V, U)
+ * def evt2u$(x0: ErasedValueType(V, U)): U
+ * The casts are used in [[Erasure]] to make it typecheck, they are then removed
+ * in [[ElimErasedValueType]].
+ * This is different from the implementation of value classes in Scala 2
+ * (see SIP-15) which uses `asInstanceOf` which does not typecheck.
*/
class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with FullParameterization { thisTransformer =>
@@ -36,15 +47,28 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful
override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match {
case ref: ClassDenotation if ref is ModuleClass =>
ref.linkedClass match {
- // In Scala 2, extension methods are added before pickling so we should not generate them again
- case origClass: ClassSymbol if isDerivedValueClass(origClass) && !(origClass is Scala2x) =>
+ case origClass: ClassSymbol if isDerivedValueClass(origClass) =>
val cinfo = ref.classInfo
val decls1 = cinfo.decls.cloneScope
ctx.atPhase(thisTransformer.next) { implicit ctx =>
- for (decl <- origClass.classInfo.decls) {
- if (isMethodWithExtension(decl))
- decls1.enter(createExtensionMethod(decl, ref.symbol))
+ // In Scala 2, extension methods are added before pickling so we should
+ // not generate them again.
+ if (!(origClass is Scala2x)) {
+ for (decl <- origClass.classInfo.decls) {
+ if (isMethodWithExtension(decl))
+ decls1.enter(createExtensionMethod(decl, ref.symbol))
+ }
}
+
+ val sym = ref.symbol
+ val underlying = erasure(underlyingOfValueClass(origClass))
+ val evt = ErasedValueType(origClass, underlying)
+ val u2evtSym = ctx.newSymbol(sym, nme.U2EVT, Synthetic | Method,
+ MethodType(List(nme.x_0), List(underlying), evt))
+ val evt2uSym = ctx.newSymbol(sym, nme.EVT2U, Synthetic | Method,
+ MethodType(List(nme.x_0), List(evt), underlying))
+ decls1.enter(u2evtSym)
+ decls1.enter(evt2uSym)
}
if (decls1.isEmpty) ref
else ref.copySymDenotation(info = cinfo.derivedClassInfo(decls = decls1))
diff --git a/src/dotty/tools/dotc/transform/ValueClasses.scala b/src/dotty/tools/dotc/transform/ValueClasses.scala
index 9cd0e1ef7..8969b9321 100644
--- a/src/dotty/tools/dotc/transform/ValueClasses.scala
+++ b/src/dotty/tools/dotc/transform/ValueClasses.scala
@@ -35,6 +35,20 @@ object ValueClasses {
.map(_.symbol)
.getOrElse(NoSymbol)
+ /** For a value class `d`, this returns the synthetic cast from the underlying type to
+ * ErasedValueType defined in the companion module. This method is added to the module
+ * and further described in [[ExtensionMethods]].
+ */
+ def u2evt(d: ClassDenotation)(implicit ctx: Context): Symbol =
+ d.linkedClass.info.decl(nme.U2EVT).symbol
+
+ /** For a value class `d`, this returns the synthetic cast from ErasedValueType to the
+ * underlying type defined in the companion module. This method is added to the module
+ * and further described in [[ExtensionMethods]].
+ */
+ def evt2u(d: ClassDenotation)(implicit ctx: Context): Symbol =
+ d.linkedClass.info.decl(nme.EVT2U).symbol
+
/** The unboxed type that underlies a derived value class */
def underlyingOfValueClass(d: ClassDenotation)(implicit ctx: Context): Type =
valueClassUnbox(d).info.resultType