From 2b1ec5598bb9a6cd9b943d57c9bb4db7e9087ce3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 17 May 2019 18:13:09 +0200 Subject: [PATCH 01/35] New test case A simplified test case which adopts several improvements from Miles Sabin's branch. Compared to typeclass-derivation2c.scala: - The alternatives and numberOfCases methods are gone - All compiler-generated info is in the companion objects; no additional classes are generated. - The derivation code largely uses the type traversals of the previous schemes instead of simple counting, as was the case in typeclass-derivation2c.scala. - Generic has been renamed to Mirror, following a suggestion by Miles (but now all mirrors are static) The key insight is that we do not need a mapping from the sum type to its alternatives except knowing what the alternative types are. Once we have an alternative type, we can use an implicit match to retrieve the mirror for that alternative. --- tests/run/typeclass-derivation2d.check | 10 + tests/run/typeclass-derivation2d.scala | 453 +++++++++++++++++++++++++ 2 files changed, 463 insertions(+) create mode 100644 tests/run/typeclass-derivation2d.check create mode 100644 tests/run/typeclass-derivation2d.scala diff --git a/tests/run/typeclass-derivation2d.check b/tests/run/typeclass-derivation2d.check new file mode 100644 index 000000000000..fd3681599ffc --- /dev/null +++ b/tests/run/typeclass-derivation2d.check @@ -0,0 +1,10 @@ +ListBuffer(0, 11, 0, 22, 0, 33, 1) +Cons(11,Cons(22,Cons(33,Nil))) +ListBuffer(0, 0, 11, 0, 22, 0, 33, 1, 0, 0, 11, 0, 22, 1, 1) +Cons(Cons(11,Cons(22,Cons(33,Nil))),Cons(Cons(11,Cons(22,Nil)),Nil)) +ListBuffer(1, 2) +Pair(1,2) +Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))) +Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))), tl = Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Nil())), tl = Nil())) +Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) +Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) diff --git a/tests/run/typeclass-derivation2d.scala b/tests/run/typeclass-derivation2d.scala new file mode 100644 index 000000000000..1951e33092bc --- /dev/null +++ b/tests/run/typeclass-derivation2d.scala @@ -0,0 +1,453 @@ +import scala.collection.mutable +import scala.annotation.tailrec + +// Simulation of an alternative typeclass derivation scheme + +// -- Classes and Objects of the Derivation Framework ---------------------------------- + +//** Core classes. In the current implementation these are in the scala.reflect package */ +object Deriving { + + /** The Generic class hierarchy allows typelevel access to + * enums, case classes and objects, and their sealed parents. + */ + sealed abstract class Mirror[T] + + object Mirror { + + /** The Mirror for a sum type */ + trait Sum[T] extends Mirror[T] { self => + + type ElemTypes <: Tuple + + /** The ordinal number of the case class of `x`. For enums, `ordinal(x) == x.ordinal` */ + def ordinal(x: T): Int + } + + /** The Mirror for a product type */ + trait Product[T] extends Mirror[T] { + + /** The types of the elements */ + type ElemTypes <: Tuple + + /** The name of the whole product type */ + type CaseLabel <: String + + /** The names of the product elements */ + type ElemLabels <: Tuple + + /** Create a new instance of type `T` with elements taken from product `p`. */ + def fromProduct(p: scala.Product): T + } + } + + /** Helper class to turn arrays into products */ + class ArrayProduct(val elems: Array[AnyRef]) extends Product { + def this(size: Int) = this(new Array[AnyRef](size)) + def canEqual(that: Any): Boolean = true + def productElement(n: Int) = elems(n) + def productArity = elems.length + override def productIterator: Iterator[Any] = elems.iterator + def update(n: Int, x: Any) = elems(n) = x.asInstanceOf[AnyRef] + } + + object EmptyProduct extends ArrayProduct(Array[AnyRef]()) + + /** Helper method to select a product element */ + def productElement[T](x: Any, idx: Int) = + x.asInstanceOf[Product].productElement(idx).asInstanceOf[T] +} +import Deriving._ + +sealed trait Lst[+T] // derives Eq, Pickler, Show + +object Lst extends Mirror.Sum[Lst[_]] { + + def ordinal(x: Lst[_]) = x match { + case x: Cons[_] => 0 + case Nil => 1 + } + + implicit def mirror[T]: Mirror.Sum[Lst[T]] { + type ElemTypes = (Cons[T], Nil.type) + } = this.asInstanceOf + + case class Cons[T](hd: T, tl: Lst[T]) extends Lst[T] + + object Cons extends Mirror.Product[Cons[_]] { + + def apply[T](x: T, xs: Lst[T]): Lst[T] = new Cons(x, xs) + + def fromProduct(p: Product): Cons[_] = + new Cons(productElement[Any](p, 0), productElement[Lst[Any]](p, 1)) + + implicit def mirror[T]: Mirror.Product[Cons[T]] { + type ElemTypes = (T, Lst[T]) + type CaseLabel = "Cons" + type ElemLabels = ("hd", "tl") + } = this.asInstanceOf + } + + case object Nil extends Lst[Nothing] with Mirror.Product[Nil.type] { + def fromProduct(p: Product): Nil.type = Nil + + implicit def mirror: Mirror.Product[Nil.type] { + type ElemTypes = Unit + type CaseLabel = "Nil" + type ElemLabels = Unit + } = this.asInstanceOf + } + + // three clauses that would be generated from a `derives` clause + implicit def derived$Eq[T: Eq]: Eq[Lst[T]] = Eq.derived + implicit def derived$Pickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived + implicit def derived$Show[T: Show]: Show[Lst[T]] = Show.derived +} + +// --------------- A simple product type ------------------------ + +case class Pair[T](x: T, y: T) // derives Eq, Pickler, Show + +object Pair extends Mirror.Product[Pair[_]] { + + def fromProduct(p: Product): Pair[_] = + Pair(productElement[Any](p, 0), productElement[Any](p, 1)) + + implicit def mirror[T]: Mirror.Product[Pair[T]] { + type ElemTypes = (T, T) + type CaseLabel = "Pair" + type ElemLabels = ("x", "y") + } = this.asInstanceOf + + // clauses that could be generated from a `derives` clause + implicit def derived$Eq[T: Eq]: Eq[Pair[T]] = Eq.derived + implicit def derived$Pickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived + implicit def derived$Show[T: Show]: Show[Pair[T]] = Show.derived +} + +// ----------- Another sum type ---------------------------------------- + +sealed trait Either[+L, +R] extends Product with Serializable // derives Eq, Pickler, Show + +object Either extends Mirror.Sum[Either[_, _]] { + + def ordinal(x: Either[_, _]) = x match { + case x: Left[_] => 0 + case x: Right[_] => 1 + } + + implicit def mirror[L, R]: Mirror.Sum[Either[L, R]] { + type ElemTypes = (Left[L], Right[R]) + } = this.asInstanceOf + + implicit def derived$Eq[L: Eq, R: Eq]: Eq[Either[L, R]] = Eq.derived + implicit def derived$Pickler[L: Pickler, R: Pickler]: Pickler[Either[L, R]] = Pickler.derived + implicit def derived$Show[L: Show, R: Show]: Show[Either[L, R]] = Show.derived +} + +case class Left[L](elem: L) extends Either[L, Nothing] +case class Right[R](elem: R) extends Either[Nothing, R] + +object Left extends Mirror.Product[Left[_]] { + def fromProduct(p: Product): Left[_] = Left(productElement[Any](p, 0)) + implicit def mirror[L]: Mirror.Product[Left[L]] { + type ElemTypes = L *: Unit + type CaseLabel = "Left" + type ElemLabels = "x" *: Unit + } = this.asInstanceOf +} + +object Right extends Mirror.Product[Right[_]] { + def fromProduct(p: Product): Right[_] = Right(productElement[Any](p, 0)) + implicit def mirror[R]: Mirror.Product[Right[R]] { + type ElemTypes = R *: Unit + type CaseLabel = "Right" + type ElemLabels = "x" *: Unit + } = this.asInstanceOf +} + +// --------------- Equality typeclass --------------------------------- + +trait Eq[T] { + def eql(x: T, y: T): Boolean +} + +object Eq { + import scala.compiletime.erasedValue + + inline def tryEql[T](x: T, y: T) = implicit match { + case eq: Eq[T] => eq.eql(x, y) + } + + inline def eqlElems[Elems <: Tuple](n: Int)(x: Any, y: Any): Boolean = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + tryEql[elem](productElement[elem](x, n), productElement[elem](y, n)) && + eqlElems[elems1](n + 1)(x, y) + case _: Unit => + true + } + + inline def eqlProduct[T](m: Mirror.Product[T])(x: Any, y: Any): Boolean = + eqlElems[m.ElemTypes](0)(x, y) + + inline def eqlCases[Alts](n: Int)(x: Any, y: Any, ord: Int): Boolean = + inline erasedValue[Alts] match { + case _: (alt *: alts1) => + if (ord == n) + implicit match { + case m: Mirror.Product[`alt`] => eqlElems[m.ElemTypes](0)(x, y) + } + else eqlCases[alts1](n + 1)(x, y, ord) + case _: Unit => + false + } + + inline def derived[T](implicit ev: Mirror[T]): Eq[T] = new Eq[T] { + def eql(x: T, y: T): Boolean = + inline ev match { + case m: Mirror.Sum[T] => + val ord = m.ordinal(x) + ord == m.ordinal(y) && eqlCases[m.ElemTypes](0)(x, y, ord) + case m: Mirror.Product[T] => + eqlElems[m.ElemTypes](0)(x, y) + } + } + + implicit object IntEq extends Eq[Int] { + def eql(x: Int, y: Int) = x == y + } +} + +// ----------- Another typeclass ----------------------------------- + +trait Pickler[T] { + def pickle(buf: mutable.ListBuffer[Int], x: T): Unit + def unpickle(buf: mutable.ListBuffer[Int]): T +} + +object Pickler { + import scala.compiletime.{erasedValue, constValue} + + def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) + + inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = implicit match { + case pkl: Pickler[T] => pkl.pickle(buf, x) + } + + inline def pickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Any): Unit = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + tryPickle[elem](buf, productElement[elem](x, n)) + pickleElems[elems1](n + 1)(buf, x) + case _: Unit => + } + + inline def pickleCases[Alts <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Any, ord: Int): Unit = + inline erasedValue[Alts] match { + case _: (alt *: alts1) => + if (ord == n) + implicit match { + case m: Mirror.Product[`alt`] => pickleElems[m.ElemTypes](0)(buf, x) + } + else pickleCases[alts1](n + 1)(buf, x, ord) + case _: Unit => + } + + inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = implicit match { + case pkl: Pickler[T] => pkl.unpickle(buf) + } + + inline def unpickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], elems: ArrayProduct): Unit = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + elems(n) = tryUnpickle[elem](buf).asInstanceOf[AnyRef] + unpickleElems[elems1](n + 1)(buf, elems) + case _: Unit => + } + + inline def unpickleCase[T, Elems <: Tuple](buf: mutable.ListBuffer[Int], m: Mirror.Product[T]): T = { + inline val size = constValue[Tuple.Size[Elems]] + inline if (size == 0) + m.fromProduct(EmptyProduct) + else { + val elems = new ArrayProduct(size) + unpickleElems[Elems](0)(buf, elems) + m.fromProduct(elems) + } + } + + inline def unpickleCases[T, Alts <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], ord: Int): T = + inline erasedValue[Alts] match { + case _: (alt *: alts1) => + if (ord == n) + implicit match { + case m: Mirror.Product[`alt` & T] => + unpickleCase[`alt` & T, m.ElemTypes](buf, m) + } + else unpickleCases[T, alts1](n + 1)(buf, ord) + case _: Unit => + throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ord") + } + + inline def derived[T](implicit ev: Mirror[T]): Pickler[T] = new { + def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = + inline ev match { + case m: Mirror.Sum[T] => + val ord = m.ordinal(x) + buf += ord + pickleCases[m.ElemTypes](0)(buf, x, ord) + case m: Mirror.Product[T] => + pickleElems[m.ElemTypes](0)(buf, x) + } + def unpickle(buf: mutable.ListBuffer[Int]): T = + inline ev match { + case m: Mirror.Sum[T] => + val ord = nextInt(buf) + unpickleCases[T, m.ElemTypes](0)(buf, ord) + case m: Mirror.Product[T] => + unpickleCase[T, m.ElemTypes](buf, m) + } + } + + implicit object IntPickler extends Pickler[Int] { + def pickle(buf: mutable.ListBuffer[Int], x: Int): Unit = buf += x + def unpickle(buf: mutable.ListBuffer[Int]): Int = nextInt(buf) + } +} + +// ----------- A third typeclass, making use of labels -------------------------- + +trait Show[T] { + def show(x: T): String +} +object Show { + import scala.compiletime.{erasedValue, constValue} + + inline def tryShow[T](x: T): String = implicit match { + case s: Show[T] => s.show(x) + } + + inline def showElems[Elems <: Tuple, Labels <: Tuple](n: Int)(x: Any): List[String] = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + inline erasedValue[Labels] match { + case _: (label *: labels1) => + val formal = constValue[label] + val actual = tryShow(productElement[elem](x, n)) + s"$formal = $actual" :: showElems[elems1, labels1](n + 1)(x) + } + case _: Unit => + Nil + } + + inline def showCase(x: Any, m: Mirror.Product[_]): String = { + val label = constValue[m.CaseLabel] + showElems[m.ElemTypes, m.ElemLabels](0)(x).mkString(s"$label(", ", ", ")") + } + + inline def showCases[Alts <: Tuple](n: Int)(x: Any, ord: Int): String = + inline erasedValue[Alts] match { + case _: (alt *: alts1) => + if (ord == n) + implicit match { + case m: Mirror.Product[`alt`] => + showCase(x, m) + } + else showCases[alts1](n + 1)(x, ord) + case _: Unit => + throw new MatchError(x) + } + + inline def derived[T](implicit ev: Mirror[T]): Show[T] = new { + def show(x: T): String = + inline ev match { + case m: Mirror.Sum[T] => + val ord = m.ordinal(x) + showCases[m.ElemTypes](0)(x, ord) + case m: Mirror.Product[T] => + showCase(x, m) + } + } + + implicit object IntShow extends Show[Int] { + def show(x: Int): String = x.toString + } +} + +object Test extends App { + val eq = implicitly[Eq[Lst[Int]]] + val xs = Lst.Cons(11, Lst.Cons(22, Lst.Cons(33, Lst.Nil))) + val ys = Lst.Cons(11, Lst.Cons(22, Lst.Nil)) + assert(eq.eql(xs, xs)) + assert(!eq.eql(xs, ys)) + assert(!eq.eql(ys, xs)) + assert(eq.eql(ys, ys)) + + val eq2 = implicitly[Eq[Lst[Lst[Int]]]] + val xss = Lst.Cons(xs, Lst.Cons(ys, Lst.Nil)) + val yss = Lst.Cons(xs, Lst.Nil) + assert(eq2.eql(xss, xss)) + assert(!eq2.eql(xss, yss)) + assert(!eq2.eql(yss, xss)) + assert(eq2.eql(yss, yss)) + + val buf = new mutable.ListBuffer[Int] + val pkl = implicitly[Pickler[Lst[Int]]] + pkl.pickle(buf, xs) + println(buf) + val xs1 = pkl.unpickle(buf) + println(xs1) + assert(xs1 == xs) + assert(eq.eql(xs1, xs)) + + val pkl2 = implicitly[Pickler[Lst[Lst[Int]]]] + pkl2.pickle(buf, xss) + println(buf) + val xss1 = pkl2.unpickle(buf) + println(xss1) + assert(xss == xss1) + assert(eq2.eql(xss, xss1)) + + val p1 = Pair(1, 2) + val p2 = Pair(1, 2) + val p3 = Pair(2, 1) + val eqp = implicitly[Eq[Pair[Int]]] + assert(eqp.eql(p1, p2)) + assert(!eqp.eql(p2, p3)) + + val pklp = implicitly[Pickler[Pair[Int]]] + pklp.pickle(buf, p1) + println(buf) + val p1a = pklp.unpickle(buf) + println(p1a) + assert(p1 == p1a) + assert(eqp.eql(p1, p1a)) + + def showPrintln[T: Show](x: T): Unit = + println(implicitly[Show[T]].show(x)) + + showPrintln(xs) + showPrintln(xss) + + val zs = Lst.Cons(Left(1), Lst.Cons(Right(Pair(2, 3)), Lst.Nil)) + showPrintln(zs) + + def pickle[T: Pickler](buf: mutable.ListBuffer[Int], x: T): Unit = + implicitly[Pickler[T]].pickle(buf, x) + + def unpickle[T: Pickler](buf: mutable.ListBuffer[Int]): T = + implicitly[Pickler[T]].unpickle(buf) + + def copy[T: Pickler](x: T): T = { + val buf = new mutable.ListBuffer[Int] + pickle(buf, x) + unpickle[T](buf) + } + + def eql[T: Eq](x: T, y: T) = implicitly[Eq[T]].eql(x, y) + + val zs1 = copy(zs) + showPrintln(zs1) + assert(eql(zs, zs1)) +} \ No newline at end of file From 844c5d7c63b7903b9eb69b28e1e2459bb62f89d0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 18 May 2019 19:43:51 +0200 Subject: [PATCH 02/35] Fix widenAbstractTypes This is significantly more subtle than it looks at first sight. We are probably still unsound for scrutinees that are aliases of matchtypes themselves. --- .../dotty/tools/dotc/core/TypeComparer.scala | 29 +++++++++++++------ .../test/dotc/run-test-pickling.blacklist | 1 + tests/neg-custom-args/matchtype-loop.scala | 2 +- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index b0a6bf25b9d8..83d1aadf93e9 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -2313,19 +2313,30 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { cas } def widenAbstractTypes(tp: Type): Type = new TypeMap { + var seen = Set[TypeParamRef]() def apply(tp: Type) = tp match { case tp: TypeRef => - if (tp.symbol.isAbstractOrParamType | tp.symbol.isOpaqueAlias) - WildcardType - else tp.info match { - case TypeAlias(alias) => - val alias1 = widenAbstractTypes(alias) - if (alias1 ne alias) alias1 else tp - case _ => mapOver(tp) + tp.info match { + case info: MatchAlias => + mapOver(tp) + // TODO: We should follow the alias in this case, but doing so + // risks infinite recursion + case TypeBounds(lo, hi) => + if (hi frozen_<:< lo) { + val alias = apply(lo) + if (alias ne lo) alias else mapOver(tp) + } + else WildcardType + case _ => + mapOver(tp) } - + case tp: TypeLambda => + val saved = seen + seen ++= tp.paramRefs + try mapOver(tp) + finally seen = saved case tp: TypeVar if !tp.isInstantiated => WildcardType - case _: TypeParamRef => WildcardType + case tp: TypeParamRef if !seen.contains(tp) => WildcardType case _ => mapOver(tp) } }.apply(tp) diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index ce8f35bdd3e8..3c59bfe5ad4e 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -8,6 +8,7 @@ typeclass-derivation1.scala typeclass-derivation2.scala typeclass-derivation2a.scala typeclass-derivation2c.scala +typeclass-derivation2d.scala typeclass-derivation3.scala derive-generic.scala mixin-forwarder-overload diff --git a/tests/neg-custom-args/matchtype-loop.scala b/tests/neg-custom-args/matchtype-loop.scala index 4c78ba03e5c8..3baf82f1b026 100644 --- a/tests/neg-custom-args/matchtype-loop.scala +++ b/tests/neg-custom-args/matchtype-loop.scala @@ -2,7 +2,7 @@ object Test { type L[X] = X match { case Int => L[X] } - type LL[X] = X match { + type LL[X] = X match { // error: recursion limit exceeded case Int => LL[LL[X]] } def a: L[Boolean] = ??? From d5c5c115e78dea910395c914d911554627b282a6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 18 May 2019 13:20:55 +0200 Subject: [PATCH 03/35] Special treatment for Singletons --- tests/run/typeclass-derivation2d.check | 8 +- tests/run/typeclass-derivation2d.scala | 106 ++++++++++++++++--------- 2 files changed, 71 insertions(+), 43 deletions(-) diff --git a/tests/run/typeclass-derivation2d.check b/tests/run/typeclass-derivation2d.check index fd3681599ffc..eee9a3d088a3 100644 --- a/tests/run/typeclass-derivation2d.check +++ b/tests/run/typeclass-derivation2d.check @@ -4,7 +4,7 @@ ListBuffer(0, 0, 11, 0, 22, 0, 33, 1, 0, 0, 11, 0, 22, 1, 1) Cons(Cons(11,Cons(22,Cons(33,Nil))),Cons(Cons(11,Cons(22,Nil)),Nil)) ListBuffer(1, 2) Pair(1,2) -Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))) -Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))), tl = Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Nil())), tl = Nil())) -Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) -Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) +Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil))) +Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil))), tl = Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Nil)), tl = Nil)) +Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil)) +Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil)) diff --git a/tests/run/typeclass-derivation2d.scala b/tests/run/typeclass-derivation2d.scala index 1951e33092bc..6d8800ef47e9 100644 --- a/tests/run/typeclass-derivation2d.scala +++ b/tests/run/typeclass-derivation2d.scala @@ -11,21 +11,29 @@ object Deriving { /** The Generic class hierarchy allows typelevel access to * enums, case classes and objects, and their sealed parents. */ - sealed abstract class Mirror[T] + sealed abstract class Mirror { + + /** The mirrored *-type */ + type MonoType + } + type MirrorOf[T] = Mirror { type MonoType = T } + type ProductMirrorOf[T] = Mirror.Product { type MonoType = T } + type SumMirrorOf[T] = Mirror.Sum { type MonoType = T } + type SingletonMirror = Mirror.Singleton object Mirror { /** The Mirror for a sum type */ - trait Sum[T] extends Mirror[T] { self => + trait Sum extends Mirror { self => type ElemTypes <: Tuple /** The ordinal number of the case class of `x`. For enums, `ordinal(x) == x.ordinal` */ - def ordinal(x: T): Int + def ordinal(x: MonoType): Int } /** The Mirror for a product type */ - trait Product[T] extends Mirror[T] { + trait Product extends Mirror { /** The types of the elements */ type ElemTypes <: Tuple @@ -37,7 +45,12 @@ object Deriving { type ElemLabels <: Tuple /** Create a new instance of type `T` with elements taken from product `p`. */ - def fromProduct(p: scala.Product): T + def fromProduct(p: scala.Product): MonoType + } + + trait Singleton extends Product { + type MonoType = this.type + def fromProduct(p: scala.Product) = this } } @@ -61,37 +74,41 @@ import Deriving._ sealed trait Lst[+T] // derives Eq, Pickler, Show -object Lst extends Mirror.Sum[Lst[_]] { +object Lst extends Mirror.Sum { + type MonoType = Lst[_] def ordinal(x: Lst[_]) = x match { case x: Cons[_] => 0 case Nil => 1 } - implicit def mirror[T]: Mirror.Sum[Lst[T]] { + implicit def mirror[T]: Mirror.Sum { + type MonoType = Lst[T] type ElemTypes = (Cons[T], Nil.type) } = this.asInstanceOf case class Cons[T](hd: T, tl: Lst[T]) extends Lst[T] - object Cons extends Mirror.Product[Cons[_]] { + object Cons extends Mirror.Product { + type MonoType = Lst[_] def apply[T](x: T, xs: Lst[T]): Lst[T] = new Cons(x, xs) def fromProduct(p: Product): Cons[_] = new Cons(productElement[Any](p, 0), productElement[Lst[Any]](p, 1)) - implicit def mirror[T]: Mirror.Product[Cons[T]] { + implicit def mirror[T]: Mirror.Product { + type MonoType = Cons[T] type ElemTypes = (T, Lst[T]) type CaseLabel = "Cons" type ElemLabels = ("hd", "tl") } = this.asInstanceOf } - case object Nil extends Lst[Nothing] with Mirror.Product[Nil.type] { - def fromProduct(p: Product): Nil.type = Nil + case object Nil extends Lst[Nothing] with Mirror.Singleton { - implicit def mirror: Mirror.Product[Nil.type] { + implicit def mirror: Mirror.Singleton { + type MonoType = Nil.type type ElemTypes = Unit type CaseLabel = "Nil" type ElemLabels = Unit @@ -108,12 +125,14 @@ object Lst extends Mirror.Sum[Lst[_]] { case class Pair[T](x: T, y: T) // derives Eq, Pickler, Show -object Pair extends Mirror.Product[Pair[_]] { +object Pair extends Mirror.Product { + type MonoType = Pair[_] def fromProduct(p: Product): Pair[_] = Pair(productElement[Any](p, 0), productElement[Any](p, 1)) - implicit def mirror[T]: Mirror.Product[Pair[T]] { + implicit def mirror[T]: Mirror.Product { + type MonoType = Pair[T] type ElemTypes = (T, T) type CaseLabel = "Pair" type ElemLabels = ("x", "y") @@ -129,14 +148,16 @@ object Pair extends Mirror.Product[Pair[_]] { sealed trait Either[+L, +R] extends Product with Serializable // derives Eq, Pickler, Show -object Either extends Mirror.Sum[Either[_, _]] { +object Either extends Mirror.Sum { + type MonoType = Either[_, _] def ordinal(x: Either[_, _]) = x match { case x: Left[_] => 0 case x: Right[_] => 1 } - implicit def mirror[L, R]: Mirror.Sum[Either[L, R]] { + implicit def mirror[L, R]: Mirror.Sum { + type MonoType = Either[L, R] type ElemTypes = (Left[L], Right[R]) } = this.asInstanceOf @@ -148,18 +169,22 @@ object Either extends Mirror.Sum[Either[_, _]] { case class Left[L](elem: L) extends Either[L, Nothing] case class Right[R](elem: R) extends Either[Nothing, R] -object Left extends Mirror.Product[Left[_]] { +object Left extends Mirror.Product { + type MonoType = Left[_] def fromProduct(p: Product): Left[_] = Left(productElement[Any](p, 0)) - implicit def mirror[L]: Mirror.Product[Left[L]] { + implicit def mirror[L]: Mirror.Product { + type MonoType = Left[L] type ElemTypes = L *: Unit type CaseLabel = "Left" type ElemLabels = "x" *: Unit } = this.asInstanceOf } -object Right extends Mirror.Product[Right[_]] { +object Right extends Mirror.Product { + type MonoType = Right[_] def fromProduct(p: Product): Right[_] = Right(productElement[Any](p, 0)) - implicit def mirror[R]: Mirror.Product[Right[R]] { + implicit def mirror[R]: Mirror.Product { + type MonoType = Right[R] type ElemTypes = R *: Unit type CaseLabel = "Right" type ElemLabels = "x" *: Unit @@ -188,7 +213,7 @@ object Eq { true } - inline def eqlProduct[T](m: Mirror.Product[T])(x: Any, y: Any): Boolean = + inline def eqlProduct[T](m: ProductMirrorOf[T])(x: Any, y: Any): Boolean = eqlElems[m.ElemTypes](0)(x, y) inline def eqlCases[Alts](n: Int)(x: Any, y: Any, ord: Int): Boolean = @@ -196,20 +221,20 @@ object Eq { case _: (alt *: alts1) => if (ord == n) implicit match { - case m: Mirror.Product[`alt`] => eqlElems[m.ElemTypes](0)(x, y) + case m: ProductMirrorOf[`alt`] => eqlElems[m.ElemTypes](0)(x, y) } else eqlCases[alts1](n + 1)(x, y, ord) case _: Unit => false } - inline def derived[T](implicit ev: Mirror[T]): Eq[T] = new Eq[T] { + inline def derived[T](implicit ev: MirrorOf[T]): Eq[T] = new Eq[T] { def eql(x: T, y: T): Boolean = inline ev match { - case m: Mirror.Sum[T] => + case m: SumMirrorOf[T] => val ord = m.ordinal(x) ord == m.ordinal(y) && eqlCases[m.ElemTypes](0)(x, y, ord) - case m: Mirror.Product[T] => + case m: ProductMirrorOf[T] => eqlElems[m.ElemTypes](0)(x, y) } } @@ -248,7 +273,7 @@ object Pickler { case _: (alt *: alts1) => if (ord == n) implicit match { - case m: Mirror.Product[`alt`] => pickleElems[m.ElemTypes](0)(buf, x) + case m: ProductMirrorOf[`alt`] => pickleElems[m.ElemTypes](0)(buf, x) } else pickleCases[alts1](n + 1)(buf, x, ord) case _: Unit => @@ -266,7 +291,7 @@ object Pickler { case _: Unit => } - inline def unpickleCase[T, Elems <: Tuple](buf: mutable.ListBuffer[Int], m: Mirror.Product[T]): T = { + inline def unpickleCase[T, Elems <: Tuple](buf: mutable.ListBuffer[Int], m: ProductMirrorOf[T]): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) m.fromProduct(EmptyProduct) @@ -282,7 +307,7 @@ object Pickler { case _: (alt *: alts1) => if (ord == n) implicit match { - case m: Mirror.Product[`alt` & T] => + case m: ProductMirrorOf[`alt` & T] => unpickleCase[`alt` & T, m.ElemTypes](buf, m) } else unpickleCases[T, alts1](n + 1)(buf, ord) @@ -290,22 +315,22 @@ object Pickler { throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ord") } - inline def derived[T](implicit ev: Mirror[T]): Pickler[T] = new { + inline def derived[T](implicit ev: MirrorOf[T]): Pickler[T] = new { def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = inline ev match { - case m: Mirror.Sum[T] => + case m: SumMirrorOf[T] => val ord = m.ordinal(x) buf += ord pickleCases[m.ElemTypes](0)(buf, x, ord) - case m: Mirror.Product[T] => + case m: ProductMirrorOf[T] => pickleElems[m.ElemTypes](0)(buf, x) } def unpickle(buf: mutable.ListBuffer[Int]): T = inline ev match { - case m: Mirror.Sum[T] => + case m: SumMirrorOf[T] => val ord = nextInt(buf) unpickleCases[T, m.ElemTypes](0)(buf, ord) - case m: Mirror.Product[T] => + case m: ProductMirrorOf[T] => unpickleCase[T, m.ElemTypes](buf, m) } } @@ -341,9 +366,12 @@ object Show { Nil } - inline def showCase(x: Any, m: Mirror.Product[_]): String = { + inline def showCase(x: Any, m: ProductMirrorOf[_]): String = { val label = constValue[m.CaseLabel] - showElems[m.ElemTypes, m.ElemLabels](0)(x).mkString(s"$label(", ", ", ")") + inline m match { + case m: SingletonMirror => label + case _ => showElems[m.ElemTypes, m.ElemLabels](0)(x).mkString(s"$label(", ", ", ")") + } } inline def showCases[Alts <: Tuple](n: Int)(x: Any, ord: Int): String = @@ -351,7 +379,7 @@ object Show { case _: (alt *: alts1) => if (ord == n) implicit match { - case m: Mirror.Product[`alt`] => + case m: ProductMirrorOf[`alt`] => showCase(x, m) } else showCases[alts1](n + 1)(x, ord) @@ -359,13 +387,13 @@ object Show { throw new MatchError(x) } - inline def derived[T](implicit ev: Mirror[T]): Show[T] = new { + inline def derived[T](implicit ev: MirrorOf[T]): Show[T] = new { def show(x: T): String = inline ev match { - case m: Mirror.Sum[T] => + case m: SumMirrorOf[T] => val ord = m.ordinal(x) showCases[m.ElemTypes](0)(x, ord) - case m: Mirror.Product[T] => + case m: ProductMirrorOf[T] => showCase(x, m) } } From 6fda1f30499d0d16b8b391eca200f217e6dc342e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 19 May 2019 17:30:58 +0200 Subject: [PATCH 04/35] Fix printer bug The recovery after a `summarized` was wrong, which meant that after a first print summarized we'd always print in summarized mode, writing lots of "...". --- compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala | 2 +- tests/run/typeclass-derivation2d.scala | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 6bf763416406..170f3a4020ce 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -547,7 +547,7 @@ class PlainPrinter(_ctx: Context) extends Printer { val saved = maxSummarized maxSummarized = ctx.base.toTextRecursions + depth try op - finally maxSummarized = depth + finally maxSummarized = saved } def summarized[T](op: => T): T = summarized(summarizeDepth)(op) diff --git a/tests/run/typeclass-derivation2d.scala b/tests/run/typeclass-derivation2d.scala index 6d8800ef47e9..39eae0a80446 100644 --- a/tests/run/typeclass-derivation2d.scala +++ b/tests/run/typeclass-derivation2d.scala @@ -19,7 +19,6 @@ object Deriving { type MirrorOf[T] = Mirror { type MonoType = T } type ProductMirrorOf[T] = Mirror.Product { type MonoType = T } type SumMirrorOf[T] = Mirror.Sum { type MonoType = T } - type SingletonMirror = Mirror.Singleton object Mirror { @@ -369,7 +368,7 @@ object Show { inline def showCase(x: Any, m: ProductMirrorOf[_]): String = { val label = constValue[m.CaseLabel] inline m match { - case m: SingletonMirror => label + case m: Mirror.Singleton => label case _ => showElems[m.ElemTypes, m.ElemLabels](0)(x).mkString(s"$label(", ", ", ")") } } From c30af3c185d8948f523f2624740712c086d032d5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 20 May 2019 10:17:26 +0200 Subject: [PATCH 05/35] Add desugar Printer Aim: better diagnostic for what is desugared --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 5 +++-- compiler/src/dotty/tools/dotc/config/Printers.scala | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index f8c4b11ee8a9..826957a75665 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -15,6 +15,7 @@ import reporting.diagnostic.messages._ import reporting.trace import annotation.constructorOnly import printing.Formatting.hl +import config.Printers import scala.annotation.internal.sharable @@ -51,7 +52,7 @@ object desugar { private type VarInfo = (NameTree, Tree) /** Is `name` the name of a method that can be invalidated as a compiler-generated - * case class method that clashes with a user-defined method? + * case class method if it clashes with a user-defined method? */ def isRetractableCaseClassMethodName(name: Name)(implicit ctx: Context): Boolean = name match { case nme.apply | nme.unapply | nme.unapplySeq | nme.copy => true @@ -765,7 +766,7 @@ object desugar { } flatTree(cdef1 :: companions ::: implicitWrappers) - } + }.reporting(res => i"desugared: $res", Printers.desugar) /** Expand * diff --git a/compiler/src/dotty/tools/dotc/config/Printers.scala b/compiler/src/dotty/tools/dotc/config/Printers.scala index f07dfe9a07a3..155f6984a7e9 100644 --- a/compiler/src/dotty/tools/dotc/config/Printers.scala +++ b/compiler/src/dotty/tools/dotc/config/Printers.scala @@ -19,6 +19,7 @@ object Printers { val cyclicErrors: Printer = noPrinter val debug = noPrinter // no type annotation here to force inlining val derive: Printer = noPrinter + val desugar: Printer = noPrinter val dottydoc: Printer = noPrinter val exhaustivity: Printer = noPrinter val gadts: Printer = noPrinter From 3617cba7f7c55b3aa91f56b3266b748f9699d3c3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 20 May 2019 10:18:12 +0200 Subject: [PATCH 06/35] Add deriving.Mirror infrastructure --- .../dotty/tools/dotc/core/Definitions.scala | 10 +++ .../src/dotty/tools/dotc/core/StdNames.scala | 2 + .../src/dotty/tools/dotc/typer/Namer.scala | 4 +- .../dotty/tools/dotc/typer/TypeAssigner.scala | 4 +- library/src/scala/deriving.scala | 69 +++++++++++++++++++ tests/run/typeclass-derivation2d.scala | 54 +++++++-------- 6 files changed, 112 insertions(+), 31 deletions(-) create mode 100644 library/src/scala/deriving.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index ad840ac6c372..017bb188fc72 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -690,6 +690,16 @@ class Definitions { lazy val ModuleSerializationProxyConstructor: TermSymbol = ModuleSerializationProxyClass.requiredMethod(nme.CONSTRUCTOR, List(ClassType(TypeBounds.empty))) + //lazy val MirrorType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror") + lazy val Mirror_ProductType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.Product") + def Mirror_ProductClass(implicit ctx: Context): ClassSymbol = Mirror_ProductType.symbol.asClass + + lazy val Mirror_Product_fromProductR: TermRef = Mirror_ProductClass.requiredMethodRef(nme.fromProduct) + def Mirror_Product_fromProduct(implicit ctx: Context): Symbol = Mirror_Product_fromProductR.symbol + + lazy val Mirror_SingletonType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.Singleton") + def Mirror_SingletonClass(implicit ctx: Context): ClassSymbol = Mirror_SingletonType.symbol.asClass + lazy val GenericType: TypeRef = ctx.requiredClassRef("scala.reflect.Generic") def GenericClass(implicit ctx: Context): ClassSymbol = GenericType.symbol.asClass lazy val ShapeType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape") diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 553f53dd9380..f751091f8f48 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -340,6 +340,7 @@ object StdNames { val longHash: N = "longHash" val MatchCase: N = "MatchCase" val Modifiers: N = "Modifiers" + val MonoType: N = "MonoType" val NestedAnnotArg: N = "NestedAnnotArg" val NoFlags: N = "NoFlags" val NoPrefix: N = "NoPrefix" @@ -432,6 +433,7 @@ object StdNames { val flagsFromBits : N = "flagsFromBits" val flatMap: N = "flatMap" val foreach: N = "foreach" + val fromProduct: N = "fromProduct" val genericArrayOps: N = "genericArrayOps" val genericClass: N = "genericClass" val get: N = "get" diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index bc5d95a3335d..01fde2c9d335 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -447,7 +447,7 @@ class Namer { typer: Typer => case _ => tree } - /** For all class definitions `stat` in `xstats`: If the companion class if + /** For all class definitions `stat` in `xstats`: If the companion class is * not also defined in `xstats`, invalidate it by setting its info to * NoType. */ @@ -702,7 +702,7 @@ class Namer { typer: Typer => // If a top-level object or class has no companion in the current run, we // enter a dummy companion (`denot.isAbsent` returns true) in scope. This // ensures that we never use a companion from a previous run or from the - // classpath. See tests/pos/false-companion for an example where this + // class path. See tests/pos/false-companion for an example where this // matters. if (ctx.owner.is(PackageClass)) { for (cdef @ TypeDef(moduleName, _) <- moduleDef.values) { diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index cc82c6c49d92..4f140a109827 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -160,9 +160,9 @@ trait TypeAssigner { private def toRepeated(tree: Tree, from: ClassSymbol)(implicit ctx: Context): Tree = Typed(tree, TypeTree(tree.tpe.widen.translateParameterized(from, defn.RepeatedParamClass))) - def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree = toRepeated(tree, defn.SeqClass) + def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree = toRepeated(tree, defn.SeqClass) - def arrayToRepeated(tree: Tree)(implicit ctx: Context): Tree = toRepeated(tree, defn.ArrayClass) + def arrayToRepeated(tree: Tree)(implicit ctx: Context): Tree = toRepeated(tree, defn.ArrayClass) /** A denotation exists really if it exists and does not point to a stale symbol. */ final def reallyExists(denot: Denotation)(implicit ctx: Context): Boolean = try diff --git a/library/src/scala/deriving.scala b/library/src/scala/deriving.scala new file mode 100644 index 000000000000..de7cba1d53f4 --- /dev/null +++ b/library/src/scala/deriving.scala @@ -0,0 +1,69 @@ +package scala + +object deriving { + + /** Mirrors allows typelevel access to enums, case classes and objects, and their sealed parents. + */ + sealed trait Mirror { + + /** The mirrored *-type */ + type MonoType + } + + object Mirror { + + /** The Mirror for a sum type */ + trait Sum extends Mirror { self => + + type ElemTypes <: Tuple + + /** The ordinal number of the case class of `x`. For enums, `ordinal(x) == x.ordinal` */ + def ordinal(x: MonoType): Int + } + + /** The Mirror for a product type */ + trait Product extends Mirror { + + /** The types of the elements */ + type ElemTypes <: Tuple + + /** The name of the whole product type */ + type CaseLabel <: String + + /** The names of the product elements */ + type ElemLabels <: Tuple + + /** Create a new instance of type `T` with elements taken from product `p`. */ + def fromProduct(p: scala.Product): MonoType + } + + trait Singleton extends Product with scala.Product { + type MonoType = this.type + def fromProduct(p: scala.Product) = this + + def productElement(n: Int): Any = throw new IndexOutOfBoundsException(n.toString) + def productArity: Int = 0 + } + } + + type MirrorOf[T] = Mirror { type MonoType = T } + type ProductMirrorOf[T] = Mirror.Product { type MonoType = T } + type SumMirrorOf[T] = Mirror.Sum { type MonoType = T } + + /** Helper class to turn arrays into products */ + class ArrayProduct(val elems: Array[AnyRef]) extends Product { + def this(size: Int) = this(new Array[AnyRef](size)) + def canEqual(that: Any): Boolean = true + def productElement(n: Int) = elems(n) + def productArity = elems.length + override def productIterator: Iterator[Any] = elems.iterator + def update(n: Int, x: Any) = elems(n) = x.asInstanceOf[AnyRef] + } + + /** The empty product */ + object EmptyProduct extends ArrayProduct(Array[AnyRef]()) + + /** Helper method to select a product element */ + def productElement[T](x: Any, idx: Int) = + x.asInstanceOf[Product].productElement(idx).asInstanceOf[T] +} \ No newline at end of file diff --git a/tests/run/typeclass-derivation2d.scala b/tests/run/typeclass-derivation2d.scala index 39eae0a80446..19686ff2fcc5 100644 --- a/tests/run/typeclass-derivation2d.scala +++ b/tests/run/typeclass-derivation2d.scala @@ -14,11 +14,11 @@ object Deriving { sealed abstract class Mirror { /** The mirrored *-type */ - type MonoType + type _MonoType } - type MirrorOf[T] = Mirror { type MonoType = T } - type ProductMirrorOf[T] = Mirror.Product { type MonoType = T } - type SumMirrorOf[T] = Mirror.Sum { type MonoType = T } + type MirrorOf[T] = Mirror { type _MonoType = T } + type ProductMirrorOf[T] = Mirror.Product { type _MonoType = T } + type SumMirrorOf[T] = Mirror.Sum { type _MonoType = T } object Mirror { @@ -28,7 +28,7 @@ object Deriving { type ElemTypes <: Tuple /** The ordinal number of the case class of `x`. For enums, `ordinal(x) == x.ordinal` */ - def ordinal(x: MonoType): Int + def ordinal(x: _MonoType): Int } /** The Mirror for a product type */ @@ -44,12 +44,12 @@ object Deriving { type ElemLabels <: Tuple /** Create a new instance of type `T` with elements taken from product `p`. */ - def fromProduct(p: scala.Product): MonoType + def _fromProduct(p: scala.Product): _MonoType } trait Singleton extends Product { - type MonoType = this.type - def fromProduct(p: scala.Product) = this + type _MonoType = this.type + def _fromProduct(p: scala.Product) = this } } @@ -74,7 +74,7 @@ import Deriving._ sealed trait Lst[+T] // derives Eq, Pickler, Show object Lst extends Mirror.Sum { - type MonoType = Lst[_] + type _MonoType = Lst[_] def ordinal(x: Lst[_]) = x match { case x: Cons[_] => 0 @@ -82,22 +82,22 @@ object Lst extends Mirror.Sum { } implicit def mirror[T]: Mirror.Sum { - type MonoType = Lst[T] + type _MonoType = Lst[T] type ElemTypes = (Cons[T], Nil.type) } = this.asInstanceOf case class Cons[T](hd: T, tl: Lst[T]) extends Lst[T] object Cons extends Mirror.Product { - type MonoType = Lst[_] + type _MonoType = Lst[_] def apply[T](x: T, xs: Lst[T]): Lst[T] = new Cons(x, xs) - def fromProduct(p: Product): Cons[_] = + def _fromProduct(p: Product): Cons[_] = new Cons(productElement[Any](p, 0), productElement[Lst[Any]](p, 1)) implicit def mirror[T]: Mirror.Product { - type MonoType = Cons[T] + type _MonoType = Cons[T] type ElemTypes = (T, Lst[T]) type CaseLabel = "Cons" type ElemLabels = ("hd", "tl") @@ -107,7 +107,7 @@ object Lst extends Mirror.Sum { case object Nil extends Lst[Nothing] with Mirror.Singleton { implicit def mirror: Mirror.Singleton { - type MonoType = Nil.type + type _MonoType = Nil.type type ElemTypes = Unit type CaseLabel = "Nil" type ElemLabels = Unit @@ -125,13 +125,13 @@ object Lst extends Mirror.Sum { case class Pair[T](x: T, y: T) // derives Eq, Pickler, Show object Pair extends Mirror.Product { - type MonoType = Pair[_] + type _MonoType = Pair[_] - def fromProduct(p: Product): Pair[_] = + def _fromProduct(p: Product): Pair[_] = Pair(productElement[Any](p, 0), productElement[Any](p, 1)) implicit def mirror[T]: Mirror.Product { - type MonoType = Pair[T] + type _MonoType = Pair[T] type ElemTypes = (T, T) type CaseLabel = "Pair" type ElemLabels = ("x", "y") @@ -148,7 +148,7 @@ object Pair extends Mirror.Product { sealed trait Either[+L, +R] extends Product with Serializable // derives Eq, Pickler, Show object Either extends Mirror.Sum { - type MonoType = Either[_, _] + type _MonoType = Either[_, _] def ordinal(x: Either[_, _]) = x match { case x: Left[_] => 0 @@ -156,7 +156,7 @@ object Either extends Mirror.Sum { } implicit def mirror[L, R]: Mirror.Sum { - type MonoType = Either[L, R] + type _MonoType = Either[L, R] type ElemTypes = (Left[L], Right[R]) } = this.asInstanceOf @@ -169,10 +169,10 @@ case class Left[L](elem: L) extends Either[L, Nothing] case class Right[R](elem: R) extends Either[Nothing, R] object Left extends Mirror.Product { - type MonoType = Left[_] - def fromProduct(p: Product): Left[_] = Left(productElement[Any](p, 0)) + type _MonoType = Left[_] + def _fromProduct(p: Product): Left[_] = Left(productElement[Any](p, 0)) implicit def mirror[L]: Mirror.Product { - type MonoType = Left[L] + type _MonoType = Left[L] type ElemTypes = L *: Unit type CaseLabel = "Left" type ElemLabels = "x" *: Unit @@ -180,10 +180,10 @@ object Left extends Mirror.Product { } object Right extends Mirror.Product { - type MonoType = Right[_] - def fromProduct(p: Product): Right[_] = Right(productElement[Any](p, 0)) + type _MonoType = Right[_] + def _fromProduct(p: Product): Right[_] = Right(productElement[Any](p, 0)) implicit def mirror[R]: Mirror.Product { - type MonoType = Right[R] + type _MonoType = Right[R] type ElemTypes = R *: Unit type CaseLabel = "Right" type ElemLabels = "x" *: Unit @@ -293,11 +293,11 @@ object Pickler { inline def unpickleCase[T, Elems <: Tuple](buf: mutable.ListBuffer[Int], m: ProductMirrorOf[T]): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) - m.fromProduct(EmptyProduct) + m._fromProduct(EmptyProduct) else { val elems = new ArrayProduct(size) unpickleElems[Elems](0)(buf, elems) - m.fromProduct(elems) + m._fromProduct(elems) } } From 42807da0125baed88f8c79a3ecb176046c9a9c57 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 20 May 2019 10:20:58 +0200 Subject: [PATCH 07/35] Add isGenericProduct test --- .../src/dotty/tools/dotc/transform/SymUtils.scala | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index deaaa39f8884..31d6355f1c34 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -12,6 +12,7 @@ import StdNames._ import NameKinds._ import Flags._ import Annotations._ +import ValueClasses.isDerivedValueClass import language.implicitConversions import scala.annotation.tailrec @@ -59,10 +60,19 @@ class SymUtils(val self: Symbol) extends AnyVal { def isSuperAccessor(implicit ctx: Context): Boolean = self.name.is(SuperAccessorName) - /** A type or term parameter or a term parameter accessor */ + /** Is this a type or term parameter or a term parameter accessor? */ def isParamOrAccessor(implicit ctx: Context): Boolean = self.is(Param) || self.is(ParamAccessor) + /** Is this a case class for which a product mirror is generated? + * Excluded are value classes, abstract classes and case classes with more than one + * parameter section. + */ + def isGenericProduct(implicit ctx: Context): Boolean = + self.is(CaseClass, butNot = Abstract) && + self.primaryConstructor.info.paramInfoss.length == 1 && + !isDerivedValueClass(self) + /** If this is a constructor, its owner: otherwise this. */ final def skipConstructor(implicit ctx: Context): Symbol = if (self.isConstructor) self.owner else self From 58299287e92c52b87fb11021cd0789beda465a10 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 20 May 2019 14:03:00 +0200 Subject: [PATCH 08/35] Synthesis for mirror infrastructure --- .../dotc/transform/SyntheticMethods.scala | 137 +++++++++++++++--- .../test/dotc/pos-test-pickling.blacklist | 3 + 2 files changed, 120 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala index fe99b0544a6b..54c1bde9606d 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -2,13 +2,16 @@ package dotty.tools.dotc package transform import core._ -import Symbols._, Types._, Contexts._, StdNames._, Constants._, SymUtils._ +import Symbols._, Types._, Contexts._, Names._, StdNames._, Constants._, SymUtils._ import Flags._ import DenotTransformers._ import Decorators._ import NameOps._ import Annotations.Annotation +import typer.ProtoTypes.constrained +import ast.untpd import ValueClasses.isDerivedValueClass +import SymUtils._ /** Synthetic method implementations for case classes, case objects, * and value classes. @@ -51,6 +54,14 @@ class SyntheticMethods(thisPhase: DenotTransformer) { def caseSymbols(implicit ctx: Context): List[Symbol] = { initSymbols; myCaseSymbols } def caseModuleSymbols(implicit ctx: Context): List[Symbol] = { initSymbols; myCaseModuleSymbols } + private def alreadyDefined(sym: Symbol, clazz: ClassSymbol)(implicit ctx: Context): Boolean = { + val existing = sym.matchingMember(clazz.thisType) + existing.exists && !(existing == sym || existing.is(Deferred)) + } + + private def synthesizeDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Context => Tree)(implicit ctx: Context): Tree = + DefDef(sym, rhsFn(_)(ctx.withOwner(sym))).withSpan(ctx.owner.span.focus) + /** If this is a case or value class, return the appropriate additional methods, * otherwise return nothing. */ @@ -68,36 +79,34 @@ class SyntheticMethods(thisPhase: DenotTransformer) { else if (isDerivedValueClass(clazz)) valueSymbols else Nil - def syntheticDefIfMissing(sym: Symbol): List[Tree] = { - val existing = sym.matchingMember(clazz.thisType) - if (existing == sym || existing.is(Deferred)) syntheticDef(sym) :: Nil - else Nil - } + def syntheticDefIfMissing(sym: Symbol): List[Tree] = + if (alreadyDefined(sym, clazz)) Nil else syntheticDef(sym) :: Nil def syntheticDef(sym: Symbol): Tree = { val synthetic = sym.copy( owner = clazz, flags = sym.flags &~ Deferred | Synthetic | Override, + info = clazz.thisType.memberInfo(sym), coord = clazz.coord).enteredAfter(thisPhase).asTerm - def forwardToRuntime(vrefss: List[List[Tree]]): Tree = - ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefss.head) + def forwardToRuntime(vrefs: List[Tree]): Tree = + ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefs) - def ownName(vrefss: List[List[Tree]]): Tree = + def ownName: Tree = Literal(Constant(clazz.name.stripModuleClassSuffix.toString)) - def syntheticRHS(implicit ctx: Context): List[List[Tree]] => Tree = synthetic.name match { - case nme.hashCode_ if isDerivedValueClass(clazz) => vrefss => valueHashCodeBody - case nme.hashCode_ => vrefss => caseHashCodeBody - case nme.toString_ => if (clazz.is(ModuleClass)) ownName else forwardToRuntime - case nme.equals_ => vrefss => equalsBody(vrefss.head.head) - case nme.canEqual_ => vrefss => canEqualBody(vrefss.head.head) - case nme.productArity => vrefss => Literal(Constant(accessors.length)) + def syntheticRHS(vrefss: List[List[Tree]])(implicit ctx: Context): Tree = synthetic.name match { + case nme.hashCode_ if isDerivedValueClass(clazz) => valueHashCodeBody + case nme.hashCode_ => caseHashCodeBody + case nme.toString_ => if (clazz.is(ModuleClass)) ownName else forwardToRuntime(vrefss.head) + case nme.equals_ => equalsBody(vrefss.head.head) + case nme.canEqual_ => canEqualBody(vrefss.head.head) + case nme.productArity => Literal(Constant(accessors.length)) case nme.productPrefix => ownName - case nme.productElement => vrefss => productElementBody(accessors.length, vrefss.head.head) + case nme.productElement => productElementBody(accessors.length, vrefss.head.head) } ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}") - DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic))).withSpan(ctx.owner.span.focus) + synthesizeDef(synthetic, syntheticRHS) } /** The class @@ -289,9 +298,97 @@ class SyntheticMethods(thisPhase: DenotTransformer) { Nil } - def addSyntheticMethods(impl: Template)(implicit ctx: Context): Template = { + /** The class + * + * ``` + * case class C[T <: U](x: T, y: String*) + * ``` + * + * gets the `fromProduct` method: + * + * ``` + * def fromProduct(x$0: Product): MonoType = + * new C[U]( + * x$0.productElement(0).asInstanceOf[U], + * x$0.productElement(1).asInstanceOf[Seq[String]]: _*) + * ``` + * where + * ``` + * type MonoType = C[_] + * ``` + */ + def fromProductBody(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 elems = + for ((formal, idx) <- methTpe.paramInfos.zipWithIndex) yield { + val elem = + param.select(defn.Product_productElement).appliedTo(Literal(Constant(idx))) + .ensureConforms(formal.underlyingIfRepeated(isJava = false)) + if (formal.isRepeatedParam) ctx.typer.seqToRepeated(elem) else elem + } + New(classRef, elems) + } + } + + def addMirrorSupport(impl: Template)(implicit ctx: Context): Template = { val clazz = ctx.owner.asClass - cpy.Template(impl)(body = serializableObjectMethod(clazz) ::: caseAndValueMethods(clazz) ::: impl.body) + var newBody = serializableObjectMethod(clazz) ::: caseAndValueMethods(clazz) ::: impl.body + var newParents = impl.parents + def addParent(parent: Type) = { + newParents = newParents :+ TypeTree(parent) + val oldClassInfo = clazz.classInfo + val newClassInfo = oldClassInfo.derivedClassInfo( + classParents = oldClassInfo.classParents :+ parent) + clazz.copySymDenotation(info = newClassInfo).installAfter(thisPhase) + } + if (clazz.is(Module)) { + if (clazz.is(Case)) addParent(defn.Mirror_SingletonType) + else { + val linked = clazz.linkedClass + if (linked.isGenericProduct) { + addParent(defn.Mirror_ProductType) + val rawClassType = + linked.typeRef.appliedTo(linked.typeParams.map(_ => TypeBounds.empty)) + val monoType = + ctx.newSymbol(clazz, tpnme.MonoType, Synthetic, TypeAlias(rawClassType), coord = clazz.coord) + if (!alreadyDefined(monoType, clazz)) { + monoType.entered + newBody = newBody :+ TypeDef(monoType).withSpan(ctx.owner.span.focus) + } + val fromProduct = + ctx.newSymbol(clazz, nme.fromProduct, Synthetic | Method, + info = MethodType(defn.ProductType :: Nil, monoType.typeRef), coord = clazz.coord) + if (!alreadyDefined(fromProduct, clazz)) { + fromProduct.entered + newBody = newBody :+ + synthesizeDef(fromProduct, vrefss => ctx => + fromProductBody(linked, vrefss.head.head)(ctx) + .ensureConforms(rawClassType)) // t4758.scala or i3381.scala are examples where a cast is needed + } + } + } + } + + cpy.Template(impl)(parents = newParents, body = newBody) } + def addSyntheticMethods(impl: Template)(implicit ctx: Context): Template = { + val clazz = ctx.owner.asClass + addMirrorSupport( + cpy.Template(impl)(body = serializableObjectMethod(clazz) ::: caseAndValueMethods(clazz) ::: impl.body)) + } } diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 19865bccd1ed..19349a044516 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -10,6 +10,9 @@ t3486 t3612.scala reference +# type of super reference changes due to late addition of Mirror.Singleton +i939.scala + # Match types typelevel0.scala matchtype.scala From c6c0f7167cb6247baa859777fee31a67da6bec22 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 20 May 2019 15:09:30 +0200 Subject: [PATCH 09/35] Rename SyntheticMethods -> SyntheticMembers --- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 8 ++++---- .../{SyntheticMethods.scala => SyntheticMembers.scala} | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) rename compiler/src/dotty/tools/dotc/transform/{SyntheticMethods.scala => SyntheticMembers.scala} (99%) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 69b4ebcb41ad..866e7e29f060 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -26,7 +26,7 @@ object PostTyper { * field (corresponding = super class field is initialized with subclass field) * (@see ForwardParamAccessors) * - * (3) Add synthetic methods (@see SyntheticMethods) + * (3) Add synthetic members (@see SyntheticMembers) * * (4) Check that `New` nodes can be instantiated, and that annotations are valid * @@ -64,7 +64,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case _ => } - override def changesMembers: Boolean = true // the phase adds super accessors and synthetic methods + override def changesMembers: Boolean = true // the phase adds super accessors and synthetic members override def transformPhase(implicit ctx: Context): Phase = thisPhase.next @@ -73,7 +73,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase val superAcc: SuperAccessors = new SuperAccessors(thisPhase) val paramFwd: ParamForwarding = new ParamForwarding(thisPhase) - val synthMth: SyntheticMethods = new SyntheticMethods(thisPhase) + val synthMbr: SyntheticMembers = new SyntheticMembers(thisPhase) private def newPart(tree: Tree): Option[New] = methPart(tree) match { case Select(nu: New, _) => Some(nu) @@ -230,7 +230,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case tree: Template => withNoCheckNews(tree.parents.flatMap(newPart)) { val templ1 = paramFwd.forwardParamAccessors(tree) - synthMth.addSyntheticMethods( + synthMbr.addSyntheticMembers( superAcc.wrapTemplate(templ1)( super.transform(_).asInstanceOf[Template])) } diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala similarity index 99% rename from compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala rename to compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 54c1bde9606d..be95f4e63872 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -35,7 +35,7 @@ import SymUtils._ * def equals(other: Any): Boolean * def hashCode(): Int */ -class SyntheticMethods(thisPhase: DenotTransformer) { +class SyntheticMembers(thisPhase: DenotTransformer) { import ast.tpd._ private[this] var myValueSymbols: List[Symbol] = Nil @@ -386,7 +386,7 @@ class SyntheticMethods(thisPhase: DenotTransformer) { cpy.Template(impl)(parents = newParents, body = newBody) } - def addSyntheticMethods(impl: Template)(implicit ctx: Context): Template = { + def addSyntheticMembers(impl: Template)(implicit ctx: Context): Template = { val clazz = ctx.owner.asClass addMirrorSupport( cpy.Template(impl)(body = serializableObjectMethod(clazz) ::: caseAndValueMethods(clazz) ::: impl.body)) From 5c924caee9a6adfe7fb150e72f2e49874c42141f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 20 May 2019 21:23:13 +0200 Subject: [PATCH 10/35] Mirror infrastructure for generic sum types --- .../dotty/tools/dotc/core/Definitions.scala | 3 + .../dotty/tools/dotc/transform/SymUtils.scala | 38 +++++++- .../dotc/transform/SyntheticMembers.scala | 96 +++++++++++++------ .../src/dotty/tools/dotc/typer/Namer.scala | 4 +- 4 files changed, 107 insertions(+), 34 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 017bb188fc72..ce9e68d784ca 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -697,6 +697,9 @@ class Definitions { lazy val Mirror_Product_fromProductR: TermRef = Mirror_ProductClass.requiredMethodRef(nme.fromProduct) def Mirror_Product_fromProduct(implicit ctx: Context): Symbol = Mirror_Product_fromProductR.symbol + lazy val Mirror_SumType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.Sum") + def Mirror_SumClass(implicit ctx: Context): ClassSymbol = Mirror_SumType.symbol.asClass + lazy val Mirror_SingletonType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.Singleton") def Mirror_SingletonClass(implicit ctx: Context): ClassSymbol = Mirror_SingletonType.symbol.asClass diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 31d6355f1c34..d7f46923251e 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -68,10 +68,36 @@ class SymUtils(val self: Symbol) extends AnyVal { * Excluded are value classes, abstract classes and case classes with more than one * parameter section. */ - def isGenericProduct(implicit ctx: Context): Boolean = - self.is(CaseClass, butNot = Abstract) && - self.primaryConstructor.info.paramInfoss.length == 1 && - !isDerivedValueClass(self) + def whyNotGenericProduct(implicit ctx: Context): String = + 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 + + /** Is this a sealed class or trait for which a sum mirror is generated? + * Excluded are + */ + def whyNotGenericSum(implicit ctx: Context): String = + if (!self.is(Sealed)) + s"it is not a sealed ${if (self.is(Trait)) "trait" else "class"}" + else { + val children = self.children + def problem(child: Symbol) = + if (child == self) "it has anonymous or inaccessible subclasses" + else if (!child.isClass) "" + else { + val s = child.whyNotGenericProduct + if (s.isEmpty) s + else "its child $child is not a generic product because $s" + } + if (children.isEmpty) "it does not have subclasses" + else children.filter(_.isClass).map(problem).find(!_.isEmpty).getOrElse("") + } + + def isGenericSum(implicit ctx: Context): Boolean = whyNotGenericSum.isEmpty /** If this is a constructor, its owner: otherwise this. */ final def skipConstructor(implicit ctx: Context): Symbol = @@ -161,6 +187,10 @@ class SymUtils(val self: Symbol) extends AnyVal { else owner.isLocal } + /** The typeRef with wildcard arguments for each type parameter */ + def rawTypeRef(implicit ctx: Context) = + self.typeRef.appliedTo(self.typeParams.map(_ => TypeBounds.empty)) + /** Is symbol a quote operation? */ def isQuote(implicit ctx: Context): Boolean = self == defn.InternalQuoted_exprQuote || self == defn.InternalQuoted_typeQuote diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index be95f4e63872..833c1abe87b8 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -54,9 +54,10 @@ class SyntheticMembers(thisPhase: DenotTransformer) { def caseSymbols(implicit ctx: Context): List[Symbol] = { initSymbols; myCaseSymbols } def caseModuleSymbols(implicit ctx: Context): List[Symbol] = { initSymbols; myCaseModuleSymbols } - private def alreadyDefined(sym: Symbol, clazz: ClassSymbol)(implicit ctx: Context): Boolean = { + private def existingDef(sym: Symbol, clazz: ClassSymbol)(implicit ctx: Context): Symbol = { val existing = sym.matchingMember(clazz.thisType) - existing.exists && !(existing == sym || existing.is(Deferred)) + if (existing != sym && !existing.is(Deferred)) existing + else NoSymbol } private def synthesizeDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Context => Tree)(implicit ctx: Context): Tree = @@ -80,7 +81,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { else Nil def syntheticDefIfMissing(sym: Symbol): List[Tree] = - if (alreadyDefined(sym, clazz)) Nil else syntheticDef(sym) :: Nil + if (existingDef(sym, clazz).exists) Nil else syntheticDef(sym) :: Nil def syntheticDef(sym: Symbol): Tree = { val synthetic = sym.copy( @@ -344,42 +345,79 @@ class SyntheticMembers(thisPhase: DenotTransformer) { } } + /** For an enum T: + * + * def ordinal(x: MonoType) = x.enumTag + * + * For sealed trait with children of normalized types C_1, ..., C_n: + * + * def ordinal(x: MonoType) = x match { + * case _: C_1 => 0 + * ... + * case _: C_n => n - 1 + * + * Here, the normalized type of a class C is C[_, ...., _] with + * a wildcard for each type parameter. The normalized type of an object + * O is O.type. + */ + def ordinalBody(cls: Symbol, param: Tree)(implicit ctx: Context): Tree = + if (cls.is(Enum)) param.select(nme.enumTag) + else { + val cases = + for ((child, idx) <- cls.children.zipWithIndex) yield { + val patType = if (child.isTerm) child.termRef else child.rawTypeRef + val pat = Typed(untpd.Ident(nme.WILDCARD).withType(patType), TypeTree(patType)) + CaseDef(pat, EmptyTree, Literal(Constant(idx))) + } + Match(param, cases) + } + + /** - If `impl` is the companion of a generic sum, add `deriving.Mirror.Sum` parent + * and `MonoType` and `ordinal` members. + * - If `impl` is the companion of a generic product, add `deriving.Mirror.Product` parent + * and `MonoType` and `fromProduct` members. + */ def addMirrorSupport(impl: Template)(implicit ctx: Context): Template = { val clazz = ctx.owner.asClass - var newBody = serializableObjectMethod(clazz) ::: caseAndValueMethods(clazz) ::: impl.body + val linked = clazz.linkedClass + + var newBody = impl.body var newParents = impl.parents - def addParent(parent: Type) = { + def addParent(parent: Type): Unit = { newParents = newParents :+ TypeTree(parent) val oldClassInfo = clazz.classInfo val newClassInfo = oldClassInfo.derivedClassInfo( classParents = oldClassInfo.classParents :+ parent) clazz.copySymDenotation(info = newClassInfo).installAfter(thisPhase) } + def addMethod(name: TermName, info: Type, body: (Symbol, Tree, Context) => Tree): Unit = { + val meth = ctx.newSymbol(clazz, name, Synthetic | Method, info, coord = clazz.coord) + if (!existingDef(meth, clazz).exists) { + meth.entered + newBody = newBody :+ + synthesizeDef(meth, vrefss => ctx => body(linked, vrefss.head.head, ctx)) + } + } + lazy val monoType = { + val monoType = + ctx.newSymbol(clazz, tpnme.MonoType, Synthetic, TypeAlias(linked.rawTypeRef), coord = clazz.coord) + existingDef(monoType, clazz).orElse { + newBody = newBody :+ TypeDef(monoType).withSpan(ctx.owner.span.focus) + monoType.entered + } + } if (clazz.is(Module)) { - if (clazz.is(Case)) addParent(defn.Mirror_SingletonType) - else { - val linked = clazz.linkedClass - if (linked.isGenericProduct) { - addParent(defn.Mirror_ProductType) - val rawClassType = - linked.typeRef.appliedTo(linked.typeParams.map(_ => TypeBounds.empty)) - val monoType = - ctx.newSymbol(clazz, tpnme.MonoType, Synthetic, TypeAlias(rawClassType), coord = clazz.coord) - if (!alreadyDefined(monoType, clazz)) { - monoType.entered - newBody = newBody :+ TypeDef(monoType).withSpan(ctx.owner.span.focus) - } - val fromProduct = - ctx.newSymbol(clazz, nme.fromProduct, Synthetic | Method, - info = MethodType(defn.ProductType :: Nil, monoType.typeRef), coord = clazz.coord) - if (!alreadyDefined(fromProduct, clazz)) { - fromProduct.entered - newBody = newBody :+ - synthesizeDef(fromProduct, vrefss => ctx => - fromProductBody(linked, vrefss.head.head)(ctx) - .ensureConforms(rawClassType)) // t4758.scala or i3381.scala are examples where a cast is needed - } - } + if (clazz.is(Case)) + addParent(defn.Mirror_SingletonType) + else if (linked.isGenericProduct) { + addParent(defn.Mirror_ProductType) + addMethod(nme.fromProduct, MethodType(defn.ProductType :: Nil, monoType.typeRef), + fromProductBody(_, _)(_).ensureConforms(monoType.typeRef)) // t4758.scala or i3381.scala are examples where a cast is needed + } + else if (linked.isGenericSum) { + addParent(defn.Mirror_SumType) + addMethod(nme.ordinal, MethodType(monoType.typeRef :: Nil, defn.IntType), + ordinalBody(_, _)(_)) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 01fde2c9d335..41fde927164e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -868,8 +868,10 @@ class Namer { typer: Typer => val child = if (denot.is(Module)) denot.sourceModule else denot.symbol register(child, parent) } - else if (denot.is(CaseVal, butNot = Method | Module)) + else if (denot.is(CaseVal, butNot = Method | Module)) { + assert(denot.is(Enum), denot) register(denot.symbol, denot.info) + } } /** Intentionally left without `implicit ctx` parameter. We need From 159c8833ca818208654ad145b6504da95e32e8d9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 21 May 2019 10:51:23 +0200 Subject: [PATCH 11/35] Refine isGenericSum condition --- .../dotty/tools/dotc/transform/SymUtils.scala | 20 ++++++++-- .../dotc/transform/SyntheticMembers.scala | 4 +- tests/run/deriving.scala | 10 +++++ tests/run/ordinal-innerclass.scala | 38 +++++++++++++++++++ 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 tests/run/deriving.scala create mode 100644 tests/run/ordinal-innerclass.scala diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index d7f46923251e..3a3873977be8 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -13,6 +13,7 @@ import NameKinds._ import Flags._ import Annotations._ import ValueClasses.isDerivedValueClass +import Decorators._ import language.implicitConversions import scala.annotation.tailrec @@ -78,23 +79,34 @@ class SymUtils(val self: Symbol) extends AnyVal { def isGenericProduct(implicit ctx: Context): Boolean = whyNotGenericProduct.isEmpty /** Is this a sealed class or trait for which a sum mirror is generated? - * Excluded are + * It must satisfy the following conditions: + * - it has at least one child class or object + * - none of its children are anonymous classes + * - all of its children are addressable through a path from its companion object + * - all of its children are generic products or singletons */ def whyNotGenericSum(implicit ctx: Context): String = if (!self.is(Sealed)) s"it is not a sealed ${if (self.is(Trait)) "trait" else "class"}" else { val children = self.children - def problem(child: Symbol) = + val companion = self.linkedClass + def problem(child: Symbol) = { + + def isAccessible(sym: Symbol): Boolean = + companion.isContainedIn(sym) || sym.is(Module) && isAccessible(sym.owner) + if (child == self) "it has anonymous or inaccessible subclasses" + else if (!isAccessible(child.owner)) i"its child $child is not accessible" else if (!child.isClass) "" else { val s = child.whyNotGenericProduct if (s.isEmpty) s - else "its child $child is not a generic product because $s" + else i"its child $child is not a generic product because $s" } + } if (children.isEmpty) "it does not have subclasses" - else children.filter(_.isClass).map(problem).find(!_.isEmpty).getOrElse("") + else children.map(problem).find(!_.isEmpty).getOrElse("") } def isGenericSum(implicit ctx: Context): Boolean = whyNotGenericSum.isEmpty diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 833c1abe87b8..04177e95a92d 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -12,6 +12,7 @@ import typer.ProtoTypes.constrained import ast.untpd import ValueClasses.isDerivedValueClass import SymUtils._ +import config.Printers.derive /** Synthetic method implementations for case classes, case objects, * and value classes. @@ -419,8 +420,9 @@ class SyntheticMembers(thisPhase: DenotTransformer) { addMethod(nme.ordinal, MethodType(monoType.typeRef :: Nil, defn.IntType), ordinalBody(_, _)(_)) } + else if (linked.is(Sealed)) + derive.println(i"$linked is not a sum because ${linked.whyNotGenericSum}") } - cpy.Template(impl)(parents = newParents, body = newBody) } diff --git a/tests/run/deriving.scala b/tests/run/deriving.scala new file mode 100644 index 000000000000..16df00d79bc3 --- /dev/null +++ b/tests/run/deriving.scala @@ -0,0 +1,10 @@ +sealed trait T +object T + +case class A(x: Int, y: Int) extends T +case object B extends T + +object Test extends App { + + case class AA[X >: Null <: AnyRef](x: X, y: X, z: String) +} \ No newline at end of file diff --git a/tests/run/ordinal-innerclass.scala b/tests/run/ordinal-innerclass.scala new file mode 100644 index 000000000000..c6c7b90f9f18 --- /dev/null +++ b/tests/run/ordinal-innerclass.scala @@ -0,0 +1,38 @@ +object Test extends App { + + class A { + class B { + object O { + sealed trait T + case class C(x: Int) extends T + object T { + case class D() extends T + } + } + } + } + + val a = new A + val b = new a.B + val o = b.O + val sum: deriving.Mirror.Sum { type MonoType = o.T }= o.T.asInstanceOf + assert(sum.ordinal(new o.C(1)) == 0) + assert(sum.ordinal(new o.T.D()) == 1) + + object MR { + // from betterFiles/ManagedResource: FM should not be treated as a generic sum + // since its children are not accessible from its companion object. If it was + // treated as a generic sum, ExplicitOuter would crash when compiling the synthesized + // `ordinal` method. + sealed trait FM + object FM { + trait I { + object fm1 extends FM + object fm2 extends FM + } + } + } + + trait T1 + case class A1() extends T1 +} \ No newline at end of file From 88cec9f7e1b55e50364f63c1e926f9dae212080d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 21 May 2019 13:07:45 +0200 Subject: [PATCH 12/35] Always generate companion objects for sealed classes Sealed classes are likely generic sums, which means they need a companion object to contain the `ordinal` method. --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 +- tests/run/ordinal-innerclass.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 826957a75665..362b6521c2a1 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -682,7 +682,7 @@ object desugar { } companionDefs(companionParent, applyMeths ::: unapplyMeth :: companionMembers) } - else if (companionMembers.nonEmpty || companionDerived.nonEmpty || isEnum) + else if (companionMembers.nonEmpty || companionDerived.nonEmpty || isEnum || mods.is(Sealed)) companionDefs(anyRef, companionMembers) else if (isValueClass) { impl.constr.vparamss match { diff --git a/tests/run/ordinal-innerclass.scala b/tests/run/ordinal-innerclass.scala index c6c7b90f9f18..8bdd2bf843b3 100644 --- a/tests/run/ordinal-innerclass.scala +++ b/tests/run/ordinal-innerclass.scala @@ -33,6 +33,6 @@ object Test extends App { } } - trait T1 + sealed trait T1 case class A1() extends T1 } \ No newline at end of file From 4cccf410ccc4d92a2b48fcf9850a809bafb6908f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 21 May 2019 14:15:14 +0200 Subject: [PATCH 13/35] Revert "Always generate companion objects for sealed classes" This reverts commit b6f63b15abbd3ae69b8274d69b34c915d52afebd. It turns out that always generating companions for sealed classes breaks existing code. An example is pos/i2300.scala. --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 +- tests/run/ordinal-innerclass.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 362b6521c2a1..826957a75665 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -682,7 +682,7 @@ object desugar { } companionDefs(companionParent, applyMeths ::: unapplyMeth :: companionMembers) } - else if (companionMembers.nonEmpty || companionDerived.nonEmpty || isEnum || mods.is(Sealed)) + else if (companionMembers.nonEmpty || companionDerived.nonEmpty || isEnum) companionDefs(anyRef, companionMembers) else if (isValueClass) { impl.constr.vparamss match { diff --git a/tests/run/ordinal-innerclass.scala b/tests/run/ordinal-innerclass.scala index 8bdd2bf843b3..c6c7b90f9f18 100644 --- a/tests/run/ordinal-innerclass.scala +++ b/tests/run/ordinal-innerclass.scala @@ -33,6 +33,6 @@ object Test extends App { } } - sealed trait T1 + trait T1 case class A1() extends T1 } \ No newline at end of file From c031b3035e84c55d1eeb38ce34b2d7b9f474e2a2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 21 May 2019 14:53:34 +0200 Subject: [PATCH 14/35] Make enum cases implement Mirror.Singleton At the same time, don't make Mirror.Singleton a scala.Product. --- .../dotty/tools/dotc/ast/DesugarEnums.scala | 13 ++++-- library/src/scala/deriving.scala | 10 ++--- tests/run/typeclass-derivation2d.scala | 44 +++++++++---------- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 42cbef882321..22458c0148bb 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -115,8 +115,13 @@ object DesugarEnums { val toStringDef = DefDef(nme.toString_, Nil, Nil, TypeTree(), Ident(nme.name)) .withFlags(Override) - def creator = New(Template(emptyConstructor, enumClassRef :: Nil, Nil, EmptyValDef, - List(enumTagDef, toStringDef) ++ registerCall)) + def creator = New(Template( + constr = emptyConstructor, + parents = enumClassRef :: TypeTree(defn.Mirror_SingletonType) :: Nil, + derived = Nil, + self = EmptyValDef, + body = List(enumTagDef, toStringDef) ++ registerCall + )) DefDef(nme.DOLLAR_NEW, Nil, List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))), TypeTree(), creator) @@ -258,7 +263,9 @@ object DesugarEnums { DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), Literal(Constant(name.toString))) .withFlags(Override) val (tagMeth, scaffolding) = enumTagMeth(CaseKind.Object) - val impl1 = cpy.Template(impl)(body = List(tagMeth, toStringMeth) ++ registerCall) + val impl1 = cpy.Template(impl)( + parents = impl.parents :+ TypeTree(defn.Mirror_SingletonType), + body = List(tagMeth, toStringMeth) ++ registerCall) val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final) flatTree(scaffolding ::: vdef :: Nil).withSpan(span) } diff --git a/library/src/scala/deriving.scala b/library/src/scala/deriving.scala index de7cba1d53f4..0b4f60651c3a 100644 --- a/library/src/scala/deriving.scala +++ b/library/src/scala/deriving.scala @@ -37,18 +37,18 @@ object deriving { def fromProduct(p: scala.Product): MonoType } - trait Singleton extends Product with scala.Product { + trait Singleton extends Product { type MonoType = this.type def fromProduct(p: scala.Product) = this def productElement(n: Int): Any = throw new IndexOutOfBoundsException(n.toString) def productArity: Int = 0 } - } - type MirrorOf[T] = Mirror { type MonoType = T } - type ProductMirrorOf[T] = Mirror.Product { type MonoType = T } - type SumMirrorOf[T] = Mirror.Sum { type MonoType = T } + type Of[T] = Mirror { type MonoType = T } + type ProductOf[T] = Mirror.Product { type MonoType = T } + type SumOf[T] = Mirror.Sum { type MonoType = T } + } /** Helper class to turn arrays into products */ class ArrayProduct(val elems: Array[AnyRef]) extends Product { diff --git a/tests/run/typeclass-derivation2d.scala b/tests/run/typeclass-derivation2d.scala index 19686ff2fcc5..4fb7132e0857 100644 --- a/tests/run/typeclass-derivation2d.scala +++ b/tests/run/typeclass-derivation2d.scala @@ -16,9 +16,6 @@ object Deriving { /** The mirrored *-type */ type _MonoType } - type MirrorOf[T] = Mirror { type _MonoType = T } - type ProductMirrorOf[T] = Mirror.Product { type _MonoType = T } - type SumMirrorOf[T] = Mirror.Sum { type _MonoType = T } object Mirror { @@ -51,7 +48,10 @@ object Deriving { type _MonoType = this.type def _fromProduct(p: scala.Product) = this } - } + type Of[T] = Mirror { type _MonoType = T } + type ProductOf[T] = Mirror.Product { type _MonoType = T } + type SumOf[T] = Mirror.Sum { type _MonoType = T } + } /** Helper class to turn arrays into products */ class ArrayProduct(val elems: Array[AnyRef]) extends Product { @@ -212,7 +212,7 @@ object Eq { true } - inline def eqlProduct[T](m: ProductMirrorOf[T])(x: Any, y: Any): Boolean = + inline def eqlProduct[T](m: Mirror.ProductOf[T])(x: Any, y: Any): Boolean = eqlElems[m.ElemTypes](0)(x, y) inline def eqlCases[Alts](n: Int)(x: Any, y: Any, ord: Int): Boolean = @@ -220,20 +220,20 @@ object Eq { case _: (alt *: alts1) => if (ord == n) implicit match { - case m: ProductMirrorOf[`alt`] => eqlElems[m.ElemTypes](0)(x, y) + case m: Mirror.ProductOf[`alt`] => eqlElems[m.ElemTypes](0)(x, y) } else eqlCases[alts1](n + 1)(x, y, ord) case _: Unit => false } - inline def derived[T](implicit ev: MirrorOf[T]): Eq[T] = new Eq[T] { + inline def derived[T](implicit ev: Mirror.Of[T]): Eq[T] = new Eq[T] { def eql(x: T, y: T): Boolean = inline ev match { - case m: SumMirrorOf[T] => + case m: Mirror.SumOf[T] => val ord = m.ordinal(x) ord == m.ordinal(y) && eqlCases[m.ElemTypes](0)(x, y, ord) - case m: ProductMirrorOf[T] => + case m: Mirror.ProductOf[T] => eqlElems[m.ElemTypes](0)(x, y) } } @@ -272,7 +272,7 @@ object Pickler { case _: (alt *: alts1) => if (ord == n) implicit match { - case m: ProductMirrorOf[`alt`] => pickleElems[m.ElemTypes](0)(buf, x) + case m: Mirror.ProductOf[`alt`] => pickleElems[m.ElemTypes](0)(buf, x) } else pickleCases[alts1](n + 1)(buf, x, ord) case _: Unit => @@ -290,7 +290,7 @@ object Pickler { case _: Unit => } - inline def unpickleCase[T, Elems <: Tuple](buf: mutable.ListBuffer[Int], m: ProductMirrorOf[T]): T = { + inline def unpickleCase[T, Elems <: Tuple](buf: mutable.ListBuffer[Int], m: Mirror.ProductOf[T]): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) m._fromProduct(EmptyProduct) @@ -306,7 +306,7 @@ object Pickler { case _: (alt *: alts1) => if (ord == n) implicit match { - case m: ProductMirrorOf[`alt` & T] => + case m: Mirror.ProductOf[`alt` & T] => unpickleCase[`alt` & T, m.ElemTypes](buf, m) } else unpickleCases[T, alts1](n + 1)(buf, ord) @@ -314,22 +314,22 @@ object Pickler { throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ord") } - inline def derived[T](implicit ev: MirrorOf[T]): Pickler[T] = new { + inline def derived[T](implicit ev: Mirror.Of[T]): Pickler[T] = new { def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = inline ev match { - case m: SumMirrorOf[T] => + case m: Mirror.SumOf[T] => val ord = m.ordinal(x) buf += ord pickleCases[m.ElemTypes](0)(buf, x, ord) - case m: ProductMirrorOf[T] => + case m: Mirror.ProductOf[T] => pickleElems[m.ElemTypes](0)(buf, x) } def unpickle(buf: mutable.ListBuffer[Int]): T = inline ev match { - case m: SumMirrorOf[T] => + case m: Mirror.SumOf[T] => val ord = nextInt(buf) unpickleCases[T, m.ElemTypes](0)(buf, ord) - case m: ProductMirrorOf[T] => + case m: Mirror.ProductOf[T] => unpickleCase[T, m.ElemTypes](buf, m) } } @@ -365,7 +365,7 @@ object Show { Nil } - inline def showCase(x: Any, m: ProductMirrorOf[_]): String = { + inline def showCase(x: Any, m: Mirror.ProductOf[_]): String = { val label = constValue[m.CaseLabel] inline m match { case m: Mirror.Singleton => label @@ -378,7 +378,7 @@ object Show { case _: (alt *: alts1) => if (ord == n) implicit match { - case m: ProductMirrorOf[`alt`] => + case m: Mirror.ProductOf[`alt`] => showCase(x, m) } else showCases[alts1](n + 1)(x, ord) @@ -386,13 +386,13 @@ object Show { throw new MatchError(x) } - inline def derived[T](implicit ev: MirrorOf[T]): Show[T] = new { + inline def derived[T](implicit ev: Mirror.Of[T]): Show[T] = new { def show(x: T): String = inline ev match { - case m: SumMirrorOf[T] => + case m: Mirror.SumOf[T] => val ord = m.ordinal(x) showCases[m.ElemTypes](0)(x, ord) - case m: ProductMirrorOf[T] => + case m: Mirror.ProductOf[T] => showCase(x, m) } } From 9120105611c88a9f204de42a6ff99572f417d22f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 21 May 2019 17:53:23 +0200 Subject: [PATCH 15/35] Add Mirror.Singleton to enum cases after typer This makes sure that the mirror type does not leak into the signatures of these cases. --- compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala | 8 +++----- compiler/src/dotty/tools/dotc/core/Flags.scala | 1 + .../src/dotty/tools/dotc/transform/SyntheticMembers.scala | 4 ++++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 22458c0148bb..551a5439a0c7 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -117,14 +117,14 @@ object DesugarEnums { .withFlags(Override) def creator = New(Template( constr = emptyConstructor, - parents = enumClassRef :: TypeTree(defn.Mirror_SingletonType) :: Nil, + parents = enumClassRef :: Nil, derived = Nil, self = EmptyValDef, body = List(enumTagDef, toStringDef) ++ registerCall )) DefDef(nme.DOLLAR_NEW, Nil, List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))), - TypeTree(), creator) + TypeTree(), creator).withFlags(Private | Synthetic) } /** The return type of an enum case apply method and any widening methods in which @@ -263,9 +263,7 @@ object DesugarEnums { DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), Literal(Constant(name.toString))) .withFlags(Override) val (tagMeth, scaffolding) = enumTagMeth(CaseKind.Object) - val impl1 = cpy.Template(impl)( - parents = impl.parents :+ TypeTree(defn.Mirror_SingletonType), - body = List(tagMeth, toStringMeth) ++ registerCall) + val impl1 = cpy.Template(impl)(body = List(tagMeth, toStringMeth) ++ registerCall) val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final) flatTree(scaffolding ::: vdef :: Nil).withSpan(span) } diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index cceebe50b219..e665c10cb1b8 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -626,6 +626,7 @@ object Flags { /** An enum case */ final val EnumCase: FlagConjunction = allOf(Enum, Case) + final val EnumCaseVal: FlagConjunction = allOf(Enum, CaseVal) /** A term parameter or parameter accessor */ final val TermParamOrAccessor: FlagSet = Param | ParamAccessor diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 04177e95a92d..d5afb9ca0984 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -423,6 +423,10 @@ class SyntheticMembers(thisPhase: DenotTransformer) { else if (linked.is(Sealed)) derive.println(i"$linked is not a sum because ${linked.whyNotGenericSum}") } + else if (clazz.isAnonymousClass && + (clazz.owner.is(EnumCaseVal) || + clazz.owner.name == nme.DOLLAR_NEW && clazz.owner.is(Synthetic))) + addParent(defn.Mirror_SingletonType) cpy.Template(impl)(parents = newParents, body = newBody) } From ed79907e52fae454b5b84fbf7aa0d6211346c705 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 21 May 2019 17:58:28 +0200 Subject: [PATCH 16/35] Refactor special case handling in implicit arguments --- .../dotty/tools/dotc/typer/Implicits.scala | 173 ++++++++++-------- 1 file changed, 96 insertions(+), 77 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index af7d0c31d621..1a1e88aa0315 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -643,15 +643,12 @@ trait Implicits { self: Typer => } } - /** Find an implicit argument for parameter `formal`. - * Return a failure as a SearchFailureType in the type of the returned tree. - */ - def inferImplicitArg(formal: Type, span: Span)(implicit ctx: Context): Tree = { + /** Handlers to synthesize implicits for special types */ + type SpecialHandler = (Type, Span) => Context => Tree + type SpecialHandlers = List[(ClassSymbol, SpecialHandler)] - /** If `formal` is of the form ClassTag[T], where `T` is a class type, - * synthesize a class tag for `T`. - */ - def synthesizedClassTag(formal: Type): Tree = formal.argInfos match { + lazy val synthesizedClassTag: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => formal.argInfos match { case arg :: Nil => fullyDefinedType(arg, "ClassTag argument", span) match { case defn.ArrayOf(elemTp) => @@ -673,7 +670,8 @@ trait Implicits { self: Typer => EmptyTree } - def synthesizedTypeTag(formal: Type): Tree = { + lazy val synthesizedTypeTag: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => { def quotedType(t: Type) = { if (StagingContext.level == 0) ctx.compilationUnit.needsStaging = true // We will need to run ReifyQuotes @@ -705,59 +703,60 @@ trait Implicits { self: Typer => } } - def synthesizedTastyContext(formal: Type): Tree = + lazy val synthesizedTastyContext: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => if (ctx.inInlineMethod || enclosingInlineds.nonEmpty) ref(defn.TastyReflection_macroContext) else EmptyTree - def synthesizedTupleFunction(formal: Type): Tree = { - formal match { - case AppliedType(_, funArgs @ fun :: tupled :: Nil) => - def functionTypeEqual(baseFun: Type, actualArgs: List[Type], actualRet: Type, expected: Type) = { - expected =:= defn.FunctionOf(actualArgs, actualRet, defn.isImplicitFunctionType(baseFun), defn.isErasedFunctionType(baseFun)) - } - val arity: Int = { - if (defn.isErasedFunctionType(fun) || defn.isErasedFunctionType(fun)) -1 // TODO support? - else if (defn.isFunctionType(fun)) { - // TupledFunction[(...) => R, ?] - fun.dropDependentRefinement.dealias.argInfos match { - case funArgs :+ funRet if functionTypeEqual(fun, defn.tupleType(funArgs) :: Nil, funRet, tupled) => - // TupledFunction[(...funArgs...) => funRet, ?] - funArgs.size - case _ => -1 - } - } else if (defn.isFunctionType(tupled)) { - // TupledFunction[?, (...) => R] - tupled.dropDependentRefinement.dealias.argInfos match { - case tupledArgs :: funRet :: Nil => - defn.tupleTypes(tupledArgs) match { - case Some(funArgs) if functionTypeEqual(tupled, funArgs, funRet, fun) => - // TupledFunction[?, ((...funArgs...)) => funRet] - funArgs.size - case _ => -1 - } - case _ => -1 - } + lazy val synthesizedTupleFunction: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => formal match { + case AppliedType(_, funArgs @ fun :: tupled :: Nil) => + def functionTypeEqual(baseFun: Type, actualArgs: List[Type], actualRet: Type, expected: Type) = { + expected =:= defn.FunctionOf(actualArgs, actualRet, defn.isImplicitFunctionType(baseFun), defn.isErasedFunctionType(baseFun)) + } + val arity: Int = { + if (defn.isErasedFunctionType(fun) || defn.isErasedFunctionType(fun)) -1 // TODO support? + else if (defn.isFunctionType(fun)) { + // TupledFunction[(...) => R, ?] + fun.dropDependentRefinement.dealias.argInfos match { + case funArgs :+ funRet if functionTypeEqual(fun, defn.tupleType(funArgs) :: Nil, funRet, tupled) => + // TupledFunction[(...funArgs...) => funRet, ?] + funArgs.size + case _ => -1 } - else { - // TupledFunction[?, ?] - -1 + } else if (defn.isFunctionType(tupled)) { + // TupledFunction[?, (...) => R] + tupled.dropDependentRefinement.dealias.argInfos match { + case tupledArgs :: funRet :: Nil => + defn.tupleTypes(tupledArgs) match { + case Some(funArgs) if functionTypeEqual(tupled, funArgs, funRet, fun) => + // TupledFunction[?, ((...funArgs...)) => funRet] + funArgs.size + case _ => -1 + } + case _ => -1 } } - if (arity == -1) - EmptyTree - else if (arity <= Definitions.MaxImplementedFunctionArity) - ref(defn.InternalTupleFunctionModule).select(s"tupledFunction$arity".toTermName).appliedToTypes(funArgs) - else - ref(defn.InternalTupleFunctionModule).select("tupledFunctionXXL".toTermName).appliedToTypes(funArgs) - case _ => + else { + // TupledFunction[?, ?] + -1 + } + } + if (arity == -1) EmptyTree - } + else if (arity <= Definitions.MaxImplementedFunctionArity) + ref(defn.InternalTupleFunctionModule).select(s"tupledFunction$arity".toTermName).appliedToTypes(funArgs) + else + ref(defn.InternalTupleFunctionModule).select("tupledFunctionXXL".toTermName).appliedToTypes(funArgs) + case _ => + EmptyTree } - /** If `formal` is of the form Eql[T, U], try to synthesize an - * `Eql.eqlAny[T, U]` as solution. - */ - def synthesizedEq(formal: Type)(implicit ctx: Context): Tree = { + /** If `formal` is of the form Eql[T, U], try to synthesize an + * `Eql.eqlAny[T, U]` as solution. + */ + lazy val synthesizedEq: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => { /** Is there an `Eql[T, T]` instance, assuming -strictEquality? */ def hasEq(tp: Type)(implicit ctx: Context): Boolean = { @@ -818,10 +817,11 @@ trait Implicits { self: Typer => } } - /** Creates a tree that will produce a ValueOf instance for the requested type. - * An EmptyTree is returned if materialization fails. - */ - def synthesizedValueOf(formal: Type)(implicit ctx: Context): Tree = { + /** Creates a tree that will produce a ValueOf instance for the requested type. + * An EmptyTree is returned if materialization fails. + */ + lazy val synthesizedValueOf: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => { def success(t: Tree) = New(defn.ValueOfClass.typeRef.appliedTo(t.tpe), t :: Nil).withSpan(span) formal.argTypes match { @@ -841,10 +841,11 @@ trait Implicits { self: Typer => } } - /** If `formal` is of the form `scala.reflect.Generic[T]` for some class type `T`, - * synthesize an instance for it. - */ - def synthesizedGeneric(formal: Type): Tree = + /** If `formal` is of the form `scala.reflect.Generic[T]` for some class type `T`, + * synthesize an instance for it. + */ + lazy val synthesizedGeneric: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => formal.argTypes match { case arg :: Nil => val pos = ctx.source.atSpan(span) @@ -855,26 +856,44 @@ trait Implicits { self: Typer => EmptyTree } + private var mySpecialHandlers: SpecialHandlers = null + + private def specialHandlers(implicit ctx: Context) = { + if (mySpecialHandlers == null) + mySpecialHandlers = List( + defn.ClassTagClass -> synthesizedClassTag, + defn.QuotedTypeClass -> synthesizedTypeTag, + defn.GenericClass -> synthesizedGeneric, + defn.TastyReflectionClass -> synthesizedTastyContext, + defn.EqlClass -> synthesizedEq, + defn.TupledFunctionClass -> synthesizedTupleFunction, + defn.ValueOfClass -> synthesizedValueOf + ) + mySpecialHandlers + } + + /** Find an implicit argument for parameter `formal`. + * Return a failure as a SearchFailureType in the type of the returned tree. + */ + def inferImplicitArg(formal: Type, span: Span)(implicit ctx: Context): Tree = { inferImplicit(formal, EmptyTree, span)(ctx) match { case SearchSuccess(arg, _, _) => arg case fail @ SearchFailure(failed) => - def trySpecialCase(cls: ClassSymbol, handler: Type => Tree, ifNot: => Tree) = { - val base = formal.baseType(cls) - if (base <:< formal) { - // With the subtype test we enforce that the searched type `formal` is of the right form - handler(base).orElse(ifNot) - } - else ifNot + def trySpecialCases(handlers: SpecialHandlers): Tree = handlers match { + case (cls, handler) :: rest => + val base = formal.baseType(cls) + val result = + if (base <:< formal) { + // With the subtype test we enforce that the searched type `formal` is of the right form + handler(base, span)(ctx) + } + else EmptyTree + result.orElse(trySpecialCases(rest)) + case Nil => + failed } - if (fail.isAmbiguous) failed - else - trySpecialCase(defn.ClassTagClass, synthesizedClassTag, - trySpecialCase(defn.QuotedTypeClass, synthesizedTypeTag, - trySpecialCase(defn.GenericClass, synthesizedGeneric, - trySpecialCase(defn.TastyReflectionClass, synthesizedTastyContext, - trySpecialCase(defn.EqlClass, synthesizedEq, - trySpecialCase(defn.TupledFunctionClass, synthesizedTupleFunction, - trySpecialCase(defn.ValueOfClass, synthesizedValueOf, failed))))))) + if (fail.isAmbiguous) failed + else trySpecialCases(specialHandlers) } } From f898fc10251d4e3cb482b4e1789c1fcd4a22464d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 May 2019 11:55:51 +0200 Subject: [PATCH 17/35] Add new utility method: withAttachment --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 3 +-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 5 +---- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- compiler/src/dotty/tools/dotc/util/Attachment.scala | 5 +++++ 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 826957a75665..daf9aa7e5c3b 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -849,8 +849,7 @@ object desugar { fwd } val moduleName = tdef.name.toTermName - val localRef = Select(Ident(moduleName), tdef.name) - localRef.pushAttachment(SuppressAccessCheck, ()) + val localRef = Select(Ident(moduleName), tdef.name).withAttachment(SuppressAccessCheck, ()) val aliasType = cpy.TypeDef(tdef)(rhs = completeForwarder(localRef)).withSpan(tdef.span.startPos) val localType = tdef.withMods(Modifiers(Synthetic | Opaque).withPrivateWithin(tdef.name)) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index e09b5c7562f3..b8885b6118b6 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -263,10 +263,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { } /** Install the derived type tree as a dependency on `sym` */ - def watching(sym: Symbol): this.type = { - pushAttachment(OriginalSymbol, sym) - this - } + def watching(sym: Symbol): this.type = withAttachment(OriginalSymbol, sym) /** A hook to ensure that all necessary symbols are completed so that * OriginalSymbol attachments are propagated to this tree diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index ff9961df0cd9..f686c7a30285 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2437,7 +2437,7 @@ class Typer extends Namer def tryApply(implicit ctx: Context) = { val pt1 = pt.withContext(ctx) val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt1) - sel.pushAttachment(InsertedApply, ()) + .withAttachment(InsertedApply, ()) if (sel.tpe.isError) sel else try adapt(simplify(sel, pt1, locked), pt1, locked) finally sel.removeAttachment(InsertedApply) } diff --git a/compiler/src/dotty/tools/dotc/util/Attachment.scala b/compiler/src/dotty/tools/dotc/util/Attachment.scala index 0fed105d3c91..7669e473a385 100644 --- a/compiler/src/dotty/tools/dotc/util/Attachment.scala +++ b/compiler/src/dotty/tools/dotc/util/Attachment.scala @@ -101,6 +101,11 @@ object Attachment { this } + def withAttachment[V](key: Key[V], value: V): this.type = { + pushAttachment(key, value) + this + } + final def pushAttachment[V](key: Key[V], value: V): Unit = { assert(!getAttachment(key).isDefined, s"duplicate attachment for key $key") next = new Link(key, value, next) From 934c4ade9a172336be1af805e86cc179ebadc439 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 May 2019 11:59:02 +0200 Subject: [PATCH 18/35] Use attachment to mark singleton cases --- compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala | 8 ++++++-- .../src/dotty/tools/dotc/transform/SyntheticMembers.scala | 5 ++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 551a5439a0c7..f1ec657ecec1 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -22,6 +22,9 @@ object DesugarEnums { /** Attachment containing the number of enum cases and the smallest kind that was seen so far. */ val EnumCaseCount: Property.Key[(Int, DesugarEnums.CaseKind.Value)] = new Property.Key + /** Attachment marking an anonymous class as a singleton case. */ + val SingletonCase: Property.StickyKey[Unit] = new Property.StickyKey + /** The enumeration class that belongs to an enum case. This works no matter * whether the case is still in the enum class or it has been transferred to the * companion object. @@ -115,13 +118,13 @@ object DesugarEnums { val toStringDef = DefDef(nme.toString_, Nil, Nil, TypeTree(), Ident(nme.name)) .withFlags(Override) - def creator = New(Template( + val creator = New(Template( constr = emptyConstructor, parents = enumClassRef :: Nil, derived = Nil, self = EmptyValDef, body = List(enumTagDef, toStringDef) ++ registerCall - )) + ).withAttachment(SingletonCase, ())) DefDef(nme.DOLLAR_NEW, Nil, List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))), TypeTree(), creator).withFlags(Private | Synthetic) @@ -264,6 +267,7 @@ object DesugarEnums { .withFlags(Override) val (tagMeth, scaffolding) = enumTagMeth(CaseKind.Object) val impl1 = cpy.Template(impl)(body = List(tagMeth, toStringMeth) ++ registerCall) + .withAttachment(SingletonCase, ()) val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final) flatTree(scaffolding ::: vdef :: Nil).withSpan(span) } diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index d5afb9ca0984..c44d7aace392 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -10,6 +10,7 @@ import NameOps._ import Annotations.Annotation import typer.ProtoTypes.constrained import ast.untpd +import ast.DesugarEnums.SingletonCase import ValueClasses.isDerivedValueClass import SymUtils._ import config.Printers.derive @@ -423,9 +424,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { else if (linked.is(Sealed)) derive.println(i"$linked is not a sum because ${linked.whyNotGenericSum}") } - else if (clazz.isAnonymousClass && - (clazz.owner.is(EnumCaseVal) || - clazz.owner.name == nme.DOLLAR_NEW && clazz.owner.is(Synthetic))) + else if (impl.removeAttachment(SingletonCase).isDefined) addParent(defn.Mirror_SingletonType) cpy.Template(impl)(parents = newParents, body = newBody) } From be1a0638ae6b1e103aaff71cd9f371c767fb8b42 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 May 2019 12:02:06 +0200 Subject: [PATCH 19/35] Synthesize implicits for product and sum mirrors --- .../dotty/tools/dotc/core/Definitions.scala | 6 +- .../src/dotty/tools/dotc/core/StdNames.scala | 3 + .../tools/dotc/transform/TypeUtils.scala | 3 + .../src/dotty/tools/dotc/typer/Deriving.scala | 4 +- .../dotty/tools/dotc/typer/Implicits.scala | 106 +++++++++++++++++- library/src/scala/deriving.scala | 3 + tests/run/deriving.check | 4 + tests/run/deriving.scala | 17 +++ 8 files changed, 136 insertions(+), 10 deletions(-) create mode 100644 tests/run/deriving.check diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index ce9e68d784ca..f6e6e447c04a 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -690,7 +690,9 @@ class Definitions { lazy val ModuleSerializationProxyConstructor: TermSymbol = ModuleSerializationProxyClass.requiredMethod(nme.CONSTRUCTOR, List(ClassType(TypeBounds.empty))) - //lazy val MirrorType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror") + lazy val MirrorType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror") + def MirrorClass(implicit ctx: Context): ClassSymbol = MirrorType.symbol.asClass + lazy val Mirror_ProductType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.Product") def Mirror_ProductClass(implicit ctx: Context): ClassSymbol = Mirror_ProductType.symbol.asClass @@ -711,7 +713,7 @@ class Definitions { def ShapeCaseClass(implicit ctx: Context): ClassSymbol = ShapeCaseType.symbol.asClass lazy val ShapeCasesType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape.Cases") def ShapeCasesClass(implicit ctx: Context): ClassSymbol = ShapeCasesType.symbol.asClass - lazy val MirrorType: TypeRef = ctx.requiredClassRef("scala.reflect.Mirror") + lazy val ReflectMirrorType: TypeRef = ctx.requiredClassRef("scala.reflect.Mirror") lazy val GenericClassType: TypeRef = ctx.requiredClassRef("scala.reflect.GenericClass") lazy val LanguageModuleRef: TermSymbol = ctx.requiredModule("scala.language") diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index f751091f8f48..5c16a5145ff9 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -326,10 +326,13 @@ object StdNames { val AnnotatedType: N = "AnnotatedType" val AppliedTypeTree: N = "AppliedTypeTree" val ArrayAnnotArg: N = "ArrayAnnotArg" + val CaseLabel: N = "CaseLabel" val CAP: N = "CAP" val Constant: N = "Constant" val ConstantType: N = "ConstantType" val doubleHash: N = "doubleHash" + val ElemLabels: N = "ElemLabels" + val ElemTypes: N = "ElemTypes" val ExistentialTypeTree: N = "ExistentialTypeTree" val Flag : N = "Flag" val floatHash: N = "floatHash" diff --git a/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala b/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala index ee6938b78248..65649899b29b 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala @@ -7,6 +7,7 @@ import TypeErasure.ErasedValueType import Types._ import Contexts._ import Symbols._ +import Names.Name object TypeUtils { /** A decorator that provides methods on types @@ -63,5 +64,7 @@ object TypeUtils { } extractAlias(lo) } + + def refinedWith(name: Name, info: Type)(implicit ctx: Context) = RefinedType(self, name, info) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index 79e2c9c20e41..94a085e72e83 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -372,7 +372,7 @@ trait Deriving { this: Typer => TypeDef(shapeAlias) } val reflectMethod: DefDef = { - val meth = newMethod(nme.reflect, MethodType(clsArg :: Nil, defn.MirrorType)).entered + val meth = newMethod(nme.reflect, MethodType(clsArg :: Nil, defn.ReflectMirrorType)).entered def rhs(paramRef: Tree)(implicit ctx: Context): Tree = { def reflectCase(scrut: Tree, idx: Int, elems: List[Type]): Tree = { val ordinal = Literal(Constant(idx)) @@ -401,7 +401,7 @@ trait Deriving { this: Typer => } val reifyMethod: DefDef = { - val meth = newMethod(nme.reify, MethodType(defn.MirrorType :: Nil, clsArg)).entered + val meth = newMethod(nme.reify, MethodType(defn.ReflectMirrorType :: Nil, clsArg)).entered def rhs(paramRef: Tree)(implicit ctx: Context): Tree = { def reifyCase(caseType: Type, elems: List[Type]): Tree = caseType match { case caseType: TermRef => diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 1a1e88aa0315..bc58051d640d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -27,6 +27,8 @@ import ErrorReporting._ import reporting.diagnostic.Message import Inferencing.fullyDefinedType import Trees._ +import transform.SymUtils._ +import transform.TypeUtils._ import Hashable._ import util.{Property, SourceFile, NoSource} import config.Config @@ -856,18 +858,104 @@ trait Implicits { self: Typer => EmptyTree } + lazy val synthesizedProductMirror: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => { + formal.member(tpnme.MonoType).info match { + case monoAlias @ TypeAlias(monoType) => + if (monoType.termSymbol.is(CaseVal)) { + val modul = monoType.termSymbol + val caseLabel = ConstantType(Constant(modul.name.toString)) + val mirrorType = defn.Mirror_SingletonType + .refinedWith(tpnme.MonoType, monoAlias) + .refinedWith(tpnme.CaseLabel, TypeAlias(caseLabel)) + ref(modul).withSpan(span).cast(mirrorType) + } + else if (monoType.classSymbol.isGenericProduct) { + val cls = monoType.classSymbol + val accessors = cls.caseAccessors.filterNot(_.is(PrivateLocal)) + val elemTypes = accessors.map(monoType.memberInfo(_)) + val caseLabel = ConstantType(Constant(cls.name.toString)) + val elemLabels = accessors.map(acc => ConstantType(Constant(acc.name.toString))) + val mirrorType = + defn.Mirror_ProductType + .refinedWith(tpnme.MonoType, monoAlias) + .refinedWith(tpnme.ElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) + .refinedWith(tpnme.CaseLabel, TypeAlias(caseLabel)) + .refinedWith(tpnme.ElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels))) + val modul = cls.linkedClass.sourceModule + assert(modul.is(Module)) + ref(modul).withSpan(span).cast(mirrorType) + } + else EmptyTree + case _ => EmptyTree + } + } + + lazy val synthesizedSumMirror: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => + formal.member(tpnme.MonoType).info match { + case monoAlias @ TypeAlias(monoType) if monoType.classSymbol.isGenericSum => + val cls = monoType.classSymbol + val elemTypes = cls.children.map { + case caseClass: ClassSymbol => + assert(caseClass.is(Case)) + if (caseClass.is(Module)) + caseClass.sourceModule.termRef + else caseClass.primaryConstructor.info match { + case info: PolyType => + def instantiate(implicit ctx: Context) = { + val poly = constrained(info, untpd.EmptyTree)._1 + val mono @ MethodType(_) = poly.resultType + val resType = mono.finalResultType + resType <:< cls.appliedRef + val tparams = poly.paramRefs + val variances = caseClass.typeParams.map(_.paramVariance) + val instanceTypes = (tparams, variances).zipped.map((tparam, variance) => + ctx.typeComparer.instanceType(tparam, fromBelow = variance < 0)) + resType.substParams(poly, instanceTypes) + } + instantiate(ctx.fresh.setExploreTyperState().setOwner(caseClass)) + case _ => + caseClass.typeRef + } + case child => child.termRef + } + val mirrorType = + defn.Mirror_SumType + .refinedWith(tpnme.MonoType, monoAlias) + .refinedWith(tpnme.ElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) + var modul = cls.linkedClass.sourceModule + if (!modul.exists) ??? + ref(modul).withSpan(span).cast(mirrorType) + case _ => + EmptyTree + } + + lazy val synthesizedMirror: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => + formal.member(tpnme.MonoType).info match { + case monoAlias @ TypeAlias(monoType) => + if (monoType.termSymbol.is(CaseVal) || monoType.classSymbol.isGenericProduct) + synthesizedProductMirror(formal, span)(ctx) + else + synthesizedSumMirror(formal, span)(ctx) + } + private var mySpecialHandlers: SpecialHandlers = null private def specialHandlers(implicit ctx: Context) = { if (mySpecialHandlers == null) mySpecialHandlers = List( - defn.ClassTagClass -> synthesizedClassTag, - defn.QuotedTypeClass -> synthesizedTypeTag, - defn.GenericClass -> synthesizedGeneric, + defn.ClassTagClass -> synthesizedClassTag, + defn.QuotedTypeClass -> synthesizedTypeTag, + defn.GenericClass -> synthesizedGeneric, defn.TastyReflectionClass -> synthesizedTastyContext, - defn.EqlClass -> synthesizedEq, + defn.EqlClass -> synthesizedEq, defn.TupledFunctionClass -> synthesizedTupleFunction, - defn.ValueOfClass -> synthesizedValueOf + defn.ValueOfClass -> synthesizedValueOf, + defn.Mirror_ProductClass -> synthesizedProductMirror, + defn.Mirror_SumClass -> synthesizedSumMirror, + defn.MirrorClass -> synthesizedMirror ) mySpecialHandlers } @@ -881,7 +969,13 @@ trait Implicits { self: Typer => case fail @ SearchFailure(failed) => def trySpecialCases(handlers: SpecialHandlers): Tree = handlers match { case (cls, handler) :: rest => - val base = formal.baseType(cls) + def baseWithRefinements(tp: Type): Type = tp.dealias match { + case tp @ RefinedType(parent, rname, rinfo) => + tp.derivedRefinedType(baseWithRefinements(parent), rname, rinfo) + case _ => + tp.baseType(cls) + } + val base = baseWithRefinements(formal) val result = if (base <:< formal) { // With the subtype test we enforce that the searched type `formal` is of the right form diff --git a/library/src/scala/deriving.scala b/library/src/scala/deriving.scala index 0b4f60651c3a..3c2fb2af59d6 100644 --- a/library/src/scala/deriving.scala +++ b/library/src/scala/deriving.scala @@ -39,6 +39,9 @@ object deriving { trait Singleton extends Product { type MonoType = this.type + type ElemTypes = Unit + type ElemLabels = Unit + def fromProduct(p: scala.Product) = this def productElement(n: Int): Any = throw new IndexOutOfBoundsException(n.toString) diff --git a/tests/run/deriving.check b/tests/run/deriving.check new file mode 100644 index 000000000000..4f9cc1285552 --- /dev/null +++ b/tests/run/deriving.check @@ -0,0 +1,4 @@ +A(1,2) +A(1,2) +B +1 diff --git a/tests/run/deriving.scala b/tests/run/deriving.scala index 16df00d79bc3..12eb849d6d83 100644 --- a/tests/run/deriving.scala +++ b/tests/run/deriving.scala @@ -5,6 +5,23 @@ case class A(x: Int, y: Int) extends T case object B extends T object Test extends App { + import deriving.{Mirror, EmptyProduct} case class AA[X >: Null <: AnyRef](x: X, y: X, z: String) + + println(the[Mirror.ProductOf[A]].fromProduct(A(1, 2))) + assert(the[Mirror.SumOf[T]].ordinal(A(1, 2)) == 0) + assert(the[Mirror.Sum { type MonoType = T }].ordinal(B) == 1) + the[Mirror.Of[A]] match { + case m: Mirror.Product => + println(m.fromProduct(A(1, 2))) + } + the[Mirror.Of[B.type]] match { + case m: Mirror.Product => + println(m.fromProduct(EmptyProduct)) + } + the[Mirror.Of[T]] match { + case m: Mirror.SumOf[T] => + println(m.ordinal(B)) + } } \ No newline at end of file From 275a0a96bdb1f28300ca8e0f045226e1e99b3df1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 May 2019 15:42:49 +0200 Subject: [PATCH 20/35] Generate sum mirrors for sealed traits that do not have a companion --- .../dotty/tools/dotc/ast/DesugarEnums.scala | 8 +-- .../dotc/transform/SyntheticMembers.scala | 67 +++++++++++++------ .../dotty/tools/dotc/typer/Implicits.scala | 21 +++++- tests/run/deriving.check | 1 + tests/run/deriving.scala | 7 ++ 5 files changed, 75 insertions(+), 29 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index f1ec657ecec1..2fcf7a107fc7 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -8,6 +8,7 @@ import Symbols._, StdNames._, Trees._ import Decorators._ import util.{Property, SourceFile} import typer.ErrorReporting._ +import transform.SyntheticMembers.ExtendsSingletonMirror import scala.annotation.internal.sharable @@ -22,9 +23,6 @@ object DesugarEnums { /** Attachment containing the number of enum cases and the smallest kind that was seen so far. */ val EnumCaseCount: Property.Key[(Int, DesugarEnums.CaseKind.Value)] = new Property.Key - /** Attachment marking an anonymous class as a singleton case. */ - val SingletonCase: Property.StickyKey[Unit] = new Property.StickyKey - /** The enumeration class that belongs to an enum case. This works no matter * whether the case is still in the enum class or it has been transferred to the * companion object. @@ -124,7 +122,7 @@ object DesugarEnums { derived = Nil, self = EmptyValDef, body = List(enumTagDef, toStringDef) ++ registerCall - ).withAttachment(SingletonCase, ())) + ).withAttachment(ExtendsSingletonMirror, ())) DefDef(nme.DOLLAR_NEW, Nil, List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))), TypeTree(), creator).withFlags(Private | Synthetic) @@ -267,7 +265,7 @@ object DesugarEnums { .withFlags(Override) val (tagMeth, scaffolding) = enumTagMeth(CaseKind.Object) val impl1 = cpy.Template(impl)(body = List(tagMeth, toStringMeth) ++ registerCall) - .withAttachment(SingletonCase, ()) + .withAttachment(ExtendsSingletonMirror, ()) val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final) flatTree(scaffolding ::: vdef :: Nil).withSpan(span) } diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index c44d7aace392..b163b26b7ebb 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -10,11 +10,20 @@ import NameOps._ import Annotations.Annotation import typer.ProtoTypes.constrained import ast.untpd -import ast.DesugarEnums.SingletonCase import ValueClasses.isDerivedValueClass import SymUtils._ +import util.Property import config.Printers.derive +object SyntheticMembers { + + /** Attachment marking an anonymous class as a singleton case that will extend from Mirror.Singleton. */ + val ExtendsSingletonMirror: Property.StickyKey[Unit] = new Property.StickyKey + + /** Attachment marking an anonymous class as a sum mirror that will extends from Mirror.Sum. */ + val ExtendsSumMirror: Property.StickyKey[Unit] = new Property.StickyKey +} + /** Synthetic method implementations for case classes, case objects, * and value classes. * @@ -38,6 +47,7 @@ import config.Printers.derive * def hashCode(): Int */ class SyntheticMembers(thisPhase: DenotTransformer) { + import SyntheticMembers._ import ast.tpd._ private[this] var myValueSymbols: List[Symbol] = Nil @@ -381,7 +391,6 @@ class SyntheticMembers(thisPhase: DenotTransformer) { */ def addMirrorSupport(impl: Template)(implicit ctx: Context): Template = { val clazz = ctx.owner.asClass - val linked = clazz.linkedClass var newBody = impl.body var newParents = impl.parents @@ -392,40 +401,56 @@ class SyntheticMembers(thisPhase: DenotTransformer) { classParents = oldClassInfo.classParents :+ parent) clazz.copySymDenotation(info = newClassInfo).installAfter(thisPhase) } - def addMethod(name: TermName, info: Type, body: (Symbol, Tree, Context) => Tree): Unit = { + def addMethod(name: TermName, info: Type, cls: Symbol, body: (Symbol, Tree, Context) => Tree): Unit = { val meth = ctx.newSymbol(clazz, name, Synthetic | Method, info, coord = clazz.coord) if (!existingDef(meth, clazz).exists) { meth.entered newBody = newBody :+ - synthesizeDef(meth, vrefss => ctx => body(linked, vrefss.head.head, ctx)) + synthesizeDef(meth, vrefss => ctx => body(cls, vrefss.head.head, ctx)) } } + val linked = clazz.linkedClass lazy val monoType = { - val monoType = - ctx.newSymbol(clazz, tpnme.MonoType, Synthetic, TypeAlias(linked.rawTypeRef), coord = clazz.coord) - existingDef(monoType, clazz).orElse { + val existing = clazz.info.member(tpnme.MonoType).symbol + if (existing.exists && !existing.is(Deferred)) existing + else { + val monoType = + ctx.newSymbol(clazz, tpnme.MonoType, Synthetic, TypeAlias(linked.rawTypeRef), coord = clazz.coord) newBody = newBody :+ TypeDef(monoType).withSpan(ctx.owner.span.focus) monoType.entered } } + def makeSingletonMirror() = + addParent(defn.Mirror_SingletonType) + def makeProductMirror() = { + addParent(defn.Mirror_ProductType) + addMethod( + nme.fromProduct, + MethodType(defn.ProductType :: Nil, monoType.typeRef), + linked, + fromProductBody(_, _)(_).ensureConforms(monoType.typeRef)) // t4758.scala or i3381.scala are examples where a cast is needed + } + def makeSumMirror(cls: Symbol) = { + addParent(defn.Mirror_SumType) + addMethod( + nme.ordinal, + MethodType(monoType.typeRef :: Nil, defn.IntType), + cls, + ordinalBody(_, _)(_)) + } + if (clazz.is(Module)) { - if (clazz.is(Case)) - addParent(defn.Mirror_SingletonType) - else if (linked.isGenericProduct) { - addParent(defn.Mirror_ProductType) - addMethod(nme.fromProduct, MethodType(defn.ProductType :: Nil, monoType.typeRef), - fromProductBody(_, _)(_).ensureConforms(monoType.typeRef)) // t4758.scala or i3381.scala are examples where a cast is needed - } - else if (linked.isGenericSum) { - addParent(defn.Mirror_SumType) - addMethod(nme.ordinal, MethodType(monoType.typeRef :: Nil, defn.IntType), - ordinalBody(_, _)(_)) - } + if (clazz.is(Case)) makeSingletonMirror() + else if (linked.isGenericProduct) makeProductMirror() + else if (linked.isGenericSum) makeSumMirror(linked) else if (linked.is(Sealed)) derive.println(i"$linked is not a sum because ${linked.whyNotGenericSum}") } - else if (impl.removeAttachment(SingletonCase).isDefined) - addParent(defn.Mirror_SingletonType) + else if (impl.removeAttachment(ExtendsSingletonMirror).isDefined) + makeSingletonMirror() + else if (impl.removeAttachment(ExtendsSumMirror).isDefined) + makeSumMirror(monoType.typeRef.dealias.classSymbol) + cpy.Template(impl)(parents = newParents, body = newBody) } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index bc58051d640d..c87f0c8c740f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -3,7 +3,7 @@ package dotc package typer import core._ -import ast.{Trees, TreeTypeMap, untpd, tpd} +import ast.{Trees, TreeTypeMap, untpd, tpd, DesugarEnums} import util.Spans._ import util.Stats.{track, record, monitored} import printing.{Showable, Printer} @@ -29,6 +29,7 @@ import Inferencing.fullyDefinedType import Trees._ import transform.SymUtils._ import transform.TypeUtils._ +import transform.SyntheticMembers.ExtendsSumMirror import Hashable._ import util.{Property, SourceFile, NoSource} import config.Config @@ -925,8 +926,22 @@ trait Implicits { self: Typer => .refinedWith(tpnme.MonoType, monoAlias) .refinedWith(tpnme.ElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) var modul = cls.linkedClass.sourceModule - if (!modul.exists) ??? - ref(modul).withSpan(span).cast(mirrorType) + val mirrorRef = + if (modul.exists) ref(modul).withSpan(span) + else { + // create an anonymous class `new Object { type MonoType = ... }` + // and mark it so that it is made into a `Mirror.Sum` at PostTyper. + val monoTypeDef = untpd.TypeDef(tpnme.MonoType, untpd.TypeTree(monoType)) + val newImpl = untpd.Template( + constr = untpd.emptyConstructor, + parents = untpd.TypeTree(defn.ObjectType) :: Nil, + derived = Nil, + self = EmptyValDef, + body = monoTypeDef :: Nil + ).withAttachment(ExtendsSumMirror, ()) + typed(untpd.New(newImpl).withSpan(span)) + } + mirrorRef.cast(mirrorType) case _ => EmptyTree } diff --git a/tests/run/deriving.check b/tests/run/deriving.check index 4f9cc1285552..7b8532eb650a 100644 --- a/tests/run/deriving.check +++ b/tests/run/deriving.check @@ -2,3 +2,4 @@ A(1,2) A(1,2) B 1 +0 diff --git a/tests/run/deriving.scala b/tests/run/deriving.scala index 12eb849d6d83..4bf150a71c6f 100644 --- a/tests/run/deriving.scala +++ b/tests/run/deriving.scala @@ -4,6 +4,9 @@ object T case class A(x: Int, y: Int) extends T case object B extends T +sealed trait U +case class C() extends U + object Test extends App { import deriving.{Mirror, EmptyProduct} @@ -24,4 +27,8 @@ object Test extends App { case m: Mirror.SumOf[T] => println(m.ordinal(B)) } + the[Mirror.Of[U]] match { + case m: Mirror.SumOf[U] => + println(m.ordinal(C())) + } } \ No newline at end of file From e50a032859b6a685c5afd72293e9e5956a2b8c4f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 May 2019 15:54:47 +0200 Subject: [PATCH 21/35] Use Label field for sum and product mirrors --- compiler/src/dotty/tools/dotc/core/StdNames.scala | 2 +- .../dotty/tools/dotc/transform/SyntheticMembers.scala | 1 + compiler/src/dotty/tools/dotc/typer/Implicits.scala | 10 ++++++---- library/src/scala/deriving.scala | 9 +++++---- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 5c16a5145ff9..4f8648892f7f 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -326,7 +326,6 @@ object StdNames { val AnnotatedType: N = "AnnotatedType" val AppliedTypeTree: N = "AppliedTypeTree" val ArrayAnnotArg: N = "ArrayAnnotArg" - val CaseLabel: N = "CaseLabel" val CAP: N = "CAP" val Constant: N = "Constant" val ConstantType: N = "ConstantType" @@ -338,6 +337,7 @@ object StdNames { val floatHash: N = "floatHash" val Ident: N = "Ident" val Import: N = "Import" + val Label: N = "Label" val Literal: N = "Literal" val LiteralAnnotArg: N = "LiteralAnnotArg" val longHash: N = "longHash" diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index b163b26b7ebb..bd0ec22278cb 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -367,6 +367,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { * case _: C_1 => 0 * ... * case _: C_n => n - 1 + * } * * Here, the normalized type of a class C is C[_, ...., _] with * a wildcard for each type parameter. The normalized type of an object diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index c87f0c8c740f..d07f1ee141bc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -865,23 +865,23 @@ trait Implicits { self: Typer => case monoAlias @ TypeAlias(monoType) => if (monoType.termSymbol.is(CaseVal)) { val modul = monoType.termSymbol - val caseLabel = ConstantType(Constant(modul.name.toString)) + val label = ConstantType(Constant(modul.name.toString)) val mirrorType = defn.Mirror_SingletonType .refinedWith(tpnme.MonoType, monoAlias) - .refinedWith(tpnme.CaseLabel, TypeAlias(caseLabel)) + .refinedWith(tpnme.Label, TypeAlias(label)) ref(modul).withSpan(span).cast(mirrorType) } else if (monoType.classSymbol.isGenericProduct) { val cls = monoType.classSymbol val accessors = cls.caseAccessors.filterNot(_.is(PrivateLocal)) val elemTypes = accessors.map(monoType.memberInfo(_)) - val caseLabel = ConstantType(Constant(cls.name.toString)) + val label = ConstantType(Constant(cls.name.toString)) val elemLabels = accessors.map(acc => ConstantType(Constant(acc.name.toString))) val mirrorType = defn.Mirror_ProductType .refinedWith(tpnme.MonoType, monoAlias) .refinedWith(tpnme.ElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) - .refinedWith(tpnme.CaseLabel, TypeAlias(caseLabel)) + .refinedWith(tpnme.Label, TypeAlias(label)) .refinedWith(tpnme.ElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels))) val modul = cls.linkedClass.sourceModule assert(modul.is(Module)) @@ -897,6 +897,7 @@ trait Implicits { self: Typer => formal.member(tpnme.MonoType).info match { case monoAlias @ TypeAlias(monoType) if monoType.classSymbol.isGenericSum => val cls = monoType.classSymbol + val label = ConstantType(Constant(cls.name.toString)) val elemTypes = cls.children.map { case caseClass: ClassSymbol => assert(caseClass.is(Case)) @@ -924,6 +925,7 @@ trait Implicits { self: Typer => val mirrorType = defn.Mirror_SumType .refinedWith(tpnme.MonoType, monoAlias) + .refinedWith(tpnme.Label, TypeAlias(label)) .refinedWith(tpnme.ElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) var modul = cls.linkedClass.sourceModule val mirrorRef = diff --git a/library/src/scala/deriving.scala b/library/src/scala/deriving.scala index 3c2fb2af59d6..351cd315fac1 100644 --- a/library/src/scala/deriving.scala +++ b/library/src/scala/deriving.scala @@ -8,6 +8,9 @@ object deriving { /** The mirrored *-type */ type MonoType + + /** The name of the type */ + type Label <: String } object Mirror { @@ -15,6 +18,7 @@ object deriving { /** The Mirror for a sum type */ trait Sum extends Mirror { self => + /** The types of the alternatives */ type ElemTypes <: Tuple /** The ordinal number of the case class of `x`. For enums, `ordinal(x) == x.ordinal` */ @@ -24,12 +28,9 @@ object deriving { /** The Mirror for a product type */ trait Product extends Mirror { - /** The types of the elements */ + /** The types of the product elements */ type ElemTypes <: Tuple - /** The name of the whole product type */ - type CaseLabel <: String - /** The names of the product elements */ type ElemLabels <: Tuple From eda423253aa8c9e30a6d3f1925fbc6457a45c6c6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 May 2019 21:19:36 +0200 Subject: [PATCH 22/35] Synthesize mirrors also for Scala 2 defined classes and objects --- .../dotty/tools/dotc/core/Definitions.scala | 3 + .../dotc/transform/SyntheticMembers.scala | 23 ++- .../dotty/tools/dotc/typer/Implicits.scala | 75 ++++--- library/src/scala/deriving.scala | 10 +- tests/run/typeclass-derivation3.check | 8 +- tests/run/typeclass-derivation3.scala | 183 ++++++++++-------- 6 files changed, 175 insertions(+), 127 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index f6e6e447c04a..3437992f3d39 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -705,6 +705,9 @@ class Definitions { lazy val Mirror_SingletonType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.Singleton") def Mirror_SingletonClass(implicit ctx: Context): ClassSymbol = Mirror_SingletonType.symbol.asClass + lazy val Mirror_SingletonProxyType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.SingletonProxy") + def Mirror_SingletonProxyClass(implicit ctx: Context): ClassSymbol = Mirror_SingletonProxyType.symbol.asClass + lazy val GenericType: TypeRef = ctx.requiredClassRef("scala.reflect.Generic") def GenericClass(implicit ctx: Context): ClassSymbol = GenericType.symbol.asClass lazy val ShapeType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape") diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index bd0ec22278cb..5095236c67b5 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -17,10 +17,13 @@ import config.Printers.derive object SyntheticMembers { - /** Attachment marking an anonymous class as a singleton case that will extend from Mirror.Singleton. */ + /** Attachment marking an anonymous class as a singleton case that will extend from Mirror.Singleton */ val ExtendsSingletonMirror: Property.StickyKey[Unit] = new Property.StickyKey - /** Attachment marking an anonymous class as a sum mirror that will extends from Mirror.Sum. */ + /** Attachment recording that an anonymous class should extend Mirror.Product */ + val ExtendsProductMirror: Property.StickyKey[Unit] = new Property.StickyKey + + /** Attachment recording that an anonymous class should extend Mirror.Sum */ val ExtendsSumMirror: Property.StickyKey[Unit] = new Property.StickyKey } @@ -423,32 +426,28 @@ class SyntheticMembers(thisPhase: DenotTransformer) { } def makeSingletonMirror() = addParent(defn.Mirror_SingletonType) - def makeProductMirror() = { + def makeProductMirror(cls: Symbol) = { addParent(defn.Mirror_ProductType) - addMethod( - nme.fromProduct, - MethodType(defn.ProductType :: Nil, monoType.typeRef), - linked, + addMethod(nme.fromProduct, MethodType(defn.ProductType :: 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_SumType) - addMethod( - nme.ordinal, - MethodType(monoType.typeRef :: Nil, defn.IntType), - cls, + addMethod(nme.ordinal, MethodType(monoType.typeRef :: Nil, defn.IntType), cls, ordinalBody(_, _)(_)) } if (clazz.is(Module)) { if (clazz.is(Case)) makeSingletonMirror() - else if (linked.isGenericProduct) makeProductMirror() + else if (linked.isGenericProduct) makeProductMirror(linked) else if (linked.isGenericSum) makeSumMirror(linked) else if (linked.is(Sealed)) derive.println(i"$linked is not a sum because ${linked.whyNotGenericSum}") } else if (impl.removeAttachment(ExtendsSingletonMirror).isDefined) makeSingletonMirror() + else if (impl.removeAttachment(ExtendsProductMirror).isDefined) + makeProductMirror(monoType.typeRef.dealias.classSymbol) else if (impl.removeAttachment(ExtendsSumMirror).isDefined) makeSumMirror(monoType.typeRef.dealias.classSymbol) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index d07f1ee141bc..aef495d8b5ff 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -29,7 +29,7 @@ import Inferencing.fullyDefinedType import Trees._ import transform.SymUtils._ import transform.TypeUtils._ -import transform.SyntheticMembers.ExtendsSumMirror +import transform.SyntheticMembers._ import Hashable._ import util.{Property, SourceFile, NoSource} import config.Config @@ -859,35 +859,69 @@ trait Implicits { self: Typer => EmptyTree } + /** Create an anonymous class `new Object { type MonoType = ... }` + * and mark it with given attachment so that it is made into a mirror at PostTyper. + */ + def anonymousMirror(monoType: Type, attachment: Property.StickyKey[Unit], span: Span)(implicit ctx: Context) = { + val monoTypeDef = untpd.TypeDef(tpnme.MonoType, untpd.TypeTree(monoType)) + val newImpl = untpd.Template( + constr = untpd.emptyConstructor, + parents = untpd.TypeTree(defn.ObjectType) :: Nil, + derived = Nil, + self = EmptyValDef, + body = monoTypeDef :: Nil + ).withAttachment(attachment, ()) + typed(untpd.New(newImpl).withSpan(span)) + } + lazy val synthesizedProductMirror: SpecialHandler = (formal: Type, span: Span) => implicit (ctx: Context) => { - formal.member(tpnme.MonoType).info match { - case monoAlias @ TypeAlias(monoType) => + def mirrorFor(monoType: Type): Tree = monoType match { + case AndType(tp1, tp2) => + mirrorFor(tp1).orElse(mirrorFor(tp2)) + case _ => if (monoType.termSymbol.is(CaseVal)) { val modul = monoType.termSymbol val label = ConstantType(Constant(modul.name.toString)) - val mirrorType = defn.Mirror_SingletonType - .refinedWith(tpnme.MonoType, monoAlias) - .refinedWith(tpnme.Label, TypeAlias(label)) - ref(modul).withSpan(span).cast(mirrorType) + if (modul.info.classSymbol.is(Scala2x)) { + val mirrorType = + defn.Mirror_SingletonProxyType + .refinedWith(tpnme.MonoType, TypeAlias(monoType)) + .refinedWith(tpnme.Label, TypeAlias(label)) + val mirrorRef = New(defn.Mirror_SingletonProxyType, ref(modul).withSpan(span) :: Nil) + mirrorRef.cast(mirrorType) + } + else { + val mirrorType = defn.Mirror_SingletonType + .refinedWith(tpnme.MonoType, TypeAlias(monoType)) + .refinedWith(tpnme.Label, TypeAlias(label)) + val mirrorRef = ref(modul).withSpan(span) + mirrorRef.cast(mirrorType) + } } else if (monoType.classSymbol.isGenericProduct) { val cls = monoType.classSymbol val accessors = cls.caseAccessors.filterNot(_.is(PrivateLocal)) - val elemTypes = accessors.map(monoType.memberInfo(_)) + val elemTypes = accessors.map(monoType.memberInfo(_).widenExpr) val label = ConstantType(Constant(cls.name.toString)) val elemLabels = accessors.map(acc => ConstantType(Constant(acc.name.toString))) val mirrorType = defn.Mirror_ProductType - .refinedWith(tpnme.MonoType, monoAlias) + .refinedWith(tpnme.MonoType, TypeAlias(monoType)) .refinedWith(tpnme.ElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) .refinedWith(tpnme.Label, TypeAlias(label)) .refinedWith(tpnme.ElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels))) val modul = cls.linkedClass.sourceModule assert(modul.is(Module)) - ref(modul).withSpan(span).cast(mirrorType) + val mirrorRef = + if (cls.is(Scala2x)) anonymousMirror(monoType, ExtendsProductMirror, span) + else ref(modul).withSpan(span) + mirrorRef.cast(mirrorType) } else EmptyTree + } + formal.member(tpnme.MonoType).info match { + case monoAlias @ TypeAlias(monoType) => mirrorFor(monoType) case _ => EmptyTree } } @@ -907,9 +941,8 @@ trait Implicits { self: Typer => case info: PolyType => def instantiate(implicit ctx: Context) = { val poly = constrained(info, untpd.EmptyTree)._1 - val mono @ MethodType(_) = poly.resultType - val resType = mono.finalResultType - resType <:< cls.appliedRef + val resType = poly.finalResultType + resType <:< monoType val tparams = poly.paramRefs val variances = caseClass.typeParams.map(_.paramVariance) val instanceTypes = (tparams, variances).zipped.map((tparam, variance) => @@ -929,20 +962,8 @@ trait Implicits { self: Typer => .refinedWith(tpnme.ElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) var modul = cls.linkedClass.sourceModule val mirrorRef = - if (modul.exists) ref(modul).withSpan(span) - else { - // create an anonymous class `new Object { type MonoType = ... }` - // and mark it so that it is made into a `Mirror.Sum` at PostTyper. - val monoTypeDef = untpd.TypeDef(tpnme.MonoType, untpd.TypeTree(monoType)) - val newImpl = untpd.Template( - constr = untpd.emptyConstructor, - parents = untpd.TypeTree(defn.ObjectType) :: Nil, - derived = Nil, - self = EmptyValDef, - body = monoTypeDef :: Nil - ).withAttachment(ExtendsSumMirror, ()) - typed(untpd.New(newImpl).withSpan(span)) - } + if (modul.exists && !cls.is(Scala2x)) ref(modul).withSpan(span) + else anonymousMirror(monoType, ExtendsSumMirror, span) mirrorRef.cast(mirrorType) case _ => EmptyTree diff --git a/library/src/scala/deriving.scala b/library/src/scala/deriving.scala index 351cd315fac1..6932cc498b5e 100644 --- a/library/src/scala/deriving.scala +++ b/library/src/scala/deriving.scala @@ -42,11 +42,15 @@ object deriving { type MonoType = this.type type ElemTypes = Unit type ElemLabels = Unit - def fromProduct(p: scala.Product) = this + } - def productElement(n: Int): Any = throw new IndexOutOfBoundsException(n.toString) - def productArity: Int = 0 + /** A proxy for Scala 2 singletons, which do not inherit `Singleton` directly */ + class SingletonProxy(val value: AnyRef) extends Product { + type MonoType = value.type + type ElemTypes = Unit + type ElemLabels = Unit + def fromProduct(p: scala.Product) = value } type Of[T] = Mirror { type MonoType = T } diff --git a/tests/run/typeclass-derivation3.check b/tests/run/typeclass-derivation3.check index 688c68e8ee41..c39266a635b2 100644 --- a/tests/run/typeclass-derivation3.check +++ b/tests/run/typeclass-derivation3.check @@ -4,10 +4,10 @@ ListBuffer(0, 0, 11, 0, 22, 0, 33, 1, 0, 0, 11, 0, 22, 1, 1) Cons(Cons(11,Cons(22,Cons(33,Nil))),Cons(Cons(11,Cons(22,Nil)),Nil)) ListBuffer(1, 2) Pair(1,2) -Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))) -Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))), tl = Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Nil())), tl = Nil())) -Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) -Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) +Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil))) +Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil))), tl = Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Nil)), tl = Nil)) +Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil)) +Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil)) true ::(head = 1, tl$access$1 = ::(head = 2, tl$access$1 = ::(head = 3, tl$access$1 = Nil()))) ::(head = ::(head = 1, tl$access$1 = Nil()), tl$access$1 = ::(head = ::(head = 2, tl$access$1 = ::(head = 3, tl$access$1 = Nil())), tl$access$1 = Nil())) diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala index 7382ceb4ed2c..9b109c03f2b1 100644 --- a/tests/run/typeclass-derivation3.scala +++ b/tests/run/typeclass-derivation3.scala @@ -30,42 +30,45 @@ object typeclasses { object Eq { import scala.compiletime.erasedValue import compiletime._ - import reflect.{Mirror, Generic} + import scala.deriving._ - inline def tryEql[T](x: T, y: T) = implicit match { - case eq: Eq[T] => eq.eql(x, y) + inline def tryEql[TT](x: TT, y: TT): Boolean = implicit match { + case eq: Eq[TT] => eq.eql(x, y) } - inline def eqlElems[Elems <: Tuple](xs: Mirror, ys: Mirror, n: Int): Boolean = + inline def eqlElems[Elems <: Tuple](n: Int)(x: Any, y: Any): Boolean = inline erasedValue[Elems] match { case _: (elem *: elems1) => - tryEql[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) && - eqlElems[elems1](xs, ys, n + 1) + tryEql[elem](productElement[elem](x, n), productElement[elem](y, n)) && + eqlElems[elems1](n + 1)(x, y) case _: Unit => true } - inline def eqlCases[Alts <: Tuple](xm: Mirror, ym: Mirror, n: Int): Boolean = + inline def eqlProduct[T](m: Mirror.ProductOf[T])(x: Any, y: Any): Boolean = + eqlElems[m.ElemTypes](0)(x, y) + + inline def eqlCases[Alts](n: Int)(x: Any, y: Any, ord: Int): Boolean = inline erasedValue[Alts] match { - case _: (Shape.Case[alt, elems] *: alts1) => - if (xm.ordinal == n) eqlElems[elems](xm, ym, 0) - else eqlCases[alts1](xm, ym, n + 1) - case _: Unit => + case _: (alt *: alts1) => + if (ord == n) + implicit match { + case m: Mirror.ProductOf[`alt`] => eqlElems[m.ElemTypes](0)(x, y) + } + else eqlCases[alts1](n + 1)(x, y, ord) + case _: Unit => false } - inline def derived[T](implicit ev: Generic[T]): Eq[T] = new { - def eql(x: T, y: T): Boolean = { - val xm = ev.reflect(x) - val ym = ev.reflect(y) - inline erasedValue[ev.Shape] match { - case _: Shape.Cases[alts] => - xm.ordinal == ym.ordinal && - eqlCases[alts](xm, ym, 0) - case _: Shape.Case[_, elems] => - eqlElems[elems](xm, ym, 0) + inline def derived[T](implicit ev: Mirror.Of[T]): Eq[T] = new Eq[T] { + def eql(x: T, y: T): Boolean = + inline ev match { + case m: Mirror.SumOf[T] => + val ord = m.ordinal(x) + ord == m.ordinal(y) && eqlCases[m.ElemTypes](0)(x, y, ord) + case m: Mirror.ProductOf[T] => + eqlElems[m.ElemTypes](0)(x, y) } - } } implicit object IntEq extends Eq[Int] { @@ -82,7 +85,7 @@ object typeclasses { object Pickler { import scala.compiletime.{erasedValue, constValue} import compiletime._ - import reflect.{Mirror, Generic} + import deriving._ def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) @@ -90,19 +93,22 @@ object typeclasses { case pkl: Pickler[T] => pkl.pickle(buf, x) } - inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Mirror, n: Int): Unit = + inline def pickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Any): Unit = inline erasedValue[Elems] match { case _: (elem *: elems1) => - tryPickle[elem](buf, elems(n).asInstanceOf[elem]) - pickleElems[elems1](buf, elems, n + 1) + tryPickle[elem](buf, productElement[elem](x, n)) + pickleElems[elems1](n + 1)(buf, x) case _: Unit => } - inline def pickleCases[Alts <: Tuple](buf: mutable.ListBuffer[Int], xm: Mirror, n: Int): Unit = + inline def pickleCases[Alts <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Any, ord: Int): Unit = inline erasedValue[Alts] match { - case _: (Shape.Case[alt, elems] *: alts1) => - if (xm.ordinal == n) pickleElems[elems](buf, xm, 0) - else pickleCases[alts1](buf, xm, n + 1) + case _: (alt *: alts1) => + if (ord == n) + implicit match { + case m: Mirror.ProductOf[`alt`] => pickleElems[m.ElemTypes](0)(buf, x) + } + else pickleCases[alts1](n + 1)(buf, x, ord) case _: Unit => } @@ -110,51 +116,55 @@ object typeclasses { case pkl: Pickler[T] => pkl.unpickle(buf) } - inline def unpickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Array[AnyRef], n: Int): Unit = + inline def unpickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], elems: ArrayProduct): Unit = inline erasedValue[Elems] match { case _: (elem *: elems1) => elems(n) = tryUnpickle[elem](buf).asInstanceOf[AnyRef] - unpickleElems[elems1](buf, elems, n + 1) + unpickleElems[elems1](n + 1)(buf, elems) case _: Unit => } - inline def unpickleCase[T, Elems <: Tuple](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + inline def unpickleCase[T, Elems <: Tuple](buf: mutable.ListBuffer[Int], m: Mirror.ProductOf[T]): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) - gen.reify(gen.common.mirror(ordinal)) + m.fromProduct(EmptyProduct) else { - val elems = new Array[Object](size) - unpickleElems[Elems](buf, elems, 0) - gen.reify(gen.common.mirror(ordinal, elems)) + val elems = new ArrayProduct(size) + unpickleElems[Elems](0)(buf, elems) + m.fromProduct(elems) } } - inline def unpickleCases[T, Alts <: Tuple](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = + inline def unpickleCases[T, Alts <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], ord: Int): T = inline erasedValue[Alts] match { - case _: (Shape.Case[_, elems] *: alts1) => - if (n == ordinal) unpickleCase[T, elems](gen, buf, ordinal) - else unpickleCases[T, alts1](gen, buf, ordinal, n + 1) - case _ => - throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal") + case _: (alt *: alts1) => + if (ord == n) + implicit match { + case m: Mirror.ProductOf[`alt` & T] => + unpickleCase[`alt` & T, m.ElemTypes](buf, m) + } + else unpickleCases[T, alts1](n + 1)(buf, ord) + case _: Unit => + throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ord") } - inline def derived[T](implicit ev: Generic[T]): Pickler[T] = new { - def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = { - val xm = ev.reflect(x) - inline erasedValue[ev.Shape] match { - case _: Shape.Cases[alts] => - buf += xm.ordinal - pickleCases[alts](buf, xm, 0) - case _: Shape.Case[_, elems] => - pickleElems[elems](buf, xm, 0) + inline def derived[T](implicit ev: Mirror.Of[T]): Pickler[T] = new { + def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = + inline ev match { + case m: Mirror.SumOf[T] => + val ord = m.ordinal(x) + buf += ord + pickleCases[m.ElemTypes](0)(buf, x, ord) + case m: Mirror.ProductOf[T] => + pickleElems[m.ElemTypes](0)(buf, x) } - } def unpickle(buf: mutable.ListBuffer[Int]): T = - inline erasedValue[ev.Shape] match { - case _: Shape.Cases[alts] => - unpickleCases[T, alts](ev, buf, nextInt(buf), 0) - case _: Shape.Case[_, elems] => - unpickleCase[T, elems](ev, buf, 0) + inline ev match { + case m: Mirror.SumOf[T] => + val ord = nextInt(buf) + unpickleCases[T, m.ElemTypes](0)(buf, ord) + case m: Mirror.ProductOf[T] => + unpickleCase[T, m.ElemTypes](buf, m) } } @@ -171,42 +181,55 @@ object typeclasses { object Show { import scala.compiletime.erasedValue import compiletime._ - import reflect.{Mirror, Generic} + import deriving._ inline def tryShow[T](x: T): String = implicit match { case s: Show[T] => s.show(x) } - inline def showElems[Elems <: Tuple](elems: Mirror, n: Int): List[String] = + inline def showElems[Elems <: Tuple, Labels <: Tuple](n: Int)(x: Any): List[String] = inline erasedValue[Elems] match { case _: (elem *: elems1) => - val formal = elems.elementLabel(n) - val actual = tryShow[elem](elems(n).asInstanceOf) - s"$formal = $actual" :: showElems[elems1](elems, n + 1) + inline erasedValue[Labels] match { + case _: (label *: labels1) => + val formal = constValue[label] + val actual = tryShow(productElement[elem](x, n)) + s"$formal = $actual" :: showElems[elems1, labels1](n + 1)(x) + } case _: Unit => Nil + } + + inline def showCase(x: Any, m: Mirror.ProductOf[_]): String = { + val label = constValue[m.Label] + inline m match { + case m: Mirror.Singleton => label + case _ => showElems[m.ElemTypes, m.ElemLabels](0)(x).mkString(s"$label(", ", ", ")") } + } - inline def showCases[Alts <: Tuple](xm: Mirror, n: Int): String = + inline def showCases[Alts <: Tuple](n: Int)(x: Any, ord: Int): String = inline erasedValue[Alts] match { - case _: (Shape.Case[alt, elems] *: alts1) => - if (xm.ordinal == n) showElems[elems](xm, 0).mkString(", ") - else showCases[alts1](xm, n + 1) + case _: (alt *: alts1) => + if (ord == n) + implicit match { + case m: Mirror.ProductOf[`alt`] => + showCase(x, m) + } + else showCases[alts1](n + 1)(x, ord) case _: Unit => - throw new MatchError(xm) + throw new MatchError(x) } - inline def derived[T](implicit ev: Generic[T]): Show[T] = new { - def show(x: T): String = { - val xm = ev.reflect(x) - val args = inline erasedValue[ev.Shape] match { - case _: Shape.Cases[alts] => - showCases[alts](xm, 0) - case _: Shape.Case[_, elems] => - showElems[elems](xm, 0).mkString(", ") + inline def derived[T](implicit ev: Mirror.Of[T]): Show[T] = new { + def show(x: T): String = + inline ev match { + case m: Mirror.SumOf[T] => + val ord = m.ordinal(x) + showCases[m.ElemTypes](0)(x, ord) + case m: Mirror.ProductOf[T] => + showCase(x, m) } - s"${xm.caseLabel}($args)" - } } implicit object IntShow extends Show[Int] { @@ -293,9 +316,6 @@ object Test extends App { showPrintln(zs1) assert(eql(zs, zs1)) - import scala.reflect.Generic - - val listGen = implicitly[Generic[scala.collection.immutable.List[Int]]] implicit def listEq[T: Eq]: Eq[List[T]] = Eq.derived val leq = implicitly[Eq[List[Int]]] println(leq.eql(List(1, 2, 3), List(1, 2, 3))) @@ -311,4 +331,5 @@ object Test extends App { val zss1 = pklList.unpickle(buf) assert(eql(zss, zss1)) showPrintln(zss1) + } From f24e24725f9bfbefa495a4e15faf2ed6e5a2e12b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 May 2019 22:34:31 +0200 Subject: [PATCH 23/35] Polishings --- .../dotty/tools/dotc/typer/Implicits.scala | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index aef495d8b5ff..53be55c52dec 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -862,7 +862,7 @@ trait Implicits { self: Typer => /** Create an anonymous class `new Object { type MonoType = ... }` * and mark it with given attachment so that it is made into a mirror at PostTyper. */ - def anonymousMirror(monoType: Type, attachment: Property.StickyKey[Unit], span: Span)(implicit ctx: Context) = { + private def anonymousMirror(monoType: Type, attachment: Property.StickyKey[Unit], span: Span)(implicit ctx: Context) = { val monoTypeDef = untpd.TypeDef(tpnme.MonoType, untpd.TypeTree(monoType)) val newImpl = untpd.Template( constr = untpd.emptyConstructor, @@ -874,6 +874,18 @@ trait Implicits { self: Typer => typed(untpd.New(newImpl).withSpan(span)) } + /** The mirror type + * + * { MonoType = } + */ + private def mirrorCore(parent: Type, monoType: Type, label: Name)(implicit ctx: Context) = + parent + .refinedWith(tpnme.MonoType, TypeAlias(monoType)) + .refinedWith(tpnme.Label, TypeAlias(ConstantType(Constant(label.toString)))) + + /** An implied instance for a type of the form `Mirror.Product { type MonoType = T }` + * where `T` is a generic product type or a case object or an enum case. + */ lazy val synthesizedProductMirror: SpecialHandler = (formal: Type, span: Span) => implicit (ctx: Context) => { def mirrorFor(monoType: Type): Tree = monoType match { @@ -882,19 +894,13 @@ trait Implicits { self: Typer => case _ => if (monoType.termSymbol.is(CaseVal)) { val modul = monoType.termSymbol - val label = ConstantType(Constant(modul.name.toString)) if (modul.info.classSymbol.is(Scala2x)) { - val mirrorType = - defn.Mirror_SingletonProxyType - .refinedWith(tpnme.MonoType, TypeAlias(monoType)) - .refinedWith(tpnme.Label, TypeAlias(label)) + val mirrorType = mirrorCore(defn.Mirror_SingletonProxyType, monoType, modul.name) val mirrorRef = New(defn.Mirror_SingletonProxyType, ref(modul).withSpan(span) :: Nil) mirrorRef.cast(mirrorType) } else { - val mirrorType = defn.Mirror_SingletonType - .refinedWith(tpnme.MonoType, TypeAlias(monoType)) - .refinedWith(tpnme.Label, TypeAlias(label)) + val mirrorType = mirrorCore(defn.Mirror_SingletonType, monoType, modul.name) val mirrorRef = ref(modul).withSpan(span) mirrorRef.cast(mirrorType) } @@ -903,13 +909,10 @@ trait Implicits { self: Typer => val cls = monoType.classSymbol val accessors = cls.caseAccessors.filterNot(_.is(PrivateLocal)) val elemTypes = accessors.map(monoType.memberInfo(_).widenExpr) - val label = ConstantType(Constant(cls.name.toString)) val elemLabels = accessors.map(acc => ConstantType(Constant(acc.name.toString))) val mirrorType = - defn.Mirror_ProductType - .refinedWith(tpnme.MonoType, TypeAlias(monoType)) + mirrorCore(defn.Mirror_ProductType, monoType, cls.name) .refinedWith(tpnme.ElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) - .refinedWith(tpnme.Label, TypeAlias(label)) .refinedWith(tpnme.ElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels))) val modul = cls.linkedClass.sourceModule assert(modul.is(Module)) @@ -926,12 +929,14 @@ trait Implicits { self: Typer => } } + /** An implied instance for a type of the form `Mirror.Sum { type MonoType = T }` + * where `T` is a generic sum type. + */ lazy val synthesizedSumMirror: SpecialHandler = (formal: Type, span: Span) => implicit (ctx: Context) => formal.member(tpnme.MonoType).info match { - case monoAlias @ TypeAlias(monoType) if monoType.classSymbol.isGenericSum => + case TypeAlias(monoType) if monoType.classSymbol.isGenericSum => val cls = monoType.classSymbol - val label = ConstantType(Constant(cls.name.toString)) val elemTypes = cls.children.map { case caseClass: ClassSymbol => assert(caseClass.is(Case)) @@ -939,6 +944,13 @@ trait Implicits { self: Typer => caseClass.sourceModule.termRef else caseClass.primaryConstructor.info match { case info: PolyType => + // Compute the the full child type by solving the subtype constraint + // `C[X1, ..., Xn] <: P`, where + // + // - P is the current `monoType` + // - C is the child class, with type parameters X1, ..., Xn + // + // Contravariant type parameters are minimized, all other type parameters are maximized. def instantiate(implicit ctx: Context) = { val poly = constrained(info, untpd.EmptyTree)._1 val resType = poly.finalResultType @@ -956,11 +968,9 @@ trait Implicits { self: Typer => case child => child.termRef } val mirrorType = - defn.Mirror_SumType - .refinedWith(tpnme.MonoType, monoAlias) - .refinedWith(tpnme.Label, TypeAlias(label)) + mirrorCore(defn.Mirror_SumType, monoType, cls.name) .refinedWith(tpnme.ElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) - var modul = cls.linkedClass.sourceModule + val modul = cls.linkedClass.sourceModule val mirrorRef = if (modul.exists && !cls.is(Scala2x)) ref(modul).withSpan(span) else anonymousMirror(monoType, ExtendsSumMirror, span) @@ -969,6 +979,9 @@ trait Implicits { self: Typer => EmptyTree } + /** An implied instance for a type of the form `Mirror { type MonoType = T }` + * where `T` is a generic sum or product or singleton type. + */ lazy val synthesizedMirror: SpecialHandler = (formal: Type, span: Span) => implicit (ctx: Context) => formal.member(tpnme.MonoType).info match { From 7583adc04ad3aee16b74711503aba958ac1b3297 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 May 2019 22:49:35 +0200 Subject: [PATCH 24/35] Avoid name clashes by prefixing all type members with Mirrored We got a community build failure since a companion object acquired a `Label` type member by auto-generating a `Mirror` parent. To avoid name clashes (or at least make thme very unlikely), we now systematically prefix all type fields with `Mirrored`. We probably should do something similar to the auto-generated `ordinal` and `fromProduct` methods. --- .../src/dotty/tools/dotc/core/StdNames.scala | 8 ++--- .../dotc/transform/SyntheticMembers.scala | 20 +++++++----- .../dotty/tools/dotc/typer/Implicits.scala | 28 ++++++++-------- library/src/scala/deriving.scala | 32 +++++++++---------- tests/run/deriving.scala | 2 +- tests/run/ordinal-innerclass.scala | 2 +- tests/run/typeclass-derivation3.scala | 26 +++++++-------- 7 files changed, 61 insertions(+), 57 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 4f8648892f7f..0657efa8998e 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -330,20 +330,20 @@ object StdNames { val Constant: N = "Constant" val ConstantType: N = "ConstantType" val doubleHash: N = "doubleHash" - val ElemLabels: N = "ElemLabels" - val ElemTypes: N = "ElemTypes" val ExistentialTypeTree: N = "ExistentialTypeTree" val Flag : N = "Flag" val floatHash: N = "floatHash" val Ident: N = "Ident" val Import: N = "Import" - val Label: N = "Label" val Literal: N = "Literal" val LiteralAnnotArg: N = "LiteralAnnotArg" val longHash: N = "longHash" val MatchCase: N = "MatchCase" + val MirroredElemTypes: N = "MirroredElemTypes" + val MirroredElemLabels: N = "MirroredElemLabels" + val MirroredLabel: N = "MirroredLabel" + val MirroredMonoType: N = "MirroredMonoType" val Modifiers: N = "Modifiers" - val MonoType: N = "MonoType" val NestedAnnotArg: N = "NestedAnnotArg" val NoFlags: N = "NoFlags" val NoPrefix: N = "NoPrefix" diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 5095236c67b5..3256860b4281 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -323,14 +323,14 @@ class SyntheticMembers(thisPhase: DenotTransformer) { * gets the `fromProduct` method: * * ``` - * def fromProduct(x$0: Product): MonoType = + * def fromProduct(x$0: Product): MirroredMonoType = * new C[U]( * x$0.productElement(0).asInstanceOf[U], * x$0.productElement(1).asInstanceOf[Seq[String]]: _*) * ``` * where * ``` - * type MonoType = C[_] + * type MirroredMonoType = C[_] * ``` */ def fromProductBody(caseClass: Symbol, param: Tree)(implicit ctx: Context): Tree = { @@ -362,11 +362,11 @@ class SyntheticMembers(thisPhase: DenotTransformer) { /** For an enum T: * - * def ordinal(x: MonoType) = x.enumTag + * def ordinal(x: MirroredMonoType) = x.enumTag * * For sealed trait with children of normalized types C_1, ..., C_n: * - * def ordinal(x: MonoType) = x match { + * def ordinal(x: MirroredMonoType) = x match { * case _: C_1 => 0 * ... * case _: C_n => n - 1 @@ -389,9 +389,13 @@ class SyntheticMembers(thisPhase: DenotTransformer) { } /** - If `impl` is the companion of a generic sum, add `deriving.Mirror.Sum` parent - * and `MonoType` and `ordinal` members. + * and `MirroredMonoType` and `ordinal` members. * - If `impl` is the companion of a generic product, add `deriving.Mirror.Product` parent - * and `MonoType` and `fromProduct` members. + * and `MirroredMonoType` and `fromProduct` members. + * - If `impl` is marked with one of the attachments ExtendsSingletonMirror, ExtendsProductMirror, + * or ExtendsSumMirror, remove the attachment and generate the corresponding mirror support, + * On this case the represented class or object is referred to in a pre-existing `MirroredMonoType` + * member of the template. */ def addMirrorSupport(impl: Template)(implicit ctx: Context): Template = { val clazz = ctx.owner.asClass @@ -415,11 +419,11 @@ class SyntheticMembers(thisPhase: DenotTransformer) { } val linked = clazz.linkedClass lazy val monoType = { - val existing = clazz.info.member(tpnme.MonoType).symbol + val existing = clazz.info.member(tpnme.MirroredMonoType).symbol if (existing.exists && !existing.is(Deferred)) existing else { val monoType = - ctx.newSymbol(clazz, tpnme.MonoType, Synthetic, TypeAlias(linked.rawTypeRef), coord = clazz.coord) + ctx.newSymbol(clazz, tpnme.MirroredMonoType, Synthetic, TypeAlias(linked.rawTypeRef), coord = clazz.coord) newBody = newBody :+ TypeDef(monoType).withSpan(ctx.owner.span.focus) monoType.entered } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 53be55c52dec..0381475d6c40 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -859,11 +859,11 @@ trait Implicits { self: Typer => EmptyTree } - /** Create an anonymous class `new Object { type MonoType = ... }` + /** Create an anonymous class `new Object { type MirroredMonoType = ... }` * and mark it with given attachment so that it is made into a mirror at PostTyper. */ private def anonymousMirror(monoType: Type, attachment: Property.StickyKey[Unit], span: Span)(implicit ctx: Context) = { - val monoTypeDef = untpd.TypeDef(tpnme.MonoType, untpd.TypeTree(monoType)) + val monoTypeDef = untpd.TypeDef(tpnme.MirroredMonoType, untpd.TypeTree(monoType)) val newImpl = untpd.Template( constr = untpd.emptyConstructor, parents = untpd.TypeTree(defn.ObjectType) :: Nil, @@ -876,14 +876,14 @@ trait Implicits { self: Typer => /** The mirror type * - * { MonoType = } + * { MirroredMonoType = } */ private def mirrorCore(parent: Type, monoType: Type, label: Name)(implicit ctx: Context) = parent - .refinedWith(tpnme.MonoType, TypeAlias(monoType)) - .refinedWith(tpnme.Label, TypeAlias(ConstantType(Constant(label.toString)))) + .refinedWith(tpnme.MirroredMonoType, TypeAlias(monoType)) + .refinedWith(tpnme.MirroredLabel, TypeAlias(ConstantType(Constant(label.toString)))) - /** An implied instance for a type of the form `Mirror.Product { type MonoType = T }` + /** An implied instance for a type of the form `Mirror.Product { type MirroredMonoType = T }` * where `T` is a generic product type or a case object or an enum case. */ lazy val synthesizedProductMirror: SpecialHandler = @@ -912,8 +912,8 @@ trait Implicits { self: Typer => val elemLabels = accessors.map(acc => ConstantType(Constant(acc.name.toString))) val mirrorType = mirrorCore(defn.Mirror_ProductType, monoType, cls.name) - .refinedWith(tpnme.ElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) - .refinedWith(tpnme.ElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels))) + .refinedWith(tpnme.MirroredElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) + .refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels))) val modul = cls.linkedClass.sourceModule assert(modul.is(Module)) val mirrorRef = @@ -923,18 +923,18 @@ trait Implicits { self: Typer => } else EmptyTree } - formal.member(tpnme.MonoType).info match { + formal.member(tpnme.MirroredMonoType).info match { case monoAlias @ TypeAlias(monoType) => mirrorFor(monoType) case _ => EmptyTree } } - /** An implied instance for a type of the form `Mirror.Sum { type MonoType = T }` + /** An implied instance for a type of the form `Mirror.Sum { type MirroredMonoType = T }` * where `T` is a generic sum type. */ lazy val synthesizedSumMirror: SpecialHandler = (formal: Type, span: Span) => implicit (ctx: Context) => - formal.member(tpnme.MonoType).info match { + formal.member(tpnme.MirroredMonoType).info match { case TypeAlias(monoType) if monoType.classSymbol.isGenericSum => val cls = monoType.classSymbol val elemTypes = cls.children.map { @@ -969,7 +969,7 @@ trait Implicits { self: Typer => } val mirrorType = mirrorCore(defn.Mirror_SumType, monoType, cls.name) - .refinedWith(tpnme.ElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) + .refinedWith(tpnme.MirroredElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) val modul = cls.linkedClass.sourceModule val mirrorRef = if (modul.exists && !cls.is(Scala2x)) ref(modul).withSpan(span) @@ -979,12 +979,12 @@ trait Implicits { self: Typer => EmptyTree } - /** An implied instance for a type of the form `Mirror { type MonoType = T }` + /** An implied instance for a type of the form `Mirror { type MirroredMonoType = T }` * where `T` is a generic sum or product or singleton type. */ lazy val synthesizedMirror: SpecialHandler = (formal: Type, span: Span) => implicit (ctx: Context) => - formal.member(tpnme.MonoType).info match { + formal.member(tpnme.MirroredMonoType).info match { case monoAlias @ TypeAlias(monoType) => if (monoType.termSymbol.is(CaseVal) || monoType.classSymbol.isGenericProduct) synthesizedProductMirror(formal, span)(ctx) diff --git a/library/src/scala/deriving.scala b/library/src/scala/deriving.scala index 6932cc498b5e..01c986e0eed6 100644 --- a/library/src/scala/deriving.scala +++ b/library/src/scala/deriving.scala @@ -7,10 +7,10 @@ object deriving { sealed trait Mirror { /** The mirrored *-type */ - type MonoType + type MirroredMonoType /** The name of the type */ - type Label <: String + type MirroredLabel <: String } object Mirror { @@ -19,43 +19,43 @@ object deriving { trait Sum extends Mirror { self => /** The types of the alternatives */ - type ElemTypes <: Tuple + type MirroredElemTypes <: Tuple /** The ordinal number of the case class of `x`. For enums, `ordinal(x) == x.ordinal` */ - def ordinal(x: MonoType): Int + def ordinal(x: MirroredMonoType): Int } /** The Mirror for a product type */ trait Product extends Mirror { /** The types of the product elements */ - type ElemTypes <: Tuple + type MirroredElemTypes <: Tuple /** The names of the product elements */ - type ElemLabels <: Tuple + type MirroredElemLabels <: Tuple /** Create a new instance of type `T` with elements taken from product `p`. */ - def fromProduct(p: scala.Product): MonoType + def fromProduct(p: scala.Product): MirroredMonoType } trait Singleton extends Product { - type MonoType = this.type - type ElemTypes = Unit - type ElemLabels = Unit + type MirroredMonoType = this.type + type MirroredElemTypes = Unit + type MirroredElemLabels = Unit def fromProduct(p: scala.Product) = this } /** A proxy for Scala 2 singletons, which do not inherit `Singleton` directly */ class SingletonProxy(val value: AnyRef) extends Product { - type MonoType = value.type - type ElemTypes = Unit - type ElemLabels = Unit + type MirroredMonoType = value.type + type MirroredElemTypes = Unit + type MirroredElemLabels = Unit def fromProduct(p: scala.Product) = value } - type Of[T] = Mirror { type MonoType = T } - type ProductOf[T] = Mirror.Product { type MonoType = T } - type SumOf[T] = Mirror.Sum { type MonoType = T } + type Of[T] = Mirror { type MirroredMonoType = T } + type ProductOf[T] = Mirror.Product { type MirroredMonoType = T } + type SumOf[T] = Mirror.Sum { type MirroredMonoType = T } } /** Helper class to turn arrays into products */ diff --git a/tests/run/deriving.scala b/tests/run/deriving.scala index 4bf150a71c6f..4a4ea12d1487 100644 --- a/tests/run/deriving.scala +++ b/tests/run/deriving.scala @@ -14,7 +14,7 @@ object Test extends App { println(the[Mirror.ProductOf[A]].fromProduct(A(1, 2))) assert(the[Mirror.SumOf[T]].ordinal(A(1, 2)) == 0) - assert(the[Mirror.Sum { type MonoType = T }].ordinal(B) == 1) + assert(the[Mirror.Sum { type MirroredMonoType = T }].ordinal(B) == 1) the[Mirror.Of[A]] match { case m: Mirror.Product => println(m.fromProduct(A(1, 2))) diff --git a/tests/run/ordinal-innerclass.scala b/tests/run/ordinal-innerclass.scala index c6c7b90f9f18..906d0b97c485 100644 --- a/tests/run/ordinal-innerclass.scala +++ b/tests/run/ordinal-innerclass.scala @@ -15,7 +15,7 @@ object Test extends App { val a = new A val b = new a.B val o = b.O - val sum: deriving.Mirror.Sum { type MonoType = o.T }= o.T.asInstanceOf + val sum: deriving.Mirror.Sum { type MirroredMonoType = o.T }= o.T.asInstanceOf assert(sum.ordinal(new o.C(1)) == 0) assert(sum.ordinal(new o.T.D()) == 1) diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala index 9b109c03f2b1..2bc137708847 100644 --- a/tests/run/typeclass-derivation3.scala +++ b/tests/run/typeclass-derivation3.scala @@ -46,14 +46,14 @@ object typeclasses { } inline def eqlProduct[T](m: Mirror.ProductOf[T])(x: Any, y: Any): Boolean = - eqlElems[m.ElemTypes](0)(x, y) + eqlElems[m.MirroredElemTypes](0)(x, y) inline def eqlCases[Alts](n: Int)(x: Any, y: Any, ord: Int): Boolean = inline erasedValue[Alts] match { case _: (alt *: alts1) => if (ord == n) implicit match { - case m: Mirror.ProductOf[`alt`] => eqlElems[m.ElemTypes](0)(x, y) + case m: Mirror.ProductOf[`alt`] => eqlElems[m.MirroredElemTypes](0)(x, y) } else eqlCases[alts1](n + 1)(x, y, ord) case _: Unit => @@ -65,9 +65,9 @@ object typeclasses { inline ev match { case m: Mirror.SumOf[T] => val ord = m.ordinal(x) - ord == m.ordinal(y) && eqlCases[m.ElemTypes](0)(x, y, ord) + ord == m.ordinal(y) && eqlCases[m.MirroredElemTypes](0)(x, y, ord) case m: Mirror.ProductOf[T] => - eqlElems[m.ElemTypes](0)(x, y) + eqlElems[m.MirroredElemTypes](0)(x, y) } } @@ -106,7 +106,7 @@ object typeclasses { case _: (alt *: alts1) => if (ord == n) implicit match { - case m: Mirror.ProductOf[`alt`] => pickleElems[m.ElemTypes](0)(buf, x) + case m: Mirror.ProductOf[`alt`] => pickleElems[m.MirroredElemTypes](0)(buf, x) } else pickleCases[alts1](n + 1)(buf, x, ord) case _: Unit => @@ -141,7 +141,7 @@ object typeclasses { if (ord == n) implicit match { case m: Mirror.ProductOf[`alt` & T] => - unpickleCase[`alt` & T, m.ElemTypes](buf, m) + unpickleCase[`alt` & T, m.MirroredElemTypes](buf, m) } else unpickleCases[T, alts1](n + 1)(buf, ord) case _: Unit => @@ -154,17 +154,17 @@ object typeclasses { case m: Mirror.SumOf[T] => val ord = m.ordinal(x) buf += ord - pickleCases[m.ElemTypes](0)(buf, x, ord) + pickleCases[m.MirroredElemTypes](0)(buf, x, ord) case m: Mirror.ProductOf[T] => - pickleElems[m.ElemTypes](0)(buf, x) + pickleElems[m.MirroredElemTypes](0)(buf, x) } def unpickle(buf: mutable.ListBuffer[Int]): T = inline ev match { case m: Mirror.SumOf[T] => val ord = nextInt(buf) - unpickleCases[T, m.ElemTypes](0)(buf, ord) + unpickleCases[T, m.MirroredElemTypes](0)(buf, ord) case m: Mirror.ProductOf[T] => - unpickleCase[T, m.ElemTypes](buf, m) + unpickleCase[T, m.MirroredElemTypes](buf, m) } } @@ -201,10 +201,10 @@ object typeclasses { } inline def showCase(x: Any, m: Mirror.ProductOf[_]): String = { - val label = constValue[m.Label] + val label = constValue[m.MirroredLabel] inline m match { case m: Mirror.Singleton => label - case _ => showElems[m.ElemTypes, m.ElemLabels](0)(x).mkString(s"$label(", ", ", ")") + case _ => showElems[m.MirroredElemTypes, m.MirroredElemLabels](0)(x).mkString(s"$label(", ", ", ")") } } @@ -226,7 +226,7 @@ object typeclasses { inline ev match { case m: Mirror.SumOf[T] => val ord = m.ordinal(x) - showCases[m.ElemTypes](0)(x, ord) + showCases[m.MirroredElemTypes](0)(x, ord) case m: Mirror.ProductOf[T] => showCase(x, m) } From 4784e2fc485910d0ed3fec564bc6c63f35b3c316 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 May 2019 11:27:40 +0200 Subject: [PATCH 25/35] Update typeclass-scaling data using old Generic scheme --- tests/pos-special/typeclass-scaling.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/pos-special/typeclass-scaling.scala b/tests/pos-special/typeclass-scaling.scala index 695eb631d350..bc0508d37537 100644 --- a/tests/pos-special/typeclass-scaling.scala +++ b/tests/pos-special/typeclass-scaling.scala @@ -7,7 +7,7 @@ import scala.annotation.tailrec // // produces an output file with `wc` measures (lines/words/chars): // -// 39161 97323 3124249 +// 83434 140554 6384738 // // The command // @@ -15,9 +15,9 @@ import scala.annotation.tailrec // // gives (best of three): // -// real 0m13.095s -// user 0m50.491s -// sys 0m0.970s +// real 0m16.061s +// user 1m3.608s +// sys 0m1.314s object datatypes { import typeclasses._ From a87b42b82c72c3d7f4b5f056fa64de37b486a50b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 May 2019 11:40:15 +0200 Subject: [PATCH 26/35] Measure typeclass scaling using new scheme Summary: - Code size increases by 13% (counting characters) to 15% (counting words) - Compile time increases by 3% (wallclock) to 4% (user) Runtime should be somewhat better for new scheme since there are fewer allocations. (Btw, the -Yshow-no-inlines option in the old measurements should be ignored; it is no linger valid and was not included in the tests) --- tests/pos-special/typeclass-scaling.scala | 136 ++++++++++++---------- 1 file changed, 73 insertions(+), 63 deletions(-) diff --git a/tests/pos-special/typeclass-scaling.scala b/tests/pos-special/typeclass-scaling.scala index bc0508d37537..56a6a3aa146f 100644 --- a/tests/pos-special/typeclass-scaling.scala +++ b/tests/pos-special/typeclass-scaling.scala @@ -3,11 +3,11 @@ import scala.annotation.tailrec // The following command: // -// sc typeclass-scaling.scala -Xmax-inlines 100 -Xprint:front -color:never -Yshow-no-inline -pagewidth 1000 >& x +// sc typeclass-scaling.scala -Xmax-inlines 100 -Xprint:front -color:never -pagewidth 1000 >& x // // produces an output file with `wc` measures (lines/words/chars): // -// 83434 140554 6384738 +// 89327 162884 7220258 // // The command // @@ -15,9 +15,9 @@ import scala.annotation.tailrec // // gives (best of three): // -// real 0m16.061s -// user 1m3.608s -// sys 0m1.314s +// real 0m16.593s +// user 1m6.337s +// sys 0m1.344s object datatypes { import typeclasses._ @@ -215,42 +215,45 @@ object typeclasses { object Eq { import scala.compiletime.erasedValue import compiletime._ - import reflect.{Mirror, Generic} + import scala.deriving._ - inline def tryEql[T](x: T, y: T) = implicit match { - case eq: Eq[T] => eq.eql(x, y) + inline def tryEql[TT](x: TT, y: TT): Boolean = implicit match { + case eq: Eq[TT] => eq.eql(x, y) } - inline def eqlElems[Elems <: Tuple](xs: Mirror, ys: Mirror, n: Int): Boolean = + inline def eqlElems[Elems <: Tuple](n: Int)(x: Any, y: Any): Boolean = inline erasedValue[Elems] match { case _: (elem *: elems1) => - tryEql[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) && - eqlElems[elems1](xs, ys, n + 1) + tryEql[elem](productElement[elem](x, n), productElement[elem](y, n)) && + eqlElems[elems1](n + 1)(x, y) case _: Unit => true } - inline def eqlCases[Alts <: Tuple](xm: Mirror, ym: Mirror, n: Int): Boolean = + inline def eqlProduct[T](m: Mirror.ProductOf[T])(x: Any, y: Any): Boolean = + eqlElems[m.MirroredElemTypes](0)(x, y) + + inline def eqlCases[Alts](n: Int)(x: Any, y: Any, ord: Int): Boolean = inline erasedValue[Alts] match { - case _: (Shape.Case[alt, elems] *: alts1) => - if (xm.ordinal == n) eqlElems[elems](xm, ym, 0) - else eqlCases[alts1](xm, ym, n + 1) - case _: Unit => + case _: (alt *: alts1) => + if (ord == n) + implicit match { + case m: Mirror.ProductOf[`alt`] => eqlElems[m.MirroredElemTypes](0)(x, y) + } + else eqlCases[alts1](n + 1)(x, y, ord) + case _: Unit => false } - inline def derived[T](implicit ev: Generic[T]): Eq[T] = new { - def eql(x: T, y: T): Boolean = { - val xm = ev.reflect(x) - val ym = ev.reflect(y) - inline erasedValue[ev.Shape] match { - case _: Shape.Cases[alts] => - xm.ordinal == ym.ordinal && - eqlCases[alts](xm, ym, 0) - case _: Shape.Case[_, elems] => - eqlElems[elems](xm, ym, 0) + inline def derived[T](implicit ev: Mirror.Of[T]): Eq[T] = new Eq[T] { + def eql(x: T, y: T): Boolean = + inline ev match { + case m: Mirror.SumOf[T] => + val ord = m.ordinal(x) + ord == m.ordinal(y) && eqlCases[m.MirroredElemTypes](0)(x, y, ord) + case m: Mirror.ProductOf[T] => + eqlElems[m.MirroredElemTypes](0)(x, y) } - } } implicit object IntEq extends Eq[Int] { @@ -267,7 +270,7 @@ object typeclasses { object Pickler { import scala.compiletime.{erasedValue, constValue} import compiletime._ - import reflect.{Mirror, Generic} + import deriving._ def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) @@ -275,19 +278,22 @@ object typeclasses { case pkl: Pickler[T] => pkl.pickle(buf, x) } - inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Mirror, n: Int): Unit = + inline def pickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Any): Unit = inline erasedValue[Elems] match { case _: (elem *: elems1) => - tryPickle[elem](buf, elems(n).asInstanceOf[elem]) - pickleElems[elems1](buf, elems, n + 1) + tryPickle[elem](buf, productElement[elem](x, n)) + pickleElems[elems1](n + 1)(buf, x) case _: Unit => } - inline def pickleCases[Alts <: Tuple](buf: mutable.ListBuffer[Int], xm: Mirror, n: Int): Unit = + inline def pickleCases[Alts <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], x: Any, ord: Int): Unit = inline erasedValue[Alts] match { - case _: (Shape.Case[alt, elems] *: alts1) => - if (xm.ordinal == n) pickleElems[elems](buf, xm, 0) - else pickleCases[alts1](buf, xm, n + 1) + case _: (alt *: alts1) => + if (ord == n) + implicit match { + case m: Mirror.ProductOf[`alt`] => pickleElems[m.MirroredElemTypes](0)(buf, x) + } + else pickleCases[alts1](n + 1)(buf, x, ord) case _: Unit => } @@ -295,51 +301,55 @@ object typeclasses { case pkl: Pickler[T] => pkl.unpickle(buf) } - inline def unpickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Array[AnyRef], n: Int): Unit = + inline def unpickleElems[Elems <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], elems: ArrayProduct): Unit = inline erasedValue[Elems] match { case _: (elem *: elems1) => elems(n) = tryUnpickle[elem](buf).asInstanceOf[AnyRef] - unpickleElems[elems1](buf, elems, n + 1) + unpickleElems[elems1](n + 1)(buf, elems) case _: Unit => } - inline def unpickleCase[T, Elems <: Tuple](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + inline def unpickleCase[T, Elems <: Tuple](buf: mutable.ListBuffer[Int], m: Mirror.ProductOf[T]): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) - gen.reify(gen.common.mirror(ordinal)) + m.fromProduct(EmptyProduct) else { - val elems = new Array[Object](size) - unpickleElems[Elems](buf, elems, 0) - gen.reify(gen.common.mirror(ordinal, elems)) + val elems = new ArrayProduct(size) + unpickleElems[Elems](0)(buf, elems) + m.fromProduct(elems) } } - inline def unpickleCases[T, Alts <: Tuple](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = + inline def unpickleCases[T, Alts <: Tuple](n: Int)(buf: mutable.ListBuffer[Int], ord: Int): T = inline erasedValue[Alts] match { - case _: (Shape.Case[_, elems] *: alts1) => - if (n == ordinal) unpickleCase[T, elems](gen, buf, ordinal) - else unpickleCases[T, alts1](gen, buf, ordinal, n + 1) - case _ => - throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal") + case _: (alt *: alts1) => + if (ord == n) + implicit match { + case m: Mirror.ProductOf[`alt` & T] => + unpickleCase[`alt` & T, m.MirroredElemTypes](buf, m) + } + else unpickleCases[T, alts1](n + 1)(buf, ord) + case _: Unit => + throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ord") } - inline def derived[T](implicit ev: Generic[T]): Pickler[T] = new { - def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = { - val xm = ev.reflect(x) - inline erasedValue[ev.Shape] match { - case _: Shape.Cases[alts] => - buf += xm.ordinal - pickleCases[alts](buf, xm, 0) - case _: Shape.Case[_, elems] => - pickleElems[elems](buf, xm, 0) + inline def derived[T](implicit ev: Mirror.Of[T]): Pickler[T] = new { + def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = + inline ev match { + case m: Mirror.SumOf[T] => + val ord = m.ordinal(x) + buf += ord + pickleCases[m.MirroredElemTypes](0)(buf, x, ord) + case m: Mirror.ProductOf[T] => + pickleElems[m.MirroredElemTypes](0)(buf, x) } - } def unpickle(buf: mutable.ListBuffer[Int]): T = - inline erasedValue[ev.Shape] match { - case _: Shape.Cases[alts] => - unpickleCases[T, alts](ev, buf, nextInt(buf), 0) - case _: Shape.Case[_, elems] => - unpickleCase[T, elems](ev, buf, 0) + inline ev match { + case m: Mirror.SumOf[T] => + val ord = nextInt(buf) + unpickleCases[T, m.MirroredElemTypes](0)(buf, ord) + case m: Mirror.ProductOf[T] => + unpickleCase[T, m.MirroredElemTypes](buf, m) } } From 56c0f96043cea3c805bab8f6c557838d90ca8557 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 May 2019 11:45:09 +0200 Subject: [PATCH 27/35] Drop old deriving infrastructure Drop old deriving infrastructure that was based on reflect.Generic --- .../dotty/tools/dotc/core/Definitions.scala | 11 - .../src/dotty/tools/dotc/typer/Deriving.scala | 434 +++--------------- .../dotty/tools/dotc/typer/Implicits.scala | 16 - library/src-3.x/scala/reflect/Generic.scala | 18 - .../src-3.x/scala/reflect/GenericClass.scala | 76 --- library/src-3.x/scala/reflect/Mirror.scala | 18 - tests/run/derive-generic.scala | 13 - 7 files changed, 58 insertions(+), 528 deletions(-) delete mode 100644 library/src-3.x/scala/reflect/Generic.scala delete mode 100644 library/src-3.x/scala/reflect/GenericClass.scala delete mode 100644 library/src-3.x/scala/reflect/Mirror.scala delete mode 100644 tests/run/derive-generic.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 3437992f3d39..2c9703315ae6 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -708,17 +708,6 @@ class Definitions { lazy val Mirror_SingletonProxyType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.SingletonProxy") def Mirror_SingletonProxyClass(implicit ctx: Context): ClassSymbol = Mirror_SingletonProxyType.symbol.asClass - lazy val GenericType: TypeRef = ctx.requiredClassRef("scala.reflect.Generic") - def GenericClass(implicit ctx: Context): ClassSymbol = GenericType.symbol.asClass - lazy val ShapeType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape") - def ShapeClass(implicit ctx: Context): ClassSymbol = ShapeType.symbol.asClass - lazy val ShapeCaseType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape.Case") - def ShapeCaseClass(implicit ctx: Context): ClassSymbol = ShapeCaseType.symbol.asClass - lazy val ShapeCasesType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape.Cases") - def ShapeCasesClass(implicit ctx: Context): ClassSymbol = ShapeCasesType.symbol.asClass - lazy val ReflectMirrorType: TypeRef = ctx.requiredClassRef("scala.reflect.Mirror") - lazy val GenericClassType: TypeRef = ctx.requiredClassRef("scala.reflect.GenericClass") - lazy val LanguageModuleRef: TermSymbol = ctx.requiredModule("scala.language") def LanguageModuleClass(implicit ctx: Context): ClassSymbol = LanguageModuleRef.moduleClass.asClass lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl") diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index 94a085e72e83..ac66634cebe1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -29,104 +29,9 @@ trait Deriving { this: Typer => */ class Deriver(cls: ClassSymbol, codePos: SourcePosition)(implicit ctx: Context) { - /** A buffer for synthesized symbols */ + /** A buffer for synthesized symbols for type class instances */ private var synthetics = new mutable.ListBuffer[Symbol] - private var derivesGeneric = false - - /** the children of `cls` ordered by textual occurrence */ - lazy val children: List[Symbol] = cls.children - - private def shapeError(explanation: => String): Unit = - ctx.error(i"cannot take shape of $cls\n$explanation", codePos) - - /** The shape (of type Shape.Case) of a case given by `sym`. `sym` is either `cls` - * itself, or a subclass of `cls`, or an instance of `cls`. - */ - private def caseShape(sym: Symbol): Type = { - val (constr, elems) = - sym match { - case caseClass: ClassSymbol => - if (!caseClass.is(Case)) { - shapeError( - if (caseClass == cls) "it has anonymous or inaccessible subclasses" - else i"its subclass $caseClass is not a case class") - return NoType - } - else if (caseClass.is(Module)) - (caseClass.sourceModule.termRef, Nil) - else caseClass.primaryConstructor.info match { - case info: PolyType => - def instantiate(implicit ctx: Context) = { - val poly = constrained(info, untpd.EmptyTree)._1 - val mono @ MethodType(_) = poly.resultType - val resType = mono.finalResultType - resType <:< cls.appliedRef - val tparams = poly.paramRefs - val variances = caseClass.typeParams.map(_.paramVariance) - val instanceTypes = (tparams, variances).zipped.map((tparam, variance) => - ctx.typeComparer.instanceType(tparam, fromBelow = variance < 0)) - (resType.substParams(poly, instanceTypes), - mono.paramInfos.map(_.substParams(poly, instanceTypes))) - } - instantiate(ctx.fresh.setExploreTyperState().setOwner(caseClass)) - case info: MethodType => - (caseClass.typeRef, info.paramInfos) - case _ => - (caseClass.typeRef, Nil) - } - case _ => - (sym.termRef, Nil) - } - val elemShape = TypeOps.nestedPairs(elems) - defn.ShapeCaseType.appliedTo(constr, elemShape) - } - - /** The shape of `cls` if `cls` is sealed */ - private def sealedShape: Type = { - val cases = children.map(caseShape).filter(_.exists) - val casesShape = TypeOps.nestedPairs(cases) - defn.ShapeCasesType.appliedTo(casesShape) - } - - /** The shape of `cls`, referring directly to the type parameters of `cls` instead - * of abstracting over them. - * Returns NoType if `cls` is neither sealed nor a case class or object. - */ - lazy val shapeWithClassParams: Type = { - if (cls.is(Case)) caseShape(cls) - else if (cls.is(Sealed)) sealedShape - else { - shapeError(i"it is neither sealed nor a case class") - defn.ShapeCasesType.appliedTo(defn.UnitType) - } - }.reporting(res => i"shape of $cls = $res", derive) - - private def shapeOfType(tp: Type) = { - val shape0 = shapeWithClassParams - val clsType = tp.baseType(cls) - if (clsType.exists) shape0.subst(cls.typeParams, clsType.argInfos) - else clsType - } - - private def add(sym: Symbol): sym.type = { - ctx.enter(sym) - synthetics += sym - sym - } - - /** Create a synthetic symbol owned by current owner */ - private def newSymbol(name: Name, info: Type, - span: Span = ctx.owner.span, - flags: FlagSet = EmptyFlags)(implicit ctx: Context): Symbol = - ctx.newSymbol(ctx.owner, name, flags | Synthetic, info, coord = span) - - /** Create a synthetic method owned by current owner */ - private def newMethod(name: TermName, info: Type, - span: Span = ctx.owner.span, - flags: FlagSet = EmptyFlags)(implicit ctx: Context): TermSymbol = - newSymbol(name, info, span, flags | Method).asTerm - /** A version of Type#underlyingClassRef that works also for higher-kinded types */ private def underlyingClassRef(tp: Type): Type = tp match { case tp: TypeRef if tp.symbol.isClass => tp @@ -140,12 +45,14 @@ trait Deriving { this: Typer => * an instance with the same name does not exist already. * @param reportErrors Report an error if an instance with the same name exists already */ - private def addDerivedInstance(clsName: Name, info: Type, pos: SourcePosition, reportErrors: Boolean) = { + private def addDerivedInstance(clsName: Name, info: Type, pos: SourcePosition): Unit = { val instanceName = s"derived$$$clsName".toTermName - if (ctx.denotNamed(instanceName).exists) { - if (reportErrors) ctx.error(i"duplicate typeclass derivation for $clsName", pos) - } - else add(newMethod(instanceName, info, pos.span, Implied)) + if (ctx.denotNamed(instanceName).exists) + ctx.error(i"duplicate typeclass derivation for $clsName", pos) + else + synthetics += + ctx.newSymbol(ctx.owner, instanceName, Implied | Synthetic | Method, info, coord = pos.span) + .entered } /** Check derived type tree `derived` for the following well-formedness conditions: @@ -177,269 +84,63 @@ trait Deriving { this: Typer => val derivedType = checkClassType(underlyingType, derived.sourcePos, traitReq = false, stablePrefixReq = true) val typeClass = derivedType.classSymbol val nparams = typeClass.typeParams.length - if (derivedType.isRef(defn.GenericClass)) - derivesGeneric = true - else { - // A matrix of all parameter combinations of current class parameters - // and derived typeclass parameters. - // Rows: parameters of current class - // Columns: parameters of typeclass - - // Running example: typeclass: class TC[X, Y, Z], deriving class: class A[T, U] - // clsParamss = - // T_X T_Y T_Z - // U_X U_Y U_Z - val clsParamss: List[List[TypeSymbol]] = cls.typeParams.map { tparam => - if (nparams == 0) Nil - else if (nparams == 1) tparam :: Nil - else typeClass.typeParams.map(tcparam => - tparam.copy(name = s"${tparam.name}_$$_${tcparam.name}".toTypeName) - .asInstanceOf[TypeSymbol]) - } - val firstKindedParamss = clsParamss.filter { - case param :: _ => !param.info.isLambdaSub - case nil => false - } - - // The types of the required evidence parameters. In the running example: - // TC[T_X, T_Y, T_Z], TC[U_X, U_Y, U_Z] - val evidenceParamInfos = - for (row <- firstKindedParamss) - yield derivedType.appliedTo(row.map(_.typeRef)) - - // The class instances in the result type. Running example: - // A[T_X, U_X], A[T_Y, U_Y], A[T_Z, U_Z] - val resultInstances = - for (n <- List.range(0, nparams)) - yield cls.typeRef.appliedTo(clsParamss.map(row => row(n).typeRef)) - - // TC[A[T_X, U_X], A[T_Y, U_Y], A[T_Z, U_Z]] - val resultType = derivedType.appliedTo(resultInstances) - val clsParams: List[TypeSymbol] = clsParamss.flatten - val instanceInfo = - if (clsParams.isEmpty) ExprType(resultType) - else PolyType.fromParams(clsParams, ImplicitMethodType(evidenceParamInfos, resultType)) - addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos, reportErrors = true) + // A matrix of all parameter combinations of current class parameters + // and derived typeclass parameters. + // Rows: parameters of current class + // Columns: parameters of typeclass + + // Running example: typeclass: class TC[X, Y, Z], deriving class: class A[T, U] + // clsParamss = + // T_X T_Y T_Z + // U_X U_Y U_Z + val clsParamss: List[List[TypeSymbol]] = cls.typeParams.map { tparam => + if (nparams == 0) Nil + else if (nparams == 1) tparam :: Nil + else typeClass.typeParams.map(tcparam => + tparam.copy(name = s"${tparam.name}_$$_${tcparam.name}".toTypeName) + .asInstanceOf[TypeSymbol]) } - } - - /** Add value corresponding to `val genericClass = new GenericClass(...)` - * to `synthetics`, unless a definition of `genericClass` exists already. - */ - private def addGenericClass(): Unit = - if (!ctx.denotNamed(nme.genericClass).exists) { - add(newSymbol(nme.genericClass, defn.GenericClassType, codePos.span)) + val firstKindedParamss = clsParamss.filter { + case param :: _ => !param.info.isLambdaSub + case nil => false } - private def addGeneric(): Unit = { - val genericCompleter = new LazyType { - def complete(denot: SymDenotation)(implicit ctx: Context) = { - val resultType = - RefinedType( - defn.GenericType.appliedTo(cls.appliedRef), - tpnme.Shape, - TypeAlias(shapeWithClassParams)) - denot.info = PolyType.fromParams(cls.typeParams, resultType).ensureMethodic - } - } - addDerivedInstance(defn.GenericType.name, genericCompleter, codePos, reportErrors = false) - } + // The types of the required evidence parameters. In the running example: + // TC[T_X, T_Y, T_Z], TC[U_X, U_Y, U_Z] + val evidenceParamInfos = + for (row <- firstKindedParamss) + yield derivedType.appliedTo(row.map(_.typeRef)) - /** If any of the instances has a companion with a `derived` member - * that refers to `scala.reflect.Generic`, add an implied instance - * of `Generic`. Note: this is just an optimization to avoid possible - * code duplication. Generic instances are created on the fly if they - * are missing from the companion. - */ - private def maybeAddGeneric(): Unit = { - val genericCls = defn.GenericClass - def refersToGeneric(sym: Symbol): Boolean = { - val companion = sym.info.finalResultType.classSymbol.companionModule - val derivd = companion.info.member(nme.derived) - derivd.hasAltWith(sd => sd.info.existsPart(p => p.typeSymbol == genericCls)) - } - if (derivesGeneric || synthetics.exists(refersToGeneric)) { - derive.println(i"add generic infrastructure for $cls") - addGeneric() - addGenericClass() - } + // The class instances in the result type. Running example: + // A[T_X, U_X], A[T_Y, U_Y], A[T_Z, U_Z] + val resultInstances = + for (n <- List.range(0, nparams)) + yield cls.typeRef.appliedTo(clsParamss.map(row => row(n).typeRef)) + + // TC[A[T_X, U_X], A[T_Y, U_Y], A[T_Z, U_Z]] + val resultType = derivedType.appliedTo(resultInstances) + + val clsParams: List[TypeSymbol] = clsParamss.flatten + val instanceInfo = + if (clsParams.isEmpty) ExprType(resultType) + else PolyType.fromParams(clsParams, ImplicitMethodType(evidenceParamInfos, resultType)) + addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos) } /** Create symbols for derived instances and infrastructure, * append them to `synthetics` buffer, and enter them into class scope. * Also, add generic instances if needed. */ - def enterDerived(derived: List[untpd.Tree]) = { + def enterDerived(derived: List[untpd.Tree]) = derived.foreach(processDerivedInstance(_)) - maybeAddGeneric() - } - - private def tupleElems(tp: Type): List[Type] = tp match { - case AppliedType(fn, hd :: tl :: Nil) if fn.classSymbol == defn.PairClass => - hd :: tupleElems(tl) - case _ => - Nil - } - - /** Extractor for the `cases` in a `Shaped.Cases(cases)` shape */ - private object ShapeCases { - def unapply(shape: Type): Option[List[Type]] = shape match { - case AppliedType(fn, cases :: Nil) if fn.classSymbol == defn.ShapeCasesClass => - Some(tupleElems(cases)) - case _ => - None - } - } - /** Extractor for the `pattern` and `elements` in a `Shaped.Case(pattern, elements)` shape */ - private object ShapeCase { - def unapply(shape: Type): Option[(Type, List[Type])] = shape match { - case AppliedType(fn, pat :: elems :: Nil) if fn.classSymbol == defn.ShapeCaseClass => - Some((pat, tupleElems(elems))) - case _ => - None - } - } - - /** A helper class to create definition trees for `synthetics` */ - class Finalizer { + /** The synthesized type class instance definitions */ + def syntheticDefs: List[tpd.Tree] = { import tpd._ - /** The previously synthetsized `genericClass` symbol */ - private def genericClass = - synthetics.find(sym => !sym.is(Method) && sym.name == nme.genericClass).get.asTerm - - /** The string to pass to `GenericClass` for initializing case and element labels. - * See documentation of `GenericClass.label` for what needs to be passed. - */ - private def labelString(sh: Type): String = sh match { - case ShapeCases(cases) => - cases.map(labelString).mkString("\u0001") - case ShapeCase(pat: TermRef, _) => - pat.symbol.name.toString - case ShapeCase(pat, elems) => - val patCls = pat.widen.classSymbol - val patLabel = patCls.name.stripModuleClassSuffix.toString - val elemLabels = patCls.caseAccessors.filterNot(_.is(PrivateLocal)).map(_.name.toString) - (patLabel :: elemLabels).mkString("\u0000") - } - - /** The RHS of the `genericClass` value definition */ - def genericClassRHS = - New(defn.GenericClassType, - List(Literal(Constant(cls.typeRef)), - Literal(Constant(labelString(shapeWithClassParams))))) - - /** The RHS of the `derived$Generic` typeclass instance. - * Example: For the class definition - * - * enum Lst[+T] derives ... { case Cons(hd: T, tl: Lst[T]); case Nil } - * - * the following typeclass instance is generated, where - * = Cases[(Case[Cons[T], (T, Lst[T])], Case[Nil.type, Unit])]: - * - * implicit def derived$Generic[T]: Generic[Lst[T]] { type Shape = } = - * new Generic[Lst[T]] { - * type Shape = - * def reflect(x$0: Lst[T]): Mirror = x$0 match { - * case x$0: Cons[T] => genericClass.mirror(0, x$0) - * case x$0: Nil.type => genericClass.mirror(1) - * } - * def reify(c: Mirror): Lst[T] = c.ordinal match { - * case 0 => Cons[T](c(0).asInstanceOf[T], c(1).asInstanceOf[Lst[T]]) - * case 1 => Nil - * } - * def common = genericClass - * } - */ - def genericRHS(genericType: Type, genericClassRef: Tree)(implicit ctx: Context) = { - val RefinedType( - genericInstance @ AppliedType(_, clsArg :: Nil), - tpnme.Shape, - TypeAlias(shapeArg)) = genericType - val shape = shapeArg.dealias - - val implClassSym = ctx.newNormalizedClassSymbol( - ctx.owner, tpnme.ANON_CLASS, EmptyFlags, genericInstance :: Nil, coord = codePos.span) - val implClassCtx = ctx.withOwner(implClassSym) - val implClassConstr = - newMethod(nme.CONSTRUCTOR, MethodType(Nil, implClassSym.typeRef))(implClassCtx).entered - - def implClassStats(implicit ctx: Context): List[Tree] = { - val shapeType: TypeDef = { - val shapeAlias = newSymbol(tpnme.Shape, TypeAlias(shape)).entered.asType - TypeDef(shapeAlias) - } - val reflectMethod: DefDef = { - val meth = newMethod(nme.reflect, MethodType(clsArg :: Nil, defn.ReflectMirrorType)).entered - def rhs(paramRef: Tree)(implicit ctx: Context): Tree = { - def reflectCase(scrut: Tree, idx: Int, elems: List[Type]): Tree = { - val ordinal = Literal(Constant(idx)) - val args = if (elems.isEmpty) List(ordinal) else List(ordinal, scrut) - val mirror = defn.GenericClassType - .member(nme.mirror) - .suchThat(sym => args.tpes.corresponds(sym.info.firstParamTypes)(_ <:< _)) - genericClassRef.select(mirror.symbol).appliedToArgs(args) - } - shape match { - case ShapeCases(cases) => - val clauses = cases.zipWithIndex.map { - case (ShapeCase(pat, elems), idx) => - val patVar = newSymbol(nme.syntheticParamName(0), pat, meth.span) - CaseDef( - Bind(patVar, Typed(untpd.Ident(nme.WILDCARD).withType(pat), TypeTree(pat))), - EmptyTree, - reflectCase(ref(patVar), idx, elems)) - } - Match(paramRef, clauses) - case ShapeCase(pat, elems) => - reflectCase(paramRef, 0, elems) - } - } - tpd.DefDef(meth, paramss => rhs(paramss.head.head)(ctx.fresh.setOwner(meth).setNewScope)) - } - - val reifyMethod: DefDef = { - val meth = newMethod(nme.reify, MethodType(defn.ReflectMirrorType :: Nil, clsArg)).entered - def rhs(paramRef: Tree)(implicit ctx: Context): Tree = { - def reifyCase(caseType: Type, elems: List[Type]): Tree = caseType match { - case caseType: TermRef => - ref(caseType) - case caseType => - val args = - for ((elemTp, idx) <- elems.zipWithIndex) - yield paramRef.select(nme.apply).appliedTo(Literal(Constant(idx))).cast(elemTp) - New(caseType, args) - } - shape match { - case ShapeCases(cases) => - val clauses = - for ((ShapeCase(pat, elems), idx) <- cases.zipWithIndex) - yield CaseDef(Literal(Constant(idx)), EmptyTree, reifyCase(pat, elems)) - Match(paramRef.select(nme.ordinal), clauses) - case ShapeCase(pat, elems) => - reifyCase(pat, elems) - } - } - - tpd.DefDef(meth, paramss => rhs(paramss.head.head)(ctx.withOwner(meth))) - } - - val commonMethod: DefDef = { - val meth = newMethod(nme.common, ExprType(defn.GenericClassType)).entered - tpd.DefDef(meth, genericClassRef) - } - - List(shapeType, reflectMethod, reifyMethod, commonMethod) - } - - val implClassDef = ClassDef(implClassSym, DefDef(implClassConstr), implClassStats(implClassCtx)) - Block(implClassDef :: Nil, New(implClassSym.typeRef, Nil)) - } - /** The type class instance definition with symbol `sym` */ - private def typeclassInstance(sym: Symbol)(implicit ctx: Context): List[Type] => (List[List[tpd.Tree]] => tpd.Tree) = + def typeclassInstance(sym: Symbol)(implicit ctx: Context): List[Type] => (List[List[tpd.Tree]] => tpd.Tree) = (tparamRefs: List[Type]) => (paramRefss: List[List[tpd.Tree]]) => { val tparams = tparamRefs.map(_.typeSymbol.asType) val params = if (paramRefss.isEmpty) Nil else paramRefss.head.map(_.symbol.asTerm) @@ -450,46 +151,27 @@ trait Deriving { this: Typer => case info: MethodType => info.instantiate(params.map(_.termRef)) case info => info.widenExpr } - def classAndCompanionRef(tp: Type): (ClassSymbol, TermRef) = tp match { + def companionRef(tp: Type): TermRef = tp match { case tp @ TypeRef(prefix, _) if tp.symbol.isClass => - (tp.symbol.asClass, prefix.select(tp.symbol.companionModule).asInstanceOf[TermRef]) + prefix.select(tp.symbol.companionModule).asInstanceOf[TermRef] case tp: TypeProxy => - classAndCompanionRef(tp.underlying) + companionRef(tp.underlying) } val resultType = instantiated(sym.info) - val (typeCls, companionRef) = classAndCompanionRef(resultType) - if (typeCls == defn.GenericClass) - genericRHS(resultType, ref(genericClass)) - else { - val module = untpd.ref(companionRef).withSpan(sym.span) - val rhs = untpd.Select(module, nme.derived) - typed(rhs, resultType) - } + val module = untpd.ref(companionRef(resultType)).withSpan(sym.span) + val rhs = untpd.Select(module, nme.derived) + typed(rhs, resultType) } def syntheticDef(sym: Symbol): Tree = - if (sym.isType) - tpd.TypeDef(sym.asType) - else if (sym.is(Method)) - tpd.polyDefDef(sym.asTerm, typeclassInstance(sym)(ctx.fresh.setOwner(sym).setNewScope)) - else - tpd.ValDef(sym.asTerm, genericClassRHS) + tpd.polyDefDef(sym.asTerm, typeclassInstance(sym)(ctx.fresh.setOwner(sym).setNewScope)) - def syntheticDefs: List[Tree] = synthetics.map(syntheticDef).toList + synthetics.map(syntheticDef).toList } def finalize(stat: tpd.TypeDef): tpd.Tree = { val templ @ Template(_, _, _, _) = stat.rhs - tpd.cpy.TypeDef(stat)( - rhs = tpd.cpy.Template(templ)(body = templ.body ++ new Finalizer().syntheticDefs)) - } - - /** Synthesized instance for `Generic[]` */ - def genericInstance(clsType: Type): tpd.Tree = { - val shape = shapeOfType(clsType) - val genericType = RefinedType(defn.GenericType.appliedTo(clsType), tpnme.Shape, TypeAlias(shape)) - val finalizer = new Finalizer - finalizer.genericRHS(genericType, finalizer.genericClassRHS) + tpd.cpy.TypeDef(stat)(rhs = tpd.cpy.Template(templ)(body = templ.body ++ syntheticDefs)) } } } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 0381475d6c40..b62523ab9521 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -844,21 +844,6 @@ trait Implicits { self: Typer => } } - /** If `formal` is of the form `scala.reflect.Generic[T]` for some class type `T`, - * synthesize an instance for it. - */ - lazy val synthesizedGeneric: SpecialHandler = - (formal: Type, span: Span) => implicit (ctx: Context) => - formal.argTypes match { - case arg :: Nil => - val pos = ctx.source.atSpan(span) - val arg1 = fullyDefinedType(arg, "Generic argument", span) - val clsType = checkClassType(arg1, pos, traitReq = false, stablePrefixReq = true) - new Deriver(clsType.classSymbol.asClass, pos).genericInstance(clsType) - case _ => - EmptyTree - } - /** Create an anonymous class `new Object { type MirroredMonoType = ... }` * and mark it with given attachment so that it is made into a mirror at PostTyper. */ @@ -999,7 +984,6 @@ trait Implicits { self: Typer => mySpecialHandlers = List( defn.ClassTagClass -> synthesizedClassTag, defn.QuotedTypeClass -> synthesizedTypeTag, - defn.GenericClass -> synthesizedGeneric, defn.TastyReflectionClass -> synthesizedTastyContext, defn.EqlClass -> synthesizedEq, defn.TupledFunctionClass -> synthesizedTupleFunction, diff --git a/library/src-3.x/scala/reflect/Generic.scala b/library/src-3.x/scala/reflect/Generic.scala deleted file mode 100644 index 6b0ed8967774..000000000000 --- a/library/src-3.x/scala/reflect/Generic.scala +++ /dev/null @@ -1,18 +0,0 @@ -package scala.reflect - -/** A class for mapping between an ADT value and - * the case mirror that represents the value. - */ -abstract class Generic[T] { - - type Shape <: scala.compiletime.Shape - - /** The case mirror corresponding to ADT instance `x` */ - def reflect(x: T): Mirror - - /** The ADT instance corresponding to given `mirror` */ - def reify(mirror: Mirror): T - - /** The companion object of the ADT */ - def common: GenericClass -} diff --git a/library/src-3.x/scala/reflect/GenericClass.scala b/library/src-3.x/scala/reflect/GenericClass.scala deleted file mode 100644 index 2738f3c5c9c9..000000000000 --- a/library/src-3.x/scala/reflect/GenericClass.scala +++ /dev/null @@ -1,76 +0,0 @@ -package scala.reflect -import annotation.tailrec -import collection.mutable.ArrayBuffer - -/** The part of `Generic` instances that is common for all instances of a class. - * @param runtimeClass The runtime class instance - * @param labelsStr A string encoding all case and element labels according to the - * following grammar: - * - * labelString ::= caseString { caseSeparator caseString } - * caseString ::= elemString { elemSeparator elemString } - * caseSeparator ::= '\u0001' - * elemSeparator ::= '\u0000' - * elemString: "any sequence of characters not containing '\u0000` or `\u0001`" - */ -class GenericClass(val runtimeClass: Class[_], labelsStr: String) { - import GenericClass._ - - /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ - def mirror(ordinal: Int, product: Product): Mirror = - new Mirror(this, ordinal, product) - - /** A mirror of a case with no elements */ - def mirror(ordinal: Int): Mirror = - mirror(ordinal, EmptyProduct) - - /** A mirror with elements given as an array */ - def mirror(ordinal: Int, elems: Array[AnyRef]): Mirror = - mirror(ordinal, new ArrayProduct(elems)) - - /** A mirror with an initial empty array of `numElems` elements, to be filled in. */ - def mirror(ordinal: Int, numElems: Int): Mirror = - mirror(ordinal, new Array[AnyRef](numElems)) - - /** Case and element labels as a two-dimensional array. - * Each row of the array contains a case label, followed by the labels of the elements of that case. - */ - val label: Array[Array[String]] = - initLabels(0, 0, new ArrayBuffer[String], new ArrayBuffer[Array[String]]) - - private def initLabels(start: Int, cur: Int, - elems: ArrayBuffer[String], - cases: ArrayBuffer[Array[String]]): Array[Array[String]] = { - def addElem = elems += labelsStr.substring(start, cur) - def addCase = cases += addElem.toArray - if (cur == labelsStr.length) - addCase.toArray - else if (labelsStr(cur) == caseSeparator) - initLabels(cur + 1, cur + 1, new ArrayBuffer, addCase) - else if (labelsStr(cur) == elemSeparator) - initLabels(cur + 1, cur + 1, addElem, cases) - else - initLabels(start, cur + 1, elems, cases) - } -} - -object GenericClass { - private final val elemSeparator = '\u0000' - private final val caseSeparator = '\u0001' - - /** Helper class to turn arrays into products */ - private class ArrayProduct(val elems: Array[AnyRef]) extends Product { - def canEqual(that: Any): Boolean = true - def productElement(n: Int) = elems(n) - def productArity = elems.length - override def productIterator: Iterator[Any] = elems.iterator - def update(n: Int, x: Any) = elems(n) = x.asInstanceOf[AnyRef] - } - - /** Helper object */ - private object EmptyProduct extends Product { - def canEqual(that: Any): Boolean = true - def productElement(n: Int) = throw new IndexOutOfBoundsException - def productArity = 0 - } -} \ No newline at end of file diff --git a/library/src-3.x/scala/reflect/Mirror.scala b/library/src-3.x/scala/reflect/Mirror.scala deleted file mode 100644 index 0b2aed2c9645..000000000000 --- a/library/src-3.x/scala/reflect/Mirror.scala +++ /dev/null @@ -1,18 +0,0 @@ -package scala.reflect - -/** A generic representation of a case in an ADT - * @param reflected The common class-specific part of this mirror - * @param ordinal The ordinal value of the case in the list of the ADT's cases - * @param elems The elements of the case - */ -class Mirror(val adtClass: GenericClass, val ordinal: Int, val elems: Product) { - - /** The `n`'th element of this generic case */ - def apply(n: Int): Any = elems.productElement(n) - - /** The name of the constructor of the case reflected by this mirror */ - def caseLabel: String = adtClass.label(ordinal)(0) - - /** The label of the `n`'th element of the case reflected by this mirror */ - def elementLabel(n: Int): String = adtClass.label(ordinal)(n + 1) -} diff --git a/tests/run/derive-generic.scala b/tests/run/derive-generic.scala deleted file mode 100644 index ee95a622c36b..000000000000 --- a/tests/run/derive-generic.scala +++ /dev/null @@ -1,13 +0,0 @@ -import reflect.Generic - -object Test extends App { - sealed trait A derives Generic - - object A { - case class B() extends A - case class C(x: Int, y: Int) extends A - } - - println(implicitly[Generic[A]].common.label.deep) -} - From 01d3ff740d60c57f2dc0829352d7945dd14fd795 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 23 May 2019 16:36:17 +0200 Subject: [PATCH 28/35] Make scheme work also for nested classes and companion objects --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 20 +++++++++++++ .../tools/dotc/transform/TypeUtils.scala | 10 +++++++ .../dotty/tools/dotc/typer/Implicits.scala | 28 +++++++++++-------- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index f6ec2704b967..9db322bb5dc4 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -400,6 +400,26 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case ConstantType(value) => Literal(value) } + /** A path that corresponds to the given type `tp`. Error if `tp` is not a refinement + * of an addressable singleton type. + */ + def pathFor(tp: Type)(implicit ctx: Context): Tree = { + def recur(tp: Type): Tree = tp match { + case tp: NamedType => + tp.info match { + case TypeAlias(alias) => recur(alias) + case _: TypeBounds => EmptyTree + case _ => singleton(tp) + } + case tp: TypeProxy => recur(tp.superType) + case _ => EmptyTree + } + recur(tp).orElse { + ctx.error(em"$tp is not an addressable singleton type") + TypeTree(tp) + } + } + /** A tree representing a `newXYZArray` operation of the right * kind for the given element type in `elemTpe`. No type arguments or * `length` arguments are given. diff --git a/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala b/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala index 65649899b29b..1453caca4010 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala @@ -66,5 +66,15 @@ object TypeUtils { } def refinedWith(name: Name, info: Type)(implicit ctx: Context) = RefinedType(self, name, info) + + /** The TermRef referring to the companion of the underlying class reference + * of this type, while keeping the same prefix. + */ + def companionRef(implicit ctx: Context): TermRef = self match { + case self @ TypeRef(prefix, _) if self.symbol.isClass => + prefix.select(self.symbol.companionModule).asInstanceOf[TermRef] + case self: TypeProxy => + self.underlying.companionRef + } } } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index b62523ab9521..8eb28b47cc9d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -868,6 +868,13 @@ trait Implicits { self: Typer => .refinedWith(tpnme.MirroredMonoType, TypeAlias(monoType)) .refinedWith(tpnme.MirroredLabel, TypeAlias(ConstantType(Constant(label.toString)))) + /** A path referencing the companion of class type `clsType` */ + private def companionPath(clsType: Type, span: Span)(implicit ctx: Context) = { + val ref = pathFor(clsType.companionRef) + assert(ref.symbol.is(Module) && ref.symbol.companionClass == clsType.classSymbol) + ref.withSpan(span) + } + /** An implied instance for a type of the form `Mirror.Product { type MirroredMonoType = T }` * where `T` is a generic product type or a case object or an enum case. */ @@ -878,16 +885,16 @@ trait Implicits { self: Typer => mirrorFor(tp1).orElse(mirrorFor(tp2)) case _ => if (monoType.termSymbol.is(CaseVal)) { - val modul = monoType.termSymbol - if (modul.info.classSymbol.is(Scala2x)) { - val mirrorType = mirrorCore(defn.Mirror_SingletonProxyType, monoType, modul.name) - val mirrorRef = New(defn.Mirror_SingletonProxyType, ref(modul).withSpan(span) :: Nil) + val module = monoType.termSymbol + val modulePath = pathFor(monoType).withSpan(span) + if (module.info.classSymbol.is(Scala2x)) { + val mirrorType = mirrorCore(defn.Mirror_SingletonProxyType, monoType, module.name) + val mirrorRef = New(defn.Mirror_SingletonProxyType, modulePath :: Nil) mirrorRef.cast(mirrorType) } else { - val mirrorType = mirrorCore(defn.Mirror_SingletonType, monoType, modul.name) - val mirrorRef = ref(modul).withSpan(span) - mirrorRef.cast(mirrorType) + val mirrorType = mirrorCore(defn.Mirror_SingletonType, monoType, module.name) + modulePath.cast(mirrorType) } } else if (monoType.classSymbol.isGenericProduct) { @@ -899,11 +906,9 @@ trait Implicits { self: Typer => mirrorCore(defn.Mirror_ProductType, monoType, cls.name) .refinedWith(tpnme.MirroredElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) .refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels))) - val modul = cls.linkedClass.sourceModule - assert(modul.is(Module)) val mirrorRef = if (cls.is(Scala2x)) anonymousMirror(monoType, ExtendsProductMirror, span) - else ref(modul).withSpan(span) + else companionPath(monoType, span) mirrorRef.cast(mirrorType) } else EmptyTree @@ -955,9 +960,8 @@ trait Implicits { self: Typer => val mirrorType = mirrorCore(defn.Mirror_SumType, monoType, cls.name) .refinedWith(tpnme.MirroredElemTypes, TypeAlias(TypeOps.nestedPairs(elemTypes))) - val modul = cls.linkedClass.sourceModule val mirrorRef = - if (modul.exists && !cls.is(Scala2x)) ref(modul).withSpan(span) + if (cls.linkedClass.exists && !cls.is(Scala2x)) companionPath(monoType, span) else anonymousMirror(monoType, ExtendsSumMirror, span) mirrorRef.cast(mirrorType) case _ => From 6d3c2f92ade9c94e049ab9a602a83b78c5733f07 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 24 May 2019 10:09:40 +0200 Subject: [PATCH 29/35] Don't constrain MirroredElemTypes to be a subtype of Tuple --- library/src/scala/deriving.scala | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/library/src/scala/deriving.scala b/library/src/scala/deriving.scala index 01c986e0eed6..27c3df937804 100644 --- a/library/src/scala/deriving.scala +++ b/library/src/scala/deriving.scala @@ -9,6 +9,12 @@ object deriving { /** The mirrored *-type */ type MirroredMonoType + /** The (possibly higher-kinded) mirrored type */ + type MirroredTypeConstructor + + /** The types of the sum's alternatives or the product's elements */ + type MirroredElemTypes + /** The name of the type */ type MirroredLabel <: String } @@ -17,10 +23,6 @@ object deriving { /** The Mirror for a sum type */ trait Sum extends Mirror { self => - - /** The types of the alternatives */ - type MirroredElemTypes <: Tuple - /** The ordinal number of the case class of `x`. For enums, `ordinal(x) == x.ordinal` */ def ordinal(x: MirroredMonoType): Int } @@ -28,9 +30,6 @@ object deriving { /** The Mirror for a product type */ trait Product extends Mirror { - /** The types of the product elements */ - type MirroredElemTypes <: Tuple - /** The names of the product elements */ type MirroredElemLabels <: Tuple @@ -40,6 +39,7 @@ object deriving { trait Singleton extends Product { type MirroredMonoType = this.type + type MirroredTypeConstructor = this.type type MirroredElemTypes = Unit type MirroredElemLabels = Unit def fromProduct(p: scala.Product) = this @@ -48,14 +48,15 @@ object deriving { /** A proxy for Scala 2 singletons, which do not inherit `Singleton` directly */ class SingletonProxy(val value: AnyRef) extends Product { type MirroredMonoType = value.type + type MirroredTypeConstructor = value.type type MirroredElemTypes = Unit type MirroredElemLabels = Unit def fromProduct(p: scala.Product) = value } - type Of[T] = Mirror { type MirroredMonoType = T } - type ProductOf[T] = Mirror.Product { type MirroredMonoType = T } - type SumOf[T] = Mirror.Sum { type MirroredMonoType = T } + type Of[T] = Mirror { type MirroredMonoType = T; type MirroredElemTypes <: Tuple } + type ProductOf[T] = Mirror.Product { type MirroredMonoType = T; type MirroredElemTypes <: Tuple } + type SumOf[T] = Mirror.Sum { type MirroredMonoType = T; type MirroredElemTypes <: Tuple } } /** Helper class to turn arrays into products */ From c02861f4c8a44787ed366db4bc0694b38aa01aa3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 24 May 2019 10:10:16 +0200 Subject: [PATCH 30/35] Introduce MirroredTypeConstructor --- .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../src/dotty/tools/dotc/typer/Implicits.scala | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 0657efa8998e..9d5e0055d0e8 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -343,6 +343,7 @@ object StdNames { val MirroredElemLabels: N = "MirroredElemLabels" val MirroredLabel: N = "MirroredLabel" val MirroredMonoType: N = "MirroredMonoType" + val MirroredTypeConstructor: N = "MirroredTypeConstructor" val Modifiers: N = "Modifiers" val NestedAnnotArg: N = "NestedAnnotArg" val NoFlags: N = "NoFlags" diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 8eb28b47cc9d..0c2f14fa1430 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -26,6 +26,7 @@ import ProtoTypes._ import ErrorReporting._ import reporting.diagnostic.Message import Inferencing.fullyDefinedType +import TypeApplications.EtaExpansion import Trees._ import transform.SymUtils._ import transform.TypeUtils._ @@ -861,12 +862,23 @@ trait Implicits { self: Typer => /** The mirror type * - * { MirroredMonoType = } + * { + * MirroredMonoType = + * MirroredTypeConstrictor = + * MirroredLabel =