From 4d80935b51253156e49deffecb811649703a9b46 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 17 Nov 2017 16:57:39 +0100 Subject: [PATCH 01/32] Register children in Typed instead of PostTyper This commit breaks things, for instance pos/i1795.scala that fail during pickling... To be investigated later. --- .../src/dotty/tools/dotc/transform/PostTyper.scala | 13 ++++--------- compiler/src/dotty/tools/dotc/typer/Typer.scala | 4 ++++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 0bec80c49cb9..51f1ef006dee 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -39,12 +39,10 @@ import reporting.diagnostic.messages.{NotAMember, SuperCallsNotAllowedInline} * * (9) Adds SourceFile annotations to all top-level classes and objects * - * (10) Adds Child annotations to all sealed classes - * - * (11) Minimizes `call` fields of `Inline` nodes to just point to the toplevel + * (10) Minimizes `call` fields of `Inline` nodes to just point to the toplevel * class from which code was inlined. * - * (12) Converts GADT bounds into normal type bounds + * (11) Converts GADT bounds into normal type bounds * * The reason for making this a macro transform is that some functions (in particular * super and protected accessors and instantiation checks) are naturally top-down and @@ -104,11 +102,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase private def transformAnnot(annot: Annotation)(implicit ctx: Context): Annotation = annot.derivedAnnotation(transformAnnot(annot.tree)) - private def transformMemberDef(tree: MemberDef)(implicit ctx: Context): Unit = { - val sym = tree.symbol - sym.registerIfChild() - sym.transformAnnotations(transformAnnot) - } + private def transformMemberDef(tree: MemberDef)(implicit ctx: Context): Unit = + tree.symbol.transformAnnotations(transformAnnot) private def transformSelect(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = { val qual = tree.qualifier diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 1519780ea8be..1b6a61134af9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1476,6 +1476,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit cls.registerTree(cdef1) + // Adds Child annotations to sealed classes + if (!cdef1.symbol.denot.isRefinementClass) + cdef1.symbol.registerIfChild() + cdef1 // todo later: check that From 1e7ab291bb3e5dc36f194e960bb31ad4cfdab9f6 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 15 Nov 2017 14:11:21 +0100 Subject: [PATCH 02/32] Add skeleton of generic library --- library/src/dotty/generic/Prod.scala | 5 +++++ library/src/dotty/generic/Representable.scala | 8 ++++++++ library/src/dotty/generic/Sum.scala | 7 +++++++ 3 files changed, 20 insertions(+) create mode 100644 library/src/dotty/generic/Prod.scala create mode 100644 library/src/dotty/generic/Representable.scala create mode 100644 library/src/dotty/generic/Sum.scala diff --git a/library/src/dotty/generic/Prod.scala b/library/src/dotty/generic/Prod.scala new file mode 100644 index 000000000000..8d1c0bd24208 --- /dev/null +++ b/library/src/dotty/generic/Prod.scala @@ -0,0 +1,5 @@ +package dotty.generic + +sealed trait Prod[X] +final case class PCons[H[_], T[t] <: Prod[t], X](head: H[X], tail: T[X]) extends Prod[X] +final case class PNil[X]() extends Prod[X] // Cannot be put in `object Prod` because of #3422 diff --git a/library/src/dotty/generic/Representable.scala b/library/src/dotty/generic/Representable.scala new file mode 100644 index 000000000000..cad029f0f056 --- /dev/null +++ b/library/src/dotty/generic/Representable.scala @@ -0,0 +1,8 @@ +package dotty.generic + +trait Representable[A] { + type Repr[t] // <: Sum[t] | Prod[t] ← for when we stop cross compiling + + def to[T](a: A): Repr[T] + def from[T](r: Repr[T]): A +} diff --git a/library/src/dotty/generic/Sum.scala b/library/src/dotty/generic/Sum.scala new file mode 100644 index 000000000000..0def714ea614 --- /dev/null +++ b/library/src/dotty/generic/Sum.scala @@ -0,0 +1,7 @@ +package dotty.generic + +sealed trait Sum[X] +sealed trait SCons[H[_], T[t] <: Sum[t], X] extends Sum[X] +final case class SLeft[H[_], T[t] <: Sum[t], X](head: H[X]) extends SCons[H, T, X] +final case class SRight[H[_], T[t] <: Sum[t], X](tail: T[X]) extends SCons[H, T, X] +sealed trait SNil[X] extends Sum[X] From e08a98cedc7767507897231cf47aab657539dc10 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 15 Nov 2017 14:36:26 +0100 Subject: [PATCH 03/32] Add implicit search hook --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 4 ++++ compiler/src/dotty/tools/dotc/typer/Implicits.scala | 9 ++++++++- tests/run/generic.scala | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/run/generic.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index e3bcd1e1fda0..bef70f73c099 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -597,6 +597,10 @@ class Definitions { def Not_value(implicit ctx: Context) = NotModule.requiredMethod(nme.value) + lazy val RepresentableType = ctx.requiredClassRef("dotty.generic.Representable") + def RepresentableClass(implicit ctx: Context) = RepresentableType.symbol.asClass + def RepresentableModule(implicit ctx: Context) = RepresentableClass.companionModule + lazy val XMLTopScopeModuleRef = ctx.requiredModuleRef("scala.xml.TopScope") // Annotation base classes diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 302d19924d13..217314df0808 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -585,7 +585,7 @@ trait Implicits { self: Typer => /** If `formal` is of the form Eq[T, U], where no `Eq` instance exists for * either `T` or `U`, synthesize `Eq.eqAny[T, U]` as solution. - */ + */ def synthesizedEq(formal: Type)(implicit ctx: Context): Tree = { //println(i"synth eq $formal / ${formal.argTypes}%, %") formal.argTypes match { @@ -598,6 +598,11 @@ trait Implicits { self: Typer => } } + /** */ + def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = { + ??? + } + def hasEq(tp: Type): Boolean = inferImplicit(defn.EqType.appliedTo(tp, tp), EmptyTree, pos).isSuccess @@ -645,6 +650,8 @@ trait Implicits { self: Typer => synthesizedClassTag(formalValue).orElse(tree) else if (formalValue.isRef(defn.EqClass)) synthesizedEq(formalValue).orElse(tree) + else if (formalValue.isRef(defn.RepresentableClass)) + synthesizedRepresentable(formalValue).orElse(tree) else tree } diff --git a/tests/run/generic.scala b/tests/run/generic.scala new file mode 100644 index 000000000000..08ee2b2bb426 --- /dev/null +++ b/tests/run/generic.scala @@ -0,0 +1,11 @@ +import dotty.generic._ + +case class Foo(i: Int, s: String) + +object Test { + def main(args: Array[String]): Unit = { + val foo = Foo(1, "s") + val g = implicitly[Representable[Foo]] + assert(g.to(foo) == PCons(1, PCons("s", PNil))) + } +} From 800bc0505345dfe276b362fab891b28e32982f33 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 16 Nov 2017 15:25:07 +0100 Subject: [PATCH 04/32] Synthesise Repr type alias for products --- .../dotty/tools/dotc/core/Definitions.scala | 7 +++++++ .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../dotty/tools/dotc/typer/Implicits.scala | 21 ++++++++++++++++++- library/src/dotty/generic/Representable.scala | 4 ++-- tests/run/generic.scala | 8 ++++--- 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index bef70f73c099..56413f32d7df 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -597,9 +597,16 @@ class Definitions { def Not_value(implicit ctx: Context) = NotModule.requiredMethod(nme.value) + // Generic classes lazy val RepresentableType = ctx.requiredClassRef("dotty.generic.Representable") def RepresentableClass(implicit ctx: Context) = RepresentableType.symbol.asClass def RepresentableModule(implicit ctx: Context) = RepresentableClass.companionModule + lazy val PNilType = ctx.requiredClassRef("dotty.generic.PNil") + def PNilClass(implicit ctx: Context) = PNilType.symbol.asClass + def PNilModule(implicit ctx: Context) = PNilClass.companionModule + lazy val PConsType = ctx.requiredClassRef("dotty.generic.PCons") + def PConsClass(implicit ctx: Context) = PConsType.symbol.asClass + def PConsModule(implicit ctx: Context) = PConsClass.companionModule lazy val XMLTopScopeModuleRef = ctx.requiredModuleRef("scala.xml.TopScope") diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index be886b2b3bd7..feb247aa9544 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -743,6 +743,7 @@ object StdNames { final val Conforms = encode("<:<") final val Uninstantiated: TypeName = "?$" + final val Repr: TypeName = "Repr" } abstract class JavaNames[N <: Name] extends DefinedNames[N] { diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 217314df0808..f4dfb7115b68 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -600,7 +600,26 @@ trait Implicits { self: Typer => /** */ def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = { - ??? + formal.argTypes match { + case arg :: Nil if defn.isProductSubType(arg) => + typed { + import untpd._ + val x = tpnme.syntheticTypeParamName(0) + val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) + val reprRhs = + productSelectorTypes(arg).foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => + val constCur = LambdaTypeTree(TypeDef(x, noBounds) :: Nil, TypeTree(cur)) + val pconsArgs = constCur :: acc :: Ident(x) :: Nil + val rhs = AppliedTypeTree(TypeTree(defn.PConsType), pconsArgs) + LambdaTypeTree(TypeDef(x, noBounds) :: Nil, rhs) + } + val repr = TypeDef(tpnme.Repr, reprRhs) // TODO: val from =; val to = + val parents = TypeTree(defn.RepresentableType.appliedTo(arg)) :: Nil + New(Template(emptyConstructor, parents, EmptyValDef, List(repr))).withPos(pos) + } + case _ => + EmptyTree + } } def hasEq(tp: Type): Boolean = diff --git a/library/src/dotty/generic/Representable.scala b/library/src/dotty/generic/Representable.scala index cad029f0f056..c2602017de54 100644 --- a/library/src/dotty/generic/Representable.scala +++ b/library/src/dotty/generic/Representable.scala @@ -3,6 +3,6 @@ package dotty.generic trait Representable[A] { type Repr[t] // <: Sum[t] | Prod[t] ← for when we stop cross compiling - def to[T](a: A): Repr[T] - def from[T](r: Repr[T]): A + // def to[T](a: A): Repr[T] + // def from[T](r: Repr[T]): A } diff --git a/tests/run/generic.scala b/tests/run/generic.scala index 08ee2b2bb426..d69fdf43ed2d 100644 --- a/tests/run/generic.scala +++ b/tests/run/generic.scala @@ -3,9 +3,11 @@ import dotty.generic._ case class Foo(i: Int, s: String) object Test { + def the[T](implicit ev: T): ev.type = ev + type &:[H, T[t] <: Prod[t]] = [X] => PCons[[Y] => H, T, X] + def main(args: Array[String]): Unit = { - val foo = Foo(1, "s") - val g = implicitly[Representable[Foo]] - assert(g.to(foo) == PCons(1, PCons("s", PNil))) + val g = the[Representable[Foo]] + val a: Representable[Foo] { type Repr = Int &: String &: PNil } = g } } From 40a8ee4331f62b236376a5eef2aaa38738e18e51 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 17 Nov 2017 14:11:10 +0100 Subject: [PATCH 05/32] Implement selectorName using productAccessorName --- compiler/src/dotty/tools/dotc/core/StdNames.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index feb247aa9544..ad5b57d999e4 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -723,7 +723,7 @@ object StdNames { def newBitmapName(bitmapPrefix: TermName, n: Int): TermName = bitmapPrefix ++ n.toString - def selectorName(n: Int): TermName = "_" + (n + 1) + def selectorName(n: Int): TermName = productAccessorName(n + 1) object primitive { val arrayApply: TermName = "[]apply" From 659ec4e69e05f35ec8a60c868d153a3c124146a8 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 17 Nov 2017 15:39:05 +0100 Subject: [PATCH 06/32] Synthesise to and from for products --- .../src/dotty/tools/dotc/core/StdNames.scala | 4 + .../dotty/tools/dotc/typer/Implicits.scala | 101 ++++++++++++++++-- library/src/dotty/generic/Representable.scala | 4 +- tests/run/generic.scala | 6 ++ 4 files changed, 103 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index ad5b57d999e4..200f62bd6ff6 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -340,6 +340,8 @@ object StdNames { val NoSymbol: N = "NoSymbol" val NoType: N = "NoType" val Pair: N = "Pair" + val PCons: N = "PCons" + val PNil: N = "PNil" val Ref: N = "Ref" val RootPackage: N = "RootPackage" val RootClass: N = "RootClass" @@ -421,6 +423,7 @@ object StdNames { val flagsFromBits : N = "flagsFromBits" val flatMap: N = "flatMap" val foreach: N = "foreach" + val from: N = "from" val genericArrayOps: N = "genericArrayOps" val get: N = "get" val getClass_ : N = "getClass" @@ -515,6 +518,7 @@ object StdNames { val this_ : N = "this" val thisPrefix : N = "thisPrefix" val throw_ : N = "throw" + val to: N = "to" val toArray: N = "toArray" val toList: N = "toList" val toObjectArray : N = "toObjectArray" diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index f4dfb7115b68..53b12bce23b8 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -598,24 +598,105 @@ trait Implicits { self: Typer => } } - /** */ + /** + * When A is a product, synthesize an instance of the following shape: + * + * ``` + * new Representable[A] { + * type Repr[t] = T1 &: T2 &: ... &: Tn &: PNil + * + * def to[T](a: A): Repr[T] = + * PCons(a._1, (PCons(a._2, ... PCons(a._m, PNil())))) + * + * def from[T](r: Repr[T]): A = { + * val _1 = r.head + * val x$1 = r.tail + * val _2 = x$1.head + * val x$2 = x$1.tail + * ... + * val _n = x$n-1.head + * new A(_1, _2, ..., _n) + * } + * } + * ``` + */ def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = { formal.argTypes match { - case arg :: Nil if defn.isProductSubType(arg) => + case (A @ _) :: Nil if defn.isProductSubType(A) => typed { import untpd._ - val x = tpnme.syntheticTypeParamName(0) + val productTypes = productSelectorTypes(A) + val productTypesSize = productTypes.size + val X = tpnme.syntheticTypeParamName(0) val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) + + // This fold generates T1 &: T2 &: ... &: Tn &: PNil, where + // type &:[H, T[t] <: Prod[t]] = [X] => PCons[[Y] => H, T, X] val reprRhs = - productSelectorTypes(arg).foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => - val constCur = LambdaTypeTree(TypeDef(x, noBounds) :: Nil, TypeTree(cur)) - val pconsArgs = constCur :: acc :: Ident(x) :: Nil + productTypes.foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => + val constCur = LambdaTypeTree(TypeDef(X, noBounds) :: Nil, TypeTree(cur)) + val pconsArgs = constCur :: acc :: Ident(X) :: Nil val rhs = AppliedTypeTree(TypeTree(defn.PConsType), pconsArgs) - LambdaTypeTree(TypeDef(x, noBounds) :: Nil, rhs) + LambdaTypeTree(TypeDef(X, noBounds) :: Nil, rhs) } - val repr = TypeDef(tpnme.Repr, reprRhs) // TODO: val from =; val to = - val parents = TypeTree(defn.RepresentableType.appliedTo(arg)) :: Nil - New(Template(emptyConstructor, parents, EmptyValDef, List(repr))).withPos(pos) + // type Repr[t] = T1 &: T2 &: ... &: Tn &: PNil + val repr = TypeDef(tpnme.Repr, reprRhs) + val reprX = AppliedTypeTree(Ident(tpnme.Repr), Ident(X) :: Nil) + + def rootQual(n: Name): Tree = Ident(n) // TODO + // val prefix = + // ((Ident(nme.ROOTPKG): Tree) /: parts.init)((qual, name) => + // Select(qual, name.toTermName)) + + // def to[T](a: A): Repr[T] = + // PCons(a._1, (PCons(a._2, ... PCons(a._m, PNil())))) + val to = { + val arg = makeSyntheticParameter(tpt = TypeTree(A)) + val pNil = Apply(rootQual(nme.PNil), Nil) + def pCons(i: Int, acc: Tree) = Apply(rootQual(nme.PCons), Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) + DefDef( + name = nme.to, + tparams = TypeDef(X, noBounds) :: Nil, + vparamss = (arg :: Nil) :: Nil, + tpt = reprX, + rhs = (1 to productTypesSize).foldRight(pNil)(pCons) + ).withFlags(Synthetic) + } + + // def from[T](r: Repr[T]): A = { + // val _1 = r.head + // val x$1 = r.tail + // val _2 = x$1.head ─ x$1 here is cur + // val x$2 = x$1.tail + // ... + // val _n = x$n-1.head ─ note the absence of .tail, that's why + // new A(_1, _2, ..., _n) ─ body gets one element before the loop + // } + val from = { + val arg = makeSyntheticParameter(tpt = reprX) + var cur = Ident(arg.name) + var body: List[Tree] = ValDef(nme._1, TypeTree(), Select(cur, nme.head)) :: Nil + var i = 1 + while (i < productTypesSize) { + i += 1 + val dotTail = ValDef(nme.syntheticParamName(i), TypeTree(), Select(cur, nme.tail)) + cur = Ident(dotTail.name) + val dotHead = ValDef(nme.productAccessorName(i), TypeTree(), Select(cur, nme.head)) + body ::= dotTail + body ::= dotHead + } + val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList :: Nil + DefDef( + name = nme.from, + tparams = TypeDef(X, noBounds) :: Nil, + vparamss = (arg :: Nil) :: Nil, + tpt = TypeTree(A), + rhs = Block(body.reverse, New(TypeTree(A), newArgs)) + ).withFlags(Synthetic) + } + + val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil + New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) } case _ => EmptyTree diff --git a/library/src/dotty/generic/Representable.scala b/library/src/dotty/generic/Representable.scala index c2602017de54..cad029f0f056 100644 --- a/library/src/dotty/generic/Representable.scala +++ b/library/src/dotty/generic/Representable.scala @@ -3,6 +3,6 @@ package dotty.generic trait Representable[A] { type Repr[t] // <: Sum[t] | Prod[t] ← for when we stop cross compiling - // def to[T](a: A): Repr[T] - // def from[T](r: Repr[T]): A + def to[T](a: A): Repr[T] + def from[T](r: Repr[T]): A } diff --git a/tests/run/generic.scala b/tests/run/generic.scala index d69fdf43ed2d..ebfd547e127b 100644 --- a/tests/run/generic.scala +++ b/tests/run/generic.scala @@ -7,7 +7,13 @@ object Test { type &:[H, T[t] <: Prod[t]] = [X] => PCons[[Y] => H, T, X] def main(args: Array[String]): Unit = { + // Representable can be synthesised via implicit search val g = the[Representable[Foo]] + + // Type is inferred correctly val a: Representable[Foo] { type Repr = Int &: String &: PNil } = g + + // Representable#to and Representable#from behave as expected: + assert(g.from(g.to(Foo(1, "s"))) == Foo(1, "s")) } } From 8936872fc43bfe77b93366569a8196fe7995a556 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Tue, 21 Nov 2017 13:26:41 +0100 Subject: [PATCH 07/32] Add generic-sum test --- .../{generic.scala => generic-product.scala} | 0 tests/run/generic-sum.scala | 23 +++++++++++++++++++ 2 files changed, 23 insertions(+) rename tests/run/{generic.scala => generic-product.scala} (100%) create mode 100644 tests/run/generic-sum.scala diff --git a/tests/run/generic.scala b/tests/run/generic-product.scala similarity index 100% rename from tests/run/generic.scala rename to tests/run/generic-product.scala diff --git a/tests/run/generic-sum.scala b/tests/run/generic-sum.scala new file mode 100644 index 000000000000..532d7407b4a0 --- /dev/null +++ b/tests/run/generic-sum.scala @@ -0,0 +1,23 @@ +import dotty.generic._ + +sealed trait Foo +case class Bar(i: Int) extends Foo +case class Bus(s: String) extends Foo +// case class Bip(s: String) extends Foo + +object Test { + def the[T](implicit ev: T): ev.type = ev + type |:[H, T[t] <: Sum[t]] = [X] => SCons[[Y] => H, T, X] + + def main(args: Array[String]): Unit = { + // Representable can be synthesised via implicit search + val g = the[Representable[Foo]] + + // Type is inferred correctly + val a: Representable[Foo] { type Repr = Bar |: Bus |: SNil } = g + + // // Representable#to and Representable#from behave as expected: + // assert(g.from(g.to(Bar(1))) == Bar(1)) + // assert(g.from(g.to(Bus("s"))) == Bus("s")) + } +} From f43fa1afca8f9d4302dfe8967a4173390515c70c Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Tue, 21 Nov 2017 13:27:32 +0100 Subject: [PATCH 08/32] Synthesise type Repr for sums --- .../dotty/tools/dotc/core/Definitions.scala | 6 +++++ .../dotty/tools/dotc/typer/Implicits.scala | 24 +++++++++++++++++++ library/src/dotty/generic/Representable.scala | 4 ++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 56413f32d7df..919029c2ae0e 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -607,6 +607,12 @@ class Definitions { lazy val PConsType = ctx.requiredClassRef("dotty.generic.PCons") def PConsClass(implicit ctx: Context) = PConsType.symbol.asClass def PConsModule(implicit ctx: Context) = PConsClass.companionModule + lazy val SNilType = ctx.requiredClassRef("dotty.generic.SNil") + def SNilClass(implicit ctx: Context) = SNilType.symbol.asClass + def SNilModule(implicit ctx: Context) = SNilClass.companionModule + lazy val SConsType = ctx.requiredClassRef("dotty.generic.SCons") + def SConsClass(implicit ctx: Context) = SConsType.symbol.asClass + def SConsModule(implicit ctx: Context) = SConsClass.companionModule lazy val XMLTopScopeModuleRef = ctx.requiredModuleRef("scala.xml.TopScope") diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 53b12bce23b8..728ed6a758bd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -698,6 +698,30 @@ trait Implicits { self: Typer => val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) } + case (A @ _) :: Nil if A.typeSymbol.is(Sealed) => + typed { + import untpd._ + import transform.SymUtils._ + + val sumTypes = A.classSymbol.children.map(_.typeRef).reverse // Reveresed to match definition order + val sumTypesSize = sumTypes.size + + val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) + val reprRhs = + sumTypes.zipWithIndex.foldRight[Tree](TypeTree(defn.SNilType)) { case ((cur, i), acc) => + val arg = tpnme.syntheticTypeParamName(i) + val constCur = LambdaTypeTree(TypeDef(arg, noBounds) :: Nil, TypeTree(cur)) + val pconsArgs = constCur :: acc :: Ident(arg) :: Nil + val rhs = AppliedTypeTree(TypeTree(defn.SConsType), pconsArgs) + LambdaTypeTree(TypeDef(arg, noBounds) :: Nil, rhs) + } + val repr: Tree = TypeDef(tpnme.Repr, reprRhs) + // val to: Tree = ??? + // val from: Tree = ??? + + val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil + New(Template(emptyConstructor, parents, EmptyValDef, repr /*:: to :: from*/ :: Nil)).withPos(pos) + } case _ => EmptyTree } diff --git a/library/src/dotty/generic/Representable.scala b/library/src/dotty/generic/Representable.scala index cad029f0f056..c2602017de54 100644 --- a/library/src/dotty/generic/Representable.scala +++ b/library/src/dotty/generic/Representable.scala @@ -3,6 +3,6 @@ package dotty.generic trait Representable[A] { type Repr[t] // <: Sum[t] | Prod[t] ← for when we stop cross compiling - def to[T](a: A): Repr[T] - def from[T](r: Repr[T]): A + // def to[T](a: A): Repr[T] + // def from[T](r: Repr[T]): A } From 5a2726e9872cff6a01d4473458aba0fdeed2e84f Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 22 Nov 2017 16:33:29 +0100 Subject: [PATCH 09/32] Synthesise to/from for sums --- .../dotty/tools/dotc/core/Definitions.scala | 6 ++ .../src/dotty/tools/dotc/core/StdNames.scala | 2 + .../dotty/tools/dotc/typer/Implicits.scala | 73 +++++++++++++++++-- library/src/dotty/generic/Representable.scala | 4 +- tests/run/generic-sum.scala | 6 +- 5 files changed, 81 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 919029c2ae0e..89e660d67e3b 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -613,6 +613,12 @@ class Definitions { lazy val SConsType = ctx.requiredClassRef("dotty.generic.SCons") def SConsClass(implicit ctx: Context) = SConsType.symbol.asClass def SConsModule(implicit ctx: Context) = SConsClass.companionModule + lazy val SLeftType = ctx.requiredClassRef("dotty.generic.SLeft") + def SLeftClass(implicit ctx: Context) = SLeftType.symbol.asClass + def SLeftModule(implicit ctx: Context) = SLeftClass.companionModule + lazy val SRightType = ctx.requiredClassRef("dotty.generic.SRight") + def SRightClass(implicit ctx: Context) = SRightType.symbol.asClass + def SRightModule(implicit ctx: Context) = SRightClass.companionModule lazy val XMLTopScopeModuleRef = ctx.requiredModuleRef("scala.xml.TopScope") diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 200f62bd6ff6..8dcf1cb000dc 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -347,6 +347,8 @@ object StdNames { val RootClass: N = "RootClass" val Scala2: N = "Scala2" val Select: N = "Select" + val SLeft: N = "SLeft" + val SRight: N = "SRight" val StringContext: N = "StringContext" val This: N = "This" val ThisType: N = "ThisType" diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 728ed6a758bd..05e18203bba3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -36,6 +36,7 @@ import config.Config import config.Printers.{implicits, implicitsDetailed, typr} import collection.mutable import reporting.trace +import annotation.tailrec /** Implicit resolution */ object Implicits { @@ -675,7 +676,7 @@ trait Implicits { self: Typer => val from = { val arg = makeSyntheticParameter(tpt = reprX) var cur = Ident(arg.name) - var body: List[Tree] = ValDef(nme._1, TypeTree(), Select(cur, nme.head)) :: Nil + var body: List[Tree] = ValDef(nme._1, TypeTree(), Select(cur, nme.head)) :: Nil // TODO: handle productTypesSize == 0 var i = 1 while (i < productTypesSize) { i += 1 @@ -705,8 +706,9 @@ trait Implicits { self: Typer => val sumTypes = A.classSymbol.children.map(_.typeRef).reverse // Reveresed to match definition order val sumTypesSize = sumTypes.size - + val X = tpnme.syntheticTypeParamName(0) val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) + val reprRhs = sumTypes.zipWithIndex.foldRight[Tree](TypeTree(defn.SNilType)) { case ((cur, i), acc) => val arg = tpnme.syntheticTypeParamName(i) @@ -716,11 +718,72 @@ trait Implicits { self: Typer => LambdaTypeTree(TypeDef(arg, noBounds) :: Nil, rhs) } val repr: Tree = TypeDef(tpnme.Repr, reprRhs) - // val to: Tree = ??? - // val from: Tree = ??? + val reprX = AppliedTypeTree(Ident(tpnme.Repr), Ident(X) :: Nil) + + def rootQual(n: Name): Tree = Ident(n) // TODO + + implicit class TreeOps(val tree: Tree) { + def select(name: Name): Select = Select(tree, name) + def appliedToTypeTrees(targs: List[Tree]): Tree = TypeApply(tree, targs) + def isInstance(tp: Type): Tree = tree.select(nme.isInstanceOf_).appliedToType(tp) + def appliedToType(targ: Type): Tree = appliedToTypeTrees(TypeTree(targ) :: Nil) + def asInstance(tp: Type): Tree = tree.select(nme.asInstanceOf_).appliedToType(tp) + } + + val to: Tree = { + val arg = makeSyntheticParameter(tpt = TypeTree(A)) + def sumInjector(elem: Tree, depth: List[Any]): Tree = { + def SLeft(t: Tree) = Apply(rootQual(nme.SLeft), t) + def SRight(t: Tree) = Apply(rootQual(nme.SRight), t) + depth.tail.foldLeft[Tree](SLeft(elem)) { case (acc, _) => SRight(acc) } + } + + @tailrec def mkIfChain(typesToTest: List[Type], acc: Tree): Tree = + typesToTest match { + case x :: xs => + mkIfChain(xs, + If(cond = Ident(arg.name).isInstance(x), + thenp = sumInjector(Ident(arg.name).asInstance(x), typesToTest), + elsep = acc + ) + ) + case Nil => acc + } + val (last :: typesToTest) = sumTypes.reverse // TODO: handle productTypesSize == 0 + val body = mkIfChain(typesToTest, sumInjector(Ident(arg.name).asInstance(last), sumTypes)) + DefDef( + name = nme.to, + tparams = TypeDef(X, noBounds) :: Nil, + vparamss = (arg :: Nil) :: Nil, + tpt = reprX, + rhs = body + ).withFlags(Synthetic) + } + + val from: Tree = { + val arg = makeSyntheticParameter(tpt = reprX) + val cases = { + val x = nme.syntheticParamName(0) + (1 to sumTypesSize).map { depth => + val pat = (1 until depth).foldLeft(Apply(Ident(nme.SLeft), Ident(x))) { + case (acc, _) => Apply(Ident(nme.SRight), acc) + } + CaseDef(pat, EmptyTree, Ident(x)) + }.toList + } + val body = Match(Ident(arg.name), cases).asInstance(A) + + DefDef( + name = nme.from, + tparams = TypeDef(X, noBounds) :: Nil, + vparamss = (arg :: Nil) :: Nil, + tpt = TypeTree(A), + rhs = body + ).withFlags(Synthetic) + } val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil - New(Template(emptyConstructor, parents, EmptyValDef, repr /*:: to :: from*/ :: Nil)).withPos(pos) + New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) } case _ => EmptyTree diff --git a/library/src/dotty/generic/Representable.scala b/library/src/dotty/generic/Representable.scala index c2602017de54..cad029f0f056 100644 --- a/library/src/dotty/generic/Representable.scala +++ b/library/src/dotty/generic/Representable.scala @@ -3,6 +3,6 @@ package dotty.generic trait Representable[A] { type Repr[t] // <: Sum[t] | Prod[t] ← for when we stop cross compiling - // def to[T](a: A): Repr[T] - // def from[T](r: Repr[T]): A + def to[T](a: A): Repr[T] + def from[T](r: Repr[T]): A } diff --git a/tests/run/generic-sum.scala b/tests/run/generic-sum.scala index 532d7407b4a0..cee5fde7f04d 100644 --- a/tests/run/generic-sum.scala +++ b/tests/run/generic-sum.scala @@ -16,8 +16,8 @@ object Test { // Type is inferred correctly val a: Representable[Foo] { type Repr = Bar |: Bus |: SNil } = g - // // Representable#to and Representable#from behave as expected: - // assert(g.from(g.to(Bar(1))) == Bar(1)) - // assert(g.from(g.to(Bus("s"))) == Bus("s")) + // Representable#to and Representable#from behave as expected: + assert(g.from(g.to(Bar(1))) == Bar(1)) + assert(g.from(g.to(Bus("s"))) == Bus("s")) } } From 89ce33fd7dfcc084ee167822e3afd117d5141d5f Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 22 Nov 2017 16:43:37 +0100 Subject: [PATCH 10/32] Simplify Product.from using a Match instead --- .../dotty/tools/dotc/typer/Implicits.scala | 41 +++++-------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 05e18203bba3..cc8253968e9a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -609,16 +609,9 @@ trait Implicits { self: Typer => * def to[T](a: A): Repr[T] = * PCons(a._1, (PCons(a._2, ... PCons(a._m, PNil())))) * - * def from[T](r: Repr[T]): A = { - * val _1 = r.head - * val x$1 = r.tail - * val _2 = x$1.head - * val x$2 = x$1.tail - * ... - * val _n = x$n-1.head - * new A(_1, _2, ..., _n) + * def from[T](r: Repr[T]): A = r match { + * case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) * } - * } * ``` */ def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = { @@ -645,15 +638,15 @@ trait Implicits { self: Typer => val reprX = AppliedTypeTree(Ident(tpnme.Repr), Ident(X) :: Nil) def rootQual(n: Name): Tree = Ident(n) // TODO + val pNil = Apply(rootQual(nme.PNil), Nil) // val prefix = // ((Ident(nme.ROOTPKG): Tree) /: parts.init)((qual, name) => // Select(qual, name.toTermName)) // def to[T](a: A): Repr[T] = - // PCons(a._1, (PCons(a._2, ... PCons(a._m, PNil())))) + // PCons(a._1, (PCons(a._2, ... PCons(a._n, PNil())))) val to = { val arg = makeSyntheticParameter(tpt = TypeTree(A)) - val pNil = Apply(rootQual(nme.PNil), Nil) def pCons(i: Int, acc: Tree) = Apply(rootQual(nme.PCons), Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) DefDef( name = nme.to, @@ -664,35 +657,23 @@ trait Implicits { self: Typer => ).withFlags(Synthetic) } - // def from[T](r: Repr[T]): A = { - // val _1 = r.head - // val x$1 = r.tail - // val _2 = x$1.head ─ x$1 here is cur - // val x$2 = x$1.tail - // ... - // val _n = x$n-1.head ─ note the absence of .tail, that's why - // new A(_1, _2, ..., _n) ─ body gets one element before the loop + // def from[T](r: Repr[T]): A = r match { + // case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) // } val from = { val arg = makeSyntheticParameter(tpt = reprX) - var cur = Ident(arg.name) - var body: List[Tree] = ValDef(nme._1, TypeTree(), Select(cur, nme.head)) :: Nil // TODO: handle productTypesSize == 0 - var i = 1 - while (i < productTypesSize) { - i += 1 - val dotTail = ValDef(nme.syntheticParamName(i), TypeTree(), Select(cur, nme.tail)) - cur = Ident(dotTail.name) - val dotHead = ValDef(nme.productAccessorName(i), TypeTree(), Select(cur, nme.head)) - body ::= dotTail - body ::= dotHead + val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => + Apply(rootQual(nme.PCons), Ident(nme.productAccessorName(i)) :: acc :: Nil) } val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList :: Nil + val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, New(TypeTree(A), newArgs)) :: Nil) + DefDef( name = nme.from, tparams = TypeDef(X, noBounds) :: Nil, vparamss = (arg :: Nil) :: Nil, tpt = TypeTree(A), - rhs = Block(body.reverse, New(TypeTree(A), newArgs)) + rhs = body ).withFlags(Synthetic) } From 3263d44c5244e5249a7164630278bff147b771f7 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 22 Nov 2017 16:56:59 +0100 Subject: [PATCH 11/32] Document sum case --- .../dotty/tools/dotc/typer/Implicits.scala | 48 +++++++++++++++++-- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index cc8253968e9a..b003b47dea54 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -600,7 +600,7 @@ trait Implicits { self: Typer => } /** - * When A is a product, synthesize an instance of the following shape: + * When A is a product, synthesize an instance with the following shape: * * ``` * new Representable[A] { @@ -613,6 +613,27 @@ trait Implicits { self: Typer => * case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) * } * ``` + * + * When A is a sum, synthesize an instance with the following shape: + * + * ``` + * new Representable[A] { + * type Repr[t] = T1 |: T2 |: ... |: Tn |: PNil + * + * def to[T](a: A): Repr[T] = a match { + * case x: T1 => SLeft(x) + * case x: T2 => SRight(SLeft(x)) + * ... + * case x: Tn => SRight(SRight(...SRight(SLeft(x)))) + * } + * + * def from[T](r: Repr[T]): A = r match { + * case SLeft(x) => x + * case SRight(SLeft(x)) => x + * ... + * case SRight(SRight(...SRight(SLeft(x)))) => x + * } + * ``` */ def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = { formal.argTypes match { @@ -667,7 +688,6 @@ trait Implicits { self: Typer => } val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList :: Nil val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, New(TypeTree(A), newArgs)) :: Nil) - DefDef( name = nme.from, tparams = TypeDef(X, noBounds) :: Nil, @@ -685,11 +705,13 @@ trait Implicits { self: Typer => import untpd._ import transform.SymUtils._ - val sumTypes = A.classSymbol.children.map(_.typeRef).reverse // Reveresed to match definition order + val sumTypes = A.classSymbol.children.map(_.typeRef).reverse // Reveresed to match order in source val sumTypesSize = sumTypes.size val X = tpnme.syntheticTypeParamName(0) val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) + // This fold generates T1 |: T2 |: ... |: Tn |: PNil, where + // type |:[H, T[t] <: Sum[t]] = [X] => SCons[[Y] => H, T, X] val reprRhs = sumTypes.zipWithIndex.foldRight[Tree](TypeTree(defn.SNilType)) { case ((cur, i), acc) => val arg = tpnme.syntheticTypeParamName(i) @@ -698,6 +720,7 @@ trait Implicits { self: Typer => val rhs = AppliedTypeTree(TypeTree(defn.SConsType), pconsArgs) LambdaTypeTree(TypeDef(arg, noBounds) :: Nil, rhs) } + // type Repr[t] = T1 |: T2 |: ... |: Tn |: PNil val repr: Tree = TypeDef(tpnme.Repr, reprRhs) val reprX = AppliedTypeTree(Ident(tpnme.Repr), Ident(X) :: Nil) @@ -711,6 +734,12 @@ trait Implicits { self: Typer => def asInstance(tp: Type): Tree = tree.select(nme.asInstanceOf_).appliedToType(tp) } + // def to[T](a: A): Repr[T] = a match { + // case x: T1 => SLeft(x) + // case x: T2 => SRight(SLeft(x)) + // ... + // case x: Tn => SRight(SRight(...SRight(SLeft(x)))) + // } val to: Tree = { val arg = makeSyntheticParameter(tpt = TypeTree(A)) def sumInjector(elem: Tree, depth: List[Any]): Tree = { @@ -718,7 +747,10 @@ trait Implicits { self: Typer => def SRight(t: Tree) = Apply(rootQual(nme.SRight), t) depth.tail.foldLeft[Tree](SLeft(elem)) { case (acc, _) => SRight(acc) } } - + // The current implementation directly generates an expended + // version of the above match expression. Assuming a case-of-case + // like optimization was added to Dotty this would need to be + // refactored into a proper match. @tailrec def mkIfChain(typesToTest: List[Type], acc: Tree): Tree = typesToTest match { case x :: xs => @@ -741,11 +773,18 @@ trait Implicits { self: Typer => ).withFlags(Synthetic) } + // def from[T](r: Repr[T]): A = r match { + // case SLeft(x) => x + // case SRight(SLeft(x)) => x + // ... + // case SRight(SRight(...SRight(SLeft(x)))) => x + // } val from: Tree = { val arg = makeSyntheticParameter(tpt = reprX) val cases = { val x = nme.syntheticParamName(0) (1 to sumTypesSize).map { depth => + // Generates the appropriate level nesting of SRight(...SRight(SLeft(x))) val pat = (1 until depth).foldLeft(Apply(Ident(nme.SLeft), Ident(x))) { case (acc, _) => Apply(Ident(nme.SRight), acc) } @@ -753,7 +792,6 @@ trait Implicits { self: Typer => }.toList } val body = Match(Ident(arg.name), cases).asInstance(A) - DefDef( name = nme.from, tparams = TypeDef(X, noBounds) :: Nil, From 01742e53fac69288994cbe6339baffd3d58287a4 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 23 Nov 2017 14:07:46 +0100 Subject: [PATCH 12/32] Rewrite sum.to using a match --- .../dotty/tools/dotc/typer/Implicits.scala | 35 ++++++++----------- tests/run/generic-product.scala | 17 +++++++++ 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index b003b47dea54..2cd06176f15a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -620,7 +620,7 @@ trait Implicits { self: Typer => * new Representable[A] { * type Repr[t] = T1 |: T2 |: ... |: Tn |: PNil * - * def to[T](a: A): Repr[T] = a match { + * def to[T](a: A): Repr[T] = (@unchecked a) match { * case x: T1 => SLeft(x) * case x: T2 => SRight(SLeft(x)) * ... @@ -637,6 +637,7 @@ trait Implicits { self: Typer => */ def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = { formal.argTypes match { + // Products case (A @ _) :: Nil if defn.isProductSubType(A) => typed { import untpd._ @@ -700,12 +701,15 @@ trait Implicits { self: Typer => val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) } + + // Sums case (A @ _) :: Nil if A.typeSymbol.is(Sealed) => typed { import untpd._ import transform.SymUtils._ val sumTypes = A.classSymbol.children.map(_.typeRef).reverse // Reveresed to match order in source + if (sumTypes.isEmpty) return typed(EmptyTree) val sumTypesSize = sumTypes.size val X = tpnme.syntheticTypeParamName(0) val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) @@ -734,7 +738,7 @@ trait Implicits { self: Typer => def asInstance(tp: Type): Tree = tree.select(nme.asInstanceOf_).appliedToType(tp) } - // def to[T](a: A): Repr[T] = a match { + // def to[T](a: A): Repr[T] = (@unchecked a) match { // case x: T1 => SLeft(x) // case x: T2 => SRight(SLeft(x)) // ... @@ -742,28 +746,19 @@ trait Implicits { self: Typer => // } val to: Tree = { val arg = makeSyntheticParameter(tpt = TypeTree(A)) - def sumInjector(elem: Tree, depth: List[Any]): Tree = { + def sumInjector(elem: Tree, depth: Int): Tree = { def SLeft(t: Tree) = Apply(rootQual(nme.SLeft), t) def SRight(t: Tree) = Apply(rootQual(nme.SRight), t) - depth.tail.foldLeft[Tree](SLeft(elem)) { case (acc, _) => SRight(acc) } + (1 to depth).foldLeft[Tree](SLeft(elem)) { case (acc, _) => SRight(acc) } } - // The current implementation directly generates an expended - // version of the above match expression. Assuming a case-of-case - // like optimization was added to Dotty this would need to be - // refactored into a proper match. - @tailrec def mkIfChain(typesToTest: List[Type], acc: Tree): Tree = - typesToTest match { - case x :: xs => - mkIfChain(xs, - If(cond = Ident(arg.name).isInstance(x), - thenp = sumInjector(Ident(arg.name).asInstance(x), typesToTest), - elsep = acc - ) - ) - case Nil => acc + val cases = { + val x = nme.syntheticParamName(0) + sumTypes.zipWithIndex.map { case (tpe, depth) => + CaseDef(Typed(Ident(x), TypeTree(tpe)), EmptyTree, sumInjector(Ident(x), depth)) } - val (last :: typesToTest) = sumTypes.reverse // TODO: handle productTypesSize == 0 - val body = mkIfChain(typesToTest, sumInjector(Ident(arg.name).asInstance(last), sumTypes)) + } + val unchecked = Select(Ident(nme.scala_), "unchecked".toTypeName) + val body = Match(Annotated(Ident(arg.name), untpd.New(unchecked, Nil)), cases) DefDef( name = nme.to, tparams = TypeDef(X, noBounds) :: Nil, diff --git a/tests/run/generic-product.scala b/tests/run/generic-product.scala index ebfd547e127b..dc2267954978 100644 --- a/tests/run/generic-product.scala +++ b/tests/run/generic-product.scala @@ -1,12 +1,18 @@ import dotty.generic._ case class Foo(i: Int, s: String) +case class Bar() object Test { def the[T](implicit ev: T): ev.type = ev type &:[H, T[t] <: Prod[t]] = [X] => PCons[[Y] => H, T, X] def main(args: Array[String]): Unit = { + testFoo() + testBar() + } + + def testFoo(): Unit = { // Representable can be synthesised via implicit search val g = the[Representable[Foo]] @@ -16,4 +22,15 @@ object Test { // Representable#to and Representable#from behave as expected: assert(g.from(g.to(Foo(1, "s"))) == Foo(1, "s")) } + + def testBar(): Unit = { + // Representable can be synthesised via implicit search + val g = the[Representable[Bar]] + + // Type is inferred correctly + val a: Representable[Bar] { type Repr = PNil } = g + + // Representable#to and Representable#from behave as expected: + assert(g.from(g.to(Bar())) == Bar()) + } } From 145ca112d61db35704d5ebeff1bc2bdb34c65aab Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 23 Nov 2017 14:50:58 +0100 Subject: [PATCH 13/32] Refactoring, root qualify names form dotty.generic._ --- .../src/dotty/tools/dotc/core/StdNames.scala | 7 - .../dotty/tools/dotc/typer/Implicits.scala | 268 ++++++++---------- 2 files changed, 119 insertions(+), 156 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 8dcf1cb000dc..19763e2df3f1 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -340,15 +340,11 @@ object StdNames { val NoSymbol: N = "NoSymbol" val NoType: N = "NoType" val Pair: N = "Pair" - val PCons: N = "PCons" - val PNil: N = "PNil" val Ref: N = "Ref" val RootPackage: N = "RootPackage" val RootClass: N = "RootClass" val Scala2: N = "Scala2" val Select: N = "Select" - val SLeft: N = "SLeft" - val SRight: N = "SRight" val StringContext: N = "StringContext" val This: N = "This" val ThisType: N = "ThisType" @@ -425,7 +421,6 @@ object StdNames { val flagsFromBits : N = "flagsFromBits" val flatMap: N = "flatMap" val foreach: N = "foreach" - val from: N = "from" val genericArrayOps: N = "genericArrayOps" val get: N = "get" val getClass_ : N = "getClass" @@ -520,7 +515,6 @@ object StdNames { val this_ : N = "this" val thisPrefix : N = "thisPrefix" val throw_ : N = "throw" - val to: N = "to" val toArray: N = "toArray" val toList: N = "toList" val toObjectArray : N = "toObjectArray" @@ -749,7 +743,6 @@ object StdNames { final val Conforms = encode("<:<") final val Uninstantiated: TypeName = "?$" - final val Repr: TypeName = "Repr" } abstract class JavaNames[N <: Name] extends DefinedNames[N] { diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 2cd06176f15a..d2d904d1ae5c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -635,170 +635,140 @@ trait Implicits { self: Typer => * } * ``` */ - def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = { + def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = typed { + import untpd._ + + def rootQual(genericClass: String): Tree = + Select(Select(Select(Ident(nme.ROOTPKG), "dotty".toTermName), "generic".toTermName), genericClass.toTermName) + val ReprNme = "Repr".toTypeName + val toNme = "to" .toTermName + val fromNme = "from".toTermName + formal.argTypes match { - // Products + // Products ----------------------------------------------------------- case (A @ _) :: Nil if defn.isProductSubType(A) => - typed { - import untpd._ - val productTypes = productSelectorTypes(A) - val productTypesSize = productTypes.size - val X = tpnme.syntheticTypeParamName(0) - val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) - - // This fold generates T1 &: T2 &: ... &: Tn &: PNil, where - // type &:[H, T[t] <: Prod[t]] = [X] => PCons[[Y] => H, T, X] - val reprRhs = - productTypes.foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => - val constCur = LambdaTypeTree(TypeDef(X, noBounds) :: Nil, TypeTree(cur)) - val pconsArgs = constCur :: acc :: Ident(X) :: Nil - val rhs = AppliedTypeTree(TypeTree(defn.PConsType), pconsArgs) - LambdaTypeTree(TypeDef(X, noBounds) :: Nil, rhs) - } - // type Repr[t] = T1 &: T2 &: ... &: Tn &: PNil - val repr = TypeDef(tpnme.Repr, reprRhs) - val reprX = AppliedTypeTree(Ident(tpnme.Repr), Ident(X) :: Nil) - - def rootQual(n: Name): Tree = Ident(n) // TODO - val pNil = Apply(rootQual(nme.PNil), Nil) - // val prefix = - // ((Ident(nme.ROOTPKG): Tree) /: parts.init)((qual, name) => - // Select(qual, name.toTermName)) - - // def to[T](a: A): Repr[T] = - // PCons(a._1, (PCons(a._2, ... PCons(a._n, PNil())))) - val to = { - val arg = makeSyntheticParameter(tpt = TypeTree(A)) - def pCons(i: Int, acc: Tree) = Apply(rootQual(nme.PCons), Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) - DefDef( - name = nme.to, - tparams = TypeDef(X, noBounds) :: Nil, - vparamss = (arg :: Nil) :: Nil, - tpt = reprX, - rhs = (1 to productTypesSize).foldRight(pNil)(pCons) - ).withFlags(Synthetic) + val PNilTree = rootQual("PNil") + val PConsTree = rootQual("PCons") + + val productTypes = productSelectorTypes(A) + val productTypesSize = productTypes.size + val X = tpnme.syntheticTypeParamName(0) + val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) + + // This fold generates T1 &: T2 &: ... &: Tn &: PNil, where + // type &:[H, T[t] <: Prod[t]] = [X] => PCons[[Y] => H, T, X] + val reprRhs = + productTypes.foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => + val constCur = LambdaTypeTree(TypeDef(X, noBounds) :: Nil, TypeTree(cur)) + val pconsArgs = constCur :: acc :: Ident(X) :: Nil + val rhs = AppliedTypeTree(TypeTree(defn.PConsType), pconsArgs) + LambdaTypeTree(TypeDef(X, noBounds) :: Nil, rhs) } - - // def from[T](r: Repr[T]): A = r match { - // case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) - // } - val from = { - val arg = makeSyntheticParameter(tpt = reprX) - val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => - Apply(rootQual(nme.PCons), Ident(nme.productAccessorName(i)) :: acc :: Nil) - } - val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList :: Nil - val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, New(TypeTree(A), newArgs)) :: Nil) - DefDef( - name = nme.from, - tparams = TypeDef(X, noBounds) :: Nil, - vparamss = (arg :: Nil) :: Nil, - tpt = TypeTree(A), - rhs = body - ).withFlags(Synthetic) + // type Repr[t] = T1 &: T2 &: ... &: Tn &: PNil + val repr = TypeDef(ReprNme, reprRhs) + val reprX = AppliedTypeTree(Ident(ReprNme), Ident(X) :: Nil) + val pNil = Apply(PNilTree, Nil) + + // def to[T](a: A): Repr[T] = + // PCons(a._1, (PCons(a._2, ... PCons(a._n, PNil())))) + val to = { + val arg = makeSyntheticParameter(tpt = TypeTree(A)) + val body = (1 to productTypesSize).foldRight(pNil) { case (i, acc) => + Apply(PConsTree, Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) } + DefDef(toNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, reprX, body).withFlags(Synthetic) + } - val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil - New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) + // def from[T](r: Repr[T]): A = r match { + // case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) + // } + val from = { + val arg = makeSyntheticParameter(tpt = reprX) + val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => + Apply(PConsTree, Ident(nme.productAccessorName(i)) :: acc :: Nil) + } + val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList :: Nil + val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, New(TypeTree(A), newArgs)) :: Nil) + DefDef(fromNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) } - // Sums + val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil + New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) + + // Sums ----------------------------------------------------------- case (A @ _) :: Nil if A.typeSymbol.is(Sealed) => - typed { - import untpd._ - import transform.SymUtils._ - - val sumTypes = A.classSymbol.children.map(_.typeRef).reverse // Reveresed to match order in source - if (sumTypes.isEmpty) return typed(EmptyTree) - val sumTypesSize = sumTypes.size - val X = tpnme.syntheticTypeParamName(0) - val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) - - // This fold generates T1 |: T2 |: ... |: Tn |: PNil, where - // type |:[H, T[t] <: Sum[t]] = [X] => SCons[[Y] => H, T, X] - val reprRhs = - sumTypes.zipWithIndex.foldRight[Tree](TypeTree(defn.SNilType)) { case ((cur, i), acc) => - val arg = tpnme.syntheticTypeParamName(i) - val constCur = LambdaTypeTree(TypeDef(arg, noBounds) :: Nil, TypeTree(cur)) - val pconsArgs = constCur :: acc :: Ident(arg) :: Nil - val rhs = AppliedTypeTree(TypeTree(defn.SConsType), pconsArgs) - LambdaTypeTree(TypeDef(arg, noBounds) :: Nil, rhs) - } - // type Repr[t] = T1 |: T2 |: ... |: Tn |: PNil - val repr: Tree = TypeDef(tpnme.Repr, reprRhs) - val reprX = AppliedTypeTree(Ident(tpnme.Repr), Ident(X) :: Nil) - - def rootQual(n: Name): Tree = Ident(n) // TODO - - implicit class TreeOps(val tree: Tree) { - def select(name: Name): Select = Select(tree, name) - def appliedToTypeTrees(targs: List[Tree]): Tree = TypeApply(tree, targs) - def isInstance(tp: Type): Tree = tree.select(nme.isInstanceOf_).appliedToType(tp) - def appliedToType(targ: Type): Tree = appliedToTypeTrees(TypeTree(targ) :: Nil) - def asInstance(tp: Type): Tree = tree.select(nme.asInstanceOf_).appliedToType(tp) + val SLeftTree = rootQual("SLeft") + val SRightTree = rootQual("SRight") + + import transform.SymUtils._ + + val sumTypes = A.classSymbol.children.map(_.typeRef).reverse // Reveresed to match order in source + if (sumTypes.isEmpty) return typed(EmptyTree) + val sumTypesSize = sumTypes.size + val X = tpnme.syntheticTypeParamName(0) + val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) + + // This fold generates T1 |: T2 |: ... |: Tn |: PNil, where + // type |:[H, T[t] <: Sum[t]] = [X] => SCons[[Y] => H, T, X] + val reprRhs = + sumTypes.zipWithIndex.foldRight[Tree](TypeTree(defn.SNilType)) { case ((cur, i), acc) => + val arg = tpnme.syntheticTypeParamName(i) + val constCur = LambdaTypeTree(TypeDef(arg, noBounds) :: Nil, TypeTree(cur)) + val pconsArgs = constCur :: acc :: Ident(arg) :: Nil + val rhs = AppliedTypeTree(TypeTree(defn.SConsType), pconsArgs) + LambdaTypeTree(TypeDef(arg, noBounds) :: Nil, rhs) } - - // def to[T](a: A): Repr[T] = (@unchecked a) match { - // case x: T1 => SLeft(x) - // case x: T2 => SRight(SLeft(x)) - // ... - // case x: Tn => SRight(SRight(...SRight(SLeft(x)))) - // } - val to: Tree = { - val arg = makeSyntheticParameter(tpt = TypeTree(A)) - def sumInjector(elem: Tree, depth: Int): Tree = { - def SLeft(t: Tree) = Apply(rootQual(nme.SLeft), t) - def SRight(t: Tree) = Apply(rootQual(nme.SRight), t) - (1 to depth).foldLeft[Tree](SLeft(elem)) { case (acc, _) => SRight(acc) } - } - val cases = { - val x = nme.syntheticParamName(0) - sumTypes.zipWithIndex.map { case (tpe, depth) => - CaseDef(Typed(Ident(x), TypeTree(tpe)), EmptyTree, sumInjector(Ident(x), depth)) + // type Repr[t] = T1 |: T2 |: ... |: Tn |: PNil + val repr: Tree = TypeDef(ReprNme, reprRhs) + val reprX = AppliedTypeTree(Ident(ReprNme), Ident(X) :: Nil) + + // def to[T](a: A): Repr[T] = (@unchecked a) match { + // case x: T1 => SLeft(x) + // case x: T2 => SRight(SLeft(x)) + // ... + // case x: Tn => SRight(SRight(...SRight(SLeft(x)))) + // } + val to: Tree = { + val arg = makeSyntheticParameter(tpt = TypeTree(A)) + val cases = { + val x = nme.syntheticParamName(0) + sumTypes.zipWithIndex.map { case (tpe, depth) => + val rhs = (1 to depth).foldLeft[Tree](Apply(SLeftTree, Ident(x))) { + case (acc, _) => Apply(SRightTree, acc) } + CaseDef(Typed(Ident(x), TypeTree(tpe)), EmptyTree, rhs) } - val unchecked = Select(Ident(nme.scala_), "unchecked".toTypeName) - val body = Match(Annotated(Ident(arg.name), untpd.New(unchecked, Nil)), cases) - DefDef( - name = nme.to, - tparams = TypeDef(X, noBounds) :: Nil, - vparamss = (arg :: Nil) :: Nil, - tpt = reprX, - rhs = body - ).withFlags(Synthetic) } + val unchecked = Select(Ident(nme.scala_), "unchecked".toTypeName) + val body = Match(Annotated(Ident(arg.name), untpd.New(unchecked, Nil)), cases) + DefDef(toNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, reprX, body).withFlags(Synthetic) + } - // def from[T](r: Repr[T]): A = r match { - // case SLeft(x) => x - // case SRight(SLeft(x)) => x - // ... - // case SRight(SRight(...SRight(SLeft(x)))) => x - // } - val from: Tree = { - val arg = makeSyntheticParameter(tpt = reprX) - val cases = { - val x = nme.syntheticParamName(0) - (1 to sumTypesSize).map { depth => - // Generates the appropriate level nesting of SRight(...SRight(SLeft(x))) - val pat = (1 until depth).foldLeft(Apply(Ident(nme.SLeft), Ident(x))) { - case (acc, _) => Apply(Ident(nme.SRight), acc) - } - CaseDef(pat, EmptyTree, Ident(x)) - }.toList - } - val body = Match(Ident(arg.name), cases).asInstance(A) - DefDef( - name = nme.from, - tparams = TypeDef(X, noBounds) :: Nil, - vparamss = (arg :: Nil) :: Nil, - tpt = TypeTree(A), - rhs = body - ).withFlags(Synthetic) + // def from[T](r: Repr[T]): A = r match { + // case SLeft(x) => x + // case SRight(SLeft(x)) => x + // ... + // case SRight(SRight(...SRight(SLeft(x)))) => x + // } + val from: Tree = { + val arg = makeSyntheticParameter(tpt = reprX) + val cases = { + val x = nme.syntheticParamName(0) + (1 to sumTypesSize).map { depth => + val pat = (1 until depth).foldLeft(Apply(SLeftTree, Ident(x))) { + case (acc, _) => Apply(SRightTree, acc) + } + CaseDef(pat, EmptyTree, Ident(x)) + }.toList } - - val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil - New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) + val matsh = Match(Ident(arg.name), cases) + val body = TypeApply(Select(matsh, nme.asInstanceOf_), TypeTree(A) :: Nil) + DefDef(fromNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) } + + val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil + New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) + case _ => EmptyTree } From 450efee85f6c6e58c1546fd1ae2e981f33528f27 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 24 Nov 2017 15:18:38 +0100 Subject: [PATCH 14/32] Wip porting shapeless tests --- library/src/dotty/generic/Representable.scala | 4 + tests/run/representable.scala | 975 ++++++++++++++++++ 2 files changed, 979 insertions(+) create mode 100644 tests/run/representable.scala diff --git a/library/src/dotty/generic/Representable.scala b/library/src/dotty/generic/Representable.scala index cad029f0f056..5bcaefa924e3 100644 --- a/library/src/dotty/generic/Representable.scala +++ b/library/src/dotty/generic/Representable.scala @@ -6,3 +6,7 @@ trait Representable[A] { def to[T](a: A): Repr[T] def from[T](r: Repr[T]): A } + +object Representable { + def apply[A](implicit r: Representable[A]): r.type = r +} diff --git a/tests/run/representable.scala b/tests/run/representable.scala new file mode 100644 index 000000000000..cb01c73d7e16 --- /dev/null +++ b/tests/run/representable.scala @@ -0,0 +1,975 @@ +import dotty.generic._ + +object Syntax { + type &:[H, T[t] <: Prod[t]] = [X] => PCons[[Y] => H, T, X] + type |:[H, T[t] <: Sum[t]] = [X] => SCons[[Y] => H, T, X] + + implicit class ProdSyntax1[T[t] <: Prod[t], X](t: T[X]) extends AnyVal { + def &:[H](h: H): PCons[[_] => H, T, X] = PCons(h, t) + } +} + +import Syntax._ + +object RepresentableTestsAux { + sealed trait Fruit + case class Apple() extends Fruit + case class Banana() extends Fruit + case class Orange() extends Fruit + case class Pear() extends Fruit + + sealed trait AbstractSingle + case class Single() extends AbstractSingle + + sealed trait Tree[T] + case class Node[T](left: Tree[T], right: Tree[T]) extends Tree[T] + case class Leaf[T](t: T) extends Tree[T] + + sealed trait Enum + case object A extends Enum + case object B extends Enum + case object C extends Enum + + sealed trait L + case object N extends L + case class C(hd: Int, tl: L) extends L + + case class Company(depts : List[Dept]) + sealed trait Subunit + case class Dept(name : String, manager : Employee, subunits : List[Subunit]) extends Subunit + case class Employee(person : Person, salary : Salary) extends Subunit + case class Person(name : String, address : String, age: Int) + + case class Salary(salary : Double) + + case class PersonWithPseudonims(name: String, nicks: String*) + + // NOT SUPPORTED + // sealed trait AbstractNonCC + // class NonCCA(val i: Int, val s: String) extends AbstractNonCC + // class NonCCB(val b: Boolean, val d: Double) extends AbstractNonCC + // class NonCCWithVars(var c: Char, var l: Long) extends AbstractNonCC + + // class NonCCWithCompanion private (val i: Int, val s: String) + // object NonCCWithCompanion { + // def apply(i: Int, s: String) = new NonCCWithCompanion(i, s) + // def unapply(s: NonCCWithCompanion): Option[(Int, String)] = Some((s.i, s.s)) + // } + + class NonCCLazy(prev0: => NonCCLazy, next0: => NonCCLazy) { + lazy val prev = prev0 + lazy val next = next0 + } + + sealed trait Xor[+A, +B] + case class Left[+LA](a: LA) extends Xor[LA, Nothing] + case class Right[+RB](b: RB) extends Xor[Nothing, RB] + + sealed trait Base[BA, BB] + case class Swap[SA, SB](a: SA, b: SB) extends Base[SB, SA] + + sealed trait Overlapping + sealed trait OA extends Overlapping + case class OAC(s: String) extends OA + sealed trait OB extends Overlapping + case class OBC(s: String) extends OB + case class OAB(i: Int) extends OA with OB +} + +class RepresentableTests { + import RepresentableTestsAux._ + + type APBO = Apple |: Banana |: Orange |: Pear |: SNil + type ABC = A.type |: B.type |: C.type |: SNil + + def testProductBasics(): Unit = { + val p = Person("Joe Soap", "Brighton", 23) + type SSI = String &: String &: Int &: PNil + val gen = Representable[Person] + + val p0 = gen.to(p) + identity(p0: SSI[Nothing]) // Typed? + assert(("Joe Soap" &: "Brighton" &: 23 &: PNil()) == p0) + + val p1 = gen.from(p0) + identity(p1: Person) // Typed? + assert(p == p1) + } + + // TODO + // def testProductVarargs(): Unit = { + // val p = PersonWithPseudonims("Joe Soap", "X", "M", "Z") + // val gen = Representable[PersonWithPseudonims] + + // val p0 = gen.to(p) + // // identity(p0: String &: Seq[String] &: PNil) // Typed? + // assert(("Joe Soap" &: Seq("X", "M", "Z") &: PNil()) == p0) + + // val p1 = gen.from(p0) + // identity(p1: PersonWithPseudonims) // Typed? + // assert(p == p1) + // } + + def testTuples(): Unit = { + val gen1 = Representable[Tuple1[Int]] + identity(gen1: Representable[Tuple1[Int]] { type Repr = Int &: PNil }) // Typed? + + val gen2 = Representable[(Int, String)] + identity(gen2: Representable[(Int, String)] { type Repr = Int &: String &: PNil }) // Typed? + + val gen3 = Representable[(Int, String, Boolean)] + identity(gen3: Representable[(Int, String, Boolean)] { type Repr = Int &: String &: Boolean &: PNil }) // Typed? + } + + def testCoproductBasics(): Unit = { + val a: Fruit = Apple() + val p: Fruit = Pear() + val b: Fruit = Banana() + val o: Fruit = Orange() + + val gen = Representable[Fruit] + + val a0 = gen.to(a) + identity(a0: APBO[Nothing]) // Typed? + + val p0 = gen.to(p) + + identity(p0: APBO[Nothing]) // Typed? + + val b0 = gen.to(b) + + identity(b0: APBO[Nothing]) // Typed? + + val o0 = gen.to(o) + + identity(o0: APBO[Nothing]) // Typed? + + val a1 = gen.from(a0) + identity(a1: Fruit) // Typed? + + val p1 = gen.from(p0) + identity(p1: Fruit) // Typed? + + val b1 = gen.from(b0) + identity(b1: Fruit) // Typed? + + val o1 = gen.from(o0) + identity(o1: Fruit) // Typed? + } + + def testSingletonCoproducts(): Unit = { + type S = Single + + val gen = Representable[AbstractSingle] + + val s: AbstractSingle = Single() + + val s0 = gen.to(s) + identity(s0: (Single |: SNil)[Nothing]) // Typed? + + val s1 = gen.from(s0) + identity(s1: AbstractSingle) // Typed? + } + + // TODO + // def testOverlappingCoproducts(): Unit = { + // val gen = Representable[Overlapping] + // val o: Overlapping = OAB(1) + + // // sealed trait Overlapping + // // sealed trait OA extends Overlapping + // // case class OAC(s: String) extends OA + // // sealed trait OB extends Overlapping + // // case class OBC(s: String) extends OB + // // case class OAB(i: Int) extends OA with OB + + + // val o0 = gen.to(o) + // val xxxxxxxx: String = gen + // // identity(o0: (OAC |: OBC |: OAB |: SNil)[Nothing]) // Typed? + + // val o1 = gen.from(o0) + // identity(o1: Overlapping) // Typed? + // } + + // TODO + def testCaseObjects(): Unit = { + val a: Enum = A + val b: Enum = B + val c: Enum = C + + // val gen = Representable[Enum] + + // val a0 = gen.to(a) + // identity(a0: ABC) // Typed? + + // val b0 = gen.to(b) + // identity(b0: ABC) // Typed? + + // val c0 = gen.to(c) + // identity(c0: ABC) // Typed? + + // val a1 = gen.from(a0) + // identity(a1: Enum) // Typed? + + // val b1 = gen.from(b0) + // identity(b1: Enum) // Typed? + + // val c1 = gen.from(c0) + // identity(c1: Enum) // Typed? + } + + // TODO + // def testParametrized(): Unit = { + // val t: Tree[Int] = Node(Node(Leaf(23), Leaf(13)), Leaf(11)) + // type NI = Leaf[Int] |: Node[Int] |: SNil + + // val gen = Representable[Tree[Int]] + + // val t0 = gen.to(t) + // identity(t0: NI[Nothing]) // Typed? + + // val t1 = gen.from(t0) + // identity(t1: Tree[Int]) // Typed? + // } + + // def testParametrizedWithVarianceOption(): Unit = { + // val o: Option[Int] = Option(23) + // type SN = None.type |: Some[Int] |: SNil + + // val gen = Representable[Option[Int]] + + // val o0 = gen.to(o) + // identity(o0: SN[Nothing]) // Typed? + + // val o1 = gen.from(o0) + // identity(o1: Option[Int]) // Typed? + // } + + // def testParametrizedWithVarianceList(): Unit = { + // import scala.collection.immutable.{ &: => Cons } + + // val l: List[Int] = List(1, 2, 3) + // type CN = Cons[Int] |: Nil.type |: SNil + + // val gen = Representable[List[Int]] + + // val l0 = gen.to(l) + // identity(l0: CN) // Typed? + + // val l1 = gen.from(l0) + // identity(l1: List[Int]) // Typed? + // } + + // def testParametrzedSubset(): Unit = { + // val l = Left(23) + // val r = Right(true) + // type IB = Left[Int] |: Right[Boolean] |: SNil + + // val gen = Representable[Xor[Int, Boolean]] + + // val c0 = gen.to(l) + // assertTypedEquals[IB](SLeft(l), c0) + + // val c1 = gen.to(r) + // assertTypedEquals[IB](SRight(SLeft(r)), c1) + // } + + // def testParametrizedPermute(): Unit = { + // val s = Swap(23, true) + // type IB = Swap[Int, Boolean] |: SNil + + // val gen = Representable[Base[Boolean, Int]] + + // val s0 = gen.to(s) + // assertTypedEquals[IB](SLeft(s), s0) + // } + + // NOT SUPPORTED + // def testAbstractNonCC(): Unit = { + // val ncca = new NonCCA(23, "foo") + // val nccb = new NonCCB(true, 2.0) + // val nccc = new NonCCWithVars('c', 42) + // val ancc: AbstractNonCC = ncca + + // val genA = Representable[NonCCA] + // val genB = Representable[NonCCB] + // val genC = Representable[NonCCWithVars] + // val genAbs = Representable[AbstractNonCC] + + // val rA = genA.to(ncca) + // assertTypedEquals[Int &: String &: PNil](23 &: "foo" &: PNil, rA) + + // val rB = genB.to(nccb) + // assertTypedEquals[Boolean &: Double &: PNil](true &: 2.0 &: PNil, rB) + + // val rC = genC.to(nccc) + // assertTypedEquals[Char &: Long &: PNil]('c' &: 42l &: PNil, rC) + + // val rAbs = genAbs.to(ancc) + // assertTypedEquals[NonCCA |: NonCCB |: NonCCWithVars |: SNil](SLeft(ncca), rAbs) + + // val fA = genA.from(13 &: "bar" &: PNil) + // identity(fA: NonCCA) // Typed? + // assert(13 == fA.i) + // assert("bar" == fA.s) + + // val fB = genB.from(false &: 3.0 &: PNil) + // identity(fB: NonCCB) // Typed? + // assert(false == fB.b) + // assert(3.0 == fB.d) // , Double.MinPositiveValue) + + // val fC = genC.from('k' &: 313l &: PNil) + // identity(fC: NonCCWithVars) // Typed? + // assert('k' == fC.c) + // assert(313l == fC.l) + + // val fAbs = genAbs.from(SRight(SLeft(nccb))) + // identity(fAbs: AbstractNonCC) // Typed? + // assertTrue(fAbs.isInstanceOf[NonCCB]) + // assert(true == fAbs.asInstanceOf[NonCCB].b) + // assert(2.0 == fAbs.asInstanceOf[NonCCB].d) // , Double.MinPositiveValue) + // } + + // NOT SUPPORTED + // def testNonCCWithCompanion(): Unit = { + // val nccc = NonCCWithCompanion(23, "foo") + + // val gen = Representable[NonCCWithCompanion] + + // val r = gen.to(nccc) + // assertTypedEquals[Int &: String &: PNil](23 &: "foo" &: PNil, r) + + // val f = gen.from(13 &: "bar" &: PNil) + // identity(f: NonCCWithCompanion) // Typed? + // assert(13 == f.i) + // assert("bar" == f.s) + // } + + // NOT SUPPORTED + // def testNonCCLazy(): Unit = { + // lazy val (a: NonCCLazy, b: NonCCLazy, c: NonCCLazy) = + // (new NonCCLazy(c, b), new NonCCLazy(a, c), new NonCCLazy(b, a)) + + // val gen = Representable[NonCCLazy] + + // val rB = gen.to(b) + // assertTypedEquals[NonCCLazy &: NonCCLazy &: PNil](a &: c &: PNil, rB) + + // val fD = gen.from(a &: c &: PNil) + // identity(fD: NonCCLazy) // Typed? + // assert(a == fD.prev) + // assert(c == fD.next) + // } + + // TODO + // trait Parent { + // case class Nested(i: Int, s: String) + + // sealed abstract class Foo extends Product with Serializable + + // case object A extends Foo + // case object B extends Foo + // case class C() extends Foo + // } + + // trait Child extends Parent { + // val gen = Representable[Nested] + // val adtGen = Representable[Foo] + // } + + // object O extends Child + + // def testNestedInherited(): Unit = { + // val n0 = O.Nested(23, "foo") + // val repr = O.gen.to(n0) + // identity(repr: Int &: String &: PNil) // Typed? + // val n1 = O.gen.from(repr) + // identity(n1: O.Nested) // Typed? + // assert(n0 == n1) + + // { + // val foo0 = O.B + // val repr = O.adtGen.to(foo0) + // identity(repr: O.A.type |: O.B.type |: O.C |: SNil) // Typed? + // } + + // { + // val foo0 = O.C() + // val repr = O.adtGen.to(foo0) + // identity(repr: O.A.type |: O.B.type |: O.C |: SNil) // Typed? + // } + // } + + def testNonRepresentable(): Unit = { + import scala.implicits.Not + + implicitly[Not[Representable[Int]]] + implicitly[Not[Representable[Array[Int]]]] + implicitly[Not[Representable[String]]] + implicitly[Not[Representable[PNil]]] + implicitly[Not[Representable[Int &: String &: PNil]]] + implicitly[Not[Representable[SNil]]] + implicitly[Not[Representable[Int |: String |: SNil]]] + } + + // sealed trait Color + // case object Green extends Color + // object Color { + // case object Red extends Color + // } + + // def testNestedCaseObjects(): Unit = { + // Representable[Green.type] + // Representable[Color.Red.type] + // // LabelledRepresentable[Green.type] + // // LabelledRepresentable[Color.Red.type] + // } + + // sealed trait Base1 + // case object Foo1 extends Base1 + // case object Bar1 extends Base1 + + // trait TC[T] + + // object TC { + // def apply[T](implicit tc: TC[T]): TC[T] = tc + + // implicit def hnilTC: TC[PNil] = new TC[PNil] {} + // implicit def hconsTC[H, T <: HList](implicit hd: Lazy[TC[H]], tl: Lazy[TC[T]]): TC[H &: T] = new TC[H &: T] {} + + // implicit def cnilTC: TC[SNil] = new TC[SNil] {} + // implicit def cconsTC[H, T <: Coproduct](implicit hd: Lazy[TC[H]], tl: Lazy[TC[T]]): TC[H |: T] = new TC[H |: T] {} + + // implicit def projectTC[F, G](implicit gen: Representable.Aux[F, G], tc: Lazy[TC[G]]): TC[F] = new TC[F] {} + // } + + // def testCaseObjectsAndLazy(): Unit = { + // TC[Base1] + // } +} + +// package RepresentableTestsAux2 { +// trait Foo[T] + +// object Foo { +// implicit val derivePNil: Foo[PNil] = ??? + +// implicit def deriveLabelledRepresentable[A, Rec <: HList] +// (implicit gen: Representable.Aux[A, Rec], auto: Foo[Rec]): Foo[A] = ??? +// } + +// class Bar[A] + +// object Bar { +// implicit def cnil: Bar[SNil] = ??? + +// implicit def deriveCoproduct[H, T <: Coproduct] +// (implicit headFoo: Foo[H], tailAux: Bar[T]): Bar[H |: T] = ??? + +// implicit def labelledRepresentable[A, U <: Coproduct] +// (implicit gen: Representable.Aux[A, U], auto: Bar[U]): Bar[A] = ??? +// } + +// class Outer1 { +// sealed trait Color +// object Inner { +// case object Red extends Color +// } + +// implicitly[Bar[Color]] +// } + +// object Outer2 { +// class Wrapper { +// sealed trait Color +// } +// val wrapper = new Wrapper +// import wrapper.Color +// case object Red extends Color +// case object Green extends Color +// case object Blue extends Color + +// implicitly[Bar[Color]] +// } + +// object Outer3 { +// class Wrapper { +// sealed trait Color +// } +// val wrapper = new Wrapper +// case object Red extends wrapper.Color +// case object Green extends wrapper.Color +// case object Blue extends wrapper.Color + +// implicitly[Bar[wrapper.Color]] +// } + +// object Outer4 { +// val wrapper = new Wrapper +// case object Red extends wrapper.Color +// case object Green extends wrapper.Color +// case object Blue extends wrapper.Color + +// class Wrapper { +// sealed trait Color +// implicitly[Bar[wrapper.Color]] +// } +// } + +// object Outer5 { +// trait Command +// object Command { +// sealed trait Execution extends Command +// } + +// case class Buzz() extends Command.Execution +// case class Door() extends Command.Execution + +// Representable[Command.Execution] +// } +// } + +// object MixedCCNonCCNested { +// // Block local +// { +// object T1{ +// sealed abstract class Tree +// final case class Node(left: Tree, right: Tree, v: Int) extends Tree +// case object Leaf extends Tree +// } + +// Representable[T1.Tree] +// import T1._ +// Representable[Tree] + +// sealed trait A +// sealed case class B(i: Int, s: String) extends A +// case object C extends A +// sealed trait D extends A +// final case class E(a: Double, b: Option[Float]) extends D +// case object F extends D +// sealed abstract class Foo extends D +// case object Baz extends Foo +// final class Bar extends Foo +// final class Baz(val i1: Int, val s1: String) extends Foo + +// Representable[A] +// Representable[B] +// Representable[C.type] +// Representable[D] +// Representable[E] +// Representable[F.type] +// Representable[Foo] +// Representable[Baz.type] +// Representable[Bar] +// Representable[Baz] +// } + +// def methodLocal: Unit = { +// object T1{ +// sealed abstract class Tree +// final case class Node(left: Tree, right: Tree, v: Int) extends Tree +// case object Leaf extends Tree +// } + +// Representable[T1.Tree] +// import T1._ +// Representable[Tree] + +// sealed trait A +// sealed case class B(i: Int, s: String) extends A +// case object C extends A +// sealed trait D extends A +// final case class E(a: Double, b: Option[Float]) extends D +// case object F extends D +// sealed abstract class Foo extends D +// case object Baz extends Foo +// final class Bar extends Foo +// final class Baz(val i1: Int, val s1: String) extends Foo + +// Representable[A] +// Representable[B] +// Representable[C.type] +// Representable[D] +// Representable[E] +// Representable[F.type] +// Representable[Foo] +// Representable[Baz.type] +// Representable[Bar] +// Representable[Baz] +// } + +// // Top level +// object T1{ +// sealed abstract class Tree +// final case class Node(left: Tree, right: Tree, v: Int) extends Tree +// case object Leaf extends Tree +// } + +// Representable[T1.Tree] +// import T1._ +// Representable[Tree] + +// sealed trait A +// sealed case class B(i: Int, s: String) extends A +// case object C extends A +// sealed trait D extends A +// final case class E(a: Double, b: Option[Float]) extends D +// case object F extends D +// sealed abstract class Foo extends D +// case object Baz extends Foo +// final class Bar extends Foo +// final class Baz(val i1: Int, val s1: String) extends Foo + +// Representable[A] +// Representable[B] +// Representable[C.type] +// Representable[D] +// Representable[E] +// Representable[F.type] +// Representable[Foo] +// Representable[Baz.type] +// Representable[Bar] +// Representable[Baz] +// } + +// object EnumDefns0 { +// sealed trait EnumVal +// val BarA = new EnumVal { val name = "A" } +// val BarB = new EnumVal { val name = "B" } +// val BarC = new EnumVal { val name = "C" } +// } + +// object EnumDefns1 { +// sealed trait EnumVal +// object BarA extends EnumVal { val name = "A" } +// object BarB extends EnumVal { val name = "B" } +// object BarC extends EnumVal { val name = "C" } +// } + +// object EnumDefns2 { +// sealed trait EnumVal +// case object BarA extends EnumVal { val name = "A" } +// case object BarB extends EnumVal { val name = "B" } +// case object BarC extends EnumVal { val name = "C" } +// } + +// object EnumDefns3 { +// sealed trait EnumVal +// val BarA, BarB, BarC = new EnumVal {} +// } + +// object EnumDefns4 { +// sealed trait EnumVal +// object EnumVal { +// val BarA = new EnumVal { val name = "A" } +// val BarB = new EnumVal { val name = "B" } +// val BarC = new EnumVal { val name = "C" } +// } +// } + +// object EnumDefns5 { +// sealed trait EnumVal +// object EnumVal { +// object BarA extends EnumVal { val name = "A" } +// object BarB extends EnumVal { val name = "B" } +// object BarC extends EnumVal { val name = "C" } +// } +// } + +// object EnumDefns6 { +// sealed trait EnumVal +// object EnumVal { +// case object BarA extends EnumVal { val name = "A" } +// case object BarB extends EnumVal { val name = "B" } +// case object BarC extends EnumVal { val name = "C" } +// } +// } + +// object EnumDefns7 { +// sealed trait EnumVal +// object EnumVal { +// val BarA, BarB, BarC = new EnumVal {} +// } +// } + +// class TestEnum { +// def testEnum0(): Unit = { +// import EnumDefns0._ + +// val gen = Representable[EnumVal] +// val a0 = gen.to(BarA) +// assert(a0 == SLeft(BarA)) + +// val b0 = gen.to(BarB) +// assert(b0 == SRight(SLeft(BarB))) + +// val c0 = gen.to(BarC) +// assert(c0 == SRight(SRight(SLeft(BarC)))) +// } + +// def testEnum1(): Unit = { +// import EnumDefns1._ + +// val gen = Representable[EnumVal] +// val a0 = gen.to(BarA) +// assert(a0 == SLeft(BarA)) + +// val b0 = gen.to(BarB) +// assert(b0 == SRight(SLeft(BarB))) + +// val c0 = gen.to(BarC) +// assert(c0 == SRight(SRight(SLeft(BarC)))) +// } + +// def testEnum2(): Unit = { +// import EnumDefns2._ + +// val gen = Representable[EnumVal] +// val a0 = gen.to(BarA) +// assert(a0 == SLeft(BarA)) + +// val b0 = gen.to(BarB) +// assert(b0 == SRight(SLeft(BarB))) + +// val c0 = gen.to(BarC) +// assert(c0 == SRight(SRight(SLeft(BarC)))) +// } + +// def testEnum3(): Unit = { +// import EnumDefns3._ + +// val gen = Representable[EnumVal] +// val a0 = gen.to(BarA) +// assert(a0 == SLeft(BarA)) + +// val b0 = gen.to(BarB) +// assert(b0 == SRight(SLeft(BarB))) + +// val c0 = gen.to(BarC) +// assert(c0 == SRight(SRight(SLeft(BarC)))) +// } + +// def testEnum4(): Unit = { +// import EnumDefns4._ +// import EnumVal._ + +// val gen = Representable[EnumVal] +// val a0 = gen.to(BarA) +// assert(a0 == SLeft(BarA)) + +// val b0 = gen.to(BarB) +// assert(b0 == SRight(SLeft(BarB))) + +// val c0 = gen.to(BarC) +// assert(c0 == SRight(SRight(SLeft(BarC)))) +// } + +// def testEnum5(): Unit = { +// import EnumDefns5._ +// import EnumVal._ + +// val gen = Representable[EnumVal] +// val a0 = gen.to(BarA) +// assert(a0 == SLeft(BarA)) + +// val b0 = gen.to(BarB) +// assert(b0 == SRight(SLeft(BarB))) + +// val c0 = gen.to(BarC) +// assert(c0 == SRight(SRight(SLeft(BarC)))) +// } + +// def testEnum6(): Unit = { +// import EnumDefns6._ +// import EnumVal._ + +// val gen = Representable[EnumVal] +// val a0 = gen.to(BarA) +// assert(a0 == SLeft(BarA)) + +// val b0 = gen.to(BarB) +// assert(b0 == SRight(SLeft(BarB))) + +// val c0 = gen.to(BarC) +// assert(c0 == SRight(SRight(SLeft(BarC)))) +// } + +// def testEnum7(): Unit = { +// import EnumDefns7._ +// import EnumVal._ + +// val gen = Representable[EnumVal] +// val a0 = gen.to(BarA) +// assert(a0 == SLeft(BarA)) + +// val b0 = gen.to(BarB) +// assert(b0 == SRight(SLeft(BarB))) + +// val c0 = gen.to(BarC) +// assert(c0 == SRight(SRight(SLeft(BarC)))) +// } + +// def main(args: Array[String]): Unit = { +// testProductBasics() +// testProductVarargs() +// testTuples() +// testCoproductBasics() +// testCoproductMapBasics() +// testSingletonCoproducts() +// testOverlappingCoproducts() +// testCaseObjects() +// testCaseObjectMap() +// testParametrized() +// testParametrizedWithVarianceOption() +// testParametrizedWithVarianceList() +// testParametrzedSubset() +// testParametrizedPermute() +// testAbstractNonCC() +// testNonCCWithCompanion() +// testNonCCLazy() +// testNestedInherited() +// testIsTuple() +// testHasProductRepresentable() +// testHasCoproductRepresentable() +// testNonRepresentable() +// testNestedCaseObjects() +// testCaseObjectsAndLazy() +// testEnum0() +// testEnum1() +// testEnum2() +// testEnum3() +// testEnum4() +// testEnum5() +// testEnum6() +// testEnum7() +// } +// } + +// package TestPrefixes1 { +// trait Defs { +// case class CC(i: Int, s: String) + +// sealed trait Sum +// case class SumI(i: Int) extends Sum +// case class SumS(s: String) extends Sum +// } + +// object Defs extends Defs + +// object Derivations { +// import shapeless._ + +// Representable[Defs.CC] +// Representable[Defs.SumI] +// Representable[Defs.SumS] + +// Representable[Defs.Sum] +// // Representable.materialize[Defs.Sum, Defs.SumI |: Defs.SumS |: SNil] +// } +// } + +// package TestSingletonMembers { +// case class CC(i: Int, s: Witness.`"msg"`.T) + +// object Derivations2 { +// Representable[CC] +// } +// } + +// object PathVariantDefns { +// sealed trait AtomBase { +// sealed trait Atom +// case class Zero(value: String) extends Atom +// } + +// trait Atom1 extends AtomBase { +// case class One(value: String) extends Atom +// } + +// trait Atom2 extends AtomBase { +// case class Two(value: String) extends Atom +// } + +// object Atoms01 extends AtomBase with Atom1 +// object Atoms02 extends AtomBase with Atom2 +// } + +// object PathVariants { +// import PathVariantDefns._ + +// val gen1 = Representable[Atoms01.Atom] +// implicitly[gen1.Repr =:= (Atoms01.One |: Atoms01.Zero |: SNil)] + +// val gen2 = Representable[Atoms02.Atom] +// implicitly[gen2.Repr =:= (Atoms02.Two |: Atoms02.Zero |: SNil)] +// } + +// object PrivateCtorDefns { +// sealed trait PublicFamily +// case class PublicChild() extends PublicFamily +// private case class PrivateChild() extends PublicFamily +// } + +// // object PrivateCtor { +// // import PrivateCtorDefns._ +// // +// // implicitlyped "" +// // Representable[Access.PublicFamily] +// // """) +// // } + +// object Thrift { +// object TProduct { +// def apply(a: Double, b: String): TProduct = new Immutable(a, b) + +// def unapply(tp: TProduct): Option[Product2[Double, String]] = Some(tp) + +// //class Immutable(val a: Double, val b: String) extends TProduct + +// class Immutable( +// val a: Double, +// val b: String, +// val _passthroughFields: scala.collection.immutable.Map[Short, Byte] +// ) extends TProduct { +// def this( +// a: Double, +// b: String +// ) = this( +// a, +// b, +// Map.empty +// ) +// } +// } + +// trait TProduct extends Product2[Double, String] { +// def a: Double +// def b: String + +// def _1 = a +// def _2 = b + +// override def productPrefix: String = "TProduct" + +// def canEqual(t: Any): Boolean = true +// } + +// Representable[TProduct.Immutable] +// } + +// object HigherKinded { +// type Id[A] = A + +// sealed trait Foo[A[_]] +// case class Bar[A[_]]() extends Foo[A] + +// Representable[Bar[Id]] +// Representable[Foo[Id]] + +// sealed trait Pipo[A[_]] +// case class Lino() extends Pipo[Id] + +// Representable[Pipo[Id]] +// } From a585c3689dbe943c53eb6314f6be54600f006f5f Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 24 Nov 2017 16:39:50 +0100 Subject: [PATCH 15/32] Fix "_ is not a type name" on objects --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index d2d904d1ae5c..e6d3a2b11d65 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -702,7 +702,7 @@ trait Implicits { self: Typer => import transform.SymUtils._ - val sumTypes = A.classSymbol.children.map(_.typeRef).reverse // Reveresed to match order in source + val sumTypes = A.classSymbol.children.map(_.namedType).reverse // Reveresed to match order in source if (sumTypes.isEmpty) return typed(EmptyTree) val sumTypesSize = sumTypes.size val X = tpnme.syntheticTypeParamName(0) From d6e1e75ea963c2acb1a11be07734f307ab6d4bc6 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Mon, 27 Nov 2017 14:26:39 +0100 Subject: [PATCH 16/32] Add asSeenFrom for sums --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index e6d3a2b11d65..9a83baf02313 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -702,7 +702,7 @@ trait Implicits { self: Typer => import transform.SymUtils._ - val sumTypes = A.classSymbol.children.map(_.namedType).reverse // Reveresed to match order in source + val sumTypes = A.classSymbol.children.map(_.namedType.asSeenFrom(A, A.classSymbol)).reverse // Reveresed to match order in source if (sumTypes.isEmpty) return typed(EmptyTree) val sumTypesSize = sumTypes.size val X = tpnme.syntheticTypeParamName(0) From 3ddb3043a0db068513c49fae87df01c34831b519 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 30 Nov 2017 13:43:47 +0100 Subject: [PATCH 17/32] Swap product/sum cases in pattern match --- .../dotty/tools/dotc/typer/Implicits.scala | 128 +++++++++--------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 9a83baf02313..69186d4864ea 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -600,20 +600,6 @@ trait Implicits { self: Typer => } /** - * When A is a product, synthesize an instance with the following shape: - * - * ``` - * new Representable[A] { - * type Repr[t] = T1 &: T2 &: ... &: Tn &: PNil - * - * def to[T](a: A): Repr[T] = - * PCons(a._1, (PCons(a._2, ... PCons(a._m, PNil())))) - * - * def from[T](r: Repr[T]): A = r match { - * case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) - * } - * ``` - * * When A is a sum, synthesize an instance with the following shape: * * ``` @@ -634,6 +620,20 @@ trait Implicits { self: Typer => * case SRight(SRight(...SRight(SLeft(x)))) => x * } * ``` + * + * When A is a product, synthesize an instance with the following shape: + * + * ``` + * new Representable[A] { + * type Repr[t] = T1 &: T2 &: ... &: Tn &: PNil + * + * def to[T](a: A): Repr[T] = + * PCons(a._1, (PCons(a._2, ... PCons(a._m, PNil())))) + * + * def from[T](r: Repr[T]): A = r match { + * case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) + * } + * ``` */ def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = typed { import untpd._ @@ -645,56 +645,6 @@ trait Implicits { self: Typer => val fromNme = "from".toTermName formal.argTypes match { - // Products ----------------------------------------------------------- - case (A @ _) :: Nil if defn.isProductSubType(A) => - val PNilTree = rootQual("PNil") - val PConsTree = rootQual("PCons") - - val productTypes = productSelectorTypes(A) - val productTypesSize = productTypes.size - val X = tpnme.syntheticTypeParamName(0) - val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) - - // This fold generates T1 &: T2 &: ... &: Tn &: PNil, where - // type &:[H, T[t] <: Prod[t]] = [X] => PCons[[Y] => H, T, X] - val reprRhs = - productTypes.foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => - val constCur = LambdaTypeTree(TypeDef(X, noBounds) :: Nil, TypeTree(cur)) - val pconsArgs = constCur :: acc :: Ident(X) :: Nil - val rhs = AppliedTypeTree(TypeTree(defn.PConsType), pconsArgs) - LambdaTypeTree(TypeDef(X, noBounds) :: Nil, rhs) - } - // type Repr[t] = T1 &: T2 &: ... &: Tn &: PNil - val repr = TypeDef(ReprNme, reprRhs) - val reprX = AppliedTypeTree(Ident(ReprNme), Ident(X) :: Nil) - val pNil = Apply(PNilTree, Nil) - - // def to[T](a: A): Repr[T] = - // PCons(a._1, (PCons(a._2, ... PCons(a._n, PNil())))) - val to = { - val arg = makeSyntheticParameter(tpt = TypeTree(A)) - val body = (1 to productTypesSize).foldRight(pNil) { case (i, acc) => - Apply(PConsTree, Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) - } - DefDef(toNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, reprX, body).withFlags(Synthetic) - } - - // def from[T](r: Repr[T]): A = r match { - // case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) - // } - val from = { - val arg = makeSyntheticParameter(tpt = reprX) - val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => - Apply(PConsTree, Ident(nme.productAccessorName(i)) :: acc :: Nil) - } - val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList :: Nil - val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, New(TypeTree(A), newArgs)) :: Nil) - DefDef(fromNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) - } - - val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil - New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) - // Sums ----------------------------------------------------------- case (A @ _) :: Nil if A.typeSymbol.is(Sealed) => val SLeftTree = rootQual("SLeft") @@ -769,6 +719,56 @@ trait Implicits { self: Typer => val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) + // Products ----------------------------------------------------------- + case (A @ _) :: Nil if defn.isProductSubType(A) => + val PNilTree = rootQual("PNil") + val PConsTree = rootQual("PCons") + + val productTypes = productSelectorTypes(A) + val productTypesSize = productTypes.size + val X = tpnme.syntheticTypeParamName(0) + val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) + + // This fold generates T1 &: T2 &: ... &: Tn &: PNil, where + // type &:[H, T[t] <: Prod[t]] = [X] => PCons[[Y] => H, T, X] + val reprRhs = + productTypes.foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => + val constCur = LambdaTypeTree(TypeDef(X, noBounds) :: Nil, TypeTree(cur)) + val pconsArgs = constCur :: acc :: Ident(X) :: Nil + val rhs = AppliedTypeTree(TypeTree(defn.PConsType), pconsArgs) + LambdaTypeTree(TypeDef(X, noBounds) :: Nil, rhs) + } + // type Repr[t] = T1 &: T2 &: ... &: Tn &: PNil + val repr = TypeDef(ReprNme, reprRhs) + val reprX = AppliedTypeTree(Ident(ReprNme), Ident(X) :: Nil) + val pNil = Apply(PNilTree, Nil) + + // def to[T](a: A): Repr[T] = + // PCons(a._1, (PCons(a._2, ... PCons(a._n, PNil())))) + val to = { + val arg = makeSyntheticParameter(tpt = TypeTree(A)) + val body = (1 to productTypesSize).foldRight(pNil) { case (i, acc) => + Apply(PConsTree, Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) + } + DefDef(toNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, reprX, body).withFlags(Synthetic) + } + + // def from[T](r: Repr[T]): A = r match { + // case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) + // } + val from = { + val arg = makeSyntheticParameter(tpt = reprX) + val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => + Apply(PConsTree, Ident(nme.productAccessorName(i)) :: acc :: Nil) + } + val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList :: Nil + val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, New(TypeTree(A), newArgs)) :: Nil) + DefDef(fromNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) + } + + val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil + New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) + case _ => EmptyTree } From 471fb3fca290d3bdd4b8542ac614374502f1e238 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 30 Nov 2017 13:44:16 +0100 Subject: [PATCH 18/32] Handle case objects --- .../src/dotty/tools/dotc/typer/Implicits.scala | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 69186d4864ea..c29c2759c5e7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -563,7 +563,7 @@ trait Implicits { self: Typer => /** If `formal` is of the form ClassTag[T], where `T` is a class type, * synthesize a class tag for `T`. - */ + */ def synthesizedClassTag(formal: Type)(implicit ctx: Context): Tree = formal.argTypes match { case arg :: Nil => @@ -761,8 +761,16 @@ trait Implicits { self: Typer => val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => Apply(PConsTree, Ident(nme.productAccessorName(i)) :: acc :: Nil) } - val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList :: Nil - val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, New(TypeTree(A), newArgs)) :: Nil) + val neuu = { + A match { + case tpe: NamedType if tpe.termSymbol.exists => // case object + untpd.ref(tpe) + case _ if A.classSymbol.exists => // case class + val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList :: Nil + New(TypeTree(A), newArgs) + } + } + val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, neuu) :: Nil) DefDef(fromNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) } From 8fbb760fb9da11d07f7d7a262f9952479ee747cd Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 30 Nov 2017 15:12:25 +0100 Subject: [PATCH 19/32] Handle varargs --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index c29c2759c5e7..2bb15911df1f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -739,7 +739,7 @@ trait Implicits { self: Typer => LambdaTypeTree(TypeDef(X, noBounds) :: Nil, rhs) } // type Repr[t] = T1 &: T2 &: ... &: Tn &: PNil - val repr = TypeDef(ReprNme, reprRhs) + val repr = TypeDef(ReprNme, reprRhs) val reprX = AppliedTypeTree(Ident(ReprNme), Ident(X) :: Nil) val pNil = Apply(PNilTree, Nil) @@ -766,8 +766,10 @@ trait Implicits { self: Typer => case tpe: NamedType if tpe.termSymbol.exists => // case object untpd.ref(tpe) case _ if A.classSymbol.exists => // case class - val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList :: Nil - New(TypeTree(A), newArgs) + val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList + val hasVarargs = A.classSymbol.primaryConstructor.info.isVarArgsMethod + val newArgs0 = if (hasVarargs) newArgs.init :+ repeated(newArgs.last) else newArgs + New(TypeTree(A), newArgs0 :: Nil) } } val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, neuu) :: Nil) From e4618358b8ce3117bb7222d6c4d765fe5589a949 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 30 Nov 2017 15:56:45 +0100 Subject: [PATCH 20/32] Remove warnings using unchecked --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 2bb15911df1f..cdf531efd45e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -618,6 +618,7 @@ trait Implicits { self: Typer => * case SRight(SLeft(x)) => x * ... * case SRight(SRight(...SRight(SLeft(x)))) => x + * case _ => throw new MatchError() * } * ``` * @@ -671,8 +672,9 @@ trait Implicits { self: Typer => // type Repr[t] = T1 |: T2 |: ... |: Tn |: PNil val repr: Tree = TypeDef(ReprNme, reprRhs) val reprX = AppliedTypeTree(Ident(ReprNme), Ident(X) :: Nil) + def unchecked(t: Tree): Tree = Annotated(t, untpd.New(TypeTree(defn.UncheckedAnnotType), Nil)) - // def to[T](a: A): Repr[T] = (@unchecked a) match { + // def to[T](a: A): Repr[T] = (a: @unchecked) match { // case x: T1 => SLeft(x) // case x: T2 => SRight(SLeft(x)) // ... @@ -689,12 +691,11 @@ trait Implicits { self: Typer => CaseDef(Typed(Ident(x), TypeTree(tpe)), EmptyTree, rhs) } } - val unchecked = Select(Ident(nme.scala_), "unchecked".toTypeName) - val body = Match(Annotated(Ident(arg.name), untpd.New(unchecked, Nil)), cases) + val body = Match(unchecked(Ident(arg.name)), cases) DefDef(toNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, reprX, body).withFlags(Synthetic) } - // def from[T](r: Repr[T]): A = r match { + // def from[T](r: Repr[T]): A = (r: @unchecked) match { // case SLeft(x) => x // case SRight(SLeft(x)) => x // ... @@ -711,7 +712,7 @@ trait Implicits { self: Typer => CaseDef(pat, EmptyTree, Ident(x)) }.toList } - val matsh = Match(Ident(arg.name), cases) + val matsh = Match(unchecked(Ident(arg.name)), cases) val body = TypeApply(Select(matsh, nme.asInstanceOf_), TypeTree(A) :: Nil) DefDef(fromNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) } From 175f022b37c2f94ba177feb84fb65b53c979b466 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 1 Dec 2017 11:21:00 +0100 Subject: [PATCH 21/32] Reuse instantiate from patmat exhaustivity --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index cdf531efd45e..153bce26b477 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -652,9 +652,13 @@ trait Implicits { self: Typer => val SRightTree = rootQual("SRight") import transform.SymUtils._ + import dotty.tools.dotc.transform.patmat.SpaceEngine + + val sumTypes = + A .classSymbol.children.map(_.namedType) + .map(t => new SpaceEngine().instantiate(t, A)) // Instantiate type all parameters + .reverse // Reveresed to match source order - val sumTypes = A.classSymbol.children.map(_.namedType.asSeenFrom(A, A.classSymbol)).reverse // Reveresed to match order in source - if (sumTypes.isEmpty) return typed(EmptyTree) val sumTypesSize = sumTypes.size val X = tpnme.syntheticTypeParamName(0) val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) From 86b3a168f2ae60ba1c36d219c2b39ee7bc07db5e Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 1 Dec 2017 11:21:40 +0100 Subject: [PATCH 22/32] Update representable tests --- tests/run/representable.scala | 876 +++++++++++++++++----------------- 1 file changed, 427 insertions(+), 449 deletions(-) diff --git a/tests/run/representable.scala b/tests/run/representable.scala index cb01c73d7e16..22311d0c144a 100644 --- a/tests/run/representable.scala +++ b/tests/run/representable.scala @@ -44,6 +44,8 @@ object RepresentableTestsAux { case class PersonWithPseudonims(name: String, nicks: String*) + case class PersonWithPseudonimsT[T](name: T, nicks: T*) + // NOT SUPPORTED // sealed trait AbstractNonCC // class NonCCA(val i: Int, val s: String) extends AbstractNonCC @@ -88,37 +90,36 @@ class RepresentableTests { val gen = Representable[Person] val p0 = gen.to(p) - identity(p0: SSI[Nothing]) // Typed? + identity[SSI[Nothing]](p0) assert(("Joe Soap" &: "Brighton" &: 23 &: PNil()) == p0) val p1 = gen.from(p0) - identity(p1: Person) // Typed? + identity[Person](p1) assert(p == p1) } - // TODO - // def testProductVarargs(): Unit = { - // val p = PersonWithPseudonims("Joe Soap", "X", "M", "Z") - // val gen = Representable[PersonWithPseudonims] + def testProductVarargs(): Unit = { + val p = PersonWithPseudonims("Joe Soap", "X", "M", "Z") + val gen = Representable[PersonWithPseudonims] - // val p0 = gen.to(p) - // // identity(p0: String &: Seq[String] &: PNil) // Typed? - // assert(("Joe Soap" &: Seq("X", "M", "Z") &: PNil()) == p0) + val p0 = gen.to(p) + // identity[String &: Seq[String] &: PNil](p0) + assert(("Joe Soap" &: Seq("X", "M", "Z") &: PNil()) == p0) - // val p1 = gen.from(p0) - // identity(p1: PersonWithPseudonims) // Typed? - // assert(p == p1) - // } + val p1 = gen.from(p0) + identity[PersonWithPseudonims](p1) + assert(p == p1) + } def testTuples(): Unit = { val gen1 = Representable[Tuple1[Int]] - identity(gen1: Representable[Tuple1[Int]] { type Repr = Int &: PNil }) // Typed? + identity[Representable[Tuple1[Int]] { type Repr = Int &: PNil }](gen1) val gen2 = Representable[(Int, String)] - identity(gen2: Representable[(Int, String)] { type Repr = Int &: String &: PNil }) // Typed? + identity[Representable[(Int, String)] { type Repr = Int &: String &: PNil }](gen2) val gen3 = Representable[(Int, String, Boolean)] - identity(gen3: Representable[(Int, String, Boolean)] { type Repr = Int &: String &: Boolean &: PNil }) // Typed? + identity[Representable[(Int, String, Boolean)] { type Repr = Int &: String &: Boolean &: PNil }](gen3) } def testCoproductBasics(): Unit = { @@ -130,31 +131,31 @@ class RepresentableTests { val gen = Representable[Fruit] val a0 = gen.to(a) - identity(a0: APBO[Nothing]) // Typed? + identity[APBO[Nothing]](a0) val p0 = gen.to(p) - identity(p0: APBO[Nothing]) // Typed? + identity[APBO[Nothing]](p0) val b0 = gen.to(b) - identity(b0: APBO[Nothing]) // Typed? + identity[APBO[Nothing]](b0) val o0 = gen.to(o) - identity(o0: APBO[Nothing]) // Typed? + identity[APBO[Nothing]](o0) val a1 = gen.from(a0) - identity(a1: Fruit) // Typed? + identity[Fruit](a1) val p1 = gen.from(p0) - identity(p1: Fruit) // Typed? + identity[Fruit](p1) val b1 = gen.from(b0) - identity(b1: Fruit) // Typed? + identity[Fruit](b1) val o1 = gen.from(o0) - identity(o1: Fruit) // Typed? + identity[Fruit](o1) } def testSingletonCoproducts(): Unit = { @@ -165,125 +166,118 @@ class RepresentableTests { val s: AbstractSingle = Single() val s0 = gen.to(s) - identity(s0: (Single |: SNil)[Nothing]) // Typed? + identity[(Single |: SNil)[Nothing]](s0) val s1 = gen.from(s0) - identity(s1: AbstractSingle) // Typed? + identity[AbstractSingle](s1) } // TODO // def testOverlappingCoproducts(): Unit = { // val gen = Representable[Overlapping] // val o: Overlapping = OAB(1) - - // // sealed trait Overlapping - // // sealed trait OA extends Overlapping - // // case class OAC(s: String) extends OA - // // sealed trait OB extends Overlapping - // // case class OBC(s: String) extends OB - // // case class OAB(i: Int) extends OA with OB - - // val o0 = gen.to(o) - // val xxxxxxxx: String = gen - // // identity(o0: (OAC |: OBC |: OAB |: SNil)[Nothing]) // Typed? + // typed[(OAB |: OAC |: OBC |: CNil)[Nothing]](o0) // val o1 = gen.from(o0) - // identity(o1: Overlapping) // Typed? + // typed[Overlapping](o1) // } - // TODO def testCaseObjects(): Unit = { val a: Enum = A val b: Enum = B val c: Enum = C - // val gen = Representable[Enum] + val gen = Representable[Enum] - // val a0 = gen.to(a) - // identity(a0: ABC) // Typed? + val a0 = gen.to(a) + identity[ABC[Nothing]](a0) - // val b0 = gen.to(b) - // identity(b0: ABC) // Typed? + val b0 = gen.to(b) + identity[ABC[Nothing]](b0) - // val c0 = gen.to(c) - // identity(c0: ABC) // Typed? + val c0 = gen.to(c) + identity[ABC[Nothing]](c0) - // val a1 = gen.from(a0) - // identity(a1: Enum) // Typed? + val a1 = gen.from(a0) + identity[Enum](a1) - // val b1 = gen.from(b0) - // identity(b1: Enum) // Typed? + val b1 = gen.from(b0) + identity[Enum](b1) - // val c1 = gen.from(c0) - // identity(c1: Enum) // Typed? + val c1 = gen.from(c0) + identity[Enum](c1) } - // TODO - // def testParametrized(): Unit = { - // val t: Tree[Int] = Node(Node(Leaf(23), Leaf(13)), Leaf(11)) - // type NI = Leaf[Int] |: Node[Int] |: SNil + def testParametrized(): Unit = { + val t: Tree[Int] = Node(Node(Leaf(23), Leaf(13)), Leaf(11)) + type NI = Node[Int] |: Leaf[Int] |: SNil - // val gen = Representable[Tree[Int]] + val gen = Representable[Tree[Int]] - // val t0 = gen.to(t) - // identity(t0: NI[Nothing]) // Typed? + val t0 = gen.to(t) + identity[NI[Nothing]](t0) - // val t1 = gen.from(t0) - // identity(t1: Tree[Int]) // Typed? - // } + val t1 = gen.from(t0) + identity[Tree[Int]](t1) + } - // def testParametrizedWithVarianceOption(): Unit = { - // val o: Option[Int] = Option(23) - // type SN = None.type |: Some[Int] |: SNil + def testParametrizedWithVarianceOption(): Unit = { + val o: Option[Int] = Option(23) + type SN = None.type |: Some[Int] |: SNil - // val gen = Representable[Option[Int]] + val gen = Representable[Option[Int]] - // val o0 = gen.to(o) - // identity(o0: SN[Nothing]) // Typed? + val o0 = gen.to(o) + identity[SN[Nothing]](o0) - // val o1 = gen.from(o0) - // identity(o1: Option[Int]) // Typed? - // } + val o1 = gen.from(o0) + identity[Option[Int]](o1) + } + // TODO ??? // def testParametrizedWithVarianceList(): Unit = { - // import scala.collection.immutable.{ &: => Cons } - // val l: List[Int] = List(1, 2, 3) - // type CN = Cons[Int] |: Nil.type |: SNil + // type CN = ::[Int] |: Nil.type |: SNil // val gen = Representable[List[Int]] // val l0 = gen.to(l) - // identity(l0: CN) // Typed? + // identity[CN[Nothing]](l0) // val l1 = gen.from(l0) - // identity(l1: List[Int]) // Typed? + // identity[List[Int]](l1) // } - // def testParametrzedSubset(): Unit = { - // val l = Left(23) - // val r = Right(true) - // type IB = Left[Int] |: Right[Boolean] |: SNil + def testParametrzedSubset(): Unit = { + val l = Left(23) + val r = Right(true) + type IB = Left[Int] |: Right[Boolean] |: SNil - // val gen = Representable[Xor[Int, Boolean]] + val gen = Representable[Xor[Int, Boolean]] - // val c0 = gen.to(l) - // assertTypedEquals[IB](SLeft(l), c0) + val c0 = gen.to(l) + val d0 = SLeft(l) + identity[IB[Nothing]](c0) + assert(d0 == c0) - // val c1 = gen.to(r) - // assertTypedEquals[IB](SRight(SLeft(r)), c1) - // } + val c1 = gen.to(r) + val d1 = SRight(SLeft(r)) + identity[IB[Nothing]](c1) + assert(d1 == c1) + } - // def testParametrizedPermute(): Unit = { - // val s = Swap(23, true) - // type IB = Swap[Int, Boolean] |: SNil + def testParametrizedPermute(): Unit = { + val s = Swap(23, true) + type IB = Swap[Int, Boolean] |: SNil - // val gen = Representable[Base[Boolean, Int]] + val gen = Representable[Base[Boolean, Int]] - // val s0 = gen.to(s) - // assertTypedEquals[IB](SLeft(s), s0) - // } + val s0 = gen.to(s) + val s1 = SLeft(s) + identity[IB[Nothing]](s0) + assert(s1 == s0) + } // NOT SUPPORTED // def testAbstractNonCC(): Unit = { @@ -310,17 +304,17 @@ class RepresentableTests { // assertTypedEquals[NonCCA |: NonCCB |: NonCCWithVars |: SNil](SLeft(ncca), rAbs) // val fA = genA.from(13 &: "bar" &: PNil) - // identity(fA: NonCCA) // Typed? + // identity[NonCCA](fA) // assert(13 == fA.i) // assert("bar" == fA.s) // val fB = genB.from(false &: 3.0 &: PNil) - // identity(fB: NonCCB) // Typed? + // identity[NonCCB](fB) // assert(false == fB.b) // assert(3.0 == fB.d) // , Double.MinPositiveValue) // val fC = genC.from('k' &: 313l &: PNil) - // identity(fC: NonCCWithVars) // Typed? + // identity[NonCCWithVars](fC) // assert('k' == fC.c) // assert(313l == fC.l) @@ -357,49 +351,48 @@ class RepresentableTests { // assertTypedEquals[NonCCLazy &: NonCCLazy &: PNil](a &: c &: PNil, rB) // val fD = gen.from(a &: c &: PNil) - // identity(fD: NonCCLazy) // Typed? + // identity[NonCCLazy](fD) // assert(a == fD.prev) // assert(c == fD.next) // } - // TODO - // trait Parent { - // case class Nested(i: Int, s: String) - - // sealed abstract class Foo extends Product with Serializable - - // case object A extends Foo - // case object B extends Foo - // case class C() extends Foo - // } - - // trait Child extends Parent { - // val gen = Representable[Nested] - // val adtGen = Representable[Foo] - // } + trait Parent { + case class Nested(i: Int, s: String) - // object O extends Child + sealed abstract class Foo extends Product with Serializable - // def testNestedInherited(): Unit = { - // val n0 = O.Nested(23, "foo") - // val repr = O.gen.to(n0) - // identity(repr: Int &: String &: PNil) // Typed? - // val n1 = O.gen.from(repr) - // identity(n1: O.Nested) // Typed? - // assert(n0 == n1) + case object A extends Foo + case object B extends Foo + case class C() extends Foo + } - // { - // val foo0 = O.B - // val repr = O.adtGen.to(foo0) - // identity(repr: O.A.type |: O.B.type |: O.C |: SNil) // Typed? - // } + trait Child extends Parent { + val gen = Representable[Nested] + val adtGen = Representable[Foo] + } - // { - // val foo0 = O.C() - // val repr = O.adtGen.to(foo0) - // identity(repr: O.A.type |: O.B.type |: O.C |: SNil) // Typed? - // } - // } + object O extends Child + + def testNestedInherited(): Unit = { + val n0 = O.Nested(23, "foo") + val repr = O.gen.to(n0) + identity[(Int &: String &: PNil)[Nothing]](repr) + val n1 = O.gen.from(repr) + identity[O.Nested](n1) + assert(n0 == n1) + + { + val foo0 = O.B + val repr = O.adtGen.to(foo0) + identity[(O.A.type |: O.B.type |: O.C |: SNil)[Nothing]](repr) + } + + { + val foo0 = O.C() + val repr = O.adtGen.to(foo0) + identity[(O.A.type |: O.B.type |: O.C |: SNil)[Nothing]](repr) + } + } def testNonRepresentable(): Unit = { import scala.implicits.Not @@ -407,24 +400,25 @@ class RepresentableTests { implicitly[Not[Representable[Int]]] implicitly[Not[Representable[Array[Int]]]] implicitly[Not[Representable[String]]] - implicitly[Not[Representable[PNil]]] - implicitly[Not[Representable[Int &: String &: PNil]]] - implicitly[Not[Representable[SNil]]] - implicitly[Not[Representable[Int |: String |: SNil]]] + implicitly[Not[Representable[PNil[Nothing]]]] + implicitly[Not[Representable[(Int &: String &: PNil)[Nothing]]]] + implicitly[Not[Representable[SNil[Nothing]]]] + implicitly[Not[Representable[(Int |: String |: SNil)[Nothing]]]] } - // sealed trait Color - // case object Green extends Color - // object Color { - // case object Red extends Color - // } + sealed trait Color + case object Green extends Color + object Color { + case object Red extends Color + } - // def testNestedCaseObjects(): Unit = { - // Representable[Green.type] - // Representable[Color.Red.type] - // // LabelledRepresentable[Green.type] - // // LabelledRepresentable[Color.Red.type] - // } + def testNestedCaseObjects(): Unit = { + val a: Option[Green.type] = None + Representable[Green.type] + Representable[Color.Red.type] + // LabelledRepresentable[Green.type] + // LabelledRepresentable[Color.Red.type] + } // sealed trait Base1 // case object Foo1 extends Base1 @@ -436,12 +430,12 @@ class RepresentableTests { // def apply[T](implicit tc: TC[T]): TC[T] = tc // implicit def hnilTC: TC[PNil] = new TC[PNil] {} - // implicit def hconsTC[H, T <: HList](implicit hd: Lazy[TC[H]], tl: Lazy[TC[T]]): TC[H &: T] = new TC[H &: T] {} + // implicit def hconsTC[H, T <: HList](implicit hd: => TC[H], tl: => TC[T]): TC[H &: T] = new TC[H &: T] {} // implicit def cnilTC: TC[SNil] = new TC[SNil] {} - // implicit def cconsTC[H, T <: Coproduct](implicit hd: Lazy[TC[H]], tl: Lazy[TC[T]]): TC[H |: T] = new TC[H |: T] {} + // implicit def cconsTC[H, T <: Coproduct](implicit hd: => TC[H], tl: => TC[T]): TC[H |: T] = new TC[H |: T] {} - // implicit def projectTC[F, G](implicit gen: Representable.Aux[F, G], tc: Lazy[TC[G]]): TC[F] = new TC[F] {} + // implicit def projectTC[F, G](implicit gen: Representable.Aux[F, G], tc: => TC[G]): TC[F] = new TC[F] {} // } // def testCaseObjectsAndLazy(): Unit = { @@ -449,26 +443,28 @@ class RepresentableTests { // } } -// package RepresentableTestsAux2 { -// trait Foo[T] +// object RepresentableTestsAux2 { +// sealed trait Foo[T] // object Foo { -// implicit val derivePNil: Foo[PNil] = ??? +// implicit def derivePNil[X]: Foo[PNil[X]] = ??? -// implicit def deriveLabelledRepresentable[A, Rec <: HList] -// (implicit gen: Representable.Aux[A, Rec], auto: Foo[Rec]): Foo[A] = ??? +// implicit def deriveRepresentable[A, Rec[t] <: Prod[t], X] +// (implicit gen: Representable[A] { type Repr = Rec }, auto: Foo[Rec[X]]): Foo[A] = ??? // } -// class Bar[A] +// sealed class Bar[A] -// object Bar { -// implicit def cnil: Bar[SNil] = ??? +// object Bar extends Bar0 { +// implicit def cnil[X]: Bar[SNil[X]] = ??? +// } -// implicit def deriveCoproduct[H, T <: Coproduct] -// (implicit headFoo: Foo[H], tailAux: Bar[T]): Bar[H |: T] = ??? +// trait Bar0 { +// implicit def deriveCoproduct[H, T[t] <: Sum[t], X] +// (implicit headFoo: Foo[H], tailAux: Bar[T[X]]): Bar[(H |: T)[X]] = ??? -// implicit def labelledRepresentable[A, U <: Coproduct] -// (implicit gen: Representable.Aux[A, U], auto: Bar[U]): Bar[A] = ??? +// implicit def representable[A, U[t] <: Sum[t], X] +// (implicit gen: Representable[A] { type Repr = U }, auto: Bar[U[X]]): Bar[A] = ??? // } // class Outer1 { @@ -477,57 +473,60 @@ class RepresentableTests { // case object Red extends Color // } +// val r = Representable[Bar[Color]] +// Bar.representable(r, implicitly) + // implicitly[Bar[Color]] // } -// object Outer2 { -// class Wrapper { -// sealed trait Color -// } -// val wrapper = new Wrapper -// import wrapper.Color -// case object Red extends Color -// case object Green extends Color -// case object Blue extends Color + // object Outer2 { + // class Wrapper { + // sealed trait Color + // } + // val wrapper = new Wrapper + // import wrapper.Color + // case object Red extends Color + // case object Green extends Color + // case object Blue extends Color -// implicitly[Bar[Color]] -// } + // implicitly[Bar[Color]] + // } -// object Outer3 { -// class Wrapper { -// sealed trait Color -// } -// val wrapper = new Wrapper -// case object Red extends wrapper.Color -// case object Green extends wrapper.Color -// case object Blue extends wrapper.Color + // object Outer3 { + // class Wrapper { + // sealed trait Color + // } + // val wrapper = new Wrapper + // case object Red extends wrapper.Color + // case object Green extends wrapper.Color + // case object Blue extends wrapper.Color -// implicitly[Bar[wrapper.Color]] -// } + // implicitly[Bar[wrapper.Color]] + // } -// object Outer4 { -// val wrapper = new Wrapper -// case object Red extends wrapper.Color -// case object Green extends wrapper.Color -// case object Blue extends wrapper.Color + // object Outer4 { + // val wrapper = new Wrapper + // case object Red extends wrapper.Color + // case object Green extends wrapper.Color + // case object Blue extends wrapper.Color -// class Wrapper { -// sealed trait Color -// implicitly[Bar[wrapper.Color]] -// } -// } + // class Wrapper { + // sealed trait Color + // implicitly[Bar[wrapper.Color]] + // } + // } -// object Outer5 { -// trait Command -// object Command { -// sealed trait Execution extends Command -// } + // object Outer5 { + // trait Command + // object Command { + // sealed trait Execution extends Command + // } -// case class Buzz() extends Command.Execution -// case class Door() extends Command.Execution + // case class Buzz() extends Command.Execution + // case class Door() extends Command.Execution -// Representable[Command.Execution] -// } + // Representable[Command.Execution] + // } // } // object MixedCCNonCCNested { @@ -634,250 +633,231 @@ class RepresentableTests { // Representable[Baz] // } -// object EnumDefns0 { -// sealed trait EnumVal -// val BarA = new EnumVal { val name = "A" } -// val BarB = new EnumVal { val name = "B" } -// val BarC = new EnumVal { val name = "C" } -// } - -// object EnumDefns1 { -// sealed trait EnumVal -// object BarA extends EnumVal { val name = "A" } -// object BarB extends EnumVal { val name = "B" } -// object BarC extends EnumVal { val name = "C" } -// } - -// object EnumDefns2 { -// sealed trait EnumVal -// case object BarA extends EnumVal { val name = "A" } -// case object BarB extends EnumVal { val name = "B" } -// case object BarC extends EnumVal { val name = "C" } -// } +object EnumDefns1 { + sealed trait EnumVal + object BarA extends EnumVal { val name = "A" } + object BarB extends EnumVal { val name = "B" } + object BarC extends EnumVal { val name = "C" } +} -// object EnumDefns3 { -// sealed trait EnumVal -// val BarA, BarB, BarC = new EnumVal {} -// } +object EnumDefns2 { + sealed trait EnumVal + case object BarA extends EnumVal { val name = "A" } + case object BarB extends EnumVal { val name = "B" } + case object BarC extends EnumVal { val name = "C" } +} -// object EnumDefns4 { -// sealed trait EnumVal -// object EnumVal { -// val BarA = new EnumVal { val name = "A" } -// val BarB = new EnumVal { val name = "B" } -// val BarC = new EnumVal { val name = "C" } -// } -// } +object EnumDefns5 { + sealed trait EnumVal + object EnumVal { + object BarA extends EnumVal { val name = "A" } + object BarB extends EnumVal { val name = "B" } + object BarC extends EnumVal { val name = "C" } + } +} -// object EnumDefns5 { -// sealed trait EnumVal -// object EnumVal { -// object BarA extends EnumVal { val name = "A" } -// object BarB extends EnumVal { val name = "B" } -// object BarC extends EnumVal { val name = "C" } -// } -// } +object EnumDefns6 { + sealed trait EnumVal + object EnumVal { + case object BarA extends EnumVal { val name = "A" } + case object BarB extends EnumVal { val name = "B" } + case object BarC extends EnumVal { val name = "C" } + } +} -// object EnumDefns6 { -// sealed trait EnumVal -// object EnumVal { -// case object BarA extends EnumVal { val name = "A" } -// case object BarB extends EnumVal { val name = "B" } -// case object BarC extends EnumVal { val name = "C" } -// } -// } +class TestEnum { + // NOT SUPPORTED + // object EnumDefns0 { + // sealed trait EnumVal + // val BarA = new EnumVal { val name = "A" } + // val BarB = new EnumVal { val name = "B" } + // val BarC = new EnumVal { val name = "C" } + // } -// object EnumDefns7 { -// sealed trait EnumVal -// object EnumVal { -// val BarA, BarB, BarC = new EnumVal {} -// } -// } + // def testEnum0(): Unit = { + // import EnumDefns0._ -// class TestEnum { -// def testEnum0(): Unit = { -// import EnumDefns0._ + // val gen = Representable[EnumVal] + // val a0 = gen.to(BarA) + // assert(a0 == SLeft(BarA)) -// val gen = Representable[EnumVal] -// val a0 = gen.to(BarA) -// assert(a0 == SLeft(BarA)) + // val b0 = gen.to(BarB) + // assert(b0 == SRight(SLeft(BarB))) -// val b0 = gen.to(BarB) -// assert(b0 == SRight(SLeft(BarB))) + // val c0 = gen.to(BarC) + // assert(c0 == SRight(SRight(SLeft(BarC)))) + // } -// val c0 = gen.to(BarC) -// assert(c0 == SRight(SRight(SLeft(BarC)))) -// } + def testEnum1(): Unit = { + import EnumDefns1._ -// def testEnum1(): Unit = { -// import EnumDefns1._ + val gen = Representable[EnumVal] + val a0 = gen.to(BarA) + val a1 = SLeft[[X] => BarA.type, SNil, Nothing](BarA) + assert(a0 == a1) -// val gen = Representable[EnumVal] -// val a0 = gen.to(BarA) -// assert(a0 == SLeft(BarA)) + val b0 = gen.to(BarB) + val b1 = SRight(SLeft[[X] => BarB.type, SNil, Nothing](BarB)) + assert(b0 == b1) -// val b0 = gen.to(BarB) -// assert(b0 == SRight(SLeft(BarB))) + val c0 = gen.to(BarC) + val c1 = SRight(SRight(SLeft[[X] => BarC.type, SNil, Nothing](BarC))) + assert(c0 == c1) + } -// val c0 = gen.to(BarC) -// assert(c0 == SRight(SRight(SLeft(BarC)))) -// } + def testEnum2(): Unit = { + import EnumDefns2._ -// def testEnum2(): Unit = { -// import EnumDefns2._ + val gen = Representable[EnumVal] + val a0 = gen.to(BarA) + val a1 = SLeft[[X] => BarA.type, SNil, Nothing](BarA) + assert(a0 == a1) -// val gen = Representable[EnumVal] -// val a0 = gen.to(BarA) -// assert(a0 == SLeft(BarA)) + val b0 = gen.to(BarB) + val b1 = SRight(SLeft[[X] => BarB.type, SNil, Nothing](BarB)) + assert(b0 == b1) -// val b0 = gen.to(BarB) -// assert(b0 == SRight(SLeft(BarB))) + val c0 = gen.to(BarC) + val c1 = SRight(SRight(SLeft[[X] => BarC.type, SNil, Nothing](BarC))) + assert(c0 == c1) + } -// val c0 = gen.to(BarC) -// assert(c0 == SRight(SRight(SLeft(BarC)))) -// } + // NOT SUPPORTED + // object EnumDefns3 { + // sealed trait EnumVal + // val BarA, BarB, BarC = new EnumVal {} + // } -// def testEnum3(): Unit = { -// import EnumDefns3._ + // def testEnum3(): Unit = { + // import EnumDefns3._ -// val gen = Representable[EnumVal] -// val a0 = gen.to(BarA) -// assert(a0 == SLeft(BarA)) + // val gen = Representable[EnumVal] + // val a0 = gen.to(BarA) + // assert(a0 == SLeft(BarA)) -// val b0 = gen.to(BarB) -// assert(b0 == SRight(SLeft(BarB))) + // val b0 = gen.to(BarB) + // assert(b0 == SRight(SLeft(BarB))) -// val c0 = gen.to(BarC) -// assert(c0 == SRight(SRight(SLeft(BarC)))) -// } + // val c0 = gen.to(BarC) + // assert(c0 == SRight(SRight(SLeft(BarC)))) + // } -// def testEnum4(): Unit = { -// import EnumDefns4._ -// import EnumVal._ + // NOT SUPPORTED + // object EnumDefns4 { + // sealed trait EnumVal + // object EnumVal { + // val BarA = new EnumVal { val name = "A" } + // val BarB = new EnumVal { val name = "B" } + // val BarC = new EnumVal { val name = "C" } + // } + // } -// val gen = Representable[EnumVal] -// val a0 = gen.to(BarA) -// assert(a0 == SLeft(BarA)) + // def testEnum4(): Unit = { + // import EnumDefns4._ + // import EnumVal._ -// val b0 = gen.to(BarB) -// assert(b0 == SRight(SLeft(BarB))) + // val gen = Representable[EnumVal] + // val a0 = gen.to(BarA) + // assert(a0 == SLeft(BarA)) -// val c0 = gen.to(BarC) -// assert(c0 == SRight(SRight(SLeft(BarC)))) -// } + // val b0 = gen.to(BarB) + // assert(b0 == SRight(SLeft(BarB))) -// def testEnum5(): Unit = { -// import EnumDefns5._ -// import EnumVal._ + // val c0 = gen.to(BarC) + // assert(c0 == SRight(SRight(SLeft(BarC)))) + // } -// val gen = Representable[EnumVal] -// val a0 = gen.to(BarA) -// assert(a0 == SLeft(BarA)) + def testEnum5(): Unit = { + import EnumDefns5._ + import EnumVal._ -// val b0 = gen.to(BarB) -// assert(b0 == SRight(SLeft(BarB))) + val gen = Representable[EnumVal] + val a0 = gen.to(BarA) + val a1 = SLeft[[X] => BarA.type, SNil, Nothing](BarA) + assert(a0 == a1) -// val c0 = gen.to(BarC) -// assert(c0 == SRight(SRight(SLeft(BarC)))) -// } + val b0 = gen.to(BarB) + val b1 = SRight(SLeft[[X] => BarB.type, SNil, Nothing](BarB)) + assert(b0 == b1) -// def testEnum6(): Unit = { -// import EnumDefns6._ -// import EnumVal._ + val c0 = gen.to(BarC) + val c1 = SRight(SRight(SLeft[[X] => BarC.type, SNil, Nothing](BarC))) + assert(c0 == c1) + } -// val gen = Representable[EnumVal] -// val a0 = gen.to(BarA) -// assert(a0 == SLeft(BarA)) + def testEnum6(): Unit = { + import EnumDefns6._ + import EnumVal._ -// val b0 = gen.to(BarB) -// assert(b0 == SRight(SLeft(BarB))) + val gen = Representable[EnumVal] + val a0 = gen.to(BarA) + val a1 = SLeft[[X] => BarA.type, SNil, Nothing](BarA) + assert(a0 == a1) -// val c0 = gen.to(BarC) -// assert(c0 == SRight(SRight(SLeft(BarC)))) -// } + val b0 = gen.to(BarB) + val b1 = SRight(SLeft[[X] => BarB.type, SNil, Nothing](BarB)) + assert(b0 == b1) -// def testEnum7(): Unit = { -// import EnumDefns7._ -// import EnumVal._ + val c0 = gen.to(BarC) + val c1 = SRight(SRight(SLeft[[X] => BarC.type, SNil, Nothing](BarC))) + assert(c0 == c1) + } -// val gen = Representable[EnumVal] -// val a0 = gen.to(BarA) -// assert(a0 == SLeft(BarA)) + // NOT SUPPORTED + // object EnumDefns7 { + // sealed trait EnumVal + // object EnumVal { + // val BarA, BarB, BarC = new EnumVal {} + // } + // } -// val b0 = gen.to(BarB) -// assert(b0 == SRight(SLeft(BarB))) + // def testEnum7(): Unit = { + // import EnumDefns7._ + // import EnumVal._ -// val c0 = gen.to(BarC) -// assert(c0 == SRight(SRight(SLeft(BarC)))) -// } + // val gen = Representable[EnumVal] + // val a0 = gen.to(BarA) + // assert(a0 == SLeft(BarA)) -// def main(args: Array[String]): Unit = { -// testProductBasics() -// testProductVarargs() -// testTuples() -// testCoproductBasics() -// testCoproductMapBasics() -// testSingletonCoproducts() -// testOverlappingCoproducts() -// testCaseObjects() -// testCaseObjectMap() -// testParametrized() -// testParametrizedWithVarianceOption() -// testParametrizedWithVarianceList() -// testParametrzedSubset() -// testParametrizedPermute() -// testAbstractNonCC() -// testNonCCWithCompanion() -// testNonCCLazy() -// testNestedInherited() -// testIsTuple() -// testHasProductRepresentable() -// testHasCoproductRepresentable() -// testNonRepresentable() -// testNestedCaseObjects() -// testCaseObjectsAndLazy() -// testEnum0() -// testEnum1() -// testEnum2() -// testEnum3() -// testEnum4() -// testEnum5() -// testEnum6() -// testEnum7() -// } -// } + // val b0 = gen.to(BarB) + // assert(b0 == SRight(SLeft(BarB))) -// package TestPrefixes1 { -// trait Defs { -// case class CC(i: Int, s: String) + // val c0 = gen.to(BarC) + // assert(c0 == SRight(SRight(SLeft(BarC)))) + // } +} -// sealed trait Sum -// case class SumI(i: Int) extends Sum -// case class SumS(s: String) extends Sum -// } +object TestPrefixes1 { + trait Defs { + case class CC(i: Int, s: String) -// object Defs extends Defs + sealed trait Sum + case class SumI(i: Int) extends Sum + case class SumS(s: String) extends Sum + } -// object Derivations { -// import shapeless._ + object Defs extends Defs -// Representable[Defs.CC] -// Representable[Defs.SumI] -// Representable[Defs.SumS] + object Derivations { + Representable[Defs.CC] + Representable[Defs.SumI] + Representable[Defs.SumS] -// Representable[Defs.Sum] -// // Representable.materialize[Defs.Sum, Defs.SumI |: Defs.SumS |: SNil] -// } -// } + Representable[Defs.Sum] + // Representable.materialize[Defs.Sum, Defs.SumI |: Defs.SumS |: SNil] + } +} +// #3564, should work otherwise // package TestSingletonMembers { -// case class CC(i: Int, s: Witness.`"msg"`.T) +// case class CC(i: Int, s: "msg") // object Derivations2 { // Representable[CC] // } // } +// TRICKY, there is no infrastructure to get "reachable" children, see #3574 // object PathVariantDefns { // sealed trait AtomBase { // sealed trait Atom @@ -906,70 +886,68 @@ class RepresentableTests { // implicitly[gen2.Repr =:= (Atoms02.Two |: Atoms02.Zero |: SNil)] // } -// object PrivateCtorDefns { -// sealed trait PublicFamily -// case class PublicChild() extends PublicFamily -// private case class PrivateChild() extends PublicFamily -// } +object PrivateCtorDefns { + sealed trait PublicFamily + case class PublicChild() extends PublicFamily + private case class PrivateChild() extends PublicFamily +} -// // object PrivateCtor { -// // import PrivateCtorDefns._ -// // -// // implicitlyped "" -// // Representable[Access.PublicFamily] -// // """) -// // } - -// object Thrift { -// object TProduct { -// def apply(a: Double, b: String): TProduct = new Immutable(a, b) - -// def unapply(tp: TProduct): Option[Product2[Double, String]] = Some(tp) - -// //class Immutable(val a: Double, val b: String) extends TProduct - -// class Immutable( -// val a: Double, -// val b: String, -// val _passthroughFields: scala.collection.immutable.Map[Short, Byte] -// ) extends TProduct { -// def this( -// a: Double, -// b: String -// ) = this( -// a, -// b, -// Map.empty -// ) -// } -// } +object PrivateCtor { + import PrivateCtorDefns._ -// trait TProduct extends Product2[Double, String] { -// def a: Double -// def b: String + // implicitlyped Representable[PublicFamily] +} -// def _1 = a -// def _2 = b +object Thrift { + object TProduct { + def apply(a: Double, b: String): TProduct = new Immutable(a, b) + + def unapply(tp: TProduct): Option[Product2[Double, String]] = Some(tp) + + // class Immutable(val a: Double, val b: String) extends TProduct + + class Immutable( + val a: Double, + val b: String, + val _passthroughFields: scala.collection.immutable.Map[Short, Byte] + ) extends TProduct { + def this( + a: Double, + b: String + ) = this( + a, + b, + Map.empty + ) + } + } -// override def productPrefix: String = "TProduct" + trait TProduct extends Product2[Double, String] { + def a: Double + def b: String -// def canEqual(t: Any): Boolean = true -// } + def _1 = a + def _2 = b -// Representable[TProduct.Immutable] -// } + override def productPrefix: String = "TProduct" + + def canEqual(t: Any): Boolean = true + } -// object HigherKinded { -// type Id[A] = A + Representable[TProduct.Immutable] +} -// sealed trait Foo[A[_]] -// case class Bar[A[_]]() extends Foo[A] +object HigherKinded { + type Id[A] = A -// Representable[Bar[Id]] -// Representable[Foo[Id]] + sealed trait Foo[A[_]] + case class Bar[A[_]]() extends Foo[A] -// sealed trait Pipo[A[_]] -// case class Lino() extends Pipo[Id] + Representable[Bar[Id]] + Representable[Foo[Id]] -// Representable[Pipo[Id]] -// } + sealed trait Pipo[A[_]] + case class Lino() extends Pipo[Id] + + Representable[Pipo[Id]] +} From a33dcfa2548198184ef1a40287d1cbd0d0fec576 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Mon, 4 Dec 2017 17:29:36 +0100 Subject: [PATCH 23/32] Use lower kinded Sum/Prod for Representatble --- .../dotty/tools/dotc/typer/Implicits.scala | 67 +++---- library/src/dotty/generic/Prod.scala | 6 +- library/src/dotty/generic/ProdK.scala | 5 + library/src/dotty/generic/Representable.scala | 6 +- .../src/dotty/generic/Representable1.scala | 12 ++ library/src/dotty/generic/Sum.scala | 10 +- library/src/dotty/generic/SumK.scala | 7 + tests/run/generic-product.scala | 9 +- tests/run/generic-sum.scala | 7 +- tests/run/representable.scala | 165 +++++++++++------- 10 files changed, 170 insertions(+), 124 deletions(-) create mode 100644 library/src/dotty/generic/ProdK.scala create mode 100644 library/src/dotty/generic/Representable1.scala create mode 100644 library/src/dotty/generic/SumK.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 153bce26b477..53bccb248cc1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -604,21 +604,20 @@ trait Implicits { self: Typer => * * ``` * new Representable[A] { - * type Repr[t] = T1 |: T2 |: ... |: Tn |: PNil + * type Repr = SCons[T1, SCons[T2, ..., SCons[Tn, SNil]]] * - * def to[T](a: A): Repr[T] = (@unchecked a) match { + * def to(a: A): Repr = (a: @unchecked) match { * case x: T1 => SLeft(x) * case x: T2 => SRight(SLeft(x)) * ... * case x: Tn => SRight(SRight(...SRight(SLeft(x)))) * } * - * def from[T](r: Repr[T]): A = r match { + * def from(r: Repr): A = (r: @unchecked) match { * case SLeft(x) => x * case SRight(SLeft(x)) => x * ... * case SRight(SRight(...SRight(SLeft(x)))) => x - * case _ => throw new MatchError() * } * ``` * @@ -626,12 +625,12 @@ trait Implicits { self: Typer => * * ``` * new Representable[A] { - * type Repr[t] = T1 &: T2 &: ... &: Tn &: PNil + * type Repr = PCons[T1, PCons[T2, ..., PCons[Tn, PNil]]] * - * def to[T](a: A): Repr[T] = - * PCons(a._1, (PCons(a._2, ... PCons(a._m, PNil())))) + * def to(a: A): Repr = + * PCons(a._1, (PCons(a._2, ... PCons(a._n, PNil())))) * - * def from[T](r: Repr[T]): A = r match { + * def from(r: Repr): A = r match { * case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) * } * ``` @@ -660,25 +659,18 @@ trait Implicits { self: Typer => .reverse // Reveresed to match source order val sumTypesSize = sumTypes.size - val X = tpnme.syntheticTypeParamName(0) val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) - // This fold generates T1 |: T2 |: ... |: Tn |: PNil, where - // type |:[H, T[t] <: Sum[t]] = [X] => SCons[[Y] => H, T, X] - val reprRhs = + // type Repr[t] = SCons[T1, SCons[T2, ..., SCons[Tn, SNil]]] + val repr = TypeDef(ReprNme, sumTypes.zipWithIndex.foldRight[Tree](TypeTree(defn.SNilType)) { case ((cur, i), acc) => - val arg = tpnme.syntheticTypeParamName(i) - val constCur = LambdaTypeTree(TypeDef(arg, noBounds) :: Nil, TypeTree(cur)) - val pconsArgs = constCur :: acc :: Ident(arg) :: Nil - val rhs = AppliedTypeTree(TypeTree(defn.SConsType), pconsArgs) - LambdaTypeTree(TypeDef(arg, noBounds) :: Nil, rhs) + AppliedTypeTree(TypeTree(defn.SConsType), TypeTree(cur) :: acc :: Nil) } - // type Repr[t] = T1 |: T2 |: ... |: Tn |: PNil - val repr: Tree = TypeDef(ReprNme, reprRhs) - val reprX = AppliedTypeTree(Ident(ReprNme), Ident(X) :: Nil) + ) + def unchecked(t: Tree): Tree = Annotated(t, untpd.New(TypeTree(defn.UncheckedAnnotType), Nil)) - // def to[T](a: A): Repr[T] = (a: @unchecked) match { + // def to(a: A): Repr = (a: @unchecked) match { // case x: T1 => SLeft(x) // case x: T2 => SRight(SLeft(x)) // ... @@ -696,17 +688,17 @@ trait Implicits { self: Typer => } } val body = Match(unchecked(Ident(arg.name)), cases) - DefDef(toNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, reprX, body).withFlags(Synthetic) + DefDef(toNme, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) } - // def from[T](r: Repr[T]): A = (r: @unchecked) match { + // def from(r: Repr): A = (r: @unchecked) match { // case SLeft(x) => x // case SRight(SLeft(x)) => x // ... // case SRight(SRight(...SRight(SLeft(x)))) => x // } val from: Tree = { - val arg = makeSyntheticParameter(tpt = reprX) + val arg = makeSyntheticParameter(tpt = Ident(repr.name)) val cases = { val x = nme.syntheticParamName(0) (1 to sumTypesSize).map { depth => @@ -718,7 +710,7 @@ trait Implicits { self: Typer => } val matsh = Match(unchecked(Ident(arg.name)), cases) val body = TypeApply(Select(matsh, nme.asInstanceOf_), TypeTree(A) :: Nil) - DefDef(fromNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) + DefDef(fromNme, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) } val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil @@ -731,38 +723,31 @@ trait Implicits { self: Typer => val productTypes = productSelectorTypes(A) val productTypesSize = productTypes.size - val X = tpnme.syntheticTypeParamName(0) val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) - // This fold generates T1 &: T2 &: ... &: Tn &: PNil, where - // type &:[H, T[t] <: Prod[t]] = [X] => PCons[[Y] => H, T, X] - val reprRhs = + // type Repr[t] = PCons[T1, PCons[T2, ..., PCons[Tn, PNil]]] + val repr = TypeDef(ReprNme, productTypes.foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => - val constCur = LambdaTypeTree(TypeDef(X, noBounds) :: Nil, TypeTree(cur)) - val pconsArgs = constCur :: acc :: Ident(X) :: Nil - val rhs = AppliedTypeTree(TypeTree(defn.PConsType), pconsArgs) - LambdaTypeTree(TypeDef(X, noBounds) :: Nil, rhs) + AppliedTypeTree(TypeTree(defn.PConsType), TypeTree(cur) :: acc :: Nil) } - // type Repr[t] = T1 &: T2 &: ... &: Tn &: PNil - val repr = TypeDef(ReprNme, reprRhs) - val reprX = AppliedTypeTree(Ident(ReprNme), Ident(X) :: Nil) + ) val pNil = Apply(PNilTree, Nil) - // def to[T](a: A): Repr[T] = + // def to(a: A): Repr = // PCons(a._1, (PCons(a._2, ... PCons(a._n, PNil())))) val to = { val arg = makeSyntheticParameter(tpt = TypeTree(A)) val body = (1 to productTypesSize).foldRight(pNil) { case (i, acc) => Apply(PConsTree, Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) } - DefDef(toNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, reprX, body).withFlags(Synthetic) + DefDef(toNme, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) } - // def from[T](r: Repr[T]): A = r match { + // def from(r: Repr): A = r match { // case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) // } val from = { - val arg = makeSyntheticParameter(tpt = reprX) + val arg = makeSyntheticParameter(tpt = Ident(repr.name)) val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => Apply(PConsTree, Ident(nme.productAccessorName(i)) :: acc :: Nil) } @@ -778,7 +763,7 @@ trait Implicits { self: Typer => } } val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, neuu) :: Nil) - DefDef(fromNme, TypeDef(X, noBounds) :: Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) + DefDef(fromNme, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) } val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil diff --git a/library/src/dotty/generic/Prod.scala b/library/src/dotty/generic/Prod.scala index 8d1c0bd24208..b4655d25e950 100644 --- a/library/src/dotty/generic/Prod.scala +++ b/library/src/dotty/generic/Prod.scala @@ -1,5 +1,5 @@ package dotty.generic -sealed trait Prod[X] -final case class PCons[H[_], T[t] <: Prod[t], X](head: H[X], tail: T[X]) extends Prod[X] -final case class PNil[X]() extends Prod[X] // Cannot be put in `object Prod` because of #3422 +sealed trait Prod +final case class PCons[H, T <: Prod](head: H, tail: T) extends Prod +final case class PNil() extends Prod // TODO: could be a case object.... diff --git a/library/src/dotty/generic/ProdK.scala b/library/src/dotty/generic/ProdK.scala new file mode 100644 index 000000000000..a45ec7654408 --- /dev/null +++ b/library/src/dotty/generic/ProdK.scala @@ -0,0 +1,5 @@ +package dotty.generic + +sealed trait ProdK[X] +final case class PConsK[H[_], T[t] <: ProdK[t], X](head: H[X], tail: T[X]) extends ProdK[X] +final case class PNilK[X]() extends ProdK[X] // Cannot be put in `object ProdK` because of #3422 diff --git a/library/src/dotty/generic/Representable.scala b/library/src/dotty/generic/Representable.scala index 5bcaefa924e3..f089894d36f5 100644 --- a/library/src/dotty/generic/Representable.scala +++ b/library/src/dotty/generic/Representable.scala @@ -1,10 +1,10 @@ package dotty.generic trait Representable[A] { - type Repr[t] // <: Sum[t] | Prod[t] ← for when we stop cross compiling + type Repr // <: Sum | Prod ← when we stop cross compiling - def to[T](a: A): Repr[T] - def from[T](r: Repr[T]): A + def to(a: A): Repr + def from(r: Repr): A } object Representable { diff --git a/library/src/dotty/generic/Representable1.scala b/library/src/dotty/generic/Representable1.scala new file mode 100644 index 000000000000..98c1fbeff4b2 --- /dev/null +++ b/library/src/dotty/generic/Representable1.scala @@ -0,0 +1,12 @@ +package dotty.generic + +trait Representable1[A] { + type Repr[t] // <: SumK[t] | ProdK[t] ← when we stop cross compiling + + def to[T](a: A): Repr[T] + def from[T](r: Repr[T]): A +} + +object Representable1 { + def apply[A](implicit r: Representable1[A]): r.type = r +} diff --git a/library/src/dotty/generic/Sum.scala b/library/src/dotty/generic/Sum.scala index 0def714ea614..02cc683321df 100644 --- a/library/src/dotty/generic/Sum.scala +++ b/library/src/dotty/generic/Sum.scala @@ -1,7 +1,7 @@ package dotty.generic -sealed trait Sum[X] -sealed trait SCons[H[_], T[t] <: Sum[t], X] extends Sum[X] -final case class SLeft[H[_], T[t] <: Sum[t], X](head: H[X]) extends SCons[H, T, X] -final case class SRight[H[_], T[t] <: Sum[t], X](tail: T[X]) extends SCons[H, T, X] -sealed trait SNil[X] extends Sum[X] +sealed trait Sum +sealed trait SCons[H, T <: Sum] extends Sum +final case class SLeft[H, T <: Sum](head: H) extends SCons[H, T] +final case class SRight[H, T <: Sum](tail: T) extends SCons[H, T] +sealed trait SNil extends Sum diff --git a/library/src/dotty/generic/SumK.scala b/library/src/dotty/generic/SumK.scala new file mode 100644 index 000000000000..7544d8fc6a77 --- /dev/null +++ b/library/src/dotty/generic/SumK.scala @@ -0,0 +1,7 @@ +package dotty.generic + +sealed trait SumK[X] +sealed trait SConsK[H[_], T[t] <: SumK[t], X] extends SumK[X] +final case class SLeftK[H[_], T[t] <: SumK[t], X](head: H[X]) extends SConsK[H, T, X] +final case class SRightK[H[_], T[t] <: SumK[t], X](tail: T[X]) extends SConsK[H, T, X] +sealed trait SNilK[X] extends SumK[X] diff --git a/tests/run/generic-product.scala b/tests/run/generic-product.scala index dc2267954978..b49958720a73 100644 --- a/tests/run/generic-product.scala +++ b/tests/run/generic-product.scala @@ -4,9 +4,6 @@ case class Foo(i: Int, s: String) case class Bar() object Test { - def the[T](implicit ev: T): ev.type = ev - type &:[H, T[t] <: Prod[t]] = [X] => PCons[[Y] => H, T, X] - def main(args: Array[String]): Unit = { testFoo() testBar() @@ -14,10 +11,10 @@ object Test { def testFoo(): Unit = { // Representable can be synthesised via implicit search - val g = the[Representable[Foo]] + val g = Representable[Foo] // Type is inferred correctly - val a: Representable[Foo] { type Repr = Int &: String &: PNil } = g + val a: Representable[Foo] { type Repr = PCons[Int, PCons[String, PNil]] } = g // Representable#to and Representable#from behave as expected: assert(g.from(g.to(Foo(1, "s"))) == Foo(1, "s")) @@ -25,7 +22,7 @@ object Test { def testBar(): Unit = { // Representable can be synthesised via implicit search - val g = the[Representable[Bar]] + val g = Representable[Bar] // Type is inferred correctly val a: Representable[Bar] { type Repr = PNil } = g diff --git a/tests/run/generic-sum.scala b/tests/run/generic-sum.scala index cee5fde7f04d..226a23e0caaa 100644 --- a/tests/run/generic-sum.scala +++ b/tests/run/generic-sum.scala @@ -6,15 +6,12 @@ case class Bus(s: String) extends Foo // case class Bip(s: String) extends Foo object Test { - def the[T](implicit ev: T): ev.type = ev - type |:[H, T[t] <: Sum[t]] = [X] => SCons[[Y] => H, T, X] - def main(args: Array[String]): Unit = { // Representable can be synthesised via implicit search - val g = the[Representable[Foo]] + val g = Representable[Foo] // Type is inferred correctly - val a: Representable[Foo] { type Repr = Bar |: Bus |: SNil } = g + val a: Representable[Foo] { type Repr = SCons[Bar, SCons[Bus, SNil]] } = g // Representable#to and Representable#from behave as expected: assert(g.from(g.to(Bar(1))) == Bar(1)) diff --git a/tests/run/representable.scala b/tests/run/representable.scala index 22311d0c144a..53da21693a93 100644 --- a/tests/run/representable.scala +++ b/tests/run/representable.scala @@ -1,11 +1,11 @@ import dotty.generic._ object Syntax { - type &:[H, T[t] <: Prod[t]] = [X] => PCons[[Y] => H, T, X] - type |:[H, T[t] <: Sum[t]] = [X] => SCons[[Y] => H, T, X] + type &:[H, T <: Prod] = PCons[H, T] + type |:[H, T <: Sum] = SCons[H, T] - implicit class ProdSyntax1[T[t] <: Prod[t], X](t: T[X]) extends AnyVal { - def &:[H](h: H): PCons[[_] => H, T, X] = PCons(h, t) + implicit class ProdSyntax1[T <: Prod](t: T) extends AnyVal { + def &:[H](h: H): PCons[H, T] = PCons(h, t) } } @@ -78,7 +78,7 @@ object RepresentableTestsAux { case class OAB(i: Int) extends OA with OB } -class RepresentableTests { +object RepresentableTests { import RepresentableTestsAux._ type APBO = Apple |: Banana |: Orange |: Pear |: SNil @@ -90,7 +90,7 @@ class RepresentableTests { val gen = Representable[Person] val p0 = gen.to(p) - identity[SSI[Nothing]](p0) + identity[SSI](p0) assert(("Joe Soap" &: "Brighton" &: 23 &: PNil()) == p0) val p1 = gen.from(p0) @@ -131,19 +131,19 @@ class RepresentableTests { val gen = Representable[Fruit] val a0 = gen.to(a) - identity[APBO[Nothing]](a0) + identity[APBO](a0) val p0 = gen.to(p) - identity[APBO[Nothing]](p0) + identity[APBO](p0) val b0 = gen.to(b) - identity[APBO[Nothing]](b0) + identity[APBO](b0) val o0 = gen.to(o) - identity[APBO[Nothing]](o0) + identity[APBO](o0) val a1 = gen.from(a0) identity[Fruit](a1) @@ -166,7 +166,7 @@ class RepresentableTests { val s: AbstractSingle = Single() val s0 = gen.to(s) - identity[(Single |: SNil)[Nothing]](s0) + identity[(Single |: SNil)](s0) val s1 = gen.from(s0) identity[AbstractSingle](s1) @@ -177,7 +177,7 @@ class RepresentableTests { // val gen = Representable[Overlapping] // val o: Overlapping = OAB(1) // val o0 = gen.to(o) - // typed[(OAB |: OAC |: OBC |: CNil)[Nothing]](o0) + // typed[(OAB |: OAC |: OBC |: CNil)](o0) // val o1 = gen.from(o0) // typed[Overlapping](o1) @@ -191,13 +191,13 @@ class RepresentableTests { val gen = Representable[Enum] val a0 = gen.to(a) - identity[ABC[Nothing]](a0) + identity[ABC](a0) val b0 = gen.to(b) - identity[ABC[Nothing]](b0) + identity[ABC](b0) val c0 = gen.to(c) - identity[ABC[Nothing]](c0) + identity[ABC](c0) val a1 = gen.from(a0) identity[Enum](a1) @@ -216,7 +216,7 @@ class RepresentableTests { val gen = Representable[Tree[Int]] val t0 = gen.to(t) - identity[NI[Nothing]](t0) + identity[NI](t0) val t1 = gen.from(t0) identity[Tree[Int]](t1) @@ -229,7 +229,7 @@ class RepresentableTests { val gen = Representable[Option[Int]] val o0 = gen.to(o) - identity[SN[Nothing]](o0) + identity[SN](o0) val o1 = gen.from(o0) identity[Option[Int]](o1) @@ -243,7 +243,7 @@ class RepresentableTests { // val gen = Representable[List[Int]] // val l0 = gen.to(l) - // identity[CN[Nothing]](l0) + // identity[CN](l0) // val l1 = gen.from(l0) // identity[List[Int]](l1) @@ -258,12 +258,12 @@ class RepresentableTests { val c0 = gen.to(l) val d0 = SLeft(l) - identity[IB[Nothing]](c0) + identity[IB](c0) assert(d0 == c0) val c1 = gen.to(r) val d1 = SRight(SLeft(r)) - identity[IB[Nothing]](c1) + identity[IB](c1) assert(d1 == c1) } @@ -275,7 +275,7 @@ class RepresentableTests { val s0 = gen.to(s) val s1 = SLeft(s) - identity[IB[Nothing]](s0) + identity[IB](s0) assert(s1 == s0) } @@ -366,7 +366,7 @@ class RepresentableTests { case class C() extends Foo } - trait Child extends Parent { + trait Child extends Parent { self => val gen = Representable[Nested] val adtGen = Representable[Foo] } @@ -376,7 +376,7 @@ class RepresentableTests { def testNestedInherited(): Unit = { val n0 = O.Nested(23, "foo") val repr = O.gen.to(n0) - identity[(Int &: String &: PNil)[Nothing]](repr) + identity[(Int &: String &: PNil)](repr) val n1 = O.gen.from(repr) identity[O.Nested](n1) assert(n0 == n1) @@ -384,14 +384,14 @@ class RepresentableTests { { val foo0 = O.B val repr = O.adtGen.to(foo0) - identity[(O.A.type |: O.B.type |: O.C |: SNil)[Nothing]](repr) + identity[(O.A.type |: O.B.type |: O.C |: SNil)](repr) } - { - val foo0 = O.C() - val repr = O.adtGen.to(foo0) - identity[(O.A.type |: O.B.type |: O.C |: SNil)[Nothing]](repr) - } + // { + // val foo0 = O.C() + // val repr = O.adtGen.to(foo0) + // identity[(O.A.type |: O.B.type |: O.C |: SNil)](repr) + // } } def testNonRepresentable(): Unit = { @@ -400,10 +400,10 @@ class RepresentableTests { implicitly[Not[Representable[Int]]] implicitly[Not[Representable[Array[Int]]]] implicitly[Not[Representable[String]]] - implicitly[Not[Representable[PNil[Nothing]]]] - implicitly[Not[Representable[(Int &: String &: PNil)[Nothing]]]] - implicitly[Not[Representable[SNil[Nothing]]]] - implicitly[Not[Representable[(Int |: String |: SNil)[Nothing]]]] + implicitly[Not[Representable[PNil]]] + implicitly[Not[Representable[(Int &: String &: PNil)]]] + implicitly[Not[Representable[SNil]]] + implicitly[Not[Representable[(Int |: String |: SNil)]]] } sealed trait Color @@ -420,9 +420,9 @@ class RepresentableTests { // LabelledRepresentable[Color.Red.type] } - // sealed trait Base1 - // case object Foo1 extends Base1 - // case object Bar1 extends Base1 + sealed trait Base1 + case object Foo1 extends Base1 + case object Bar1 extends Base1 // trait TC[T] @@ -430,12 +430,12 @@ class RepresentableTests { // def apply[T](implicit tc: TC[T]): TC[T] = tc // implicit def hnilTC: TC[PNil] = new TC[PNil] {} - // implicit def hconsTC[H, T <: HList](implicit hd: => TC[H], tl: => TC[T]): TC[H &: T] = new TC[H &: T] {} + // implicit def hconsTC[H, T <: Prod](implicit hd: => TC[H], tl: => TC[T]): TC[H &: T] = new TC[H &: T] {} // implicit def cnilTC: TC[SNil] = new TC[SNil] {} - // implicit def cconsTC[H, T <: Coproduct](implicit hd: => TC[H], tl: => TC[T]): TC[H |: T] = new TC[H |: T] {} + // implicit def cconsTC[H, T <: Sum](implicit hd: => TC[H], tl: => TC[T]): TC[H |: T] = new TC[H |: T] {} - // implicit def projectTC[F, G](implicit gen: Representable.Aux[F, G], tc: => TC[G]): TC[F] = new TC[F] {} + // implicit def projectTC[F, G](implicit gen: Representable[F] { type Repr = G }, tc: => TC[G]): TC[F] = new TC[F] {} // } // def testCaseObjectsAndLazy(): Unit = { @@ -447,24 +447,24 @@ class RepresentableTests { // sealed trait Foo[T] // object Foo { -// implicit def derivePNil[X]: Foo[PNil[X]] = ??? +// implicit def derivePNil: Foo[PNil] = ??? -// implicit def deriveRepresentable[A, Rec[t] <: Prod[t], X] -// (implicit gen: Representable[A] { type Repr = Rec }, auto: Foo[Rec[X]]): Foo[A] = ??? +// implicit def deriveRepresentable[A, Rec <: Prod] +// (implicit gen: Representable[A] { type Repr = Rec }, auto: Foo[Rec]): Foo[A] = ??? // } // sealed class Bar[A] // object Bar extends Bar0 { -// implicit def cnil[X]: Bar[SNil[X]] = ??? +// implicit def cnil: Bar[SNil] = ??? // } // trait Bar0 { -// implicit def deriveCoproduct[H, T[t] <: Sum[t], X] -// (implicit headFoo: Foo[H], tailAux: Bar[T[X]]): Bar[(H |: T)[X]] = ??? +// implicit def deriveCoproduct[H, T <: Sum] +// (implicit headFoo: Foo[H], tailAux: Bar[T]): Bar[(H |: T)] = ??? -// implicit def representable[A, U[t] <: Sum[t], X] -// (implicit gen: Representable[A] { type Repr = U }, auto: Bar[U[X]]): Bar[A] = ??? +// implicit def representable[A, U <: Sum] +// (implicit gen: Representable[A] { type Repr = U }, auto: Bar[U]): Bar[A] = ??? // } // class Outer1 { @@ -665,7 +665,7 @@ object EnumDefns6 { } } -class TestEnum { +object TestEnum { // NOT SUPPORTED // object EnumDefns0 { // sealed trait EnumVal @@ -693,15 +693,15 @@ class TestEnum { val gen = Representable[EnumVal] val a0 = gen.to(BarA) - val a1 = SLeft[[X] => BarA.type, SNil, Nothing](BarA) + val a1 = SLeft[BarA.type, SNil](BarA) assert(a0 == a1) val b0 = gen.to(BarB) - val b1 = SRight(SLeft[[X] => BarB.type, SNil, Nothing](BarB)) + val b1 = SRight(SLeft[BarB.type, SNil](BarB)) assert(b0 == b1) val c0 = gen.to(BarC) - val c1 = SRight(SRight(SLeft[[X] => BarC.type, SNil, Nothing](BarC))) + val c1 = SRight(SRight(SLeft[BarC.type, SNil](BarC))) assert(c0 == c1) } @@ -710,15 +710,15 @@ class TestEnum { val gen = Representable[EnumVal] val a0 = gen.to(BarA) - val a1 = SLeft[[X] => BarA.type, SNil, Nothing](BarA) + val a1 = SLeft[BarA.type, SNil](BarA) assert(a0 == a1) val b0 = gen.to(BarB) - val b1 = SRight(SLeft[[X] => BarB.type, SNil, Nothing](BarB)) + val b1 = SRight(SLeft[BarB.type, SNil](BarB)) assert(b0 == b1) val c0 = gen.to(BarC) - val c1 = SRight(SRight(SLeft[[X] => BarC.type, SNil, Nothing](BarC))) + val c1 = SRight(SRight(SLeft[BarC.type, SNil](BarC))) assert(c0 == c1) } @@ -773,15 +773,15 @@ class TestEnum { val gen = Representable[EnumVal] val a0 = gen.to(BarA) - val a1 = SLeft[[X] => BarA.type, SNil, Nothing](BarA) + val a1 = SLeft[BarA.type, SNil](BarA) assert(a0 == a1) val b0 = gen.to(BarB) - val b1 = SRight(SLeft[[X] => BarB.type, SNil, Nothing](BarB)) + val b1 = SRight(SLeft[BarB.type, SNil](BarB)) assert(b0 == b1) val c0 = gen.to(BarC) - val c1 = SRight(SRight(SLeft[[X] => BarC.type, SNil, Nothing](BarC))) + val c1 = SRight(SRight(SLeft[BarC.type, SNil](BarC))) assert(c0 == c1) } @@ -791,15 +791,15 @@ class TestEnum { val gen = Representable[EnumVal] val a0 = gen.to(BarA) - val a1 = SLeft[[X] => BarA.type, SNil, Nothing](BarA) + val a1 = SLeft[BarA.type, SNil](BarA) assert(a0 == a1) val b0 = gen.to(BarB) - val b1 = SRight(SLeft[[X] => BarB.type, SNil, Nothing](BarB)) + val b1 = SRight(SLeft[BarB.type, SNil](BarB)) assert(b0 == b1) val c0 = gen.to(BarC) - val c1 = SRight(SRight(SLeft[[X] => BarC.type, SNil, Nothing](BarC))) + val c1 = SRight(SRight(SLeft[BarC.type, SNil](BarC))) assert(c0 == c1) } @@ -895,7 +895,7 @@ object PrivateCtorDefns { object PrivateCtor { import PrivateCtorDefns._ - // implicitlyped Representable[PublicFamily] + // illTyped Representable[PublicFamily] } object Thrift { @@ -951,3 +951,46 @@ object HigherKinded { Representable[Pipo[Id]] } + +object Test { + def main(args: Array[String]): Unit = { + Syntax + RepresentableTestsAux + EnumDefns1 + EnumDefns2 + EnumDefns5 + EnumDefns6 + TestPrefixes1 + PrivateCtorDefns + PrivateCtor + Thrift + HigherKinded + RepresentableTests.testProductBasics() + RepresentableTests.testProductVarargs() + RepresentableTests.testTuples() + RepresentableTests.testCoproductBasics() + RepresentableTests.testSingletonCoproducts() + // RepresentableTests.testOverlappingCoproducts() + RepresentableTests.testCaseObjects() + RepresentableTests.testParametrized() + RepresentableTests.testParametrizedWithVarianceOption() + // RepresentableTests.testParametrizedWithVarianceList() + RepresentableTests.testParametrzedSubset() + RepresentableTests.testParametrizedPermute() + // RepresentableTests.testAbstractNonCC() + // RepresentableTests.testNonCCWithCompanion() + // RepresentableTests.testNonCCLazy() + // RepresentableTests.testNestedInherited() // Fails at runtime because of #3624 + RepresentableTests.testNonRepresentable() + RepresentableTests.testNestedCaseObjects() + // RepresentableTests.testCaseObjectsAndLazy() + // TestEnum.testEnum0() + TestEnum.testEnum1() + TestEnum.testEnum2() + // TestEnum.testEnum3() + // TestEnum.testEnum4() + TestEnum.testEnum5() + TestEnum.testEnum6() + // TestEnum.testEnum7() + } +} From 1bd9e712af574ec188bac5ea5e7524b29bd03d84 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Mon, 4 Dec 2017 18:22:40 +0100 Subject: [PATCH 24/32] Use fullyDefinedType to synthesise type arguments --- .../dotty/tools/dotc/typer/Implicits.scala | 231 +++++++++--------- 1 file changed, 117 insertions(+), 114 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 53bccb248cc1..8d7624f0977f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -645,132 +645,135 @@ trait Implicits { self: Typer => val fromNme = "from".toTermName formal.argTypes match { - // Sums ----------------------------------------------------------- - case (A @ _) :: Nil if A.typeSymbol.is(Sealed) => - val SLeftTree = rootQual("SLeft") - val SRightTree = rootQual("SRight") - - import transform.SymUtils._ - import dotty.tools.dotc.transform.patmat.SpaceEngine - - val sumTypes = - A .classSymbol.children.map(_.namedType) - .map(t => new SpaceEngine().instantiate(t, A)) // Instantiate type all parameters - .reverse // Reveresed to match source order - - val sumTypesSize = sumTypes.size - val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) - - // type Repr[t] = SCons[T1, SCons[T2, ..., SCons[Tn, SNil]]] - val repr = TypeDef(ReprNme, - sumTypes.zipWithIndex.foldRight[Tree](TypeTree(defn.SNilType)) { case ((cur, i), acc) => - AppliedTypeTree(TypeTree(defn.SConsType), TypeTree(cur) :: acc :: Nil) - } - ) - - def unchecked(t: Tree): Tree = Annotated(t, untpd.New(TypeTree(defn.UncheckedAnnotType), Nil)) - - // def to(a: A): Repr = (a: @unchecked) match { - // case x: T1 => SLeft(x) - // case x: T2 => SRight(SLeft(x)) - // ... - // case x: Tn => SRight(SRight(...SRight(SLeft(x)))) - // } - val to: Tree = { - val arg = makeSyntheticParameter(tpt = TypeTree(A)) - val cases = { - val x = nme.syntheticParamName(0) - sumTypes.zipWithIndex.map { case (tpe, depth) => - val rhs = (1 to depth).foldLeft[Tree](Apply(SLeftTree, Ident(x))) { - case (acc, _) => Apply(SRightTree, acc) + case (arg @ _) :: Nil => + fullyDefinedType(arg, "Representable argument", pos) match { + // Sums ----------------------------------------------------------- + case (A @ _) if A.typeSymbol.is(Sealed) => + val SLeftTree = rootQual("SLeft") + val SRightTree = rootQual("SRight") + + import transform.SymUtils._ + import dotty.tools.dotc.transform.patmat.SpaceEngine + + val sumTypes = + A .classSymbol.children.map(_.namedType) + .map(t => new SpaceEngine().instantiate(t, A)) // Instantiate type all parameters + .reverse // Reveresed to match source order + + val sumTypesSize = sumTypes.size + val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) + + // type Repr[t] = SCons[T1, SCons[T2, ..., SCons[Tn, SNil]]] + val repr = TypeDef(ReprNme, + sumTypes.zipWithIndex.foldRight[Tree](TypeTree(defn.SNilType)) { case ((cur, i), acc) => + AppliedTypeTree(TypeTree(defn.SConsType), TypeTree(cur) :: acc :: Nil) + } + ) + + def unchecked(t: Tree): Tree = Annotated(t, untpd.New(TypeTree(defn.UncheckedAnnotType), Nil)) + + // def to(a: A): Repr = (a: @unchecked) match { + // case x: T1 => SLeft(x) + // case x: T2 => SRight(SLeft(x)) + // ... + // case x: Tn => SRight(SRight(...SRight(SLeft(x)))) + // } + val to: Tree = { + val arg = makeSyntheticParameter(tpt = TypeTree(A)) + val cases = { + val x = nme.syntheticParamName(0) + sumTypes.zipWithIndex.map { case (tpe, depth) => + val rhs = (1 to depth).foldLeft[Tree](Apply(SLeftTree, Ident(x))) { + case (acc, _) => Apply(SRightTree, acc) + } + CaseDef(Typed(Ident(x), TypeTree(tpe)), EmptyTree, rhs) + } } - CaseDef(Typed(Ident(x), TypeTree(tpe)), EmptyTree, rhs) + val body = Match(unchecked(Ident(arg.name)), cases) + DefDef(toNme, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) } - } - val body = Match(unchecked(Ident(arg.name)), cases) - DefDef(toNme, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) - } - // def from(r: Repr): A = (r: @unchecked) match { - // case SLeft(x) => x - // case SRight(SLeft(x)) => x - // ... - // case SRight(SRight(...SRight(SLeft(x)))) => x - // } - val from: Tree = { - val arg = makeSyntheticParameter(tpt = Ident(repr.name)) - val cases = { - val x = nme.syntheticParamName(0) - (1 to sumTypesSize).map { depth => - val pat = (1 until depth).foldLeft(Apply(SLeftTree, Ident(x))) { - case (acc, _) => Apply(SRightTree, acc) + // def from(r: Repr): A = (r: @unchecked) match { + // case SLeft(x) => x + // case SRight(SLeft(x)) => x + // ... + // case SRight(SRight(...SRight(SLeft(x)))) => x + // } + val from: Tree = { + val arg = makeSyntheticParameter(tpt = Ident(repr.name)) + val cases = { + val x = nme.syntheticParamName(0) + (1 to sumTypesSize).map { depth => + val pat = (1 until depth).foldLeft(Apply(SLeftTree, Ident(x))) { + case (acc, _) => Apply(SRightTree, acc) + } + CaseDef(pat, EmptyTree, Ident(x)) + }.toList } - CaseDef(pat, EmptyTree, Ident(x)) - }.toList - } - val matsh = Match(unchecked(Ident(arg.name)), cases) - val body = TypeApply(Select(matsh, nme.asInstanceOf_), TypeTree(A) :: Nil) - DefDef(fromNme, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) - } + val matsh = Match(unchecked(Ident(arg.name)), cases) + val body = TypeApply(Select(matsh, nme.asInstanceOf_), TypeTree(A) :: Nil) + DefDef(fromNme, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) + } - val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil - New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) + val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil + New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) - // Products ----------------------------------------------------------- - case (A @ _) :: Nil if defn.isProductSubType(A) => - val PNilTree = rootQual("PNil") - val PConsTree = rootQual("PCons") + // Products ----------------------------------------------------------- + case (A @ _) if defn.isProductSubType(A) => + val PNilTree = rootQual("PNil") + val PConsTree = rootQual("PCons") - val productTypes = productSelectorTypes(A) - val productTypesSize = productTypes.size - val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) + val productTypes = productSelectorTypes(A) + val productTypesSize = productTypes.size + val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) - // type Repr[t] = PCons[T1, PCons[T2, ..., PCons[Tn, PNil]]] - val repr = TypeDef(ReprNme, - productTypes.foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => - AppliedTypeTree(TypeTree(defn.PConsType), TypeTree(cur) :: acc :: Nil) - } - ) - val pNil = Apply(PNilTree, Nil) - - // def to(a: A): Repr = - // PCons(a._1, (PCons(a._2, ... PCons(a._n, PNil())))) - val to = { - val arg = makeSyntheticParameter(tpt = TypeTree(A)) - val body = (1 to productTypesSize).foldRight(pNil) { case (i, acc) => - Apply(PConsTree, Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) - } - DefDef(toNme, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) - } + // type Repr[t] = PCons[T1, PCons[T2, ..., PCons[Tn, PNil]]] + val repr = TypeDef(ReprNme, + productTypes.foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => + AppliedTypeTree(TypeTree(defn.PConsType), TypeTree(cur) :: acc :: Nil) + } + ) + val pNil = Apply(PNilTree, Nil) + + // def to(a: A): Repr = + // PCons(a._1, (PCons(a._2, ... PCons(a._n, PNil())))) + val to = { + val arg = makeSyntheticParameter(tpt = TypeTree(A)) + val body = (1 to productTypesSize).foldRight(pNil) { case (i, acc) => + Apply(PConsTree, Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) + } + DefDef(toNme, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) + } - // def from(r: Repr): A = r match { - // case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) - // } - val from = { - val arg = makeSyntheticParameter(tpt = Ident(repr.name)) - val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => - Apply(PConsTree, Ident(nme.productAccessorName(i)) :: acc :: Nil) - } - val neuu = { - A match { - case tpe: NamedType if tpe.termSymbol.exists => // case object - untpd.ref(tpe) - case _ if A.classSymbol.exists => // case class - val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList - val hasVarargs = A.classSymbol.primaryConstructor.info.isVarArgsMethod - val newArgs0 = if (hasVarargs) newArgs.init :+ repeated(newArgs.last) else newArgs - New(TypeTree(A), newArgs0 :: Nil) + // def from(r: Repr): A = r match { + // case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) + // } + val from = { + val arg = makeSyntheticParameter(tpt = Ident(repr.name)) + val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => + Apply(PConsTree, Ident(nme.productAccessorName(i)) :: acc :: Nil) + } + val neuu = { + A match { + case tpe: NamedType if tpe.termSymbol.exists => // case object + untpd.ref(tpe) + case _ if A.classSymbol.exists => // case class + val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList + val hasVarargs = A.classSymbol.primaryConstructor.info.isVarArgsMethod + val newArgs0 = if (hasVarargs) newArgs.init :+ repeated(newArgs.last) else newArgs + New(TypeTree(A), newArgs0 :: Nil) + } + } + val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, neuu) :: Nil) + DefDef(fromNme, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) } - } - val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, neuu) :: Nil) - DefDef(fromNme, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) - } - val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil - New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) + val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil + New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) - case _ => - EmptyTree + case _ => EmptyTree + } + case _ => EmptyTree } } From e35ef67465c7b01272acf60fe11848c155f08724 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 6 Dec 2017 14:31:29 +0100 Subject: [PATCH 25/32] Adapt to make typer aware of the new contraints --- .../dotty/tools/dotc/typer/Implicits.scala | 239 +++++++++--------- tests/run/representable.scala | 24 +- 2 files changed, 132 insertions(+), 131 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 8d7624f0977f..86edcb9b38b7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -635,146 +635,147 @@ trait Implicits { self: Typer => * } * ``` */ - def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = typed { + def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = { import untpd._ def rootQual(genericClass: String): Tree = Select(Select(Select(Ident(nme.ROOTPKG), "dotty".toTermName), "generic".toTermName), genericClass.toTermName) - val ReprNme = "Repr".toTypeName - val toNme = "to" .toTermName - val fromNme = "from".toTermName - formal.argTypes match { + val args = formal.baseType(defn.RepresentableClass).argTypes + val generated = args match { case (arg @ _) :: Nil => - fullyDefinedType(arg, "Representable argument", pos) match { - // Sums ----------------------------------------------------------- - case (A @ _) if A.typeSymbol.is(Sealed) => - val SLeftTree = rootQual("SLeft") - val SRightTree = rootQual("SRight") - - import transform.SymUtils._ - import dotty.tools.dotc.transform.patmat.SpaceEngine - - val sumTypes = - A .classSymbol.children.map(_.namedType) - .map(t => new SpaceEngine().instantiate(t, A)) // Instantiate type all parameters - .reverse // Reveresed to match source order - - val sumTypesSize = sumTypes.size - val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) - - // type Repr[t] = SCons[T1, SCons[T2, ..., SCons[Tn, SNil]]] - val repr = TypeDef(ReprNme, - sumTypes.zipWithIndex.foldRight[Tree](TypeTree(defn.SNilType)) { case ((cur, i), acc) => - AppliedTypeTree(TypeTree(defn.SConsType), TypeTree(cur) :: acc :: Nil) - } - ) - - def unchecked(t: Tree): Tree = Annotated(t, untpd.New(TypeTree(defn.UncheckedAnnotType), Nil)) - - // def to(a: A): Repr = (a: @unchecked) match { - // case x: T1 => SLeft(x) - // case x: T2 => SRight(SLeft(x)) - // ... - // case x: Tn => SRight(SRight(...SRight(SLeft(x)))) - // } - val to: Tree = { - val arg = makeSyntheticParameter(tpt = TypeTree(A)) - val cases = { - val x = nme.syntheticParamName(0) - sumTypes.zipWithIndex.map { case (tpe, depth) => - val rhs = (1 to depth).foldLeft[Tree](Apply(SLeftTree, Ident(x))) { - case (acc, _) => Apply(SRightTree, acc) - } - CaseDef(Typed(Ident(x), TypeTree(tpe)), EmptyTree, rhs) - } - } - val body = Match(unchecked(Ident(arg.name)), cases) - DefDef(toNme, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) - } + val A = fullyDefinedType(arg, "Representable argument", pos).stripTypeVar - // def from(r: Repr): A = (r: @unchecked) match { - // case SLeft(x) => x - // case SRight(SLeft(x)) => x - // ... - // case SRight(SRight(...SRight(SLeft(x)))) => x - // } - val from: Tree = { - val arg = makeSyntheticParameter(tpt = Ident(repr.name)) - val cases = { - val x = nme.syntheticParamName(0) - (1 to sumTypesSize).map { depth => - val pat = (1 until depth).foldLeft(Apply(SLeftTree, Ident(x))) { - case (acc, _) => Apply(SRightTree, acc) - } - CaseDef(pat, EmptyTree, Ident(x)) - }.toList - } - val matsh = Match(unchecked(Ident(arg.name)), cases) - val body = TypeApply(Select(matsh, nme.asInstanceOf_), TypeTree(A) :: Nil) - DefDef(fromNme, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) - } + // Sums ----------------------------------------------------------- + if (A.typeSymbol.is(Sealed)) { + val SLeftTree = rootQual("SLeft") + val SRightTree = rootQual("SRight") - val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil - New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) + import transform.SymUtils._ + import dotty.tools.dotc.transform.patmat.SpaceEngine - // Products ----------------------------------------------------------- - case (A @ _) if defn.isProductSubType(A) => - val PNilTree = rootQual("PNil") - val PConsTree = rootQual("PCons") + val sumTypes = + A .classSymbol.children.map(_.namedType) + .map(t => new SpaceEngine().instantiate(t, A)) // Instantiate type all parameters + .reverse // Reveresed to match source order - val productTypes = productSelectorTypes(A) - val productTypesSize = productTypes.size - val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) + val sumTypesSize = sumTypes.size + val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) - // type Repr[t] = PCons[T1, PCons[T2, ..., PCons[Tn, PNil]]] - val repr = TypeDef(ReprNme, - productTypes.foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => - AppliedTypeTree(TypeTree(defn.PConsType), TypeTree(cur) :: acc :: Nil) - } - ) - val pNil = Apply(PNilTree, Nil) - - // def to(a: A): Repr = - // PCons(a._1, (PCons(a._2, ... PCons(a._n, PNil())))) - val to = { - val arg = makeSyntheticParameter(tpt = TypeTree(A)) - val body = (1 to productTypesSize).foldRight(pNil) { case (i, acc) => - Apply(PConsTree, Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) + // type Repr[t] = SCons[T1, SCons[T2, ..., SCons[Tn, SNil]]] + val repr = TypeDef("Repr".toTypeName, + sumTypes.zipWithIndex.foldRight[Tree](TypeTree(defn.SNilType)) { case ((cur, i), acc) => + AppliedTypeTree(TypeTree(defn.SConsType), TypeTree(cur) :: acc :: Nil) + } + ) + + def unchecked(t: Tree): Tree = Annotated(t, untpd.New(TypeTree(defn.UncheckedAnnotType), Nil)) + + // def to(a: A): Repr = (a: @unchecked) match { + // case x: T1 => SLeft(x) + // case x: T2 => SRight(SLeft(x)) + // ... + // case x: Tn => SRight(SRight(...SRight(SLeft(x)))) + // } + val to: Tree = { + val arg = makeSyntheticParameter(tpt = TypeTree(A)) + val cases = { + val x = nme.syntheticParamName(0) + sumTypes.zipWithIndex.map { case (tpe, depth) => + val rhs = (1 to depth).foldLeft[Tree](Apply(SLeftTree, Ident(x))) { + case (acc, _) => Apply(SRightTree, acc) + } + CaseDef(Typed(Ident(x), TypeTree(tpe)), EmptyTree, rhs) } - DefDef(toNme, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) } + val body = Match(unchecked(Ident(arg.name)), cases) + DefDef("to".toTermName, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) + } - // def from(r: Repr): A = r match { - // case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) - // } - val from = { - val arg = makeSyntheticParameter(tpt = Ident(repr.name)) - val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => - Apply(PConsTree, Ident(nme.productAccessorName(i)) :: acc :: Nil) - } - val neuu = { - A match { - case tpe: NamedType if tpe.termSymbol.exists => // case object - untpd.ref(tpe) - case _ if A.classSymbol.exists => // case class - val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList - val hasVarargs = A.classSymbol.primaryConstructor.info.isVarArgsMethod - val newArgs0 = if (hasVarargs) newArgs.init :+ repeated(newArgs.last) else newArgs - New(TypeTree(A), newArgs0 :: Nil) + // def from(r: Repr): A = (r: @unchecked) match { + // case SLeft(x) => x + // case SRight(SLeft(x)) => x + // ... + // case SRight(SRight(...SRight(SLeft(x)))) => x + // } + val from: Tree = { + val arg = makeSyntheticParameter(tpt = Ident(repr.name)) + val cases = { + val x = nme.syntheticParamName(0) + (1 to sumTypesSize).map { depth => + val pat = (1 until depth).foldLeft(Apply(SLeftTree, Ident(x))) { + case (acc, _) => Apply(SRightTree, acc) } - } - val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, neuu) :: Nil) - DefDef(fromNme, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) + CaseDef(pat, EmptyTree, Ident(x)) + }.toList + } + val matsh = Match(unchecked(Ident(arg.name)), cases) + val body = TypeApply(Select(matsh, nme.asInstanceOf_), TypeTree(A) :: Nil) + DefDef("from".toTermName, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) + } + + val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil + New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) + } + // Products ----------------------------------------------------------- + else if (defn.isProductSubType(A)) { + val PNilTree = rootQual("PNil") + val PConsTree = rootQual("PCons") + + val productTypes = productSelectorTypes(A) + val productTypesSize = productTypes.size + val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) + + // type Repr[t] = PCons[T1, PCons[T2, ..., PCons[Tn, PNil]]] + val repr = TypeDef("Repr".toTypeName, + productTypes.foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => + AppliedTypeTree(TypeTree(defn.PConsType), TypeTree(cur) :: acc :: Nil) + } + ) + val pNil = Apply(PNilTree, Nil) + + // def to(a: A): Repr = + // PCons(a._1, (PCons(a._2, ... PCons(a._n, PNil())))) + val to = { + val arg = makeSyntheticParameter(tpt = TypeTree(A)) + val body = (1 to productTypesSize).foldRight(pNil) { case (i, acc) => + Apply(PConsTree, Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) } + DefDef("to".toTermName, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) + } - val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil - New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) + // def from(r: Repr): A = r match { + // case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) + // } + val from = { + val arg = makeSyntheticParameter(tpt = Ident(repr.name)) + val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => + Apply(PConsTree, Ident(nme.productAccessorName(i)) :: acc :: Nil) + } + val neuu = { + A match { + case tpe: NamedType if tpe.termSymbol.exists => // case object + untpd.ref(tpe) + case _ if A.classSymbol.exists => // case class + val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList + val hasVarargs = A.classSymbol.primaryConstructor.info.isVarArgsMethod + val newArgs0 = if (hasVarargs) newArgs.init :+ repeated(newArgs.last) else newArgs + New(TypeTree(A), newArgs0 :: Nil) + } + } + val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, neuu) :: Nil) + DefDef("from".toTermName, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) + } - case _ => EmptyTree + val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil + New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) } + else EmptyTree case _ => EmptyTree } + + val typed = typedUnadapted(generated) + adapt(typed, formal) } def hasEq(tp: Type): Boolean = diff --git a/tests/run/representable.scala b/tests/run/representable.scala index 53da21693a93..7797af70e2ec 100644 --- a/tests/run/representable.scala +++ b/tests/run/representable.scala @@ -424,23 +424,23 @@ object RepresentableTests { case object Foo1 extends Base1 case object Bar1 extends Base1 - // trait TC[T] + trait TC[T] - // object TC { - // def apply[T](implicit tc: TC[T]): TC[T] = tc + object TC { + def apply[T](implicit tc: TC[T]): TC[T] = tc - // implicit def hnilTC: TC[PNil] = new TC[PNil] {} - // implicit def hconsTC[H, T <: Prod](implicit hd: => TC[H], tl: => TC[T]): TC[H &: T] = new TC[H &: T] {} + implicit def hnilTC: TC[PNil] = new TC[PNil] {} + implicit def hconsTC[H, T <: Prod](implicit hd: => TC[H], tl: => TC[T]): TC[H &: T] = new TC[H &: T] {} - // implicit def cnilTC: TC[SNil] = new TC[SNil] {} - // implicit def cconsTC[H, T <: Sum](implicit hd: => TC[H], tl: => TC[T]): TC[H |: T] = new TC[H |: T] {} + implicit def cnilTC: TC[SNil] = new TC[SNil] {} + implicit def cconsTC[H, T <: Sum](implicit hd: => TC[H], tl: => TC[T]): TC[H |: T] = new TC[H |: T] {} - // implicit def projectTC[F, G](implicit gen: Representable[F] { type Repr = G }, tc: => TC[G]): TC[F] = new TC[F] {} - // } + implicit def projectTC[F, G](implicit gen: Representable[F] { type Repr = G }, tc: => TC[G]): TC[F] = new TC[F] {} + } - // def testCaseObjectsAndLazy(): Unit = { - // TC[Base1] - // } + def testCaseObjectsAndLazy(): Unit = { + TC[Base1] + } } // object RepresentableTestsAux2 { From 09eb24136ce219fdf9f60bf0ef2de5f5be645aba Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Wed, 6 Dec 2017 17:03:50 +0100 Subject: [PATCH 26/32] Update representable.scala --- tests/run/representable.scala | 351 +++++++++++++++++----------------- 1 file changed, 172 insertions(+), 179 deletions(-) diff --git a/tests/run/representable.scala b/tests/run/representable.scala index 7797af70e2ec..23ca6f89c4a7 100644 --- a/tests/run/representable.scala +++ b/tests/run/representable.scala @@ -387,11 +387,11 @@ object RepresentableTests { identity[(O.A.type |: O.B.type |: O.C |: SNil)](repr) } - // { - // val foo0 = O.C() - // val repr = O.adtGen.to(foo0) - // identity[(O.A.type |: O.B.type |: O.C |: SNil)](repr) - // } + { + val foo0 = O.C() + val repr = O.adtGen.to(foo0) + identity[(O.A.type |: O.B.type |: O.C |: SNil)](repr) + } } def testNonRepresentable(): Unit = { @@ -443,195 +443,196 @@ object RepresentableTests { } } -// object RepresentableTestsAux2 { -// sealed trait Foo[T] +object RepresentableTestsAux2 { + sealed trait Foo[T] -// object Foo { -// implicit def derivePNil: Foo[PNil] = ??? + object Foo { + implicit def derivePNil: Foo[PNil] = ??? -// implicit def deriveRepresentable[A, Rec <: Prod] -// (implicit gen: Representable[A] { type Repr = Rec }, auto: Foo[Rec]): Foo[A] = ??? -// } + implicit def deriveRepresentable[A, Rec <: Prod] + (implicit gen: Representable[A] { type Repr = Rec }, auto: Foo[Rec]): Foo[A] = ??? + } -// sealed class Bar[A] + sealed class Bar[A] -// object Bar extends Bar0 { -// implicit def cnil: Bar[SNil] = ??? -// } + object Bar extends Bar0 { + implicit def cnil: Bar[SNil] = ??? + } -// trait Bar0 { -// implicit def deriveCoproduct[H, T <: Sum] -// (implicit headFoo: Foo[H], tailAux: Bar[T]): Bar[(H |: T)] = ??? + trait Bar0 { + implicit def deriveCoproduct[H, T <: Sum] + (implicit headFoo: Foo[H], tailAux: Bar[T]): Bar[(H |: T)] = ??? -// implicit def representable[A, U <: Sum] -// (implicit gen: Representable[A] { type Repr = U }, auto: Bar[U]): Bar[A] = ??? -// } + implicit def representable[A, U <: Sum] + (implicit gen: Representable[A] { type Repr = U }, auto: Bar[U]): Bar[A] = ??? + } -// class Outer1 { -// sealed trait Color -// object Inner { -// case object Red extends Color -// } + // TODO + // class Outer1 { + // sealed trait Color + // object Inner { + // case object Red extends Color + // } + // implicit val r: Representable[Bar[Color]] { type Repr = SNil } = null + // implicitly[Bar[Color]] + // Fails with "Unexpected tree in genLoad" caused by the untpd.ref(tpe)... + // Unexpected tree in genLoad: TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class generic)),module class RepresentableTestsAux2$)),class Outer1)),module class RepresentableTestsAux2$Outer1$Inner$)] + // } -// val r = Representable[Bar[Color]] -// Bar.representable(r, implicitly) + object Outer2 { + class Wrapper { + sealed trait Color + } + val wrapper = new Wrapper + import wrapper.Color + case object Red extends Color + case object Green extends Color + case object Blue extends Color -// implicitly[Bar[Color]] -// } + implicitly[Bar[Color]] + } - // object Outer2 { - // class Wrapper { - // sealed trait Color - // } - // val wrapper = new Wrapper - // import wrapper.Color - // case object Red extends Color - // case object Green extends Color - // case object Blue extends Color + object Outer3 { + class Wrapper { + sealed trait Color + } + val wrapper = new Wrapper + case object Red extends wrapper.Color + case object Green extends wrapper.Color + case object Blue extends wrapper.Color - // implicitly[Bar[Color]] - // } + implicitly[Bar[wrapper.Color]] + } - // object Outer3 { - // class Wrapper { - // sealed trait Color - // } - // val wrapper = new Wrapper - // case object Red extends wrapper.Color - // case object Green extends wrapper.Color - // case object Blue extends wrapper.Color + object Outer4 { + val wrapper = new Wrapper + case object Red extends wrapper.Color + case object Green extends wrapper.Color + case object Blue extends wrapper.Color - // implicitly[Bar[wrapper.Color]] - // } + class Wrapper { + sealed trait Color + implicitly[Bar[wrapper.Color]] + } + } - // object Outer4 { - // val wrapper = new Wrapper - // case object Red extends wrapper.Color - // case object Green extends wrapper.Color - // case object Blue extends wrapper.Color + object Outer5 { + trait Command + object Command { + sealed trait Execution extends Command + } - // class Wrapper { - // sealed trait Color - // implicitly[Bar[wrapper.Color]] - // } - // } + case class Buzz() extends Command.Execution + case class Door() extends Command.Execution - // object Outer5 { - // trait Command - // object Command { - // sealed trait Execution extends Command - // } + Representable[Command.Execution] + } +} - // case class Buzz() extends Command.Execution - // case class Door() extends Command.Execution +object MixedCCNonCCNested { + // Block local + // Fails because of dotty.tools.dotc.typer.Typer.ensureNoLocalRefs(Typer.scala:671) + // { + // object T1 { + // sealed abstract class Tree + // final case class Node(left: Tree, right: Tree, v: Int) extends Tree + // case object Leaf extends Tree + // } - // Representable[Command.Execution] + // // Representable[T1.Tree] + // import T1._ + // // Representable[Tree] + + // sealed trait A + // sealed case class B(i: Int, s: String) extends A + // case object C extends A + // sealed trait D extends A + // final case class E(a: Double, b: Option[Float]) extends D + // case object F extends D + // sealed abstract class Foo extends D + // case object Baz extends Foo + // final case class Bar() extends Foo + // final case class Baz(i1: Int, s1: String) extends Foo + + // // Representable[A] + // // Representable[B] + // // Representable[C.type] + // // Representable[D] + // // Representable[E] + // // Representable[F.type] + // // Representable[Foo] + // // Representable[Baz.type] + // // Representable[Bar] + // // Representable[Baz] // } -// } -// object MixedCCNonCCNested { -// // Block local -// { -// object T1{ -// sealed abstract class Tree -// final case class Node(left: Tree, right: Tree, v: Int) extends Tree -// case object Leaf extends Tree -// } - -// Representable[T1.Tree] -// import T1._ -// Representable[Tree] - -// sealed trait A -// sealed case class B(i: Int, s: String) extends A -// case object C extends A -// sealed trait D extends A -// final case class E(a: Double, b: Option[Float]) extends D -// case object F extends D -// sealed abstract class Foo extends D -// case object Baz extends Foo -// final class Bar extends Foo -// final class Baz(val i1: Int, val s1: String) extends Foo - -// Representable[A] -// Representable[B] -// Representable[C.type] -// Representable[D] -// Representable[E] -// Representable[F.type] -// Representable[Foo] -// Representable[Baz.type] -// Representable[Bar] -// Representable[Baz] -// } + def methodLocal: Unit = { + object T1 { + sealed abstract class Tree + final case class Node(left: Tree, right: Tree, v: Int) extends Tree + case object Leaf extends Tree + } -// def methodLocal: Unit = { -// object T1{ -// sealed abstract class Tree -// final case class Node(left: Tree, right: Tree, v: Int) extends Tree -// case object Leaf extends Tree -// } - -// Representable[T1.Tree] -// import T1._ -// Representable[Tree] - -// sealed trait A -// sealed case class B(i: Int, s: String) extends A -// case object C extends A -// sealed trait D extends A -// final case class E(a: Double, b: Option[Float]) extends D -// case object F extends D -// sealed abstract class Foo extends D -// case object Baz extends Foo -// final class Bar extends Foo -// final class Baz(val i1: Int, val s1: String) extends Foo - -// Representable[A] -// Representable[B] -// Representable[C.type] -// Representable[D] -// Representable[E] -// Representable[F.type] -// Representable[Foo] -// Representable[Baz.type] -// Representable[Bar] -// Representable[Baz] -// } + // Representable[T1.Tree] // TODO genLoad + import T1._ + // Representable[Tree] // TODO genLoad + + sealed trait A + sealed case class B(i: Int, s: String) extends A + case object C extends A + sealed trait D extends A + final case class E(a: Double, b: Option[Float]) extends D + case object F extends D + sealed abstract class Foo extends D + case object Baz extends Foo + final case class Bar() extends Foo + final case class Baz(i1: Int, s1: String) extends Foo + + Representable[A] + Representable[B] + Representable[C.type] + Representable[D] + Representable[E] + Representable[F.type] + Representable[Foo] + Representable[Baz.type] + Representable[Bar] + Representable[Baz] + } -// // Top level -// object T1{ -// sealed abstract class Tree -// final case class Node(left: Tree, right: Tree, v: Int) extends Tree -// case object Leaf extends Tree -// } + // Top level + object T1 { + sealed abstract class Tree + final case class Node(left: Tree, right: Tree, v: Int) extends Tree + case object Leaf extends Tree + } -// Representable[T1.Tree] -// import T1._ -// Representable[Tree] - -// sealed trait A -// sealed case class B(i: Int, s: String) extends A -// case object C extends A -// sealed trait D extends A -// final case class E(a: Double, b: Option[Float]) extends D -// case object F extends D -// sealed abstract class Foo extends D -// case object Baz extends Foo -// final class Bar extends Foo -// final class Baz(val i1: Int, val s1: String) extends Foo - -// Representable[A] -// Representable[B] -// Representable[C.type] -// Representable[D] -// Representable[E] -// Representable[F.type] -// Representable[Foo] -// Representable[Baz.type] -// Representable[Bar] -// Representable[Baz] -// } + Representable[T1.Tree] + import T1._ + Representable[Tree] + + sealed trait A + sealed case class B(i: Int, s: String) extends A + case object C extends A + sealed trait D extends A + final case class E(a: Double, b: Option[Float]) extends D + case object F extends D + sealed abstract class Foo extends D + case object Baz extends Foo + final case class Bar() extends Foo + final case class Baz(i1: Int, s1: String) extends Foo + + Representable[A] + Representable[B] + Representable[C.type] + Representable[D] + Representable[E] + Representable[F.type] + Representable[Foo] + Representable[Baz.type] + Representable[Bar] + Representable[Baz] +} object EnumDefns1 { sealed trait EnumVal @@ -751,18 +752,14 @@ object TestEnum { // val BarC = new EnumVal { val name = "C" } // } // } - // def testEnum4(): Unit = { // import EnumDefns4._ // import EnumVal._ - // val gen = Representable[EnumVal] // val a0 = gen.to(BarA) // assert(a0 == SLeft(BarA)) - // val b0 = gen.to(BarB) // assert(b0 == SRight(SLeft(BarB))) - // val c0 = gen.to(BarC) // assert(c0 == SRight(SRight(SLeft(BarC)))) // } @@ -810,18 +807,14 @@ object TestEnum { // val BarA, BarB, BarC = new EnumVal {} // } // } - // def testEnum7(): Unit = { // import EnumDefns7._ // import EnumVal._ - // val gen = Representable[EnumVal] // val a0 = gen.to(BarA) // assert(a0 == SLeft(BarA)) - // val b0 = gen.to(BarB) // assert(b0 == SRight(SLeft(BarB))) - // val c0 = gen.to(BarC) // assert(c0 == SRight(SRight(SLeft(BarC)))) // } From 62c0037d457195a9a0544e3da314a2c267fe85e6 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Thu, 7 Dec 2017 17:00:47 +0100 Subject: [PATCH 27/32] Replace rootQual by tpd.ref :) --- .../dotty/tools/dotc/typer/Implicits.scala | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 86edcb9b38b7..9559e5e48040 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -637,20 +637,12 @@ trait Implicits { self: Typer => */ def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = { import untpd._ - - def rootQual(genericClass: String): Tree = - Select(Select(Select(Ident(nme.ROOTPKG), "dotty".toTermName), "generic".toTermName), genericClass.toTermName) - - val args = formal.baseType(defn.RepresentableClass).argTypes - val generated = args match { + val generated = formal.baseType(defn.RepresentableClass).argTypes match { case (arg @ _) :: Nil => val A = fullyDefinedType(arg, "Representable argument", pos).stripTypeVar // Sums ----------------------------------------------------------- if (A.typeSymbol.is(Sealed)) { - val SLeftTree = rootQual("SLeft") - val SRightTree = rootQual("SRight") - import transform.SymUtils._ import dotty.tools.dotc.transform.patmat.SpaceEngine @@ -682,8 +674,8 @@ trait Implicits { self: Typer => val cases = { val x = nme.syntheticParamName(0) sumTypes.zipWithIndex.map { case (tpe, depth) => - val rhs = (1 to depth).foldLeft[Tree](Apply(SLeftTree, Ident(x))) { - case (acc, _) => Apply(SRightTree, acc) + val rhs = (1 to depth).foldLeft[Tree](Apply(tpd.ref(defn.SLeftModule), Ident(x))) { + case (acc, _) => Apply(tpd.ref(defn.SRightModule), acc) } CaseDef(Typed(Ident(x), TypeTree(tpe)), EmptyTree, rhs) } @@ -703,8 +695,8 @@ trait Implicits { self: Typer => val cases = { val x = nme.syntheticParamName(0) (1 to sumTypesSize).map { depth => - val pat = (1 until depth).foldLeft(Apply(SLeftTree, Ident(x))) { - case (acc, _) => Apply(SRightTree, acc) + val pat = (1 until depth).foldLeft(Apply(tpd.ref(defn.SLeftModule), Ident(x))) { + case (acc, _) => Apply(tpd.ref(defn.SRightModule), acc) } CaseDef(pat, EmptyTree, Ident(x)) }.toList @@ -719,9 +711,6 @@ trait Implicits { self: Typer => } // Products ----------------------------------------------------------- else if (defn.isProductSubType(A)) { - val PNilTree = rootQual("PNil") - val PConsTree = rootQual("PCons") - val productTypes = productSelectorTypes(A) val productTypesSize = productTypes.size val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) @@ -732,14 +721,14 @@ trait Implicits { self: Typer => AppliedTypeTree(TypeTree(defn.PConsType), TypeTree(cur) :: acc :: Nil) } ) - val pNil = Apply(PNilTree, Nil) + val pNil = Apply(tpd.ref(defn.PNilModule), Nil) // def to(a: A): Repr = // PCons(a._1, (PCons(a._2, ... PCons(a._n, PNil())))) val to = { val arg = makeSyntheticParameter(tpt = TypeTree(A)) val body = (1 to productTypesSize).foldRight(pNil) { case (i, acc) => - Apply(PConsTree, Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) + Apply(tpd.ref(defn.PConsModule), Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) } DefDef("to".toTermName, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) } @@ -750,12 +739,12 @@ trait Implicits { self: Typer => val from = { val arg = makeSyntheticParameter(tpt = Ident(repr.name)) val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => - Apply(PConsTree, Ident(nme.productAccessorName(i)) :: acc :: Nil) + Apply(tpd.ref(defn.PConsModule), Ident(nme.productAccessorName(i)) :: acc :: Nil) } val neuu = { A match { case tpe: NamedType if tpe.termSymbol.exists => // case object - untpd.ref(tpe) + ref(tpe) case _ if A.classSymbol.exists => // case class val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList val hasVarargs = A.classSymbol.primaryConstructor.info.isVarArgsMethod From a24612b97235958bb90cdfa1d047289e9fc90062 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 8 Dec 2017 10:53:09 +0100 Subject: [PATCH 28/32] Fix a genLoad bug with .widen --- .../src/dotty/tools/dotc/typer/Implicits.scala | 3 ++- tests/run/representable.scala | 17 ++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 9559e5e48040..4e0dff485c80 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -677,7 +677,7 @@ trait Implicits { self: Typer => val rhs = (1 to depth).foldLeft[Tree](Apply(tpd.ref(defn.SLeftModule), Ident(x))) { case (acc, _) => Apply(tpd.ref(defn.SRightModule), acc) } - CaseDef(Typed(Ident(x), TypeTree(tpe)), EmptyTree, rhs) + CaseDef(Typed(Ident(x), TypeTree(tpe.widen)), EmptyTree, rhs) } } val body = Match(unchecked(Ident(arg.name)), cases) @@ -709,6 +709,7 @@ trait Implicits { self: Typer => val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) } + // Products ----------------------------------------------------------- else if (defn.isProductSubType(A)) { val productTypes = productSelectorTypes(A) diff --git a/tests/run/representable.scala b/tests/run/representable.scala index 23ca6f89c4a7..6b9249b91027 100644 --- a/tests/run/representable.scala +++ b/tests/run/representable.scala @@ -467,7 +467,7 @@ object RepresentableTestsAux2 { (implicit gen: Representable[A] { type Repr = U }, auto: Bar[U]): Bar[A] = ??? } - // TODO + // TODO genLoad // class Outer1 { // sealed trait Color // object Inner { @@ -530,8 +530,7 @@ object RepresentableTestsAux2 { } object MixedCCNonCCNested { - // Block local - // Fails because of dotty.tools.dotc.typer.Typer.ensureNoLocalRefs(Typer.scala:671) + // #3637 // { // object T1 { // sealed abstract class Tree @@ -573,9 +572,9 @@ object MixedCCNonCCNested { case object Leaf extends Tree } - // Representable[T1.Tree] // TODO genLoad + Representable[T1.Tree] import T1._ - // Representable[Tree] // TODO genLoad + Representable[Tree] sealed trait A sealed case class B(i: Int, s: String) extends A @@ -850,7 +849,9 @@ object TestPrefixes1 { // } // } -// TRICKY, there is no infrastructure to get "reachable" children, see #3574 +// Rather tricky given than there is no infrastructure to get "reachable" +// children, see #3574. I would postpone these cases for later (this is also +// an issue in pattern matching exhaustivity) // object PathVariantDefns { // sealed trait AtomBase { // sealed trait Atom @@ -897,8 +898,6 @@ object Thrift { def unapply(tp: TProduct): Option[Product2[Double, String]] = Some(tp) - // class Immutable(val a: Double, val b: String) extends TProduct - class Immutable( val a: Double, val b: String, @@ -976,7 +975,7 @@ object Test { // RepresentableTests.testNestedInherited() // Fails at runtime because of #3624 RepresentableTests.testNonRepresentable() RepresentableTests.testNestedCaseObjects() - // RepresentableTests.testCaseObjectsAndLazy() + RepresentableTests.testCaseObjectsAndLazy() // TestEnum.testEnum0() TestEnum.testEnum1() TestEnum.testEnum2() From e53e6909f20565c17246ed21b6ac454da1fd6f6f Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 8 Dec 2017 11:56:41 +0100 Subject: [PATCH 29/32] Split synthesizedRepresentable into 3 methods --- .../dotty/tools/dotc/typer/Implicits.scala | 244 +++++++++--------- 1 file changed, 117 insertions(+), 127 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 4e0dff485c80..23dc1705e815 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -37,6 +37,7 @@ import config.Printers.{implicits, implicitsDetailed, typr} import collection.mutable import reporting.trace import annotation.tailrec +import transform.SymUtils._ /** Implicit resolution */ object Implicits { @@ -599,8 +600,27 @@ trait Implicits { self: Typer => } } - /** - * When A is a sum, synthesize an instance with the following shape: + /** Synthesized dotty.generic.Representable instance when `formal` is + * a case class or of sealed class. + */ + def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = { + val generated = formal.baseType(defn.RepresentableClass).argTypes match { + case (arg @ _) :: Nil => + val A = fullyDefinedType(arg, "Representable argument", pos).stripTypeVar + if (A.typeSymbol.is(Sealed)) + synthesizedRepresentableSum(A) + else if (defn.isProductSubType(A)) + synthesizedRepresentableProduct(A) + else + EmptyTree + case _ => EmptyTree + } + + val typed = typedUnadapted(generated) + adapt(typed, formal) + } + + /** Assuming A is a sum, synthesize a Representable instance of the following shape: * * ``` * new Representable[A] { @@ -620,8 +640,65 @@ trait Implicits { self: Typer => * case SRight(SRight(...SRight(SLeft(x)))) => x * } * ``` - * - * When A is a product, synthesize an instance with the following shape: + */ + def synthesizedRepresentableSum(A: Type): untpd.Tree = { + import untpd._ + // TODO: Factor SpaceEngine.instantiate out of pattern matching logic. + // I'm not sure where would be the best place to put it... + import dotty.tools.dotc.transform.patmat.SpaceEngine + + val sumTypes = + A .classSymbol.children.map(_.namedType) + .map(t => new SpaceEngine().instantiate(t, A)) // Instantiate type all parameters + .reverse // Reveresed to match source order + + val sumTypesSize = sumTypes.size + val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) + + val repr = TypeDef("Repr".toTypeName, + sumTypes.zipWithIndex.foldRight[Tree](TypeTree(defn.SNilType)) { case ((cur, i), acc) => + AppliedTypeTree(TypeTree(defn.SConsType), TypeTree(cur) :: acc :: Nil) + } + ) + + def unchecked(t: Tree): Tree = Annotated(t, untpd.New(TypeTree(defn.UncheckedAnnotType), Nil)) + + val to: Tree = { + val arg = makeSyntheticParameter(tpt = TypeTree(A)) + val cases = { + val x = nme.syntheticParamName(0) + sumTypes.zipWithIndex.map { case (tpe, depth) => + val rhs = (1 to depth).foldLeft[Tree](Apply(tpd.ref(defn.SLeftModule), Ident(x))) { + case (acc, _) => Apply(tpd.ref(defn.SRightModule), acc) + } + CaseDef(Typed(Ident(x), TypeTree(tpe.widen)), EmptyTree, rhs) + } + } + val body = Match(unchecked(Ident(arg.name)), cases) + DefDef("to".toTermName, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) + } + + val from: Tree = { + val arg = makeSyntheticParameter(tpt = Ident(repr.name)) + val cases = { + val x = nme.syntheticParamName(0) + (1 to sumTypesSize).map { depth => + val pat = (1 until depth).foldLeft(Apply(tpd.ref(defn.SLeftModule), Ident(x))) { + case (acc, _) => Apply(tpd.ref(defn.SRightModule), acc) + } + CaseDef(pat, EmptyTree, Ident(x)) + }.toList + } + val matsh = Match(unchecked(Ident(arg.name)), cases) + val body = TypeApply(Select(matsh, nme.asInstanceOf_), TypeTree(A) :: Nil) + DefDef("from".toTermName, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) + } + + val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil + New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) + } + + /** Assuming A is a product, synthesize a Representable instance of the following shape: * * ``` * new Representable[A] { @@ -635,137 +712,50 @@ trait Implicits { self: Typer => * } * ``` */ - def synthesizedRepresentable(formal: Type)(implicit ctx: Context): Tree = { + def synthesizedRepresentableProduct(A: Type): untpd.Tree = { import untpd._ - val generated = formal.baseType(defn.RepresentableClass).argTypes match { - case (arg @ _) :: Nil => - val A = fullyDefinedType(arg, "Representable argument", pos).stripTypeVar - - // Sums ----------------------------------------------------------- - if (A.typeSymbol.is(Sealed)) { - import transform.SymUtils._ - import dotty.tools.dotc.transform.patmat.SpaceEngine - - val sumTypes = - A .classSymbol.children.map(_.namedType) - .map(t => new SpaceEngine().instantiate(t, A)) // Instantiate type all parameters - .reverse // Reveresed to match source order - val sumTypesSize = sumTypes.size - val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) - - // type Repr[t] = SCons[T1, SCons[T2, ..., SCons[Tn, SNil]]] - val repr = TypeDef("Repr".toTypeName, - sumTypes.zipWithIndex.foldRight[Tree](TypeTree(defn.SNilType)) { case ((cur, i), acc) => - AppliedTypeTree(TypeTree(defn.SConsType), TypeTree(cur) :: acc :: Nil) - } - ) - - def unchecked(t: Tree): Tree = Annotated(t, untpd.New(TypeTree(defn.UncheckedAnnotType), Nil)) - - // def to(a: A): Repr = (a: @unchecked) match { - // case x: T1 => SLeft(x) - // case x: T2 => SRight(SLeft(x)) - // ... - // case x: Tn => SRight(SRight(...SRight(SLeft(x)))) - // } - val to: Tree = { - val arg = makeSyntheticParameter(tpt = TypeTree(A)) - val cases = { - val x = nme.syntheticParamName(0) - sumTypes.zipWithIndex.map { case (tpe, depth) => - val rhs = (1 to depth).foldLeft[Tree](Apply(tpd.ref(defn.SLeftModule), Ident(x))) { - case (acc, _) => Apply(tpd.ref(defn.SRightModule), acc) - } - CaseDef(Typed(Ident(x), TypeTree(tpe.widen)), EmptyTree, rhs) - } - } - val body = Match(unchecked(Ident(arg.name)), cases) - DefDef("to".toTermName, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) - } - - // def from(r: Repr): A = (r: @unchecked) match { - // case SLeft(x) => x - // case SRight(SLeft(x)) => x - // ... - // case SRight(SRight(...SRight(SLeft(x)))) => x - // } - val from: Tree = { - val arg = makeSyntheticParameter(tpt = Ident(repr.name)) - val cases = { - val x = nme.syntheticParamName(0) - (1 to sumTypesSize).map { depth => - val pat = (1 until depth).foldLeft(Apply(tpd.ref(defn.SLeftModule), Ident(x))) { - case (acc, _) => Apply(tpd.ref(defn.SRightModule), acc) - } - CaseDef(pat, EmptyTree, Ident(x)) - }.toList - } - val matsh = Match(unchecked(Ident(arg.name)), cases) - val body = TypeApply(Select(matsh, nme.asInstanceOf_), TypeTree(A) :: Nil) - DefDef("from".toTermName, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) - } - - val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil - New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) - } + val productTypes = productSelectorTypes(A) + val productTypesSize = productTypes.size + val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) - // Products ----------------------------------------------------------- - else if (defn.isProductSubType(A)) { - val productTypes = productSelectorTypes(A) - val productTypesSize = productTypes.size - val noBounds = TypeBoundsTree(EmptyTree, EmptyTree) - - // type Repr[t] = PCons[T1, PCons[T2, ..., PCons[Tn, PNil]]] - val repr = TypeDef("Repr".toTypeName, - productTypes.foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => - AppliedTypeTree(TypeTree(defn.PConsType), TypeTree(cur) :: acc :: Nil) - } - ) - val pNil = Apply(tpd.ref(defn.PNilModule), Nil) - - // def to(a: A): Repr = - // PCons(a._1, (PCons(a._2, ... PCons(a._n, PNil())))) - val to = { - val arg = makeSyntheticParameter(tpt = TypeTree(A)) - val body = (1 to productTypesSize).foldRight(pNil) { case (i, acc) => - Apply(tpd.ref(defn.PConsModule), Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) - } - DefDef("to".toTermName, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) - } + val repr = TypeDef("Repr".toTypeName, + productTypes.foldRight[Tree](TypeTree(defn.PNilType)) { case (cur, acc) => + AppliedTypeTree(TypeTree(defn.PConsType), TypeTree(cur) :: acc :: Nil) + } + ) + val pNil = Apply(tpd.ref(defn.PNilModule), Nil) - // def from(r: Repr): A = r match { - // case PCons(_1, Pcons(_2, ..., Pcons(_n, PNil()))) => new A(_1, _2, ..., _n) - // } - val from = { - val arg = makeSyntheticParameter(tpt = Ident(repr.name)) - val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => - Apply(tpd.ref(defn.PConsModule), Ident(nme.productAccessorName(i)) :: acc :: Nil) - } - val neuu = { - A match { - case tpe: NamedType if tpe.termSymbol.exists => // case object - ref(tpe) - case _ if A.classSymbol.exists => // case class - val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList - val hasVarargs = A.classSymbol.primaryConstructor.info.isVarArgsMethod - val newArgs0 = if (hasVarargs) newArgs.init :+ repeated(newArgs.last) else newArgs - New(TypeTree(A), newArgs0 :: Nil) - } - } - val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, neuu) :: Nil) - DefDef("from".toTermName, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) - } + val to = { + val arg = makeSyntheticParameter(tpt = TypeTree(A)) + val body = (1 to productTypesSize).foldRight(pNil) { case (i, acc) => + Apply(tpd.ref(defn.PConsModule), Select(Ident(arg.name), nme.productAccessorName(i)) :: acc :: Nil) + } + DefDef("to".toTermName, Nil, (arg :: Nil) :: Nil, Ident(repr.name), body).withFlags(Synthetic) + } - val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil - New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) + val from = { + val arg = makeSyntheticParameter(tpt = Ident(repr.name)) + val pat = (1 to productTypesSize).reverse.foldLeft(pNil) { case (acc, i) => + Apply(tpd.ref(defn.PConsModule), Ident(nme.productAccessorName(i)) :: acc :: Nil) + } + val neuu = { + A match { + case tpe: NamedType if tpe.termSymbol.exists => // case object + ref(tpe) + case _ if A.classSymbol.exists => // case class + val newArgs = (1 to productTypesSize).map(i => Ident(nme.productAccessorName(i))).toList + val hasVarargs = A.classSymbol.primaryConstructor.info.isVarArgsMethod + val newArgs0 = if (hasVarargs) newArgs.init :+ repeated(newArgs.last) else newArgs + New(TypeTree(A), newArgs0 :: Nil) } - else EmptyTree - case _ => EmptyTree + } + val body = Match(Ident(arg.name), CaseDef(pat, EmptyTree, neuu) :: Nil) + DefDef("from".toTermName, Nil, (arg :: Nil) :: Nil, TypeTree(A), body).withFlags(Synthetic) } - val typed = typedUnadapted(generated) - adapt(typed, formal) + val parents = TypeTree(defn.RepresentableType.appliedTo(A)) :: Nil + New(Template(emptyConstructor, parents, EmptyValDef, repr :: to :: from :: Nil)).withPos(pos) } def hasEq(tp: Type): Boolean = From ab52281e0c045b5f03c0c36ea7a4721341dc3ce1 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Tue, 19 Sep 2017 13:42:18 +0200 Subject: [PATCH 30/32] Strip "../" from test output --- compiler/test/dotty/tools/vulpix/ParallelTesting.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 8b2a76164a81..8f63720b378e 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -54,14 +54,14 @@ trait ParallelTesting extends RunnerOrchestration { self => def runClassPath: String = outDir.getAbsolutePath + ":" + flags.runClassPath - def title: String = self match { + def title: String = (self match { case self: JointCompilationSource => if (self.files.length > 1) name else self.files.head.getPath case self: SeparateCompilationSource => self.dir.getPath - } + }).stripPrefix("../") /** Adds the flags specified in `newFlags0` if they do not already exist */ def withFlags(newFlags0: String*) = { From 460cf6df1d8248e060b12d8f44420587b42d33b7 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Tue, 12 Dec 2017 11:47:13 +0100 Subject: [PATCH 31/32] Cleanup --- .../dotty/tools/dotc/typer/Implicits.scala | 2 +- library/src/dotty/generic/Prod.scala | 2 +- library/src/dotty/generic/ProdK.scala | 5 - .../src/dotty/generic/Representable1.scala | 12 - library/src/dotty/generic/SumK.scala | 7 - tests/run/generic-sum.scala | 1 - tests/run/representable.scala | 233 +++--------------- 7 files changed, 30 insertions(+), 232 deletions(-) delete mode 100644 library/src/dotty/generic/ProdK.scala delete mode 100644 library/src/dotty/generic/Representable1.scala delete mode 100644 library/src/dotty/generic/SumK.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 23dc1705e815..3e3a1f6e190e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -649,7 +649,7 @@ trait Implicits { self: Typer => val sumTypes = A .classSymbol.children.map(_.namedType) - .map(t => new SpaceEngine().instantiate(t, A)) // Instantiate type all parameters + .map(t => new SpaceEngine().instantiate(t, A)) // Instantiate type parameters .reverse // Reveresed to match source order val sumTypesSize = sumTypes.size diff --git a/library/src/dotty/generic/Prod.scala b/library/src/dotty/generic/Prod.scala index b4655d25e950..c619892d0789 100644 --- a/library/src/dotty/generic/Prod.scala +++ b/library/src/dotty/generic/Prod.scala @@ -2,4 +2,4 @@ package dotty.generic sealed trait Prod final case class PCons[H, T <: Prod](head: H, tail: T) extends Prod -final case class PNil() extends Prod // TODO: could be a case object.... +final case class PNil() extends Prod diff --git a/library/src/dotty/generic/ProdK.scala b/library/src/dotty/generic/ProdK.scala deleted file mode 100644 index a45ec7654408..000000000000 --- a/library/src/dotty/generic/ProdK.scala +++ /dev/null @@ -1,5 +0,0 @@ -package dotty.generic - -sealed trait ProdK[X] -final case class PConsK[H[_], T[t] <: ProdK[t], X](head: H[X], tail: T[X]) extends ProdK[X] -final case class PNilK[X]() extends ProdK[X] // Cannot be put in `object ProdK` because of #3422 diff --git a/library/src/dotty/generic/Representable1.scala b/library/src/dotty/generic/Representable1.scala deleted file mode 100644 index 98c1fbeff4b2..000000000000 --- a/library/src/dotty/generic/Representable1.scala +++ /dev/null @@ -1,12 +0,0 @@ -package dotty.generic - -trait Representable1[A] { - type Repr[t] // <: SumK[t] | ProdK[t] ← when we stop cross compiling - - def to[T](a: A): Repr[T] - def from[T](r: Repr[T]): A -} - -object Representable1 { - def apply[A](implicit r: Representable1[A]): r.type = r -} diff --git a/library/src/dotty/generic/SumK.scala b/library/src/dotty/generic/SumK.scala deleted file mode 100644 index 7544d8fc6a77..000000000000 --- a/library/src/dotty/generic/SumK.scala +++ /dev/null @@ -1,7 +0,0 @@ -package dotty.generic - -sealed trait SumK[X] -sealed trait SConsK[H[_], T[t] <: SumK[t], X] extends SumK[X] -final case class SLeftK[H[_], T[t] <: SumK[t], X](head: H[X]) extends SConsK[H, T, X] -final case class SRightK[H[_], T[t] <: SumK[t], X](tail: T[X]) extends SConsK[H, T, X] -sealed trait SNilK[X] extends SumK[X] diff --git a/tests/run/generic-sum.scala b/tests/run/generic-sum.scala index 226a23e0caaa..b1c85fdcdbea 100644 --- a/tests/run/generic-sum.scala +++ b/tests/run/generic-sum.scala @@ -3,7 +3,6 @@ import dotty.generic._ sealed trait Foo case class Bar(i: Int) extends Foo case class Bus(s: String) extends Foo -// case class Bip(s: String) extends Foo object Test { def main(args: Array[String]): Unit = { diff --git a/tests/run/representable.scala b/tests/run/representable.scala index 6b9249b91027..f1e903ad37b1 100644 --- a/tests/run/representable.scala +++ b/tests/run/representable.scala @@ -1,5 +1,8 @@ import dotty.generic._ +// Adapted from shapeless' Generic test suite available at +// https://github.com/milessabin/shapeless/blob/shapeless-2.3.2/core/src/test/scala/shapeless/generic.scala + object Syntax { type &:[H, T <: Prod] = PCons[H, T] type |:[H, T <: Sum] = SCons[H, T] @@ -46,18 +49,6 @@ object RepresentableTestsAux { case class PersonWithPseudonimsT[T](name: T, nicks: T*) - // NOT SUPPORTED - // sealed trait AbstractNonCC - // class NonCCA(val i: Int, val s: String) extends AbstractNonCC - // class NonCCB(val b: Boolean, val d: Double) extends AbstractNonCC - // class NonCCWithVars(var c: Char, var l: Long) extends AbstractNonCC - - // class NonCCWithCompanion private (val i: Int, val s: String) - // object NonCCWithCompanion { - // def apply(i: Int, s: String) = new NonCCWithCompanion(i, s) - // def unapply(s: NonCCWithCompanion): Option[(Int, String)] = Some((s.i, s.s)) - // } - class NonCCLazy(prev0: => NonCCLazy, next0: => NonCCLazy) { lazy val prev = prev0 lazy val next = next0 @@ -172,16 +163,18 @@ object RepresentableTests { identity[AbstractSingle](s1) } - // TODO - // def testOverlappingCoproducts(): Unit = { - // val gen = Representable[Overlapping] - // val o: Overlapping = OAB(1) - // val o0 = gen.to(o) - // typed[(OAB |: OAC |: OBC |: CNil)](o0) + // Dotty deviation: we infer `type Repr = OA |: OB |: SNil` + // instead of `type Repr = OAB |: OAC |: OBC |: SNil` in this case. + def testOverlappingCoproducts(): Unit = { + val gen = Representable[Overlapping] - // val o1 = gen.from(o0) - // typed[Overlapping](o1) - // } + val o: Overlapping = OAB(1) + val o0 = gen.to(o) + identity[(OA |: OB |: SNil)](o0) + + val o1 = gen.from(o0) + identity[Overlapping](o1) + } def testCaseObjects(): Unit = { val a: Enum = A @@ -235,19 +228,18 @@ object RepresentableTests { identity[Option[Int]](o1) } - // TODO ??? - // def testParametrizedWithVarianceList(): Unit = { - // val l: List[Int] = List(1, 2, 3) - // type CN = ::[Int] |: Nil.type |: SNil + def testParametrizedWithVarianceList(): Unit = { + val l: List[Int] = List(1, 2, 3) + type CN = ::[Int] |: scala.collection.immutable.Nil.type |: SNil - // val gen = Representable[List[Int]] + val gen = Representable[List[Int]] - // val l0 = gen.to(l) - // identity[CN](l0) + val l0 = gen.to(l) + identity[CN](l0) - // val l1 = gen.from(l0) - // identity[List[Int]](l1) - // } + val l1 = gen.from(l0) + identity[List[Int]](l1) + } def testParametrzedSubset(): Unit = { val l = Left(23) @@ -279,83 +271,6 @@ object RepresentableTests { assert(s1 == s0) } - // NOT SUPPORTED - // def testAbstractNonCC(): Unit = { - // val ncca = new NonCCA(23, "foo") - // val nccb = new NonCCB(true, 2.0) - // val nccc = new NonCCWithVars('c', 42) - // val ancc: AbstractNonCC = ncca - - // val genA = Representable[NonCCA] - // val genB = Representable[NonCCB] - // val genC = Representable[NonCCWithVars] - // val genAbs = Representable[AbstractNonCC] - - // val rA = genA.to(ncca) - // assertTypedEquals[Int &: String &: PNil](23 &: "foo" &: PNil, rA) - - // val rB = genB.to(nccb) - // assertTypedEquals[Boolean &: Double &: PNil](true &: 2.0 &: PNil, rB) - - // val rC = genC.to(nccc) - // assertTypedEquals[Char &: Long &: PNil]('c' &: 42l &: PNil, rC) - - // val rAbs = genAbs.to(ancc) - // assertTypedEquals[NonCCA |: NonCCB |: NonCCWithVars |: SNil](SLeft(ncca), rAbs) - - // val fA = genA.from(13 &: "bar" &: PNil) - // identity[NonCCA](fA) - // assert(13 == fA.i) - // assert("bar" == fA.s) - - // val fB = genB.from(false &: 3.0 &: PNil) - // identity[NonCCB](fB) - // assert(false == fB.b) - // assert(3.0 == fB.d) // , Double.MinPositiveValue) - - // val fC = genC.from('k' &: 313l &: PNil) - // identity[NonCCWithVars](fC) - // assert('k' == fC.c) - // assert(313l == fC.l) - - // val fAbs = genAbs.from(SRight(SLeft(nccb))) - // identity(fAbs: AbstractNonCC) // Typed? - // assertTrue(fAbs.isInstanceOf[NonCCB]) - // assert(true == fAbs.asInstanceOf[NonCCB].b) - // assert(2.0 == fAbs.asInstanceOf[NonCCB].d) // , Double.MinPositiveValue) - // } - - // NOT SUPPORTED - // def testNonCCWithCompanion(): Unit = { - // val nccc = NonCCWithCompanion(23, "foo") - - // val gen = Representable[NonCCWithCompanion] - - // val r = gen.to(nccc) - // assertTypedEquals[Int &: String &: PNil](23 &: "foo" &: PNil, r) - - // val f = gen.from(13 &: "bar" &: PNil) - // identity(f: NonCCWithCompanion) // Typed? - // assert(13 == f.i) - // assert("bar" == f.s) - // } - - // NOT SUPPORTED - // def testNonCCLazy(): Unit = { - // lazy val (a: NonCCLazy, b: NonCCLazy, c: NonCCLazy) = - // (new NonCCLazy(c, b), new NonCCLazy(a, c), new NonCCLazy(b, a)) - - // val gen = Representable[NonCCLazy] - - // val rB = gen.to(b) - // assertTypedEquals[NonCCLazy &: NonCCLazy &: PNil](a &: c &: PNil, rB) - - // val fD = gen.from(a &: c &: PNil) - // identity[NonCCLazy](fD) - // assert(a == fD.prev) - // assert(c == fD.next) - // } - trait Parent { case class Nested(i: Int, s: String) @@ -416,8 +331,6 @@ object RepresentableTests { val a: Option[Green.type] = None Representable[Green.type] Representable[Color.Red.type] - // LabelledRepresentable[Green.type] - // LabelledRepresentable[Color.Red.type] } sealed trait Base1 @@ -467,7 +380,9 @@ object RepresentableTestsAux2 { (implicit gen: Representable[A] { type Repr = U }, auto: Bar[U]): Bar[A] = ??? } - // TODO genLoad + // TODO "Unexpected tree in genLoad", somehow `untpd.ref` generates something + // that survives until backend but shouldn't. To be investigated... + // Unexpected tree in genLoad: TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class generic)),module class RepresentableTestsAux2$)),class Outer1)),module class RepresentableTestsAux2$Outer1$Inner$)] // class Outer1 { // sealed trait Color // object Inner { @@ -475,8 +390,6 @@ object RepresentableTestsAux2 { // } // implicit val r: Representable[Bar[Color]] { type Repr = SNil } = null // implicitly[Bar[Color]] - // Fails with "Unexpected tree in genLoad" caused by the untpd.ref(tpe)... - // Unexpected tree in genLoad: TypeTree[TypeRef(ThisType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class generic)),module class RepresentableTestsAux2$)),class Outer1)),module class RepresentableTestsAux2$Outer1$Inner$)] // } object Outer2 { @@ -666,28 +579,6 @@ object EnumDefns6 { } object TestEnum { - // NOT SUPPORTED - // object EnumDefns0 { - // sealed trait EnumVal - // val BarA = new EnumVal { val name = "A" } - // val BarB = new EnumVal { val name = "B" } - // val BarC = new EnumVal { val name = "C" } - // } - - // def testEnum0(): Unit = { - // import EnumDefns0._ - - // val gen = Representable[EnumVal] - // val a0 = gen.to(BarA) - // assert(a0 == SLeft(BarA)) - - // val b0 = gen.to(BarB) - // assert(b0 == SRight(SLeft(BarB))) - - // val c0 = gen.to(BarC) - // assert(c0 == SRight(SRight(SLeft(BarC)))) - // } - def testEnum1(): Unit = { import EnumDefns1._ @@ -722,47 +613,6 @@ object TestEnum { assert(c0 == c1) } - // NOT SUPPORTED - // object EnumDefns3 { - // sealed trait EnumVal - // val BarA, BarB, BarC = new EnumVal {} - // } - - // def testEnum3(): Unit = { - // import EnumDefns3._ - - // val gen = Representable[EnumVal] - // val a0 = gen.to(BarA) - // assert(a0 == SLeft(BarA)) - - // val b0 = gen.to(BarB) - // assert(b0 == SRight(SLeft(BarB))) - - // val c0 = gen.to(BarC) - // assert(c0 == SRight(SRight(SLeft(BarC)))) - // } - - // NOT SUPPORTED - // object EnumDefns4 { - // sealed trait EnumVal - // object EnumVal { - // val BarA = new EnumVal { val name = "A" } - // val BarB = new EnumVal { val name = "B" } - // val BarC = new EnumVal { val name = "C" } - // } - // } - // def testEnum4(): Unit = { - // import EnumDefns4._ - // import EnumVal._ - // val gen = Representable[EnumVal] - // val a0 = gen.to(BarA) - // assert(a0 == SLeft(BarA)) - // val b0 = gen.to(BarB) - // assert(b0 == SRight(SLeft(BarB))) - // val c0 = gen.to(BarC) - // assert(c0 == SRight(SRight(SLeft(BarC)))) - // } - def testEnum5(): Unit = { import EnumDefns5._ import EnumVal._ @@ -798,25 +648,6 @@ object TestEnum { val c1 = SRight(SRight(SLeft[BarC.type, SNil](BarC))) assert(c0 == c1) } - - // NOT SUPPORTED - // object EnumDefns7 { - // sealed trait EnumVal - // object EnumVal { - // val BarA, BarB, BarC = new EnumVal {} - // } - // } - // def testEnum7(): Unit = { - // import EnumDefns7._ - // import EnumVal._ - // val gen = Representable[EnumVal] - // val a0 = gen.to(BarA) - // assert(a0 == SLeft(BarA)) - // val b0 = gen.to(BarB) - // assert(b0 == SRight(SLeft(BarB))) - // val c0 = gen.to(BarC) - // assert(c0 == SRight(SRight(SLeft(BarC)))) - // } } object TestPrefixes1 { @@ -836,7 +667,6 @@ object TestPrefixes1 { Representable[Defs.SumS] Representable[Defs.Sum] - // Representable.materialize[Defs.Sum, Defs.SumI |: Defs.SumS |: SNil] } } @@ -962,27 +792,20 @@ object Test { RepresentableTests.testTuples() RepresentableTests.testCoproductBasics() RepresentableTests.testSingletonCoproducts() - // RepresentableTests.testOverlappingCoproducts() + RepresentableTests.testOverlappingCoproducts() RepresentableTests.testCaseObjects() RepresentableTests.testParametrized() RepresentableTests.testParametrizedWithVarianceOption() - // RepresentableTests.testParametrizedWithVarianceList() + RepresentableTests.testParametrizedWithVarianceList() RepresentableTests.testParametrzedSubset() RepresentableTests.testParametrizedPermute() - // RepresentableTests.testAbstractNonCC() - // RepresentableTests.testNonCCWithCompanion() - // RepresentableTests.testNonCCLazy() // RepresentableTests.testNestedInherited() // Fails at runtime because of #3624 RepresentableTests.testNonRepresentable() RepresentableTests.testNestedCaseObjects() RepresentableTests.testCaseObjectsAndLazy() - // TestEnum.testEnum0() TestEnum.testEnum1() TestEnum.testEnum2() - // TestEnum.testEnum3() - // TestEnum.testEnum4() TestEnum.testEnum5() TestEnum.testEnum6() - // TestEnum.testEnum7() } } From 2858e537fae73a80b5b3090092bb9770fb490870 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Tue, 12 Dec 2017 16:10:14 +0100 Subject: [PATCH 32/32] Add documentation --- library/src/dotty/generic/Prod.scala | 7 +++++++ library/src/dotty/generic/Representable.scala | 9 +++++++++ library/src/dotty/generic/Sum.scala | 11 +++++++++++ 3 files changed, 27 insertions(+) diff --git a/library/src/dotty/generic/Prod.scala b/library/src/dotty/generic/Prod.scala index c619892d0789..24c43c3a9077 100644 --- a/library/src/dotty/generic/Prod.scala +++ b/library/src/dotty/generic/Prod.scala @@ -1,5 +1,12 @@ package dotty.generic +/** Represents a heterogeneous collection: a datatype capable of storing data + * of different types. Generalises Tuples to arbitrary arity. + */ sealed trait Prod + +/** Non-empty product */ final case class PCons[H, T <: Prod](head: H, tail: T) extends Prod + +/** Empty product */ final case class PNil() extends Prod diff --git a/library/src/dotty/generic/Representable.scala b/library/src/dotty/generic/Representable.scala index f089894d36f5..2814f2ed3c82 100644 --- a/library/src/dotty/generic/Representable.scala +++ b/library/src/dotty/generic/Representable.scala @@ -1,9 +1,18 @@ package dotty.generic +/** A generic representation of `A` as a sum of products. Provides back and + * forth conversions between values of type `A` and values of type `Repr`. + * Instances of `Representable` for sealed traits and case classes are + * automatically synthesized by the compiler when requested implicitly. + */ trait Representable[A] { + /** Type of the generic representation of `A`, composed of `Sum` and `Prod` types. */ type Repr // <: Sum | Prod ← when we stop cross compiling + /** Converts a value of type `A` to it's generic representation */ def to(a: A): Repr + + /** Converts a value in the generic representation of `A` to its concrete representation. */ def from(r: Repr): A } diff --git a/library/src/dotty/generic/Sum.scala b/library/src/dotty/generic/Sum.scala index 02cc683321df..3f4564a24eb9 100644 --- a/library/src/dotty/generic/Sum.scala +++ b/library/src/dotty/generic/Sum.scala @@ -1,7 +1,18 @@ package dotty.generic +/** Represents a value of several possible types. An instance of Sum is either + * an instance of SLeft or of SRight. Generalises Either to arbitrary arity. + */ sealed trait Sum + +/** Non-empty sum */ sealed trait SCons[H, T <: Sum] extends Sum + +/** Left case of a sum */ final case class SLeft[H, T <: Sum](head: H) extends SCons[H, T] + +/** Right case of a sum */ final case class SRight[H, T <: Sum](tail: T) extends SCons[H, T] + +/** Empty sum */ sealed trait SNil extends Sum