diff options
author | Jon Pretty <jon.pretty@propensive.com> | 2017-06-22 18:30:59 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-06-22 18:30:59 +0100 |
commit | f8ce6450c7dc1985db8ac29312bd76d3295257ab (patch) | |
tree | ba4aeb4336269ab565f555e7323e2be1231a5247 /core/src | |
parent | d3bf8bef81124ef47097d808a0b9299641fe5c7b (diff) | |
parent | 27a55611230b772eae1f0a37822decb43c21ac25 (diff) | |
download | magnolia-f8ce6450c7dc1985db8ac29312bd76d3295257ab.tar.gz magnolia-f8ce6450c7dc1985db8ac29312bd76d3295257ab.tar.bz2 magnolia-f8ce6450c7dc1985db8ac29312bd76d3295257ab.zip |
Merge branch 'master' into cats-contravariant-example
Diffstat (limited to 'core/src')
-rw-r--r-- | core/src/main/scala/magnolia.scala | 171 |
1 files changed, 105 insertions, 66 deletions
diff --git a/core/src/main/scala/magnolia.scala b/core/src/main/scala/magnolia.scala index d2978b5..66d59d0 100644 --- a/core/src/main/scala/magnolia.scala +++ b/core/src/main/scala/magnolia.scala @@ -2,7 +2,6 @@ package magnolia import scala.reflect._, macros._ import macrocompat.bundle -import scala.util.Try import scala.collection.immutable.ListMap import language.existentials import language.higherKinds @@ -12,6 +11,14 @@ class Macros(val c: whitebox.Context) { import c.universe._ import CompileTimeState._ + + sealed trait DerivationImplicit { def tree: Tree } + case class CovariantDerivationImplicit(tree: Tree) extends DerivationImplicit + sealed trait ContravariantDerivationImplicit extends DerivationImplicit + case class ContravariantDerivation1Implicit(tree: Tree) extends ContravariantDerivationImplicit + case class ContravariantDerivation2Implicit(tree: Tree) extends ContravariantDerivationImplicit + + private def findType(key: Type): Option[TermName] = recursionStack(c.enclosingPosition).frames.find(_.genericType == key).map(_.termName(c)) @@ -22,14 +29,14 @@ class Macros(val c: whitebox.Context) { recursionStack.get(c.enclosingPosition).map(_.push(path, key, value)).getOrElse( Stack(List(Frame(path, key, value)), Nil)) ) - + try Some(fn) catch { case e: Exception => None } finally { val currentStack = recursionStack(c.enclosingPosition) recursionStack = recursionStack.updated(c.enclosingPosition, currentStack.pop()) } } - + private val removeLazy: Transformer = new Transformer { override def transform(tree: Tree): Tree = tree match { case q"_root_.magnolia.Lazy.apply[$returnType](${Literal(Constant(method: String))})" => @@ -38,49 +45,44 @@ class Macros(val c: whitebox.Context) { super.transform(tree) } } - + private def getImplicit(paramName: Option[String], genericType: Type, typeConstructor: Type, assignedName: TermName, - derivationImplicit: Either[Tree, Tree]): Tree = { - + derivationImplicit: DerivationImplicit): Tree = { + + val searchType = appliedType(typeConstructor, genericType) findType(genericType).map { methodName => val methodAsString = methodName.encodedName.toString - val searchType = appliedType(typeConstructor, genericType) q"_root_.magnolia.Lazy.apply[$searchType]($methodAsString)" }.orElse { - val searchType = appliedType(typeConstructor, genericType) - findType(genericType).map { _ => - directInferImplicit(genericType, typeConstructor, derivationImplicit) - }.getOrElse { - scala.util.Try { - val genericTypeName: String = genericType.typeSymbol.name.encodedName.toString.toLowerCase - val assignedName: TermName = TermName(c.freshName(s"${genericTypeName}Typeclass")) - recurse(ChainedImplicit(genericType.toString), genericType, assignedName) { - val inferredImplicit = c.inferImplicitValue(searchType, false, false) - q"""{ - def $assignedName: $searchType = $inferredImplicit - $assignedName - }""" - }.get - }.toOption.orElse(directInferImplicit(genericType, typeConstructor, derivationImplicit)) - } + scala.util.Try { + val genericTypeName: String = genericType.typeSymbol.name.encodedName.toString.toLowerCase + val assignedName: TermName = TermName(c.freshName(s"${genericTypeName}Typeclass")) + recurse(ChainedImplicit(genericType.toString), genericType, assignedName) { + val inferredImplicit = c.inferImplicitValue(searchType, false, false) + q"""{ + def $assignedName: $searchType = $inferredImplicit + $assignedName + }""" + }.get + }.toOption.orElse(directInferImplicit(genericType, typeConstructor, derivationImplicit)) }.getOrElse { val currentStack: Stack = recursionStack(c.enclosingPosition) - + val error = ImplicitNotFound(genericType.toString, recursionStack(c.enclosingPosition).frames.map(_.path)) - - val updatedStack = currentStack.copy(errors = error :: currentStack.errors) + + val updatedStack = currentStack.copy(errors = error :: currentStack.errors) recursionStack = recursionStack.updated(c.enclosingPosition, updatedStack) c.abort(c.enclosingPosition, s"Could not find type class for type $genericType") } } - + private def directInferImplicit(genericType: Type, typeConstructor: Type, - derivationImplicit: Either[Tree, Tree]): Option[Tree] = { + derivationImplicit: DerivationImplicit): Option[Tree] = { val genericTypeName: String = genericType.typeSymbol.name.encodedName.toString.toLowerCase val assignedName: TermName = TermName(c.freshName(s"${genericTypeName}Typeclass")) @@ -89,7 +91,7 @@ class Macros(val c: whitebox.Context) { val isCaseClass = classType.map(_.isCaseClass).getOrElse(false) val isSealedTrait = classType.map(_.isSealed).getOrElse(false) val isValueClass = genericType <:< typeOf[AnyVal] - + val resultType = appliedType(typeConstructor, genericType) val construct = if(isCaseClass) { @@ -99,37 +101,42 @@ class Macros(val c: whitebox.Context) { val implicits = caseClassParameters.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) - + }.getOrElse { c.abort(c.enclosingPosition, s"failed to get implicit for type $genericType") } - + derivationImplicit match { - case Left(impl) => + case CovariantDerivationImplicit(impl) => val dereferencedValue = q"$impl.dereference(sourceParameter, ${param.name.toString})" q"$impl.call($derivedImplicit, $dereferencedValue)" - case Right(impl) => + case ContravariantDerivation1Implicit(impl) => val paramName = TermName(param.name.toString) val dereferencedValue = q"sourceParameter.$paramName" q"$impl.call($derivedImplicit, $dereferencedValue)" + case ContravariantDerivation2Implicit(impl) => + val paramName = TermName(param.name.toString) + val dereferencedValue1 = q"sourceParameter1.$paramName" + val dereferencedValue2 = q"sourceParameter2.$paramName" + q"$impl.call($derivedImplicit, $dereferencedValue1, $dereferencedValue2)" } } derivationImplicit match { - case Left(_) => + case CovariantDerivationImplicit(_) => Some(q"new $genericType(..$implicits)") - case Right(impl) => + case contra: ContravariantDerivationImplicit => val namedImplicits = caseClassParameters.zip(implicits).map { case (param, tree) => q"(${param.name.encodedName.toString}, $tree)" } - Some(q"$impl.join(_root_.scala.collection.immutable.ListMap(..$namedImplicits))") + Some(q"${contra.tree}.join(_root_.scala.collection.immutable.ListMap(..$namedImplicits))") } } else if(isSealedTrait) { @@ -150,36 +157,56 @@ class Macros(val c: whitebox.Context) { } derivationImplicit match { - case Left(impl) => + case CovariantDerivationImplicit(impl) => val reduction = components.reduce { (left, right) => q"$impl.combine($left, $right)" } q"$impl.call($reduction, sourceParameter)" - case Right(impl) => + + case ContravariantDerivation2Implicit(impl) => val parts = subtypes.tail.zip(components.tail) - val base = q""" - $impl.call(${components.head}, sourceParameter.asInstanceOf[${subtypes.head}]) + $impl.call(${components.head}, sourceParameter1.asInstanceOf[${subtypes.head}], sourceParameter2.asInstanceOf[${subtypes.head}]) """ - parts.foldLeft(base) { case (aggregated, (componentType, derivedImplicit)) => q""" - if(sourceParameter.isInstanceOf[$componentType]) - $impl.call($derivedImplicit, sourceParameter.asInstanceOf[$componentType]) + if(sourceParameter1.isInstanceOf[$componentType] && sourceParameter2.isInstanceOf[$componentType]) + $impl.call($derivedImplicit, sourceParameter1.asInstanceOf[$componentType], sourceParameter2.asInstanceOf[$componentType]) else $aggregated""" } + case ContravariantDerivation1Implicit(impl) => + val parts = subtypes.zip(components) + + val caseClauses = parts.map { case (subtype, component) => + cq"(value: $subtype) => $impl.call($component, value)" + } + + q"""(sourceParameter: @_root_.scala.annotation.switch) match { + case ..$caseClauses + }""" } } } else None construct.map { const => - val impl = derivationImplicit.merge - q"""{ - def $assignedName: $resultType = $impl.construct { sourceParameter => $const } - $assignedName - }""" + + derivationImplicit match { + case CovariantDerivationImplicit(_) => + ??? + case ContravariantDerivation1Implicit(impl) => + q"""{ + def $assignedName: $resultType = $impl.construct { sourceParameter => $const } + $assignedName + }""" + case ContravariantDerivation2Implicit(impl) => + q"""{ + def $assignedName: $resultType = $impl.construct { case (sourceParameter1, sourceParameter2) => $const } + $assignedName + }""" + } } } def magnolia[T: WeakTypeTag, Typeclass: WeakTypeTag]: Tree = { + import scala.util.{Try, Success, Failure} val genericType: Type = weakTypeOf[T] val currentStack: Stack = recursionStack.get(c.enclosingPosition).getOrElse(Stack(List(), List())) @@ -188,23 +215,29 @@ class Macros(val c: whitebox.Context) { val coDerivationTypeclass = weakTypeOf[CovariantDerivation[_]].typeConstructor val contraDerivationTypeclass = weakTypeOf[ContravariantDerivation[_]].typeConstructor - + val contraDerivation2Typeclass = weakTypeOf[ContravariantDerivation2[_]].typeConstructor + val coDerivationType = appliedType(coDerivationTypeclass, List(typeConstructor)) val contraDerivationType = appliedType(contraDerivationTypeclass, List(typeConstructor)) - val derivationImplicit = try { - Left(c.untypecheck(c.inferImplicitValue(coDerivationType, false, false))) - } catch { - case e: Exception => - try Right(c.untypecheck(c.inferImplicitValue(contraDerivationType, false, false))) catch { - case e: Exception => - c.info(c.enclosingPosition, s"could not find an implicit instance of "+ - s"CovariantDerivation[$typeConstructor] or "+ - s"ContravariantDerivation[$typeConstructor]", true) - - throw e - } - } - + val contraDerivation2Type = appliedType(contraDerivation2Typeclass, List(typeConstructor)) + + def findDerivationImplicit[T <: DerivationImplicit](tpe: c.Type, cons: Tree => T): Try[DerivationImplicit] = + Try(cons(c.untypecheck(c.inferImplicitValue(tpe, false, false)))) + + val derivationImplicit = + findDerivationImplicit(coDerivationType, CovariantDerivationImplicit) + .orElse(findDerivationImplicit(contraDerivationType, ContravariantDerivation1Implicit)) + .orElse(findDerivationImplicit(contraDerivation2Type, ContravariantDerivation2Implicit)) match { + case Failure(e) => + c.info(c.enclosingPosition, s"could not find an implicit instance of "+ + s"CovariantDerivation[$typeConstructor] or "+ + s"ContravariantDerivation[$typeConstructor] or "+ + s"ContravariantDerivation2[$typeConstructor]", true) + throw e + case Success(di) => + di + } + if(directlyReentrant) throw DirectlyReentrantException() currentStack.errors.foreach { error => @@ -226,7 +259,6 @@ class Macros(val c: whitebox.Context) { Some(q"_root_.magnolia.Lazy[$searchType]($methodAsString)") } } else { - val typeConstructor: Type = weakTypeOf[Typeclass].typeConstructor directInferImplicit(genericType, typeConstructor, derivationImplicit) } @@ -301,5 +333,12 @@ trait ContravariantDerivation[Typeclass[_]] { def call[T](typeclass: Typeclass[T], value: T): Return def construct[T](body: T => Return): Typeclass[T] def join(elements: ListMap[String, Return]): Return +} +trait ContravariantDerivation2[Typeclass[_]] { + type Return + def call[T](typeclass: Typeclass[T], value1: T, value2: T): Return + def construct[T](body: (T, T) => Return): Typeclass[T] + def join(elements: ListMap[String, Return]): Return } + |