From 65d549c6d9aee94f65a90ea46fca408c40db24af Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 22 Nov 2018 16:52:54 +0100 Subject: [PATCH 01/13] Don't show inline BodyAnnotations when printing trees They are redundant, and can cause a cyclic reference when trying to evaluate a lazy body annotation. We did filter them before, but not everywhere. --- .../tools/dotc/printing/DecompilerPrinter.scala | 6 +++--- .../dotty/tools/dotc/printing/RefinedPrinter.scala | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala index de0da060a01f..9630bdc98c87 100644 --- a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala @@ -9,12 +9,12 @@ import dotty.tools.dotc.core.StdNames.nme import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.StdNames._ - +import dotty.tools.dotc.core.Annotations.Annotation class DecompilerPrinter(_ctx: Context) extends RefinedPrinter(_ctx) { - override protected def filterModTextAnnots(annots: List[untpd.Tree]): List[untpd.Tree] = - super.filterModTextAnnots(annots).filter(_.tpe != defn.SourceFileAnnotType) + override protected def dropAnnotForModText(sym: Symbol): Boolean = + super.dropAnnotForModText(sym) || sym == defn.SourceFileAnnot override protected def blockToText[T >: Untyped](block: Block[T]): Text = block match { diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index d19834955908..82c2fb6c9a0b 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -10,6 +10,7 @@ import Symbols._ import NameOps._ import TypeErasure.ErasedValueType import Contexts.Context +import Annotations.Annotation import Denotations._ import SymDenotations._ import StdNames.{nme, tpnme} @@ -633,7 +634,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def Modifiers(sym: Symbol)(implicit ctx: Context): Modifiers = untpd.Modifiers( sym.flags & (if (sym.isType) ModifierFlags | VarianceFlags else ModifierFlags), if (sym.privateWithin.exists) sym.privateWithin.asType.name else tpnme.EMPTY, - sym.annotations map (_.tree)) + sym.annotations.filterNot(ann => dropAnnotForModText(ann.symbol)).map(_.tree)) + + protected def dropAnnotForModText(sym: Symbol): Boolean = sym == defn.BodyAnnot protected def optAscription[T >: Untyped](tpt: Tree[T]): Text = optText(tpt)(": " ~ _) @@ -757,14 +760,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (homogenizedView && mods.flags.isTypeFlags) flagMask &~= Implicit // drop implicit from classes val flags = (if (sym.exists) sym.flags else (mods.flags)) & flagMask val flagsText = if (flags.isEmpty) "" else keywordStr(flags.toString) - val annotations = filterModTextAnnots( - if (sym.exists) sym.annotations.filterNot(_.isInstanceOf[Annotations.BodyAnnotation]).map(_.tree) - else mods.annotations) + val annotations = + if (sym.exists) sym.annotations.filterNot(ann => dropAnnotForModText(ann.symbol)).map(_.tree) + else mods.annotations.filterNot(tree => dropAnnotForModText(tree.symbol)) Text(annotations.map(annotText), " ") ~~ flagsText ~~ (Str(kw) provided !suppressKw) } - protected def filterModTextAnnots(annots: List[untpd.Tree]): List[untpd.Tree] = annots - def optText(name: Name)(encl: Text => Text): Text = if (name.isEmpty) "" else encl(toText(name)) From 7bd0f980c3a1b37267ea3ab9691388097fae359d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 22 Nov 2018 18:36:32 +0100 Subject: [PATCH 02/13] Inliner fixes - package case bindings with reduced expression - don't map wildcards to TypeTrees --- .../src/dotty/tools/dotc/typer/Inliner.scala | 38 ++++---- .../test/dotc/pos-test-pickling.blacklist | 1 + .../test/dotc/run-test-pickling.blacklist | 1 + tests/run/typeclass-derivation1.scala | 92 +++++++++++++++++++ 4 files changed, 111 insertions(+), 21 deletions(-) create mode 100644 tests/run/typeclass-derivation1.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 5b1c9fe47ec4..5fa35d1d6716 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -461,24 +461,19 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { } // Drop unused bindings - val matchBindings = reducer.matchBindingsBuf.toList - val (finalBindings, finalExpansion) = dropUnusedDefs(bindingsBuf.toList ++ matchBindings, expansion1) - val (finalMatchBindings, finalArgBindings) = finalBindings.partition(matchBindings.contains(_)) + val (finalBindings, finalExpansion) = dropUnusedDefs(bindingsBuf.toList, expansion1) if (inlinedMethod == defn.Typelevel_error) issueError() // Take care that only argument bindings go into `bindings`, since positions are // different for bindings from arguments and bindings from body. - tpd.Inlined(call, finalArgBindings, seq(finalMatchBindings, finalExpansion)) + tpd.Inlined(call, finalBindings, finalExpansion) } } /** A utility object offering methods for rewriting inlined code */ object reducer { - /** Additional bindings established by reducing match expressions */ - val matchBindingsBuf = new mutable.ListBuffer[MemberDef] - /** An extractor for terms equivalent to `new C(args)`, returning the class `C`, * a list of bindings, and the arguments `args`. Can see inside blocks and Inlined nodes and can * follow a reference to an inline value binding to its right hand side. @@ -599,7 +594,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { def unapply(tree: Trees.Ident[_])(implicit ctx: Context): Option[Tree] = { def search(buf: mutable.ListBuffer[MemberDef]) = buf.find(_.name == tree.name) if (paramProxies.contains(tree.typeOpt)) - search(bindingsBuf).orElse(search(matchBindingsBuf)) match { + search(bindingsBuf) match { case Some(vdef: ValDef) if vdef.symbol.is(Inline) => Some(integrate(vdef.rhs, vdef.symbol)) case Some(ddef: DefDef) => @@ -662,7 +657,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { * for the pattern-bound variables and the RHS of the selected case. * Returns `None` if no case was selected. */ - type MatchRedux = Option[(List[MemberDef], untpd.Tree)] + type MatchRedux = Option[(List[MemberDef], tpd.Tree)] /** Reduce an inline match * @param mtch the match tree @@ -674,7 +669,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { * @return optionally, if match can be reduced to a matching case: A pair of * bindings for all pattern-bound variables and the RHS of the case. */ - def reduceInlineMatch(scrutinee: Tree, scrutType: Type, cases: List[untpd.CaseDef], typer: Typer)(implicit ctx: Context): MatchRedux = { + def reduceInlineMatch(scrutinee: Tree, scrutType: Type, cases: List[CaseDef], typer: Typer)(implicit ctx: Context): MatchRedux = { val isImplicit = scrutinee.isEmpty val gadtSyms = typer.gadtSyms(scrutType) @@ -805,7 +800,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { val scrutineeSym = newSym(InlineScrutineeName.fresh(), Synthetic, scrutType).asTerm val scrutineeBinding = normalizeBinding(ValDef(scrutineeSym, scrutinee)) - def reduceCase(cdef: untpd.CaseDef): MatchRedux = { + def reduceCase(cdef: CaseDef): MatchRedux = { val caseBindingsBuf = new mutable.ListBuffer[MemberDef]() def guardOK(implicit ctx: Context) = cdef.guard.isEmpty || { val guardCtx = ctx.fresh.setNewScope @@ -824,7 +819,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { None } - def recur(cases: List[untpd.CaseDef]): MatchRedux = cases match { + def recur(cases: List[CaseDef]): MatchRedux = cases match { case Nil => None case cdef :: cases1 => reduceCase(cdef) `orElse` recur(cases1) } @@ -895,14 +890,15 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { super.typedMatchFinish(tree, sel, wideSelType, cases, pt) else { val selType = if (sel.isEmpty) wideSelType else sel.tpe - reduceInlineMatch(sel, selType, cases, this) match { - case Some((caseBindings, rhs)) => - var rhsCtx = ctx.fresh.setNewScope - for (binding <- caseBindings) { - matchBindingsBuf += binding - rhsCtx.enter(binding.symbol) - } - typedExpr(rhs, pt)(rhsCtx) + reduceInlineMatch(sel, selType, cases.asInstanceOf[List[CaseDef]], this) match { + case Some((caseBindings, rhs0)) => + val (usedBindings, rhs1) = dropUnusedDefs(caseBindings, rhs0) + val rhs = seq(usedBindings, rhs1) + inlining.println(i"""--- reduce: + |$tree + |--- to: + |$rhs""") + typedExpr(rhs, pt) case None => def guardStr(guard: untpd.Tree) = if (guard.isEmpty) "" else i" if $guard" def patStr(cdef: untpd.CaseDef) = i"case ${cdef.pat}${guardStr(cdef.guard)}" @@ -993,7 +989,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { val dealiasedType = dealias(t.tpe) val t1 = t match { case t: RefTree => - if (boundTypes.contains(t.symbol)) TypeTree(dealiasedType).withPos(t.pos) + if (t.name != nme.WILDCARD && boundTypes.contains(t.symbol)) TypeTree(dealiasedType).withPos(t.pos) else t.withType(dealiasedType) case t: DefTree => t.symbol.info = dealias(t.symbol.info) diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 22a41321bf77..28accd4f5cfa 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -12,6 +12,7 @@ i4125.scala implicit-dep.scala inline-access-levels inline-rewrite.scala +inline-caseclass.scala macro-with-array macro-with-type matchtype.scala diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index 4e30708a3def..2571ff4266db 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -9,3 +9,4 @@ t8133b tuples1.scala tuples1a.scala implicitMatch.scala +typeclass-derivation1.scala diff --git a/tests/run/typeclass-derivation1.scala b/tests/run/typeclass-derivation1.scala new file mode 100644 index 000000000000..2e9cf2492922 --- /dev/null +++ b/tests/run/typeclass-derivation1.scala @@ -0,0 +1,92 @@ +object Deriving { + import scala.typelevel._ + + sealed trait Shape + + class HasSumShape[T, S <: Tuple] + + abstract class HasProductShape[T, Xs <: Tuple] { + def toProduct(x: T): Xs + def fromProduct(x: Xs): T + } + + enum Lst[+T] { + case Cons(hd: T, tl: Lst[T]) + case Nil + } + + object Lst { + implicit def lstShape[T]: HasSumShape[Lst[T], (Cons[T], Nil.type)] = new HasSumShape + + implicit def consShape[T]: HasProductShape[Lst.Cons[T], (T, Lst[T])] = new { + def toProduct(xs: Lst.Cons[T]) = (xs.hd, xs.tl) + def fromProduct(xs: (T, Lst[T])): Lst.Cons[T] = Lst.Cons(xs(0), xs(1)).asInstanceOf + } + + implicit def nilShape[T]: HasProductShape[Lst.Nil.type, Unit] = new { + def toProduct(xs: Lst.Nil.type) = () + def fromProduct(xs: Unit) = Lst.Nil + } + + implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derivedForSum + implicit def ConsEq[T: Eq]: Eq[Cons[T]] = Eq.derivedForProduct + implicit def NilEq[T]: Eq[Nil.type] = Eq.derivedForProduct + } + + trait Eq[T] { + def equals(x: T, y: T): Boolean + } + + object Eq { + inline def tryEq[T](x: T, y: T) = implicit match { + case eq: Eq[T] => eq.equals(x, y) + } + + inline def deriveForSum[Alts <: Tuple](x: Any, y: Any): Boolean = inline erasedValue[Alts] match { + case _: (alt *: alts1) => + x match { + case x: `alt` => + y match { + case y: `alt` => tryEq[alt](x, y) + case _ => false + } + case _ => deriveForSum[alts1](x, y) + } + case _: Unit => + false + } + + inline def deriveForProduct[Elems <: Tuple](xs: Elems, ys: Elems): Boolean = inline erasedValue[Elems] match { + case _: (elem *: elems1) => + val xs1 = xs.asInstanceOf[elem *: elems1] + val ys1 = ys.asInstanceOf[elem *: elems1] + tryEq[elem](xs1.head, ys1.head) && + deriveForProduct[elems1](xs1.tail, ys1.tail) + case _: Unit => + true + } + + inline def derivedForSum[T, Alts <: Tuple](implicit ev: HasSumShape[T, Alts]): Eq[T] = new { + def equals(x: T, y: T): Boolean = deriveForSum[Alts](x, y) + } + + inline def derivedForProduct[T, Elems <: Tuple](implicit ev: HasProductShape[T, Elems]): Eq[T] = new { + def equals(x: T, y: T): Boolean = deriveForProduct[Elems](ev.toProduct(x), ev.toProduct(y)) + } + + implicit object eqInt extends Eq[Int] { + def equals(x: Int, y: Int) = x == y + } + } +} + +object Test extends App { + import Deriving._ + val eq = implicitly[Eq[Lst[Int]]] + val xs = Lst.Cons(1, Lst.Cons(2, Lst.Cons(3, Lst.Nil))) + val ys = Lst.Cons(1, Lst.Cons(2, Lst.Nil)) + assert(eq.equals(xs, xs)) + assert(!eq.equals(xs, ys)) + assert(!eq.equals(ys, xs)) + assert(eq.equals(ys, ys)) +} \ No newline at end of file From 793aeff1b0cfdc397db9659ed926495d61bdd64d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 22 Nov 2018 18:56:34 +0100 Subject: [PATCH 03/13] Blacklist another fromTasty test --- compiler/test/dotc/run-from-tasty.blacklist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/test/dotc/run-from-tasty.blacklist b/compiler/test/dotc/run-from-tasty.blacklist index ad83b4eb8b8b..ddd3ec1f2d5a 100644 --- a/compiler/test/dotc/run-from-tasty.blacklist +++ b/compiler/test/dotc/run-from-tasty.blacklist @@ -6,3 +6,5 @@ puzzle.scala # Need to print empty tree for implicit match implicitMatch.scala +typeclass-derivation1.scala + From d28b702aa253dda86c7db549f7948f504d62ec11 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 23 Nov 2018 10:48:16 +0100 Subject: [PATCH 04/13] Another typeclass derivation scheme for Eq This one resembles Magnolia, in that it uses some degree of reflection --- compiler/test/dotc/run-from-tasty.blacklist | 1 + .../test/dotc/run-test-pickling.blacklist | 1 + tests/run/typeclass-derivation1.scala | 8 ++ tests/run/typeclass-derivation2.scala | 111 ++++++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 tests/run/typeclass-derivation2.scala diff --git a/compiler/test/dotc/run-from-tasty.blacklist b/compiler/test/dotc/run-from-tasty.blacklist index ddd3ec1f2d5a..a2fbf589a9c8 100644 --- a/compiler/test/dotc/run-from-tasty.blacklist +++ b/compiler/test/dotc/run-from-tasty.blacklist @@ -7,4 +7,5 @@ puzzle.scala # Need to print empty tree for implicit match implicitMatch.scala typeclass-derivation1.scala +typeclass-derivation2.scala diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index 2571ff4266db..91ab8c1a70a8 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -10,3 +10,4 @@ tuples1.scala tuples1a.scala implicitMatch.scala typeclass-derivation1.scala +typeclass-derivation2.scala diff --git a/tests/run/typeclass-derivation1.scala b/tests/run/typeclass-derivation1.scala index 2e9cf2492922..47e457e8ed10 100644 --- a/tests/run/typeclass-derivation1.scala +++ b/tests/run/typeclass-derivation1.scala @@ -89,4 +89,12 @@ object Test extends App { assert(!eq.equals(xs, ys)) assert(!eq.equals(ys, xs)) assert(eq.equals(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.equals(xss, xss)) + assert(!eq2.equals(xss, yss)) + assert(!eq2.equals(yss, xss)) + assert(eq2.equals(yss, yss)) } \ No newline at end of file diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala new file mode 100644 index 000000000000..ae9a591eb284 --- /dev/null +++ b/tests/run/typeclass-derivation2.scala @@ -0,0 +1,111 @@ +object Deriving { + import scala.typelevel._ + + enum Shape { + case Sum[Alts <: Tuple] + case Product[T, Elems <: Tuple] + } + + case class GenericCase[+T](ordinal: Int, elems: Array[Object]) + + abstract class GenericMapper[T] { + def toGenericCase(x: T): GenericCase[T] + def fromGenericCase(c: GenericCase[T]): T + } + + abstract class HasShape[T, S <: Shape] extends GenericMapper[T] + + enum Lst[+T] { + case Cons(hd: T, tl: Lst[T]) + case Nil + } + + object Lst { + type LstShape[T] = Shape.Sum[( + Shape.Product[Cons[T], (T, Lst[T])], + Shape.Product[Nil.type, Unit] + )] + + implicit def lstShape[T]: HasShape[Lst[T], LstShape[T]] = new { + def toGenericCase(xs: Lst[T]): GenericCase[Lst[T]] = xs match { + case Cons(x, xs1) => GenericCase[Cons[T]](0, Array(x.asInstanceOf, xs1)) + case Nil => GenericCase[Nil.type](1, Array()) + } + def fromGenericCase(c: GenericCase[Lst[T]]): Lst[T] = c.ordinal match { + case 0 => Cons[T](c.elems(0).asInstanceOf, c.elems(1).asInstanceOf) + case 1 => Nil + } + } + + implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derived + } + + trait Eq[T] { + def equals(x: T, y: T): Boolean + } + + object Eq { + inline def tryEq[T](x: T, y: T) = implicit match { + case eq: Eq[T] => eq.equals(x, y) + } + + inline def deriveForSum[T, Alts <: Tuple](mapper: GenericMapper[T], x: T, y: T): Boolean = + inline erasedValue[Alts] match { + case _: (Shape.Product[alt, elems] *: alts1) => + x match { + case x: `alt` => + y match { + case y: `alt` => deriveForProduct[T, elems](mapper, x, y) + case _ => false + } + case _ => deriveForSum[T, alts1](mapper, x, y) + } + case _: Unit => + false + } + + inline def deriveForProduct[T, Elems <: Tuple](mapper: GenericMapper[T], x: T, y: T) = + deriveForTuple[Elems](0, mapper.toGenericCase(x).elems, mapper.toGenericCase(y).elems) + + inline def deriveForTuple[Elems <: Tuple](n: Int, xs: Array[Object], ys: Array[Object]): Boolean = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + tryEq[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) && + deriveForTuple[elems1](n + 1, xs, ys) + case _: Unit => + true + } + + inline def derived[T, S <: Shape](implicit ev: HasShape[T, S]): Eq[T] = new { + def equals(x: T, y: T): Boolean = inline erasedValue[S] match { + case _: Shape.Sum[alts] => + deriveForSum[T, alts](ev, x, y) + case _: Shape.Product[_, elems] => + deriveForProduct[T, elems](ev, x, y) + } + } + + implicit object eqInt extends Eq[Int] { + def equals(x: Int, y: Int) = x == y + } + } +} + +object Test extends App { + import Deriving._ + val eq = implicitly[Eq[Lst[Int]]] + val xs = Lst.Cons(1, Lst.Cons(2, Lst.Cons(3, Lst.Nil))) + val ys = Lst.Cons(1, Lst.Cons(2, Lst.Nil)) + assert(eq.equals(xs, xs)) + assert(!eq.equals(xs, ys)) + assert(!eq.equals(ys, xs)) + assert(eq.equals(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.equals(xss, xss)) + assert(!eq2.equals(xss, yss)) + assert(!eq2.equals(yss, xss)) + assert(eq2.equals(yss, yss)) +} \ No newline at end of file From 6b53a8a7e3e435cf97e8106847c5d2c32978d79b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 23 Nov 2018 13:43:26 +0100 Subject: [PATCH 05/13] Add Pickler typeclass to test --- tests/run/typeclass-derivation2.check | 4 + tests/run/typeclass-derivation2.scala | 230 +++++++++++++++++++------- 2 files changed, 178 insertions(+), 56 deletions(-) create mode 100644 tests/run/typeclass-derivation2.check diff --git a/tests/run/typeclass-derivation2.check b/tests/run/typeclass-derivation2.check new file mode 100644 index 000000000000..1224af74525d --- /dev/null +++ b/tests/run/typeclass-derivation2.check @@ -0,0 +1,4 @@ +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)) diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index ae9a591eb284..f812fbd8ea69 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -1,9 +1,11 @@ +import scala.collection.mutable + +// Generic deriving infrastructure object Deriving { - import scala.typelevel._ enum Shape { case Sum[Alts <: Tuple] - case Product[T, Elems <: Tuple] + case Case[T, Elems <: Tuple] } case class GenericCase[+T](ordinal: Int, elems: Array[Object]) @@ -14,88 +16,187 @@ object Deriving { } abstract class HasShape[T, S <: Shape] extends GenericMapper[T] +} + +// A datatype +enum Lst[+T] { + case Cons(hd: T, tl: Lst[T]) + case Nil +} + +object Lst { + // common compiler-generated infrastructure + import Deriving._ + + type LstShape[T] = Shape.Sum[( + Shape.Case[Cons[T], (T, Lst[T])], + Shape.Case[Nil.type, Unit] + )] - enum Lst[+T] { - case Cons(hd: T, tl: Lst[T]) - case Nil + implicit def lstShape[T]: HasShape[Lst[T], LstShape[T]] = new { + def toGenericCase(xs: Lst[T]): GenericCase[Lst[T]] = xs match { + case Cons(x, xs1) => GenericCase[Cons[T]](0, Array(x.asInstanceOf, xs1)) + case Nil => GenericCase[Nil.type](1, Array()) + } + def fromGenericCase(c: GenericCase[Lst[T]]): Lst[T] = c.ordinal match { + case 0 => Cons[T](c.elems(0).asInstanceOf, c.elems(1).asInstanceOf) + case 1 => Nil + } } - object Lst { - type LstShape[T] = Shape.Sum[( - Shape.Product[Cons[T], (T, Lst[T])], - Shape.Product[Nil.type, Unit] - )] + // two clauses that could be generated from a `derives` clause + implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derived + implicit def LstPickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived +} - implicit def lstShape[T]: HasShape[Lst[T], LstShape[T]] = new { - def toGenericCase(xs: Lst[T]): GenericCase[Lst[T]] = xs match { - case Cons(x, xs1) => GenericCase[Cons[T]](0, Array(x.asInstanceOf, xs1)) - case Nil => GenericCase[Nil.type](1, Array()) - } - def fromGenericCase(c: GenericCase[Lst[T]]): Lst[T] = c.ordinal match { - case 0 => Cons[T](c.elems(0).asInstanceOf, c.elems(1).asInstanceOf) - case 1 => Nil +// A typeclass +trait Eq[T] { + def equals(x: T, y: T): Boolean +} + +object Eq { + import scala.typelevel._ + import Deriving._ + + inline def tryEquals[T](x: T, y: T) = implicit match { + case eq: Eq[T] => eq.equals(x, y) + } + + inline def equalsElems[Elems <: Tuple](xs: Array[Object], ys: Array[Object], n: Int): Boolean = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + tryEquals[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) && + equalsElems[elems1](xs, ys, n + 1) + case _: Unit => + true + } + + inline def equalsCase[T, Elems <: Tuple](mapper: GenericMapper[T], x: T, y: T) = + equalsElems[Elems](mapper.toGenericCase(x).elems, mapper.toGenericCase(y).elems, 0) + + inline def equalsSum[T, Alts <: Tuple](mapper: GenericMapper[T], x: T, y: T): Boolean = + inline erasedValue[Alts] match { + case _: (Shape.Case[alt, elems] *: alts1) => + x match { + case x: `alt` => + y match { + case y: `alt` => equalsCase[T, elems](mapper, x, y) + case _ => false + } + case _ => equalsSum[T, alts1](mapper, x, y) } + case _: Unit => + false + } + + inline def derived[T, S <: Shape](implicit ev: HasShape[T, S]): Eq[T] = new { + def equals(x: T, y: T): Boolean = inline erasedValue[S] match { + case _: Shape.Sum[alts] => + equalsSum[T, alts](ev, x, y) + case _: Shape.Case[_, elems] => + equalsCase[T, elems](ev, x, y) } + } - implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derived + implicit object IntEq extends Eq[Int] { + def equals(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.typelevel._ + import Deriving._ + + def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) - trait Eq[T] { - def equals(x: T, y: T): Boolean + inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = implicit match { + case pkl: Pickler[T] => pkl.pickle(buf, x) } - object Eq { - inline def tryEq[T](x: T, y: T) = implicit match { - case eq: Eq[T] => eq.equals(x, y) + inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Array[AnyRef], n: Int): Unit = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + tryPickle[elem](buf, elems(n).asInstanceOf[elem]) + pickleElems[elems1](buf, elems, n + 1) + case _: Unit => } - inline def deriveForSum[T, Alts <: Tuple](mapper: GenericMapper[T], x: T, y: T): Boolean = - inline erasedValue[Alts] match { - case _: (Shape.Product[alt, elems] *: alts1) => - x match { - case x: `alt` => - y match { - case y: `alt` => deriveForProduct[T, elems](mapper, x, y) - case _ => false - } - case _ => deriveForSum[T, alts1](mapper, x, y) + inline def pickleCase[T, Elems <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T): Unit = { + val c = mapper.toGenericCase(x) + buf += c.ordinal + pickleElems[Elems](buf, c.elems, 0) + } + + inline def pickleSum[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T): Unit = + inline erasedValue[Alts] match { + case _: (Shape.Case[alt, elems] *: alts1) => + x match { + case x: `alt` => pickleCase[T, elems](mapper, buf, x) + case _ => pickleSum[T, alts1](mapper, buf, x) } case _: Unit => - false } - inline def deriveForProduct[T, Elems <: Tuple](mapper: GenericMapper[T], x: T, y: T) = - deriveForTuple[Elems](0, mapper.toGenericCase(x).elems, mapper.toGenericCase(y).elems) + inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = implicit match { + case pkl: Pickler[T] => pkl.unpickle(buf) + } - inline def deriveForTuple[Elems <: Tuple](n: Int, xs: Array[Object], ys: Array[Object]): Boolean = - inline erasedValue[Elems] match { - case _: (elem *: elems1) => - tryEq[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) && - deriveForTuple[elems1](n + 1, xs, ys) - case _: Unit => - true - } + inline def unpickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Array[AnyRef], n: Int): Unit = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + elems(n) = tryUnpickle[elem](buf).asInstanceOf[AnyRef] + unpickleElems[elems1](buf, elems, n + 1) + case _: Unit => + } - inline def derived[T, S <: Shape](implicit ev: HasShape[T, S]): Eq[T] = new { - def equals(x: T, y: T): Boolean = inline erasedValue[S] match { - case _: Shape.Sum[alts] => - deriveForSum[T, alts](ev, x, y) - case _: Shape.Product[_, elems] => - deriveForProduct[T, elems](ev, x, y) - } + inline def unpickleCase[T, Elems <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + val elems = new Array[Object](constValue[Tuple.Size[Elems]]) + unpickleElems[Elems](buf, elems, 0) + mapper.fromGenericCase(GenericCase(ordinal, elems)) + } + + inline def unpickleSum[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = + inline erasedValue[Alts] match { + case _: (Shape.Case[alt, elems] *: alts1) => + if (n == ordinal) unpickleCase[T, elems](mapper, buf, ordinal) + else unpickleSum[T, alts1](mapper, buf, ordinal, n + 1) + case _ => + throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal") } - implicit object eqInt extends Eq[Int] { - def equals(x: Int, y: Int) = x == y + inline def derived[T, S <: Shape](implicit ev: HasShape[T, S]): Pickler[T] = new { + def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = inline erasedValue[S] match { + case _: Shape.Sum[alts] => + pickleSum[T, alts](ev, buf, x) + case _: Shape.Case[_, elems] => + pickleCase[T, elems](ev, buf, x) + } + def unpickle(buf: mutable.ListBuffer[Int]): T = inline erasedValue[S] match { + case _: Shape.Sum[alts] => + unpickleSum[T, alts](ev, buf, nextInt(buf), 0) + case _: Shape.Case[_, elems] => + unpickleCase[T, elems](ev, buf, 0) } } + + 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) + } } +// Tests object Test extends App { import Deriving._ val eq = implicitly[Eq[Lst[Int]]] - val xs = Lst.Cons(1, Lst.Cons(2, Lst.Cons(3, Lst.Nil))) - val ys = Lst.Cons(1, Lst.Cons(2, Lst.Nil)) + 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.equals(xs, xs)) assert(!eq.equals(xs, ys)) assert(!eq.equals(ys, xs)) @@ -108,4 +209,21 @@ object Test extends App { assert(!eq2.equals(xss, yss)) assert(!eq2.equals(yss, xss)) assert(eq2.equals(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.equals(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.equals(xss, xss1)) } \ No newline at end of file From 3f31087b0fdd11a7b7951c8e94d6ff9620d7a104 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 23 Nov 2018 14:39:15 +0100 Subject: [PATCH 06/13] Add a simple product class --- tests/run/typeclass-derivation2.scala | 64 +++++++++++++++++++++------ 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index f812fbd8ea69..cc9b1d02e107 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -18,8 +18,9 @@ object Deriving { abstract class HasShape[T, S <: Shape] extends GenericMapper[T] } -// A datatype -enum Lst[+T] { +// An algebraic datatype +enum Lst[+T] // derives Eq, Pickler +{ case Cons(hd: T, tl: Lst[T]) case Nil } @@ -49,6 +50,27 @@ object Lst { implicit def LstPickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived } +// A simple product type +case class Pair[T](x: T, y: T) // derives Eq, Pickler + +object Pair { + // common compiler-generated infrastructure + import Deriving._ + + type PairShape[T] = Shape.Case[Pair[T], (T, T)] + + implicit def pairShape[T]: HasShape[Pair[T], PairShape[T]] = new { + def toGenericCase(xy: Pair[T]) = + GenericCase[Pair[T]](0, Array(xy._1.asInstanceOf, xy._2.asInstanceOf)) + def fromGenericCase(c: GenericCase[Pair[T]]): Pair[T] = + Pair(c.elems(0).asInstanceOf, c.elems(1).asInstanceOf) + } + + // two clauses that could be generated from a `derives` clause + implicit def PairEq[T: Eq]: Eq[Pair[T]] = Eq.derived + implicit def PairPickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived +} + // A typeclass trait Eq[T] { def equals(x: T, y: T): Boolean @@ -93,7 +115,7 @@ object Eq { def equals(x: T, y: T): Boolean = inline erasedValue[S] match { case _: Shape.Sum[alts] => equalsSum[T, alts](ev, x, y) - case _: Shape.Case[_, elems] => + case _: Shape.Case[alt, elems] => equalsCase[T, elems](ev, x, y) } } @@ -127,18 +149,18 @@ object Pickler { case _: Unit => } - inline def pickleCase[T, Elems <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T): Unit = { - val c = mapper.toGenericCase(x) - buf += c.ordinal - pickleElems[Elems](buf, c.elems, 0) - } + inline def pickleCase[T, Elems <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T): Unit = + pickleElems[Elems](buf, mapper.toGenericCase(x).elems, 0) - inline def pickleSum[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T): Unit = + inline def pickleSum[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => x match { - case x: `alt` => pickleCase[T, elems](mapper, buf, x) - case _ => pickleSum[T, alts1](mapper, buf, x) + case x: `alt` => + buf += n + pickleCase[T, elems](mapper, buf, x) + case _ => + pickleSum[T, alts1](mapper, buf, x, n + 1) } case _: Unit => } @@ -173,14 +195,14 @@ object Pickler { inline def derived[T, S <: Shape](implicit ev: HasShape[T, S]): Pickler[T] = new { def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = inline erasedValue[S] match { case _: Shape.Sum[alts] => - pickleSum[T, alts](ev, buf, x) - case _: Shape.Case[_, elems] => + pickleSum[T, alts](ev, buf, x, 0) + case _: Shape.Case[alt, elems] => pickleCase[T, elems](ev, buf, x) } def unpickle(buf: mutable.ListBuffer[Int]): T = inline erasedValue[S] match { case _: Shape.Sum[alts] => unpickleSum[T, alts](ev, buf, nextInt(buf), 0) - case _: Shape.Case[_, elems] => + case _: Shape.Case[alt, elems] => unpickleCase[T, elems](ev, buf, 0) } } @@ -226,4 +248,18 @@ object Test extends App { println(xss1) assert(xss == xss1) assert(eq2.equals(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.equals(p1, p2)) + assert(!eqp.equals(p2, p3)) + + val pklp = implicitly[Pickler[Pair[Int]]] + pklp.pickle(buf, p1) + println(buf) + val p1a = pklp.unpickle(buf) + assert(p1 == p1a) + assert(eqp.equals(p1, p1a)) } \ No newline at end of file From c3160a345614e9fef1aa57b4fcb7281fe279ebe2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 23 Nov 2018 15:59:44 +0100 Subject: [PATCH 07/13] Fix check file ... and some polishings --- tests/run/typeclass-derivation2.check | 2 ++ tests/run/typeclass-derivation2.scala | 29 ++++++++++++++------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/tests/run/typeclass-derivation2.check b/tests/run/typeclass-derivation2.check index 1224af74525d..0ab528b9da36 100644 --- a/tests/run/typeclass-derivation2.check +++ b/tests/run/typeclass-derivation2.check @@ -2,3 +2,5 @@ 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) diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index cc9b1d02e107..abfac9121d8f 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -4,7 +4,7 @@ import scala.collection.mutable object Deriving { enum Shape { - case Sum[Alts <: Tuple] + case Cases[Alts <: Tuple] case Case[T, Elems <: Tuple] } @@ -29,7 +29,7 @@ object Lst { // common compiler-generated infrastructure import Deriving._ - type LstShape[T] = Shape.Sum[( + type LstShape[T] = Shape.Cases[( Shape.Case[Cons[T], (T, Lst[T])], Shape.Case[Nil.type, Unit] )] @@ -96,7 +96,7 @@ object Eq { inline def equalsCase[T, Elems <: Tuple](mapper: GenericMapper[T], x: T, y: T) = equalsElems[Elems](mapper.toGenericCase(x).elems, mapper.toGenericCase(y).elems, 0) - inline def equalsSum[T, Alts <: Tuple](mapper: GenericMapper[T], x: T, y: T): Boolean = + inline def equalsCases[T, Alts <: Tuple](mapper: GenericMapper[T], x: T, y: T): Boolean = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => x match { @@ -105,7 +105,7 @@ object Eq { case y: `alt` => equalsCase[T, elems](mapper, x, y) case _ => false } - case _ => equalsSum[T, alts1](mapper, x, y) + case _ => equalsCases[T, alts1](mapper, x, y) } case _: Unit => false @@ -113,8 +113,8 @@ object Eq { inline def derived[T, S <: Shape](implicit ev: HasShape[T, S]): Eq[T] = new { def equals(x: T, y: T): Boolean = inline erasedValue[S] match { - case _: Shape.Sum[alts] => - equalsSum[T, alts](ev, x, y) + case _: Shape.Cases[alts] => + equalsCases[T, alts](ev, x, y) case _: Shape.Case[alt, elems] => equalsCase[T, elems](ev, x, y) } @@ -152,7 +152,7 @@ object Pickler { inline def pickleCase[T, Elems <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T): Unit = pickleElems[Elems](buf, mapper.toGenericCase(x).elems, 0) - inline def pickleSum[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit = + inline def pickleCases[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => x match { @@ -160,7 +160,7 @@ object Pickler { buf += n pickleCase[T, elems](mapper, buf, x) case _ => - pickleSum[T, alts1](mapper, buf, x, n + 1) + pickleCases[T, alts1](mapper, buf, x, n + 1) } case _: Unit => } @@ -183,25 +183,25 @@ object Pickler { mapper.fromGenericCase(GenericCase(ordinal, elems)) } - inline def unpickleSum[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = + inline def unpickleCases[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => if (n == ordinal) unpickleCase[T, elems](mapper, buf, ordinal) - else unpickleSum[T, alts1](mapper, buf, ordinal, n + 1) + else unpickleCases[T, alts1](mapper, buf, ordinal, n + 1) case _ => throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal") } inline def derived[T, S <: Shape](implicit ev: HasShape[T, S]): Pickler[T] = new { def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = inline erasedValue[S] match { - case _: Shape.Sum[alts] => - pickleSum[T, alts](ev, buf, x, 0) + case _: Shape.Cases[alts] => + pickleCases[T, alts](ev, buf, x, 0) case _: Shape.Case[alt, elems] => pickleCase[T, elems](ev, buf, x) } def unpickle(buf: mutable.ListBuffer[Int]): T = inline erasedValue[S] match { - case _: Shape.Sum[alts] => - unpickleSum[T, alts](ev, buf, nextInt(buf), 0) + case _: Shape.Cases[alts] => + unpickleCases[T, alts](ev, buf, nextInt(buf), 0) case _: Shape.Case[alt, elems] => unpickleCase[T, elems](ev, buf, 0) } @@ -260,6 +260,7 @@ object Test extends App { pklp.pickle(buf, p1) println(buf) val p1a = pklp.unpickle(buf) + println(p1a) assert(p1 == p1a) assert(eqp.equals(p1, p1a)) } \ No newline at end of file From 574f4bbb69fc03869a270ac5b06861d4c5ebc0a8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 23 Nov 2018 17:48:15 +0100 Subject: [PATCH 08/13] Fix wildcard handling in Inliner We could not reduce a pattern with wildcard type arguments because these were not treated as GADT bound variables but as abstract types. --- .../src/dotty/tools/dotc/typer/Inliner.scala | 4 +- tests/run/typeclass-derivation2.scala | 66 +++++++++---------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 5fa35d1d6716..3e4703251554 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -707,7 +707,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { val getBoundVars = new TreeAccumulator[List[TypeSymbol]] { def apply(syms: List[TypeSymbol], t: Tree)(implicit ctx: Context) = { val syms1 = t match { - case t: Bind if t.symbol.isType && t.name != tpnme.WILDCARD => + case t: Bind if t.symbol.isType => t.symbol.asType :: syms case _ => syms @@ -734,7 +734,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { // ConstraintHandler#approximation does. However, this only works for constrained paramrefs // not GADT-bound variables. Hopefully we will get some way to improve this when we // re-implement GADTs in terms of constraints. - bindingsBuf += TypeDef(bv) + if (bv.name != nme.WILDCARD) bindingsBuf += TypeDef(bv) } reducePattern(bindingsBuf, scrut, pat1) } diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index abfac9121d8f..2c7300c26c1e 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -73,55 +73,55 @@ object Pair { // A typeclass trait Eq[T] { - def equals(x: T, y: T): Boolean + def eql(x: T, y: T): Boolean } object Eq { import scala.typelevel._ import Deriving._ - inline def tryEquals[T](x: T, y: T) = implicit match { - case eq: Eq[T] => eq.equals(x, y) + inline def tryEql[T](x: T, y: T) = implicit match { + case eq: Eq[T] => eq.eql(x, y) } - inline def equalsElems[Elems <: Tuple](xs: Array[Object], ys: Array[Object], n: Int): Boolean = + inline def eqlElems[Elems <: Tuple](xs: Array[Object], ys: Array[Object], n: Int): Boolean = inline erasedValue[Elems] match { case _: (elem *: elems1) => - tryEquals[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) && - equalsElems[elems1](xs, ys, n + 1) + tryEql[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) && + eqlElems[elems1](xs, ys, n + 1) case _: Unit => true } - inline def equalsCase[T, Elems <: Tuple](mapper: GenericMapper[T], x: T, y: T) = - equalsElems[Elems](mapper.toGenericCase(x).elems, mapper.toGenericCase(y).elems, 0) + inline def eqlCase[T, Elems <: Tuple](mapper: GenericMapper[T], x: T, y: T) = + eqlElems[Elems](mapper.toGenericCase(x).elems, mapper.toGenericCase(y).elems, 0) - inline def equalsCases[T, Alts <: Tuple](mapper: GenericMapper[T], x: T, y: T): Boolean = + inline def eqlCases[T, Alts <: Tuple](mapper: GenericMapper[T], x: T, y: T): Boolean = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => x match { case x: `alt` => y match { - case y: `alt` => equalsCase[T, elems](mapper, x, y) + case y: `alt` => eqlCase[T, elems](mapper, x, y) case _ => false } - case _ => equalsCases[T, alts1](mapper, x, y) + case _ => eqlCases[T, alts1](mapper, x, y) } case _: Unit => false } - inline def derived[T, S <: Shape](implicit ev: HasShape[T, S]): Eq[T] = new { - def equals(x: T, y: T): Boolean = inline erasedValue[S] match { + inline def derived[T, S <: Shape](implicit ev: HasShape[T, S]) <: Eq[T] = new { + def eql(x: T, y: T): Boolean = inline erasedValue[S] match { case _: Shape.Cases[alts] => - equalsCases[T, alts](ev, x, y) - case _: Shape.Case[alt, elems] => - equalsCase[T, elems](ev, x, y) + eqlCases[T, alts](ev, x, y) + case _: Shape.Case[_, elems] => + eqlCase[T, elems](ev, x, y) } } implicit object IntEq extends Eq[Int] { - def equals(x: Int, y: Int) = x == y + def eql(x: Int, y: Int) = x == y } } @@ -185,7 +185,7 @@ object Pickler { inline def unpickleCases[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = inline erasedValue[Alts] match { - case _: (Shape.Case[alt, elems] *: alts1) => + case _: (Shape.Case[_, elems] *: alts1) => if (n == ordinal) unpickleCase[T, elems](mapper, buf, ordinal) else unpickleCases[T, alts1](mapper, buf, ordinal, n + 1) case _ => @@ -196,13 +196,13 @@ object Pickler { def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = inline erasedValue[S] match { case _: Shape.Cases[alts] => pickleCases[T, alts](ev, buf, x, 0) - case _: Shape.Case[alt, elems] => + case _: Shape.Case[_, elems] => pickleCase[T, elems](ev, buf, x) } def unpickle(buf: mutable.ListBuffer[Int]): T = inline erasedValue[S] match { case _: Shape.Cases[alts] => unpickleCases[T, alts](ev, buf, nextInt(buf), 0) - case _: Shape.Case[alt, elems] => + case _: Shape.Case[_, elems] => unpickleCase[T, elems](ev, buf, 0) } } @@ -219,18 +219,18 @@ 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.equals(xs, xs)) - assert(!eq.equals(xs, ys)) - assert(!eq.equals(ys, xs)) - assert(eq.equals(ys, ys)) + 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.equals(xss, xss)) - assert(!eq2.equals(xss, yss)) - assert(!eq2.equals(yss, xss)) - assert(eq2.equals(yss, yss)) + 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]]] @@ -239,7 +239,7 @@ object Test extends App { val xs1 = pkl.unpickle(buf) println(xs1) assert(xs1 == xs) - assert(eq.equals(xs1, xs)) + assert(eq.eql(xs1, xs)) val pkl2 = implicitly[Pickler[Lst[Lst[Int]]]] pkl2.pickle(buf, xss) @@ -247,14 +247,14 @@ object Test extends App { val xss1 = pkl2.unpickle(buf) println(xss1) assert(xss == xss1) - assert(eq2.equals(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.equals(p1, p2)) - assert(!eqp.equals(p2, p3)) + assert(eqp.eql(p1, p2)) + assert(!eqp.eql(p2, p3)) val pklp = implicitly[Pickler[Pair[Int]]] pklp.pickle(buf, p1) @@ -262,5 +262,5 @@ object Test extends App { val p1a = pklp.unpickle(buf) println(p1a) assert(p1 == p1a) - assert(eqp.equals(p1, p1a)) + assert(eqp.eql(p1, p1a)) } \ No newline at end of file From ed9d00134f329b6740205d4b8e3420981503ca0e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 24 Nov 2018 11:13:38 +0100 Subject: [PATCH 09/13] Normalize when constant folding It can happen that what is really a constant type is still in the form of an unreduced MatchType. We need to normalize the type to reveal the constant. The new version typeclass-derivation2.scala test needs this fix to compile without errors. --- .../dotty/tools/dotc/typer/ConstFold.scala | 4 +- .../src/dotty/tools/dotc/typer/Inliner.scala | 2 +- tests/run/typeclass-derivation2.scala | 82 +++++++++++++++---- 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/ConstFold.scala b/compiler/src/dotty/tools/dotc/typer/ConstFold.scala index 68a5d05f5f26..882a2274a5c5 100644 --- a/compiler/src/dotty/tools/dotc/typer/ConstFold.scala +++ b/compiler/src/dotty/tools/dotc/typer/ConstFold.scala @@ -20,7 +20,7 @@ object ConstFold { def apply(tree: Tree)(implicit ctx: Context): Tree = finish(tree) { tree match { case Apply(Select(xt, op), yt :: Nil) => - xt.tpe.widenTermRefExpr match { + xt.tpe.widenTermRefExpr.normalized match { case ConstantType(x) => yt.tpe.widenTermRefExpr match { case ConstantType(y) => foldBinop(op, x, y) @@ -42,7 +42,7 @@ object ConstFold { */ def apply(tree: Tree, pt: Type)(implicit ctx: Context): Tree = finish(apply(tree)) { - tree.tpe.widenTermRefExpr match { + tree.tpe.widenTermRefExpr.normalized match { case ConstantType(x) => x convertTo pt case _ => null } diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 3e4703251554..1aef548cd2ae 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -606,7 +606,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { } object ConstantValue { - def unapply(tree: Tree)(implicit ctx: Context): Option[Any] = tree.tpe.widenTermRefExpr match { + def unapply(tree: Tree)(implicit ctx: Context): Option[Any] = tree.tpe.widenTermRefExpr.normalized match { case ConstantType(Constant(x)) => Some(x) case _ => None } diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 2c7300c26c1e..3be42c87bcd2 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -3,18 +3,65 @@ import scala.collection.mutable // Generic deriving infrastructure object Deriving { + /** The shape of an ADT in a sum of products representation */ enum Shape { + + /** A sum with alternative types `Alts` */ case Cases[Alts <: Tuple] + + /** A product type `T` with element types `Elems` */ case Case[T, Elems <: Tuple] } - case class GenericCase[+T](ordinal: Int, elems: Array[Object]) + /** A generic representation of a case in an ADT + * @param ordinal The ordinal value of the case in the list of the ADT's cases + * @param elems The elements of the case + */ + class GenericCase[+T](val ordinal: Int, val elems: Product) { + + /** A generic case with elements given as an array */ + def this(ordinal: Int, elems: Array[AnyRef]) = + this(ordinal, new ArrayProduct(elems)) + + /** A generic case with an initial empty array of `numElems` elements, to be filled in. */ + def this(ordinal: Int, numElems: Int) = + this(ordinal, new Array[AnyRef](numElems)) + + /** A generic case with no elements */ + def this(ordinal: Int) = + this(ordinal, EmptyProduct) + + /** The `n`'th element of this generic case */ + def apply(n: Int): Any = elems.productElement(n) + } + + /** 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 + } + /** A class for mapping between an ADT value and + * the generic case that represents the value + */ abstract class GenericMapper[T] { def toGenericCase(x: T): GenericCase[T] def fromGenericCase(c: GenericCase[T]): T } + /** Every generic derivation starts with a typeclass instance of this type. + * It informs that type `T` has shape `S` and is backed by the extended generic mapper. + */ abstract class HasShape[T, S <: Shape] extends GenericMapper[T] } @@ -34,13 +81,15 @@ object Lst { Shape.Case[Nil.type, Unit] )] + val NilCase = new GenericCase[Nil.type](1) + implicit def lstShape[T]: HasShape[Lst[T], LstShape[T]] = new { def toGenericCase(xs: Lst[T]): GenericCase[Lst[T]] = xs match { - case Cons(x, xs1) => GenericCase[Cons[T]](0, Array(x.asInstanceOf, xs1)) - case Nil => GenericCase[Nil.type](1, Array()) - } + case xs: Cons[T] => new GenericCase[Cons[T]](0, xs) + case Nil => NilCase + } def fromGenericCase(c: GenericCase[Lst[T]]): Lst[T] = c.ordinal match { - case 0 => Cons[T](c.elems(0).asInstanceOf, c.elems(1).asInstanceOf) + case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) case 1 => Nil } } @@ -61,9 +110,9 @@ object Pair { implicit def pairShape[T]: HasShape[Pair[T], PairShape[T]] = new { def toGenericCase(xy: Pair[T]) = - GenericCase[Pair[T]](0, Array(xy._1.asInstanceOf, xy._2.asInstanceOf)) + new GenericCase[Pair[T]](0, xy) def fromGenericCase(c: GenericCase[Pair[T]]): Pair[T] = - Pair(c.elems(0).asInstanceOf, c.elems(1).asInstanceOf) + Pair(c(0).asInstanceOf, c(1).asInstanceOf) } // two clauses that could be generated from a `derives` clause @@ -84,7 +133,7 @@ object Eq { case eq: Eq[T] => eq.eql(x, y) } - inline def eqlElems[Elems <: Tuple](xs: Array[Object], ys: Array[Object], n: Int): Boolean = + inline def eqlElems[Elems <: Tuple](xs: GenericCase[_], ys: GenericCase[_], n: Int): Boolean = inline erasedValue[Elems] match { case _: (elem *: elems1) => tryEql[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) && @@ -94,7 +143,7 @@ object Eq { } inline def eqlCase[T, Elems <: Tuple](mapper: GenericMapper[T], x: T, y: T) = - eqlElems[Elems](mapper.toGenericCase(x).elems, mapper.toGenericCase(y).elems, 0) + eqlElems[Elems](mapper.toGenericCase(x), mapper.toGenericCase(y), 0) inline def eqlCases[T, Alts <: Tuple](mapper: GenericMapper[T], x: T, y: T): Boolean = inline erasedValue[Alts] match { @@ -141,7 +190,7 @@ object Pickler { case pkl: Pickler[T] => pkl.pickle(buf, x) } - inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Array[AnyRef], n: Int): Unit = + inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: GenericCase[_], n: Int): Unit = inline erasedValue[Elems] match { case _: (elem *: elems1) => tryPickle[elem](buf, elems(n).asInstanceOf[elem]) @@ -150,7 +199,7 @@ object Pickler { } inline def pickleCase[T, Elems <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T): Unit = - pickleElems[Elems](buf, mapper.toGenericCase(x).elems, 0) + pickleElems[Elems](buf, mapper.toGenericCase(x), 0) inline def pickleCases[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit = inline erasedValue[Alts] match { @@ -178,9 +227,14 @@ object Pickler { } inline def unpickleCase[T, Elems <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { - val elems = new Array[Object](constValue[Tuple.Size[Elems]]) - unpickleElems[Elems](buf, elems, 0) - mapper.fromGenericCase(GenericCase(ordinal, elems)) + inline val size = constValue[Tuple.Size[Elems]] + inline if (size == 0) + mapper.fromGenericCase(new GenericCase[T](ordinal)) + else { + val elems = new Array[Object](size) + unpickleElems[Elems](buf, elems, 0) + mapper.fromGenericCase(new GenericCase[T](ordinal, elems)) + } } inline def unpickleCases[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = From f3d9330dac0e202afd0296a15321f8011dada4eb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 24 Nov 2018 11:56:44 +0100 Subject: [PATCH 10/13] Rename HasShape -> Shaped, GenericCase -> CaseMirror and some related renamings --- tests/run/typeclass-derivation2.scala | 62 +++++++++++++-------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 3be42c87bcd2..45822b2f767d 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -17,7 +17,7 @@ object Deriving { * @param ordinal The ordinal value of the case in the list of the ADT's cases * @param elems The elements of the case */ - class GenericCase[+T](val ordinal: Int, val elems: Product) { + class CaseMirror[+T](val ordinal: Int, val elems: Product) { /** A generic case with elements given as an array */ def this(ordinal: Int, elems: Array[AnyRef]) = @@ -54,15 +54,15 @@ object Deriving { /** A class for mapping between an ADT value and * the generic case that represents the value */ - abstract class GenericMapper[T] { - def toGenericCase(x: T): GenericCase[T] - def fromGenericCase(c: GenericCase[T]): T + abstract class MirrorMapper[T] { + def toMirror(x: T): CaseMirror[T] + def fromMirror(c: CaseMirror[T]): T } /** Every generic derivation starts with a typeclass instance of this type. * It informs that type `T` has shape `S` and is backed by the extended generic mapper. */ - abstract class HasShape[T, S <: Shape] extends GenericMapper[T] + abstract class Shaped[T, S <: Shape] extends MirrorMapper[T] } // An algebraic datatype @@ -76,19 +76,19 @@ object Lst { // common compiler-generated infrastructure import Deriving._ - type LstShape[T] = Shape.Cases[( + type Shape[T] = Shape.Cases[( Shape.Case[Cons[T], (T, Lst[T])], Shape.Case[Nil.type, Unit] )] - val NilCase = new GenericCase[Nil.type](1) + val NilMirror = new CaseMirror[Nil.type](1) - implicit def lstShape[T]: HasShape[Lst[T], LstShape[T]] = new { - def toGenericCase(xs: Lst[T]): GenericCase[Lst[T]] = xs match { - case xs: Cons[T] => new GenericCase[Cons[T]](0, xs) - case Nil => NilCase + implicit def lstShape[T]: Shaped[Lst[T], Shape[T]] = new { + def toMirror(xs: Lst[T]): CaseMirror[Lst[T]] = xs match { + case xs: Cons[T] => new CaseMirror[Cons[T]](0, xs) + case Nil => NilMirror } - def fromGenericCase(c: GenericCase[Lst[T]]): Lst[T] = c.ordinal match { + def fromMirror(c: CaseMirror[Lst[T]]): Lst[T] = c.ordinal match { case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) case 1 => Nil } @@ -106,12 +106,12 @@ object Pair { // common compiler-generated infrastructure import Deriving._ - type PairShape[T] = Shape.Case[Pair[T], (T, T)] + type Shape[T] = Shape.Case[Pair[T], (T, T)] - implicit def pairShape[T]: HasShape[Pair[T], PairShape[T]] = new { - def toGenericCase(xy: Pair[T]) = - new GenericCase[Pair[T]](0, xy) - def fromGenericCase(c: GenericCase[Pair[T]]): Pair[T] = + implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new { + def toMirror(xy: Pair[T]) = + new CaseMirror[Pair[T]](0, xy) + def fromMirror(c: CaseMirror[Pair[T]]): Pair[T] = Pair(c(0).asInstanceOf, c(1).asInstanceOf) } @@ -133,7 +133,7 @@ object Eq { case eq: Eq[T] => eq.eql(x, y) } - inline def eqlElems[Elems <: Tuple](xs: GenericCase[_], ys: GenericCase[_], n: Int): Boolean = + inline def eqlElems[Elems <: Tuple](xs: CaseMirror[_], ys: CaseMirror[_], n: Int): Boolean = inline erasedValue[Elems] match { case _: (elem *: elems1) => tryEql[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) && @@ -142,10 +142,10 @@ object Eq { true } - inline def eqlCase[T, Elems <: Tuple](mapper: GenericMapper[T], x: T, y: T) = - eqlElems[Elems](mapper.toGenericCase(x), mapper.toGenericCase(y), 0) + inline def eqlCase[T, Elems <: Tuple](mapper: MirrorMapper[T], x: T, y: T) = + eqlElems[Elems](mapper.toMirror(x), mapper.toMirror(y), 0) - inline def eqlCases[T, Alts <: Tuple](mapper: GenericMapper[T], x: T, y: T): Boolean = + inline def eqlCases[T, Alts <: Tuple](mapper: MirrorMapper[T], x: T, y: T): Boolean = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => x match { @@ -160,7 +160,7 @@ object Eq { false } - inline def derived[T, S <: Shape](implicit ev: HasShape[T, S]) <: Eq[T] = new { + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]) <: Eq[T] = new { def eql(x: T, y: T): Boolean = inline erasedValue[S] match { case _: Shape.Cases[alts] => eqlCases[T, alts](ev, x, y) @@ -190,7 +190,7 @@ object Pickler { case pkl: Pickler[T] => pkl.pickle(buf, x) } - inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: GenericCase[_], n: Int): Unit = + inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: CaseMirror[_], n: Int): Unit = inline erasedValue[Elems] match { case _: (elem *: elems1) => tryPickle[elem](buf, elems(n).asInstanceOf[elem]) @@ -198,10 +198,10 @@ object Pickler { case _: Unit => } - inline def pickleCase[T, Elems <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T): Unit = - pickleElems[Elems](buf, mapper.toGenericCase(x), 0) + inline def pickleCase[T, Elems <: Tuple](mapper: MirrorMapper[T], buf: mutable.ListBuffer[Int], x: T): Unit = + pickleElems[Elems](buf, mapper.toMirror(x), 0) - inline def pickleCases[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit = + inline def pickleCases[T, Alts <: Tuple](mapper: MirrorMapper[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => x match { @@ -226,18 +226,18 @@ object Pickler { case _: Unit => } - inline def unpickleCase[T, Elems <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + inline def unpickleCase[T, Elems <: Tuple](mapper: MirrorMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) - mapper.fromGenericCase(new GenericCase[T](ordinal)) + mapper.fromMirror(new CaseMirror[T](ordinal)) else { val elems = new Array[Object](size) unpickleElems[Elems](buf, elems, 0) - mapper.fromGenericCase(new GenericCase[T](ordinal, elems)) + mapper.fromMirror(new CaseMirror[T](ordinal, elems)) } } - inline def unpickleCases[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = + inline def unpickleCases[T, Alts <: Tuple](mapper: MirrorMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = inline erasedValue[Alts] match { case _: (Shape.Case[_, elems] *: alts1) => if (n == ordinal) unpickleCase[T, elems](mapper, buf, ordinal) @@ -246,7 +246,7 @@ object Pickler { throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal") } - inline def derived[T, S <: Shape](implicit ev: HasShape[T, S]): Pickler[T] = new { + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Pickler[T] = new { def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = inline erasedValue[S] match { case _: Shape.Cases[alts] => pickleCases[T, alts](ev, buf, x, 0) From ba601becf3342a5cb93c4c30ba3e109342e6b3f1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 24 Nov 2018 19:54:25 +0100 Subject: [PATCH 11/13] Handle labels --- tests/run/typeclass-derivation2.scala | 188 +++++++++++++++++++------- 1 file changed, 140 insertions(+), 48 deletions(-) diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 45822b2f767d..56140ba74042 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -1,11 +1,34 @@ import scala.collection.mutable +import scala.annotation.tailrec + +trait Deriving { + import Deriving._ + + protected def caseNames: Array[String] + + private final val separator = '\000' + + private def cname(ordinal: Int, idx: Int): String = { + val cnames = caseNames(ordinal) + @tailrec def separatorPos(from: Int): Int = + if (from == cnames.length || cnames(from) == separator) from + else separatorPos(from + 1) + @tailrec def findName(count: Int, idx: Int): String = + if (idx == cnames.length) "" + else if (count == 0) cnames.substring(idx, separatorPos(idx)) + else findName(if (cnames(idx) == separator) count - 1 else count, idx + 1) + findName(idx, 0) + } + + def caseName(ordinal: Int): String = cname(ordinal, 0) + def elementName(ordinal: Int, idx: Int) = cname(ordinal, idx + 1) +} // Generic deriving infrastructure object Deriving { /** The shape of an ADT in a sum of products representation */ enum Shape { - /** A sum with alternative types `Alts` */ case Cases[Alts <: Tuple] @@ -17,24 +40,41 @@ object Deriving { * @param ordinal The ordinal value of the case in the list of the ADT's cases * @param elems The elements of the case */ - class CaseMirror[+T](val ordinal: Int, val elems: Product) { + class CaseMirror[+T](val deriving: Deriving, val ordinal: Int, val elems: Product) { /** A generic case with elements given as an array */ - def this(ordinal: Int, elems: Array[AnyRef]) = - this(ordinal, new ArrayProduct(elems)) + def this(deriving: Deriving, ordinal: Int, elems: Array[AnyRef]) = + this(deriving, ordinal, new ArrayProduct(elems)) /** A generic case with an initial empty array of `numElems` elements, to be filled in. */ - def this(ordinal: Int, numElems: Int) = - this(ordinal, new Array[AnyRef](numElems)) + def this(deriving: Deriving, ordinal: Int, numElems: Int) = + this(deriving, ordinal, new Array[AnyRef](numElems)) /** A generic case with no elements */ - def this(ordinal: Int) = - this(ordinal, EmptyProduct) + def this(deriving: Deriving, ordinal: Int) = + this(deriving, ordinal, EmptyProduct) /** The `n`'th element of this generic case */ def apply(n: Int): Any = elems.productElement(n) + + def caseName: String = deriving.caseName(ordinal) + def elementName(idx: Int) = deriving.elementName(ordinal, idx) + } + + /** A class for mapping between an ADT value and + * the case mirror that represents the value. + */ + abstract class Reflected[T] { + def reflect(x: T): CaseMirror[T] + def reify(c: CaseMirror[T]): T + def deriving: Deriving } + /** Every generic derivation starts with a typeclass instance of this type. + * It informs that type `T` has shape `S` and also allows runtime reflection on `T`. + */ + abstract class Shaped[T, S <: Shape] extends Reflected[T] + /** Helper class to turn arrays into products */ private class ArrayProduct(val elems: Array[AnyRef]) extends Product { def canEqual(that: Any): Boolean = true @@ -50,19 +90,6 @@ object Deriving { def productElement(n: Int) = throw new IndexOutOfBoundsException def productArity = 0 } - - /** A class for mapping between an ADT value and - * the generic case that represents the value - */ - abstract class MirrorMapper[T] { - def toMirror(x: T): CaseMirror[T] - def fromMirror(c: CaseMirror[T]): T - } - - /** Every generic derivation starts with a typeclass instance of this type. - * It informs that type `T` has shape `S` and is backed by the extended generic mapper. - */ - abstract class Shaped[T, S <: Shape] extends MirrorMapper[T] } // An algebraic datatype @@ -72,7 +99,7 @@ enum Lst[+T] // derives Eq, Pickler case Nil } -object Lst { +object Lst extends Deriving { // common compiler-generated infrastructure import Deriving._ @@ -81,40 +108,47 @@ object Lst { Shape.Case[Nil.type, Unit] )] - val NilMirror = new CaseMirror[Nil.type](1) + val NilMirror = new CaseMirror[Nil.type](Lst, 1) implicit def lstShape[T]: Shaped[Lst[T], Shape[T]] = new { - def toMirror(xs: Lst[T]): CaseMirror[Lst[T]] = xs match { - case xs: Cons[T] => new CaseMirror[Cons[T]](0, xs) + def reflect(xs: Lst[T]): CaseMirror[Lst[T]] = xs match { + case xs: Cons[T] => new CaseMirror[Cons[T]](Lst, 0, xs) case Nil => NilMirror } - def fromMirror(c: CaseMirror[Lst[T]]): Lst[T] = c.ordinal match { + def reify(c: CaseMirror[Lst[T]]): Lst[T] = c.ordinal match { case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) case 1 => Nil } + def deriving = Lst } - // two clauses that could be generated from a `derives` clause + protected val caseNames = Array("Cons\000hd\000tl", "Nil") + + // three clauses that could be generated from a `derives` clause implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derived implicit def LstPickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived + implicit def LstShow[T: Show]: Show[Lst[T]] = Show.derived } // A simple product type case class Pair[T](x: T, y: T) // derives Eq, Pickler -object Pair { +object Pair extends Deriving { // common compiler-generated infrastructure import Deriving._ type Shape[T] = Shape.Case[Pair[T], (T, T)] implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new { - def toMirror(xy: Pair[T]) = - new CaseMirror[Pair[T]](0, xy) - def fromMirror(c: CaseMirror[Pair[T]]): Pair[T] = + def reflect(xy: Pair[T]) = + new CaseMirror[Pair[T]](Pair, 0, xy) + def reify(c: CaseMirror[Pair[T]]): Pair[T] = Pair(c(0).asInstanceOf, c(1).asInstanceOf) + def deriving = Pair } + protected val caseNames = Array("Pair\000x\000y") + // two clauses that could be generated from a `derives` clause implicit def PairEq[T: Eq]: Eq[Pair[T]] = Eq.derived implicit def PairPickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived @@ -142,25 +176,25 @@ object Eq { true } - inline def eqlCase[T, Elems <: Tuple](mapper: MirrorMapper[T], x: T, y: T) = - eqlElems[Elems](mapper.toMirror(x), mapper.toMirror(y), 0) + inline def eqlCase[T, Elems <: Tuple](r: Reflected[T], x: T, y: T) = + eqlElems[Elems](r.reflect(x), r.reflect(y), 0) - inline def eqlCases[T, Alts <: Tuple](mapper: MirrorMapper[T], x: T, y: T): Boolean = + inline def eqlCases[T, Alts <: Tuple](r: Reflected[T], x: T, y: T): Boolean = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => x match { case x: `alt` => y match { - case y: `alt` => eqlCase[T, elems](mapper, x, y) + case y: `alt` => eqlCase[T, elems](r, x, y) case _ => false } - case _ => eqlCases[T, alts1](mapper, x, y) + case _ => eqlCases[T, alts1](r, x, y) } case _: Unit => false } - inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]) <: Eq[T] = new { + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Eq[T] = new { def eql(x: T, y: T): Boolean = inline erasedValue[S] match { case _: Shape.Cases[alts] => eqlCases[T, alts](ev, x, y) @@ -198,18 +232,18 @@ object Pickler { case _: Unit => } - inline def pickleCase[T, Elems <: Tuple](mapper: MirrorMapper[T], buf: mutable.ListBuffer[Int], x: T): Unit = - pickleElems[Elems](buf, mapper.toMirror(x), 0) + inline def pickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], x: T): Unit = + pickleElems[Elems](buf, r.reflect(x), 0) - inline def pickleCases[T, Alts <: Tuple](mapper: MirrorMapper[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit = + inline def pickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => x match { case x: `alt` => buf += n - pickleCase[T, elems](mapper, buf, x) + pickleCase[T, elems](r, buf, x) case _ => - pickleCases[T, alts1](mapper, buf, x, n + 1) + pickleCases[T, alts1](r, buf, x, n + 1) } case _: Unit => } @@ -226,22 +260,22 @@ object Pickler { case _: Unit => } - inline def unpickleCase[T, Elems <: Tuple](mapper: MirrorMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + inline def unpickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) - mapper.fromMirror(new CaseMirror[T](ordinal)) + r.reify(new CaseMirror[T](r.deriving, ordinal)) else { val elems = new Array[Object](size) unpickleElems[Elems](buf, elems, 0) - mapper.fromMirror(new CaseMirror[T](ordinal, elems)) + r.reify(new CaseMirror[T](r.deriving, ordinal, elems)) } } - inline def unpickleCases[T, Alts <: Tuple](mapper: MirrorMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = + inline def unpickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = inline erasedValue[Alts] match { case _: (Shape.Case[_, elems] *: alts1) => - if (n == ordinal) unpickleCase[T, elems](mapper, buf, ordinal) - else unpickleCases[T, alts1](mapper, buf, ordinal, n + 1) + if (n == ordinal) unpickleCase[T, elems](r, buf, ordinal) + else unpickleCases[T, alts1](r, buf, ordinal, n + 1) case _ => throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal") } @@ -267,6 +301,59 @@ object Pickler { } } +// A third typeclass, making use of names +trait Show[T] { + def show(x: T): String +} +object Show { + import scala.typelevel._ + import Deriving._ + + inline def tryShow[T](x: T): String = implicit match { + case s: Show[T] => s.show(x) + } + + inline def showElems[Elems <: Tuple](elems: CaseMirror[_], n: Int): List[String] = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + val formal = elems.elementName(n) + val actual = tryShow[elem](elems(n).asInstanceOf) + s"$formal = $actual" :: showElems[elems1](elems, n + 1) + case _: Unit => + Nil + } + + inline def showCase[T, Elems <: Tuple](r: Reflected[T], x: T): String = { + val mirror = r.reflect(x) + val args = showElems[Elems](mirror, 0).mkString(", ") + s"${mirror.caseName}($args)" + } + + inline def showCases[T, Alts <: Tuple](r: Reflected[T], x: T): String = + inline erasedValue[Alts] match { + case _: (Shape.Case[alt, elems] *: alts1) => + x match { + case x: `alt` => showCase[T, elems](r, x) + case _ => showCases[T, alts1](r, x) + } + case _: Unit => + throw new MatchError(x) + } + + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Show[T] = new { + def show(x: T): String = inline erasedValue[S] match { + case _: Shape.Cases[alts] => + showCases[T, alts](ev, x) + case _: Shape.Case[_, elems] => + showCase[T, elems](ev, x) + } + } + + implicit object IntShow extends Show[Int] { + def show(x: Int): String = x.toString + } +} + // Tests object Test extends App { import Deriving._ @@ -317,4 +404,9 @@ object Test extends App { 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) } \ No newline at end of file From 06125b5e65fe8cb1ebbcbd118095461de6fcd0ac Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 24 Nov 2018 22:04:53 +0100 Subject: [PATCH 12/13] Add labels to derivation framework --- tests/run/typeclass-derivation2.check | 2 + tests/run/typeclass-derivation2.scala | 105 +++++++++++++++----------- 2 files changed, 61 insertions(+), 46 deletions(-) diff --git a/tests/run/typeclass-derivation2.check b/tests/run/typeclass-derivation2.check index 0ab528b9da36..4ec92ef1a1e0 100644 --- a/tests/run/typeclass-derivation2.check +++ b/tests/run/typeclass-derivation2.check @@ -4,3 +4,5 @@ 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())) diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 56140ba74042..1a078341ab53 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -4,72 +4,85 @@ import scala.annotation.tailrec trait Deriving { import Deriving._ - protected def caseNames: Array[String] + /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ + def mirror[T](ordinal: Int, product: Product): CaseMirror[T] = + new CaseMirror(this, ordinal, product) + + /** A mirror with elements given as an array */ + def mirror[T](ordinal: Int, elems: Array[AnyRef]): CaseMirror[T] = + mirror(ordinal, new ArrayProduct(elems)) + + /** A mirror with an initial empty array of `numElems` elements, to be filled in. */ + def mirror[T](ordinal: Int, numElems: Int): CaseMirror[T] = + mirror(ordinal, new Array[AnyRef](numElems)) + + /** A mirror of a case with no elements */ + def mirror[T](ordinal: Int): CaseMirror[T] = + mirror(ordinal, EmptyProduct) + + /** The case and element labels of the described ADT as encoded strings. */ + protected def caseLabels: Array[String] private final val separator = '\000' - private def cname(ordinal: Int, idx: Int): String = { - val cnames = caseNames(ordinal) + private def label(ordinal: Int, idx: Int): String = { + val labels = caseLabels(ordinal) @tailrec def separatorPos(from: Int): Int = - if (from == cnames.length || cnames(from) == separator) from + if (from == labels.length || labels(from) == separator) from else separatorPos(from + 1) - @tailrec def findName(count: Int, idx: Int): String = - if (idx == cnames.length) "" - else if (count == 0) cnames.substring(idx, separatorPos(idx)) - else findName(if (cnames(idx) == separator) count - 1 else count, idx + 1) - findName(idx, 0) + @tailrec def findLabel(count: Int, idx: Int): String = + if (idx == labels.length) "" + else if (count == 0) labels.substring(idx, separatorPos(idx)) + else findLabel(if (labels(idx) == separator) count - 1 else count, idx + 1) + findLabel(idx, 0) } - - def caseName(ordinal: Int): String = cname(ordinal, 0) - def elementName(ordinal: Int, idx: Int) = cname(ordinal, idx + 1) } // Generic deriving infrastructure object Deriving { - /** The shape of an ADT in a sum of products representation */ - enum Shape { - /** A sum with alternative types `Alts` */ - case Cases[Alts <: Tuple] - - /** A product type `T` with element types `Elems` */ - case Case[T, Elems <: Tuple] - } - /** A generic representation of a case in an ADT + * @param deriving The companion object of the ADT * @param ordinal The ordinal value of the case in the list of the ADT's cases * @param elems The elements of the case */ class CaseMirror[+T](val deriving: Deriving, val ordinal: Int, val elems: Product) { - /** A generic case with elements given as an array */ - def this(deriving: Deriving, ordinal: Int, elems: Array[AnyRef]) = - this(deriving, ordinal, new ArrayProduct(elems)) - - /** A generic case with an initial empty array of `numElems` elements, to be filled in. */ - def this(deriving: Deriving, ordinal: Int, numElems: Int) = - this(deriving, ordinal, new Array[AnyRef](numElems)) - - /** A generic case with no elements */ - def this(deriving: Deriving, ordinal: Int) = - this(deriving, ordinal, EmptyProduct) - /** The `n`'th element of this generic case */ def apply(n: Int): Any = elems.productElement(n) - def caseName: String = deriving.caseName(ordinal) - def elementName(idx: Int) = deriving.elementName(ordinal, idx) + /** The name of the constructor of the case reflected by this mirror */ + def caseLabel: String = deriving.label(ordinal, 0) + + /** The label of the `n`'th element of the case reflected by this mirror */ + def elementLabel(n: Int) = deriving.label(ordinal, n + 1) } /** A class for mapping between an ADT value and * the case mirror that represents the value. */ abstract class Reflected[T] { + + /** The case mirror corresponding to ADT instance `x` */ def reflect(x: T): CaseMirror[T] - def reify(c: CaseMirror[T]): T + + /** The ADT instance corresponding to given `mirror` */ + def reify(mirror: CaseMirror[T]): T + + /** The companion object of the ADT */ def deriving: Deriving } + /** The shape of an ADT in a sum of products representation */ + enum Shape { + + /** A sum with alternative types `Alts` */ + case Cases[Alts <: Tuple] + + /** A product type `T` with element types `Elems` */ + case Case[T, Elems <: Tuple] + } + /** Every generic derivation starts with a typeclass instance of this type. * It informs that type `T` has shape `S` and also allows runtime reflection on `T`. */ @@ -108,11 +121,11 @@ object Lst extends Deriving { Shape.Case[Nil.type, Unit] )] - val NilMirror = new CaseMirror[Nil.type](Lst, 1) + val NilMirror = mirror[Nil.type](1) implicit def lstShape[T]: Shaped[Lst[T], Shape[T]] = new { def reflect(xs: Lst[T]): CaseMirror[Lst[T]] = xs match { - case xs: Cons[T] => new CaseMirror[Cons[T]](Lst, 0, xs) + case xs: Cons[T] => mirror[Cons[T]](0, xs) case Nil => NilMirror } def reify(c: CaseMirror[Lst[T]]): Lst[T] = c.ordinal match { @@ -122,7 +135,7 @@ object Lst extends Deriving { def deriving = Lst } - protected val caseNames = Array("Cons\000hd\000tl", "Nil") + protected val caseLabels = Array("Cons\000hd\000tl", "Nil") // three clauses that could be generated from a `derives` clause implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derived @@ -141,13 +154,13 @@ object Pair extends Deriving { implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new { def reflect(xy: Pair[T]) = - new CaseMirror[Pair[T]](Pair, 0, xy) + mirror[Pair[T]](0, xy) def reify(c: CaseMirror[Pair[T]]): Pair[T] = Pair(c(0).asInstanceOf, c(1).asInstanceOf) def deriving = Pair } - protected val caseNames = Array("Pair\000x\000y") + protected val caseLabels = Array("Pair\000x\000y") // two clauses that could be generated from a `derives` clause implicit def PairEq[T: Eq]: Eq[Pair[T]] = Eq.derived @@ -263,11 +276,11 @@ object Pickler { inline def unpickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) - r.reify(new CaseMirror[T](r.deriving, ordinal)) + r.reify(r.deriving.mirror[T](ordinal)) else { val elems = new Array[Object](size) unpickleElems[Elems](buf, elems, 0) - r.reify(new CaseMirror[T](r.deriving, ordinal, elems)) + r.reify(r.deriving.mirror[T](ordinal, elems)) } } @@ -301,7 +314,7 @@ object Pickler { } } -// A third typeclass, making use of names +// A third typeclass, making use of labels trait Show[T] { def show(x: T): String } @@ -316,7 +329,7 @@ object Show { inline def showElems[Elems <: Tuple](elems: CaseMirror[_], n: Int): List[String] = inline erasedValue[Elems] match { case _: (elem *: elems1) => - val formal = elems.elementName(n) + val formal = elems.elementLabel(n) val actual = tryShow[elem](elems(n).asInstanceOf) s"$formal = $actual" :: showElems[elems1](elems, n + 1) case _: Unit => @@ -326,7 +339,7 @@ object Show { inline def showCase[T, Elems <: Tuple](r: Reflected[T], x: T): String = { val mirror = r.reflect(x) val args = showElems[Elems](mirror, 0).mkString(", ") - s"${mirror.caseName}($args)" + s"${mirror.caseLabel}($args)" } inline def showCases[T, Alts <: Tuple](r: Reflected[T], x: T): String = From 6eace0606379e35cd21abed266274023ecddefa4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 24 Nov 2018 22:42:27 +0100 Subject: [PATCH 13/13] Drop unused parameter --- tests/run/typeclass-derivation2.scala | 44 ++++++++++++++------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 1a078341ab53..69863715976f 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -5,19 +5,19 @@ trait Deriving { import Deriving._ /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ - def mirror[T](ordinal: Int, product: Product): CaseMirror[T] = - new CaseMirror(this, ordinal, product) + def mirror(ordinal: Int, product: Product): Mirror = + new Mirror(this, ordinal, product) /** A mirror with elements given as an array */ - def mirror[T](ordinal: Int, elems: Array[AnyRef]): CaseMirror[T] = + 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[T](ordinal: Int, numElems: Int): CaseMirror[T] = + def mirror(ordinal: Int, numElems: Int): Mirror = mirror(ordinal, new Array[AnyRef](numElems)) /** A mirror of a case with no elements */ - def mirror[T](ordinal: Int): CaseMirror[T] = + def mirror(ordinal: Int): Mirror = mirror(ordinal, EmptyProduct) /** The case and element labels of the described ADT as encoded strings. */ @@ -46,7 +46,7 @@ object Deriving { * @param ordinal The ordinal value of the case in the list of the ADT's cases * @param elems The elements of the case */ - class CaseMirror[+T](val deriving: Deriving, val ordinal: Int, val elems: Product) { + class Mirror(val deriving: Deriving, val ordinal: Int, val elems: Product) { /** The `n`'th element of this generic case */ def apply(n: Int): Any = elems.productElement(n) @@ -64,16 +64,18 @@ object Deriving { abstract class Reflected[T] { /** The case mirror corresponding to ADT instance `x` */ - def reflect(x: T): CaseMirror[T] + def reflect(x: T): Mirror /** The ADT instance corresponding to given `mirror` */ - def reify(mirror: CaseMirror[T]): T + def reify(mirror: Mirror): T /** The companion object of the ADT */ def deriving: Deriving } - /** The shape of an ADT in a sum of products representation */ + /** The shape of an ADT. + * This is eithe a product (`Case`) or a sum (`Cases`) of products. + */ enum Shape { /** A sum with alternative types `Alts` */ @@ -84,7 +86,7 @@ object Deriving { } /** Every generic derivation starts with a typeclass instance of this type. - * It informs that type `T` has shape `S` and also allows runtime reflection on `T`. + * It informs that type `T` has shape `S` and also implements runtime reflection on `T`. */ abstract class Shaped[T, S <: Shape] extends Reflected[T] @@ -121,14 +123,14 @@ object Lst extends Deriving { Shape.Case[Nil.type, Unit] )] - val NilMirror = mirror[Nil.type](1) + val NilMirror = mirror(1) implicit def lstShape[T]: Shaped[Lst[T], Shape[T]] = new { - def reflect(xs: Lst[T]): CaseMirror[Lst[T]] = xs match { - case xs: Cons[T] => mirror[Cons[T]](0, xs) + def reflect(xs: Lst[T]): Mirror = xs match { + case xs: Cons[T] => mirror(0, xs) case Nil => NilMirror } - def reify(c: CaseMirror[Lst[T]]): Lst[T] = c.ordinal match { + def reify(c: Mirror): Lst[T] = c.ordinal match { case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) case 1 => Nil } @@ -154,8 +156,8 @@ object Pair extends Deriving { implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new { def reflect(xy: Pair[T]) = - mirror[Pair[T]](0, xy) - def reify(c: CaseMirror[Pair[T]]): Pair[T] = + mirror(0, xy) + def reify(c: Mirror): Pair[T] = Pair(c(0).asInstanceOf, c(1).asInstanceOf) def deriving = Pair } @@ -180,7 +182,7 @@ object Eq { case eq: Eq[T] => eq.eql(x, y) } - inline def eqlElems[Elems <: Tuple](xs: CaseMirror[_], ys: CaseMirror[_], n: Int): Boolean = + inline def eqlElems[Elems <: Tuple](xs: Mirror, ys: Mirror, n: Int): Boolean = inline erasedValue[Elems] match { case _: (elem *: elems1) => tryEql[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) && @@ -237,7 +239,7 @@ object Pickler { case pkl: Pickler[T] => pkl.pickle(buf, x) } - inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: CaseMirror[_], n: Int): Unit = + inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Mirror, n: Int): Unit = inline erasedValue[Elems] match { case _: (elem *: elems1) => tryPickle[elem](buf, elems(n).asInstanceOf[elem]) @@ -276,11 +278,11 @@ object Pickler { inline def unpickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) - r.reify(r.deriving.mirror[T](ordinal)) + r.reify(r.deriving.mirror(ordinal)) else { val elems = new Array[Object](size) unpickleElems[Elems](buf, elems, 0) - r.reify(r.deriving.mirror[T](ordinal, elems)) + r.reify(r.deriving.mirror(ordinal, elems)) } } @@ -326,7 +328,7 @@ object Show { case s: Show[T] => s.show(x) } - inline def showElems[Elems <: Tuple](elems: CaseMirror[_], n: Int): List[String] = + inline def showElems[Elems <: Tuple](elems: Mirror, n: Int): List[String] = inline erasedValue[Elems] match { case _: (elem *: elems1) => val formal = elems.elementLabel(n)