aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/transform/ExtensionMethods.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/dotty/tools/dotc/transform/ExtensionMethods.scala')
-rw-r--r--src/dotty/tools/dotc/transform/ExtensionMethods.scala34
1 files changed, 29 insertions, 5 deletions
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))