aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorgi Krastev <joro.kr.21@gmail.com>2017-11-29 19:53:01 +0100
committerGeorgi Krastev <joro.kr.21@gmail.com>2017-11-29 20:05:50 +0100
commitd8fb2f5ca2edc1a34ed7831f06d5eca08ba4989c (patch)
tree879238ab17d24e05457d158e6af76879a53aea5a
parent93ff9742cf8cd9f2c60986ff4f7af8d24e268b1f (diff)
downloadmagnolia-d8fb2f5ca2edc1a34ed7831f06d5eca08ba4989c.tar.gz
magnolia-d8fb2f5ca2edc1a34ed7831f06d5eca08ba4989c.tar.bz2
magnolia-d8fb2f5ca2edc1a34ed7831f06d5eca08ba4989c.zip
Existentially abstract unbound subtype parameters
That happens when the subtype of a sealed trait has more type parameters than its parent. When those extra type parameters are covariant they are replaced by their upper bounds, otherwise they are existentially quantified.
-rw-r--r--CONTRIBUTORS2
-rw-r--r--core/shared/src/main/scala/magnolia.scala16
-rw-r--r--examples/shared/src/main/scala/show.scala6
-rw-r--r--tests/src/main/scala/tests.scala23
4 files changed, 39 insertions, 8 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index f495d8d..e376ca4 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -1,3 +1,3 @@
Jon Pretty <jon.pretty@propensive.com> [@propensive](https://twitter.com/propensive/)
Loïc Descotte
-
+Georgi Krastev <jorokr21@gmail.com> [@joro_kr](https://twitter.com/joro_kr/)
diff --git a/core/shared/src/main/scala/magnolia.scala b/core/shared/src/main/scala/magnolia.scala
index 2113dff..62a36a5 100644
--- a/core/shared/src/main/scala/magnolia.scala
+++ b/core/shared/src/main/scala/magnolia.scala
@@ -65,6 +65,7 @@ object Magnolia {
* */
def gen[T: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
import c.universe._
+ import internal._
val magnoliaPkg = q"_root_.magnolia"
val scalaPkg = q"_root_.scala"
@@ -246,9 +247,7 @@ object Magnolia {
val caseParamsReversed = caseClassParameters.foldLeft[List[CaseParam]](Nil) {
(acc, param) =>
val paramName = param.name.decodedName.toString
- val paramType = param.returnType.substituteTypes(genericType.etaExpand.typeParams,
- genericType.typeArgs)
-
+ val paramType = param.typeSignatureIn(genericType).resultType
val predefinedRef = acc.find(_.paramType == paramType)
val caseParamOpt = predefinedRef.map { backRef =>
@@ -332,10 +331,13 @@ object Magnolia {
} else if (isSealedTrait) {
val genericSubtypes = classType.get.knownDirectSubclasses.to[List]
val subtypes = genericSubtypes.map { sub =>
- val typeArgs = sub.asType.typeSignature.baseType(genericType.typeSymbol).typeArgs
- val mapping = typeArgs.zip(genericType.typeArgs).toMap
- val newTypeParams = sub.asType.toType.typeArgs.map(mapping(_))
- appliedType(sub.asType.toType.typeConstructor, newTypeParams)
+ val subType = sub.asType.toType // FIXME: Broken for path dependent types
+ val typeParams = sub.asType.typeParams
+ val typeArgs = thisType(sub).baseType(genericType.typeSymbol).typeArgs
+ val mapping = (typeArgs.map(_.typeSymbol), genericType.typeArgs).zipped.toMap
+ val newTypeArgs = typeParams.map(mapping.withDefault(_.asType.toType))
+ val applied = appliedType(subType.typeConstructor, newTypeArgs)
+ existentialAbstraction(typeParams, applied)
}
if (subtypes.isEmpty) {
diff --git a/examples/shared/src/main/scala/show.scala b/examples/shared/src/main/scala/show.scala
index 50b34ee..9f634ba 100644
--- a/examples/shared/src/main/scala/show.scala
+++ b/examples/shared/src/main/scala/show.scala
@@ -60,4 +60,10 @@ object Show extends GenericShow[String] {
implicit val int: Show[String, Int] = new Show[String, Int] {
def show(s: Int): String = s.toString
}
+
+ /** show typeclass for sequences */
+ implicit def seq[A](implicit A: Show[String, A]): Show[String, Seq[A]] =
+ new Show[String, Seq[A]] {
+ def show(as: Seq[A]): String = as.iterator.map(A.show).mkString("[", ",", "]")
+ }
}
diff --git a/tests/src/main/scala/tests.scala b/tests/src/main/scala/tests.scala
index 4b0e5c1..bc33969 100644
--- a/tests/src/main/scala/tests.scala
+++ b/tests/src/main/scala/tests.scala
@@ -47,6 +47,14 @@ object Test {
def apply(a: String, b: String): Test = Test(Param(a, b))
}
+sealed trait Politician[+S]
+case class Accountable[+S](slogan: S) extends Politician[S]
+case class Corrupt[+S, +L <: Seq[Company]](slogan: S, lobby: L) extends Politician[S]
+
+sealed trait Box[+A]
+case class SimpleBox[+A](value: A) extends Box[A]
+case class LabelledBox[+A, L <: String](value: A, var label: L) extends Box[A]
+
object Tests extends TestApp {
def tests() = for (i <- 1 to 1000) {
@@ -201,5 +209,20 @@ object Tests extends TestApp {
test("serialize a value class") {
Show.gen[Length].show(new Length(100))
}.assert(_ == "100")
+
+ // Corrupt being covariant in L <: Seq[Company] enables the derivation for Corrupt[String, _]
+ test("show a Politician with covariant lobby") {
+ Show.gen[Politician[String]].show(Corrupt("wall", Seq(Company("Alice Inc"))))
+ }.assert(_ == "Corrupt(slogan=wall,lobby=[Company(name=Alice Inc)])")
+
+ // LabelledBox being invariant in L <: String prohibits the derivation for LabelledBox[Int, _]
+ test("can't show a Box with invariant label") {
+ scalac"Show.gen[Box[Int]]"
+ }.assert { _ == TypecheckError(
+ txt"""magnolia: could not find typeclass for type L
+ | in parameter 'label' of product type magnolia.tests.LabelledBox[Int, _ <: String]
+ | in coproduct type magnolia.tests.Box[Int]
+ |""")
+ }
}
}