aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.sbt2
-rw-r--r--core/src/main/scala/magnolia.scala56
-rw-r--r--examples/src/main/scala/example.scala10
-rw-r--r--tests/shared/src/main/scala/magnolia/main.scala3
4 files changed, 55 insertions, 16 deletions
diff --git a/build.sbt b/build.sbt
index 79f28a2..e943e73 100644
--- a/build.sbt
+++ b/build.sbt
@@ -48,7 +48,7 @@ lazy val buildSettings = Seq(
organization := "com.propensive",
scalaVersion := "2.12.2",
name := "magnolia",
- version := "2.0.0",
+ version := "0.1.0",
scalacOptions ++= Seq("-deprecation", "-feature", "-Ywarn-value-discard", "-Ywarn-dead-code", "-Ywarn-nullary-unit", "-Ywarn-numeric-widen", "-Ywarn-inaccessible", "-Ywarn-adapted-args"),
crossScalaVersions := Seq("2.10.6", "2.11.11", "2.12.2"),
scmInfo := Some(ScmInfo(url("https://github.com/propensive/magnolia"),
diff --git a/core/src/main/scala/magnolia.scala b/core/src/main/scala/magnolia.scala
index 7342c67..08210f8 100644
--- a/core/src/main/scala/magnolia.scala
+++ b/core/src/main/scala/magnolia.scala
@@ -56,7 +56,7 @@ class Macros(val c: whitebox.Context) {
scala.util.Try {
val genericTypeName: String = genericType.typeSymbol.name.encodedName.toString.toLowerCase
val assignedName: TermName = TermName(c.freshName(s"${genericTypeName}Typeclass"))
- recurse(RecursiveCall(genericType.toString), genericType, assignedName) {
+ recurse(ChainedImplicit(genericType.toString), genericType, assignedName) {
val inferredImplicit = c.inferImplicitValue(searchType, false, false)
q"""{
def $assignedName: $searchType = $inferredImplicit
@@ -96,8 +96,13 @@ class Macros(val c: whitebox.Context) {
case m: MethodSymbol if m.isCaseAccessor => m.asMethod
}.map { param =>
val paramName = param.name.encodedName.toString
- val derivedImplicit = recurse(ProductType(paramName, genericType.toString), genericType, assignedName) {
- getImplicit(Some(paramName), param.returnType, typeConstructor, assignedName, derivationImplicit)
+
+ val derivedImplicit = recurse(ProductType(paramName, genericType.toString), genericType,
+ assignedName) {
+
+ getImplicit(Some(paramName), param.returnType, typeConstructor, assignedName,
+ derivationImplicit)
+
}.getOrElse {
c.abort(c.enclosingPosition, s"failed to get implicit for type $genericType")
}
@@ -137,9 +142,17 @@ class Macros(val c: whitebox.Context) {
val reduction = components.reduce { (left, right) => q"$impl.combine($left, $right)" }
q"$impl.call($reduction, sourceParameter)"
case Right(impl) =>
- val parts = subtypes.zip(components)
- parts.tail.foldLeft(q"$impl.call(${parts.head._2}, sourceParameter.asInstanceOf[${parts.head._1}])") { case (aggregated, (componentType, derivedImplicit)) =>
- q"if(sourceParameter.isInstanceOf[$componentType]) $impl.call($derivedImplicit, sourceParameter.asInstanceOf[$componentType]) else $aggregated"
+ val parts = subtypes.tail.zip(components.tail)
+
+ val base = q"""
+ $impl.call(${components.head}, sourceParameter.asInstanceOf[${subtypes.head}])
+ """
+
+ parts.foldLeft(base) { case (aggregated, (componentType, derivedImplicit)) =>
+ q"""
+ if(sourceParameter.isInstanceOf[$componentType])
+ $impl.call($derivedImplicit, sourceParameter.asInstanceOf[$componentType])
+ else $aggregated"""
}
}
}
@@ -163,8 +176,8 @@ class Macros(val c: whitebox.Context) {
def magnolia[T: WeakTypeTag, Typeclass: WeakTypeTag]: Tree = try {
val genericType: Type = weakTypeOf[T]
- val currentStack: List[Frame] = recursionStack.get(c.enclosingPosition).map(_.frames).getOrElse(List())
- val directlyReentrant = Some(genericType) == currentStack.headOption.map(_.genericType)
+ val currentStack: Stack = recursionStack.get(c.enclosingPosition).getOrElse(Stack(List(), List()))
+ val directlyReentrant = Some(genericType) == currentStack.frames.headOption.map(_.genericType)
val typeConstructor: Type = weakTypeOf[Typeclass].typeConstructor
val coDerivationTypeclass = weakTypeOf[CovariantDerivation[_]].typeConstructor
@@ -180,8 +193,17 @@ class Macros(val c: whitebox.Context) {
}
if(directlyReentrant) throw DirectlyReentrantException()
-
- val result: Option[Tree] = if(!recursionStack.isEmpty) {
+
+ currentStack.errors.foreach { error =>
+ if(!emittedErrors.contains(error)) {
+ emittedErrors += error
+ val trace = error.path.mkString("\n in ", "\n in ", "\n \n")
+ val msg = s"could not derive ${typeConstructor} instance for type ${error.genericType}"
+ c.info(c.enclosingPosition, msg+trace, true)
+ }
+ }
+
+ val result: Option[Tree] = if(!currentStack.frames.isEmpty) {
findType(genericType) match {
case None =>
directInferImplicit(genericType, typeConstructor, derivationImplicit)
@@ -195,10 +217,10 @@ class Macros(val c: whitebox.Context) {
directInferImplicit(genericType, typeConstructor, derivationImplicit)
}
- if(currentStack.isEmpty) recursionStack = Map()
+ if(currentStack.frames.isEmpty) recursionStack = Map()
result.map { tree =>
- if(currentStack.isEmpty) {
+ if(currentStack.frames.isEmpty) {
val res = c.untypecheck(removeLazy.transform(tree))
res
} else tree
@@ -228,8 +250,8 @@ private[magnolia] object CompileTimeState {
override def toString = s"parameter '$paramName' of product type $typeName"
}
- case class RecursiveCall(typeName: String) extends TypePath {
- override def toString = s"recursive implicit of type $typeName"
+ case class ChainedImplicit(typeName: String) extends TypePath {
+ override def toString = s"chained implicit of type $typeName"
}
case class ImplicitNotFound(genericType: String, path: List[TypePath])
@@ -250,14 +272,18 @@ private[magnolia] object CompileTimeState {
private[magnolia] var recursionStack: Map[api.Position, Stack] =
Map()
+
+ private[magnolia] var emittedErrors: Set[ImplicitNotFound] = Set()
}
trait CovariantDerivation[Typeclass[_]] {
type Value
def dereference(value: Value, param: String): Value
def call[T](typeclass: Typeclass[T], value: Value): T
- def combine[Supertype, Right <: Supertype](left: Typeclass[_ <: Supertype], right: Typeclass[Right]): Typeclass[Supertype]
def construct[T](body: Value => T): Typeclass[T]
+
+ def combine[Supertype, Right <: Supertype](left: Typeclass[_ <: Supertype],
+ right: Typeclass[Right]): Typeclass[Supertype]
}
trait ContravariantDerivation[Typeclass[_]] {
diff --git a/examples/src/main/scala/example.scala b/examples/src/main/scala/example.scala
index 5dd1f47..afd202e 100644
--- a/examples/src/main/scala/example.scala
+++ b/examples/src/main/scala/example.scala
@@ -89,12 +89,22 @@ object `package` {
implicit class Showable[T: Show](t: T) {
def show: String = implicitly[Show[T]].show(t)
}
+ implicit val showString: Show[String] = identity
+ implicit val showBool: Show[Boolean] = _.toString
+ implicit def showList[T: Show]: Show[List[T]] = xs => xs.map { x => s"list:${implicitly[Show[T]].show(x)}" }.mkString(";")
+ implicit def showSet[T: Show]: Show[Set[T]] = s => "set"
}
sealed trait Tree
case class Branch(left: Tree, right: Tree) extends Tree
case class Leaf(value: Int) extends Tree
+sealed trait Entity
+case class Person(name: String, address: Address) extends Entity
+case class Organization(name: String, contacts: Set[Person]) extends Entity
+case class Address(lines: List[String], country: Country)
+case class Country(name: String, code: String, salesTax: Boolean)
+
trait Show[T] { def show(t: T): String }
object Show extends Show_1 {
implicit val showInt: Show[Int] = _.toString
diff --git a/tests/shared/src/main/scala/magnolia/main.scala b/tests/shared/src/main/scala/magnolia/main.scala
index 362a0fe..e826bca 100644
--- a/tests/shared/src/main/scala/magnolia/main.scala
+++ b/tests/shared/src/main/scala/magnolia/main.scala
@@ -5,6 +5,9 @@ import examples._
object Main {
def main(args: Array[String]): Unit = {
println(Branch(Branch(Leaf(1), Leaf(2)), Leaf(3)).show)
+
+ println(List[Entity](Person("Jon Pretty", Address(List("Home"), Country("UK", "GBR", false)))).show)
+
}
}