From 6e07bcba38181fd6841ae03f828d9d53e68e6a7e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 17 May 2017 10:23:45 +0200 Subject: [PATCH 1/2] Use ErasedPhantom instead of BoxedUnit as erased unit type --- .../dotty/tools/dotc/core/Definitions.scala | 6 ++++ .../tools/dotc/core/PhantomErasure.scala | 4 +-- .../dotty/tools/dotc/transform/Memoize.scala | 12 ++++---- library/src/dotty/runtime/ErasedPhantom.java | 30 +++++++++++++++++++ 4 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 library/src/dotty/runtime/ErasedPhantom.java diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 5c01c8ca59d1..e1d96af39e33 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -990,4 +990,10 @@ class Definitions { /** If the symbol is of the class scala.Phantom.Any or scala.Phantom.Nothing */ def isPhantomTerminalClass(sym: Symbol) = (sym eq Phantom_AnyClass) || (sym eq Phantom_NothingClass) + + lazy val ErasedPhantomType: TypeRef = ctx.requiredClassRef("dotty.runtime.ErasedPhantom") + def ErasedPhantomClass(implicit ctx: Context) = ErasedPhantomType.symbol.asClass + + def ErasedPhantom_UNIT(implicit ctx: Context) = ErasedPhantomClass.linkedClass.requiredValue("UNIT") + } diff --git a/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala b/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala index fbd10f004c49..78a3c262161e 100644 --- a/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala @@ -20,9 +20,9 @@ import dotty.tools.dotc.core.Types.Type object PhantomErasure { /** Returns the default erased type of a phantom type */ - def erasedPhantomType(implicit ctx: Context): Type = defn.BoxedUnitType + def erasedPhantomType(implicit ctx: Context): Type = defn.ErasedPhantomType /** Returns the default erased tree for a call to Phantom.assume */ - def erasedAssume(implicit ctx: Context): Tree = ref(defn.BoxedUnit_UNIT) + def erasedAssume(implicit ctx: Context): Tree = ref(defn.ErasedPhantom_UNIT) } diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index 293bac40887b..fb34bc6b10e3 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -99,17 +99,19 @@ import Decorators._ val NoFieldNeeded = Lazy | Deferred | JavaDefined | (if (ctx.settings.YnoInline.value) EmptyFlags else Inline) def isErasableBottomField(cls: Symbol): Boolean = { - // TODO: For Scala.js, return false if this field is in a js.Object unless it was a Phantom before erasure. - // Could time travel to detect phantom types or add an annotation before erasure. - !field.isVolatile && ((cls eq defn.NothingClass) || (cls eq defn.NullClass) || (cls eq defn.BoxedUnitClass)) + // TODO: For Scala.js, return false if this field is in a js.Object unless it is an ErasedPhantomClass. + !field.isVolatile && + ((cls eq defn.NothingClass) || (cls eq defn.NullClass) || (cls eq defn.BoxedUnitClass) || (cls eq defn.ErasedPhantomClass)) } def erasedBottomTree(sym: Symbol) = { if (sym eq defn.NothingClass) Throw(Literal(Constant(null))) else if (sym eq defn.NullClass) Literal(Constant(null)) + else if (sym eq defn.BoxedUnitClass) ref(defn.BoxedUnit_UNIT) + else if (sym eq defn.ErasedPhantomClass) ref(defn.ErasedPhantom_UNIT) else { - assert(sym eq defn.BoxedUnitClass) - ref(defn.BoxedUnit_UNIT) + assert(false) // Not in sync with isErasableBottomField + EmptyTree } } diff --git a/library/src/dotty/runtime/ErasedPhantom.java b/library/src/dotty/runtime/ErasedPhantom.java new file mode 100644 index 000000000000..98fac71b4304 --- /dev/null +++ b/library/src/dotty/runtime/ErasedPhantom.java @@ -0,0 +1,30 @@ +package dotty.runtime; + + +/** Unit type representing an erased phantom value. + * + * Based on implementation of BoxedUnit. + */ +public final class ErasedPhantom implements java.io.Serializable { + private static final long serialVersionUID = 4116021023472525845L; + + public final static ErasedPhantom UNIT = new ErasedPhantom(); + + public final static Class TYPE = java.lang.Void.TYPE; + + private Object readResolve() { return UNIT; } + + private ErasedPhantom() { } + + public boolean equals(java.lang.Object other) { + return this == other; + } + + public int hashCode() { + return 0; + } + + public String toString() { + return "(\uD83D\uDC7B )"; + } +} From 9e7174106ca1f895fc17398d338cfa5ff47d5d1e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 17 May 2017 11:10:20 +0200 Subject: [PATCH 2/2] Update doc --- .../src/dotty/tools/dotc/core/PhantomErasure.scala | 12 ++++-------- .../src/dotty/tools/dotc/transform/Memoize.scala | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala b/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala index 78a3c262161e..9064bc5aaad9 100644 --- a/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/PhantomErasure.scala @@ -7,15 +7,11 @@ import dotty.tools.dotc.core.Types.Type /** Phantom erasure erases (minimal erasure): * - * - Parameters/arguments are erased to BoxedUnit. The next step will remove the parameters + * - Parameters/arguments are erased to ErasedPhantom. The next step will remove the parameters * from the method definitions and calls (implemented in branch implement-phantom-types-part-2). - * - Definitions of `def`, `val`, `lazy val` and `var` returning a phantom type to return a BoxedUnit. Where fields - * with BoxedUnit type are not memoized (see transform/Memoize.scala). - * - Calls to Phantom.assume become calls to BoxedUnit. Intended to be optimized away by local optimizations. - * - * BoxedUnit is used as it fits perfectly and homogeneously in all locations where phantoms can be found. - * Additionally some of the optimizations that can be performed on phantom types can also be directly implemented - * on all boxed units. + * - Definitions of `def`, `val`, `lazy val` and `var` returning a phantom type to return a ErasedPhantom. Where fields + * with ErasedPhantom type are not memoized (see transform/Memoize.scala). + * - Calls to Phantom.assume become calls to ErasedPhantom.UNIT. Intended to be optimized away by local optimizations. */ object PhantomErasure { diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index fb34bc6b10e3..a6bb93e4ecf4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -110,7 +110,7 @@ import Decorators._ else if (sym eq defn.BoxedUnitClass) ref(defn.BoxedUnit_UNIT) else if (sym eq defn.ErasedPhantomClass) ref(defn.ErasedPhantom_UNIT) else { - assert(false) // Not in sync with isErasableBottomField + assert(false, sym + " has no erased bottom tree") EmptyTree } }