From 92bb33c3fef0e1e078f6c1d0bdbfb0a038469cf9 Mon Sep 17 00:00:00 2001 From: Miles Sabin Date: Sat, 10 Aug 2019 22:34:49 +0100 Subject: [PATCH] Support Mirrors for value classes --- .../dotty/tools/dotc/core/Definitions.scala | 2 ++ .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../dotty/tools/dotc/transform/SymUtils.scala | 2 -- .../dotc/transform/SyntheticMembers.scala | 34 +++++++++++++++++-- .../dotty/tools/dotc/typer/Implicits.scala | 4 ++- library/src/scala/deriving.scala | 7 +++- tests/run/i7000.scala | 16 +++++++++ 7 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 tests/run/i7000.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 8351fb67ddfe..8db9d2a5ac98 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -625,6 +625,8 @@ class Definitions { @tu lazy val Mirror_SumClass: ClassSymbol = ctx.requiredClass("scala.deriving.Mirror.Sum") @tu lazy val Mirror_SingletonClass: ClassSymbol = ctx.requiredClass("scala.deriving.Mirror.Singleton") @tu lazy val Mirror_SingletonProxyClass: ClassSymbol = ctx.requiredClass("scala.deriving.Mirror.SingletonProxy") + @tu lazy val Mirror_ValueClassClass: ClassSymbol = ctx.requiredClass("scala.deriving.Mirror.ValueClass") + @tu lazy val Mirror_ValueClassClass_wrapValue: Symbol = Mirror_ValueClassClass.requiredMethod(nme.wrapValue) @tu lazy val LanguageModule: Symbol = ctx.requiredModule("scala.language") @tu lazy val NonLocalReturnControlClass: ClassSymbol = ctx.requiredClass("scala.runtime.NonLocalReturnControl") diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index e331c3a84dc1..32a994226ebf 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -573,6 +573,7 @@ object StdNames { val withFilterIfRefutable: N = "withFilterIfRefutable$" val WorksheetWrapper: N = "WorksheetWrapper" val wrap: N = "wrap" + val wrapValue: N = "wrapValue" val writeReplace: N = "writeReplace" val zero: N = "zero" val zip: N = "zip" diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 824ec7db8b01..b610ca81176b 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -12,7 +12,6 @@ import StdNames._ import NameKinds._ import Flags._ import Annotations._ -import ValueClasses.isDerivedValueClass import Decorators._ import language.implicitConversions @@ -79,7 +78,6 @@ class SymUtils(val self: Symbol) extends AnyVal { if (!self.is(CaseClass)) "it is not a case class" else if (self.is(Abstract)) "it is an abstract class" else if (self.primaryConstructor.info.paramInfoss.length != 1) "it takes more than one parameter list" - else if (isDerivedValueClass(self)) "it is a value class" else "" def isGenericProduct(implicit ctx: Context): Boolean = whyNotGenericProduct.isEmpty diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 75f86dcfabe1..287708c4636a 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -367,6 +367,28 @@ class SyntheticMembers(thisPhase: DenotTransformer) { } } + def wrapValueBody(caseClass: Symbol, param: Tree)(implicit ctx: Context): Tree = { + val (classRef, methTpe) = + caseClass.primaryConstructor.info match { + case tl: PolyType => + val (tl1, tpts) = constrained(tl, untpd.EmptyTree, alwaysAddTypeVars = true) + val targs = + for (tpt <- tpts) yield + tpt.tpe match { + case tvar: TypeVar => tvar.instantiate(fromBelow = false) + } + (caseClass.typeRef.appliedTo(targs), tl.instantiate(targs)) + case methTpe => + (caseClass.typeRef, methTpe) + } + methTpe match { + case methTpe: MethodType => + val formal = methTpe.paramInfos(0) + val elem = param.ensureConforms(formal) + New(classRef, List(elem)) + } + } + /** For an enum T: * * def ordinal(x: MirroredMonoType) = x.ordinal @@ -438,9 +460,15 @@ class SyntheticMembers(thisPhase: DenotTransformer) { def makeSingletonMirror() = addParent(defn.Mirror_SingletonClass.typeRef) def makeProductMirror(cls: Symbol) = { - addParent(defn.Mirror_ProductClass.typeRef) - addMethod(nme.fromProduct, MethodType(defn.ProductClass.typeRef :: Nil, monoType.typeRef), cls, - fromProductBody(_, _)(_).ensureConforms(monoType.typeRef)) // t4758.scala or i3381.scala are examples where a cast is needed + if (isDerivedValueClass(cls)) { + addParent(defn.Mirror_ValueClassClass.typeRef) + addMethod(nme.wrapValue, MethodType(defn.AnyClass.typeRef :: Nil, monoType.typeRef), cls, + wrapValueBody(_, _)(_).ensureConforms(monoType.typeRef)) + } else { + addParent(defn.Mirror_ProductClass.typeRef) + addMethod(nme.fromProduct, MethodType(defn.ProductClass.typeRef :: Nil, monoType.typeRef), cls, + fromProductBody(_, _)(_).ensureConforms(monoType.typeRef)) // t4758.scala or i3381.scala are examples where a cast is needed + } } def makeSumMirror(cls: Symbol) = { addParent(defn.Mirror_SumClass.typeRef) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 702c9866000a..b1ba8a8ba1a8 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -30,6 +30,7 @@ import Trees._ import transform.SymUtils._ import transform.TypeUtils._ import transform.SyntheticMembers._ +import transform.ValueClasses.isDerivedValueClass import Hashable._ import util.{Property, SourceFile, NoSource} import config.Config @@ -960,8 +961,9 @@ trait Implicits { self: Typer => val elems = TypeOps.nestedPairs(accessors.map(mirroredType.memberInfo(_).widenExpr)) (mirroredType, elems) } + val mirrorClass = if (isDerivedValueClass(cls)) defn.Mirror_ValueClassClass else defn.Mirror_ProductClass val mirrorType = - mirrorCore(defn.Mirror_ProductClass, monoType, mirroredType, cls.name, formal) + mirrorCore(mirrorClass, monoType, mirroredType, cls.name, formal) .refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType)) .refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels))) val mirrorRef = diff --git a/library/src/scala/deriving.scala b/library/src/scala/deriving.scala index 5172a3ac350e..21f9b3a47523 100644 --- a/library/src/scala/deriving.scala +++ b/library/src/scala/deriving.scala @@ -26,7 +26,6 @@ object deriving { /** The Mirror for a product type */ trait Product extends Mirror { - /** Create a new instance of type `T` with elements taken from product `p`. */ def fromProduct(p: scala.Product): MirroredMonoType } @@ -48,6 +47,12 @@ object deriving { def fromProduct(p: scala.Product) = value } + trait ValueClass extends Product { + type MirroredMonoType <: scala.Product + def fromProduct(p: scala.Product): MirroredMonoType = wrapValue(p.productElement(0)) + def wrapValue(v: Any): MirroredMonoType + } + type Of[T] = Mirror { type MirroredType = T; type MirroredMonoType = T ; type MirroredElemTypes <: Tuple } type ProductOf[T] = Mirror.Product { type MirroredType = T; type MirroredMonoType = T ; type MirroredElemTypes <: Tuple } type SumOf[T] = Mirror.Sum { type MirroredType = T; type MirroredMonoType = T; type MirroredElemTypes <: Tuple } diff --git a/tests/run/i7000.scala b/tests/run/i7000.scala new file mode 100644 index 000000000000..9f3ab438887c --- /dev/null +++ b/tests/run/i7000.scala @@ -0,0 +1,16 @@ +import scala.deriving._ +import compiletime._ + +object Test extends App { + case class B(v: Double) extends AnyVal + val m0 = the[Mirror.ProductOf[B]] + + val v0 = m0.fromProduct(Tuple1(23.0)) + assert(v0 == B(23.0)) + + case class C[T](v: T) extends AnyVal + val m1 = the[Mirror.ProductOf[C[Double]]] + + val v1 = m1.fromProduct(Tuple1(23.0)) + assert(v1 == C(23.0)) +}