From 02dc0ea3289b51d254e43c4e40ec56b4929aeea8 Mon Sep 17 00:00:00 2001 From: Jon Pretty Date: Thu, 28 Dec 2017 11:55:39 +0000 Subject: Revert "Make `SealedTrait.dispatch` allocation-free" --- core/shared/src/main/scala/interface.scala | 19 ++++++------- core/shared/src/main/scala/magnolia.scala | 45 +++++++++++++++--------------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/core/shared/src/main/scala/interface.scala b/core/shared/src/main/scala/interface.scala index a82a54c..193a6f9 100644 --- a/core/shared/src/main/scala/interface.scala +++ b/core/shared/src/main/scala/interface.scala @@ -1,7 +1,6 @@ package magnolia import language.higherKinds -import scala.annotation.tailrec /** represents a subtype of a sealed trait * @@ -129,7 +128,7 @@ abstract class CaseClass[Typeclass[_], Type] private[magnolia] ( * * For efficiency, this sequence is implemented by an `Array`, but upcast to a * [[scala.collection.Seq]] to hide the mutable collection API. */ - final def parameters: Seq[Param[Typeclass, Type]] = parametersArray + def parameters: Seq[Param[Typeclass, Type]] = parametersArray } /** represents a sealed trait and the context required to construct a new typeclass instance @@ -158,12 +157,12 @@ final class SealedTrait[Typeclass[_], Type](val typeName: String, * matches * @return the result of applying the `handle` lambda to subtype of the sealed trait which * matches the parameter `value` */ - def dispatch[Return](value: Type)(handle: Subtype[Typeclass, Type] => Return): Return = { - @tailrec def rec(ix: Int): Return = - if (ix < subtypesArray.length) { - val sub = subtypesArray(ix) - if (sub.cast.isDefinedAt(value)) handle(sub) else rec(ix + 1) - } else throw new IllegalArgumentException(s"The given value `$value` is not a sub type of `$typeName`") - rec(0) - } + def dispatch[Return](value: Type)(handle: Subtype[Typeclass, Type] => Return): Return = + subtypes + .map { sub => + sub.cast.andThen { v => + handle(sub) + } + } + .reduce(_ orElse _)(value) } diff --git a/core/shared/src/main/scala/magnolia.scala b/core/shared/src/main/scala/magnolia.scala index 2e7dae8..f55dfa7 100644 --- a/core/shared/src/main/scala/magnolia.scala +++ b/core/shared/src/main/scala/magnolia.scala @@ -1,8 +1,6 @@ package magnolia -import scala.util.control.NonFatal -import scala.reflect._ -import macros._ +import scala.reflect._, macros._ import scala.collection.immutable.ListMap import language.existentials import language.higherKinds @@ -113,7 +111,7 @@ object Magnolia { ) } val firstParamBlock = combineClass.asType.toType.decl(term).asTerm.asMethod.paramLists.head - if (firstParamBlock.lengthCompare(1) != 0) + if (firstParamBlock.length != 1) c.abort(c.enclosingPosition, s"magnolia: the method `combine` should take a single parameter of type $expected") } @@ -125,9 +123,9 @@ object Magnolia { def findType(key: Type): Option[TermName] = recursionStack(c.enclosingPosition).frames.find(_.genericType == key).map(_.termName(c)) - final case class Typeclass(typ: c.Type, tree: c.Tree) + case class Typeclass(typ: c.Type, tree: c.Tree) - def recurse[A](path: TypePath, key: Type, value: TermName)(fn: => A): Option[A] = { + def recurse[T](path: TypePath, key: Type, value: TermName)(fn: => T): Option[T] = { val oldRecursionStack = recursionStack.get(c.enclosingPosition) recursionStack = recursionStack.updated( c.enclosingPosition, @@ -137,7 +135,7 @@ object Magnolia { ) try Some(fn) - catch { case NonFatal(_) => None } finally { + catch { case e: Exception => None } finally { val currentStack = recursionStack(c.enclosingPosition) recursionStack = recursionStack.updated(c.enclosingPosition, currentStack.pop()) } @@ -294,7 +292,7 @@ object Magnolia { m.asMethod } - final case class CaseParam(sym: c.universe.MethodSymbol, + case class CaseParam(sym: c.universe.MethodSymbol, repeated: Boolean, typeclass: c.Tree, paramType: c.Type, @@ -507,14 +505,15 @@ object Magnolia { * * This method is intended to be called only from code generated by the Magnolia macro, and * should not be called directly from users' code. */ - def subtype[Tc[_], T, S <: T](name: String, tc: => Tc[S], isType: T => Boolean, asType: T => S): Subtype[Tc, T] = - new Subtype[Tc, T] with PartialFunction[T, S] { + def subtype[Tc[_], T, S <: T](name: String, tc: => Tc[S], isType: T => Boolean, asType: T => S) = + new Subtype[Tc, T] { type SType = S def label: String = name def typeclass: Tc[SType] = tc - def cast: PartialFunction[T, SType] = this - def isDefinedAt(t: T) = isType(t) - def apply(t: T): SType = asType(t) + def cast: PartialFunction[T, SType] = new PartialFunction[T, S] { + def isDefinedAt(t: T) = isType(t) + def apply(t: T): SType = asType(t) + } } /** constructs a new [[Param]] instance @@ -525,7 +524,7 @@ object Magnolia { isRepeated: Boolean, typeclassParam: Tc[P], defaultVal: => Option[P], - deref: T => P): Param[Tc, T] = new Param[Tc, T] { + deref: T => P) = new Param[Tc, T] { type PType = P def label: String = name def repeated: Boolean = isRepeated @@ -542,31 +541,31 @@ object Magnolia { obj: Boolean, valClass: Boolean, params: Array[Param[Tc, T]], - constructor: (Param[Tc, T] => Any) => T): CaseClass[Tc, T] = + constructor: (Param[Tc, T] => Any) => T) = new CaseClass[Tc, T](name, obj, valClass, params) { def construct[R](param: Param[Tc, T] => R): T = constructor(param) } } -private[magnolia] final case class DirectlyReentrantException() +private[magnolia] case class DirectlyReentrantException() extends Exception("attempt to recurse directly") private[magnolia] object Deferred { def apply[T](method: String): T = ??? } private[magnolia] object CompileTimeState { - sealed abstract class TypePath(path: String) { override def toString = path } - final case class CoproductType(typeName: String) extends TypePath(s"coproduct type $typeName") + sealed class TypePath(path: String) { override def toString = path } + case class CoproductType(typeName: String) extends TypePath(s"coproduct type $typeName") - final case class ProductType(paramName: String, typeName: String) + case class ProductType(paramName: String, typeName: String) extends TypePath(s"parameter '$paramName' of product type $typeName") - final case class ChainedImplicit(typeName: String) + case class ChainedImplicit(typeName: String) extends TypePath(s"chained implicit of type $typeName") - final case class ImplicitNotFound(genericType: String, path: List[TypePath]) + case class ImplicitNotFound(genericType: String, path: List[TypePath]) - final case class Stack(cache: Map[whitebox.Context#Type, Option[whitebox.Context#Tree]], + case class Stack(cache: Map[whitebox.Context#Type, Option[whitebox.Context#Tree]], frames: List[Frame], errors: List[ImplicitNotFound]) { @@ -584,7 +583,7 @@ private[magnolia] object CompileTimeState { def pop(): Stack = Stack(cache, frames.tail, errors) } - final case class Frame(path: TypePath, + case class Frame(path: TypePath, genericType: whitebox.Context#Type, term: whitebox.Context#TermName) { def termName(c: whitebox.Context): c.TermName = term.asInstanceOf[c.TermName] -- cgit v1.2.3