Skip to content

Commit afa492d

Browse files
authored
Merge pull request #1252 from joroKr21/ctor-dtor
Replace CtorDtor with simplified FromTo
2 parents 93c1c77 + 6bfdef3 commit afa492d

File tree

2 files changed

+89
-96
lines changed

2 files changed

+89
-96
lines changed

core/src/main/scala/shapeless/generic.scala

+85-87
Original file line numberDiff line numberDiff line change
@@ -502,10 +502,12 @@ trait CaseClassMacros extends ReprTypes with CaseClassMacrosVersionSpecifics {
502502

503503
def nameOf(tpe: Type) = tpe.typeSymbol.name
504504

505-
def mkHListValue(elems: List[Tree]): Tree =
506-
elems.foldRight(q"_root_.shapeless.HNil": Tree) {
507-
case (elem, acc) => q"_root_.shapeless.::($elem, $acc)"
505+
def mkHListValue(elems: List[Tree]): Tree = {
506+
val cons = objectRef[::.type]
507+
elems.foldRight(objectRef[HNil.type]) {
508+
case (elem, acc) => q"$cons($elem, $acc)"
508509
}
510+
}
509511

510512
/**
511513
* Fold `items` into a type using `cons` as a type constructor.
@@ -937,110 +939,110 @@ trait CaseClassMacros extends ReprTypes with CaseClassMacrosVersionSpecifics {
937939
}
938940
}
939941

942+
@deprecated("Use CtorDtor.fromTo instead", "2.3.9")
940943
trait CtorDtor {
941944
def construct(args: List[Tree]): Tree
942945
def binding: (Tree, List[Tree])
943946
def reprBinding: (Tree, List[Tree])
944947
}
945948

946949
object CtorDtor {
947-
def apply(tpe: Type): CtorDtor = {
948-
val sym = tpe.typeSymbol
949-
val isCaseClass = sym.asClass.isCaseClass
950-
951-
val repWCard = Star(Ident(termNames.WILDCARD)) // like pq"_*" except that it does work
952-
953-
def narrow(tree: Tree, tpe: Type): Tree =
954-
tpe match {
955-
case ConstantType(c) =>
956-
q"$c.asInstanceOf[$tpe]"
957-
case _ =>
958-
tree
959-
}
950+
@deprecated("Use CtorDtor.fromTo instead", "2.3.9")
951+
def apply(tpe: Type): CtorDtor = new CtorDtor {
952+
private[this] val (from, to) =
953+
fromTo(tpe, TypeTree(tpe))
954+
private[this] val (funs, argss) =
955+
from.body.collect { case Apply(fun, args) => (fun, args) }.reverse.unzip
956+
def construct(args: List[Tree]): Tree =
957+
funs.headOption.fold(from.body)(ctr => q"$ctr(...${args :: argss.drop(1)})")
958+
val binding: (Tree, List[Tree]) =
959+
(from.pat, argss.headOption.getOrElse(Nil))
960+
val reprBinding: (Tree, List[Tree]) =
961+
(to.pat, to.body.collect { case Apply(_, List(arg, _*)) => arg })
962+
}
960963

961-
def narrow1(tree: Tree, tpe: Type): Tree =
962-
if(isVararg(tpe))
963-
q"$tree: _*"
964-
else
965-
narrow(tree, tpe)
966-
967-
def mkCtorDtor0(elems0: List[(TermName, Type)]) = {
968-
val elems = elems0.map { case (_, tpe) => (c.freshName(TermName("pat")), tpe) }
969-
val pattern = pq"${companionRef(tpe)}(..${elems.map { case (binder, tpe) => if(isVararg(tpe)) pq"$binder @ $repWCard" else pq"$binder"}})"
970-
val reprPattern =
971-
elems.foldRight(q"_root_.shapeless.HNil": Tree) {
972-
case ((bound, _), acc) => pq"_root_.shapeless.::($bound, $acc)"
973-
}
974-
val nonCaseParamLists: List[List[Tree]] = List.fill(numNonCaseParamLists(tpe))(Nil)
975-
new CtorDtor {
976-
def construct(args: List[Tree]): Tree = q"${companionRef(tpe)}[..${tpe.typeArgs}](...${args :: nonCaseParamLists})"
977-
def binding: (Tree, List[Tree]) = (pattern, elems.map { case (binder, tpe) => narrow(q"$binder", tpe) })
978-
def reprBinding: (Tree, List[Tree]) = (reprPattern, elems.map { case (binder, tpe) => narrow1(q"$binder", tpe) })
979-
}
964+
final def fromTo(tpe: Type, reprTpt: Tree): (CaseDef, CaseDef) = {
965+
import c.internal.gen
966+
967+
val wildcard = Ident(termNames.WILDCARD)
968+
// like pq"_*" except that it does work
969+
val repWCard = Star(wildcard)
970+
971+
def narrow(tree: Tree, tpe: Type): Tree = tpe match {
972+
case ConstantType(c) => q"$c.asInstanceOf[$tpe]"
973+
case _ => tree
980974
}
981975

982-
def mkCtorDtor1(elems: List[(TermName, TermName, Type)], pattern: Tree, rhs: List[Tree]) = {
983-
val reprPattern =
984-
elems.foldRight(q"_root_.shapeless.HNil": Tree) {
985-
case ((bound, _, _), acc) => pq"_root_.shapeless.::($bound, $acc)"
986-
}
987-
new CtorDtor {
988-
def construct(args: List[Tree]): Tree = q"new $tpe(..$args)"
989-
def binding: (Tree, List[Tree]) = (pattern, rhs)
990-
def reprBinding: (Tree, List[Tree]) = (reprPattern, elems.map { case (binder, _, tpe) => narrow1(q"$binder", tpe) })
976+
def const(tree: Tree): CaseDef =
977+
cq"_ => $tree"
978+
979+
def mkHListPattern(elems: List[TermName]): Tree = {
980+
val cons = objectRef[::.type]
981+
elems.foldRight(objectRef[HNil.type]) {
982+
case (elem, acc) => pq"$cons($elem, $acc)"
991983
}
992984
}
993985

986+
def from(fields: List[(TermName, Type)])(construct: List[Tree] => Tree): CaseDef = {
987+
val (pats, args) = fields.map { case (field, tpe) =>
988+
val pat = c.freshName(field)
989+
(pat, if (isVararg(tpe)) q"$pat: _*" else narrow(q"$pat", tpe))
990+
}.unzip
991+
cq"${mkHListPattern(pats)} => ${construct(args)}"
992+
}
993+
994+
def to(pattern: Tree, args: List[Tree]): CaseDef =
995+
cq"$pattern => ${mkHListValue(args)}.asInstanceOf[$reprTpt]"
996+
997+
def fromApply(fields: List[(TermName, Type)]): CaseDef = from(fields) { args =>
998+
val nonCaseArgs = List.fill(numNonCaseParamLists(tpe))(List.empty[Tree])
999+
q"${companionRef(tpe)}[..${tpe.typeArgs}](..$args)(...$nonCaseArgs)"
1000+
}
1001+
1002+
def fromConstructor(fields: List[(TermName, Type)]): CaseDef =
1003+
from(fields)(args => q"new $tpe(..$args)")
1004+
1005+
def toUnapply(fields: List[(TermName, Type)]): CaseDef = {
1006+
val (pats, args) = fields.map { case (field, tpe) =>
1007+
val pat = c.freshName(field)
1008+
(Bind(pat, if (isVararg(tpe)) repWCard else wildcard), narrow(Ident(pat), tpe))
1009+
}.unzip
1010+
to(pq"${companionRef(tpe)}(..$pats)", args)
1011+
}
1012+
1013+
def toGetters(fields: List[(TermName, Type)]): CaseDef = {
1014+
val pattern = c.freshName(TermName("x"))
1015+
to(pq"$pattern", fields.map { case (field, tpe) => narrow(q"$pattern.$field", tpe) })
1016+
}
1017+
9941018
lowerKind(tpe) match {
9951019
// case 1: Unit
9961020
case tpe if tpe =:= typeOf[Unit] =>
997-
new CtorDtor {
998-
def construct(args: List[Tree]): Tree = q"()"
999-
def binding: (Tree, List[Tree]) = (pq"()", Nil)
1000-
def reprBinding: (Tree, List[Tree]) = (pq"_root_.shapeless.HNil", Nil)
1001-
}
1002-
1021+
(const(q"()"), const(objectRef[HNil.type]))
10031022
// case 2: singleton
10041023
case tpe if isCaseObjectLike(tpe.typeSymbol.asClass) =>
1005-
val singleton =
1006-
tpe match {
1007-
case SingleType(pre, sym) =>
1008-
c.internal.gen.mkAttributedRef(pre, sym)
1009-
case TypeRef(pre, sym, List()) if sym.isModule =>
1010-
c.internal.gen.mkAttributedRef(pre, sym.asModule)
1011-
case TypeRef(pre, sym, List()) if sym.isModuleClass =>
1012-
c.internal.gen.mkAttributedRef(pre, sym.asClass.module)
1013-
case _ =>
1014-
abort(s"Bad case object-like type $tpe")
1015-
}
1016-
new CtorDtor {
1017-
def construct(args: List[Tree]): Tree = q"$singleton: $tpe"
1018-
def binding: (Tree, List[Tree]) = (pq"_: $tpe", Nil)
1019-
def reprBinding: (Tree, List[Tree]) = (pq"_root_.shapeless.HNil", Nil)
1024+
val singleton = tpe match {
1025+
case SingleType(pre, sym) => gen.mkAttributedRef(pre, sym)
1026+
case TypeRef(pre, sym, Nil) if sym.isModule => gen.mkAttributedRef(pre, sym.asModule)
1027+
case TypeRef(pre, sym, Nil) if sym.isModuleClass => gen.mkAttributedRef(pre, sym.asClass.module)
1028+
case _ => abort(s"Bad case object-like type $tpe")
10201029
}
1021-
1030+
(const(singleton), const(objectRef[HNil.type]))
10221031
// case 3: case class
1023-
case tpe if isCaseClass => mkCtorDtor0(fieldsOf(tpe))
1024-
1032+
case tpe if tpe.typeSymbol.asClass.isCaseClass =>
1033+
val fields = fieldsOf(tpe)
1034+
(fromApply(fields), toUnapply(fields))
10251035
// case 4: exactly one matching public apply/unapply
1026-
case HasApplyUnapply(args) => mkCtorDtor0(args)
1027-
1036+
case HasApplyUnapply(args) =>
1037+
(fromApply(args), toUnapply(args))
10281038
// case 5: concrete, exactly one public constructor with matching public unapply
10291039
case HasCtorUnapply(args) =>
1030-
val elems = args.map { case (name, tpe) => (TermName(c.freshName("pat")), name, tpe) }
1031-
val pattern = pq"${companionRef(tpe)}(..${elems.map { case (binder, _, tpe) => if(isVararg(tpe)) pq"$binder @ $repWCard" else pq"$binder" }})"
1032-
val rhs = elems.map { case (binder, _, tpe) => narrow(q"$binder", tpe) }
1033-
mkCtorDtor1(elems, pattern, rhs)
1034-
1040+
(fromConstructor(args), toUnapply(args))
10351041
// case 6: concrete, exactly one public constructor with matching accessible fields
10361042
case HasUniqueCtor(args) =>
1037-
val elems = args.map { case (name, tpe) => (TermName(c.freshName("pat")), name, tpe) }
1038-
val binder = TermName(c.freshName("pat"))
1039-
val pattern = pq"$binder"
1040-
val rhs = elems.map { case (_, name, tpe) => narrow(q"$binder.$name", tpe) }
1041-
mkCtorDtor1(elems, pattern, rhs)
1042-
1043-
case _ => abort(s"Bad product type $tpe")
1043+
(fromConstructor(args), toGetters(args))
1044+
case _ =>
1045+
abort(s"Bad product type $tpe")
10441046
}
10451047
}
10461048
}
@@ -1062,11 +1064,7 @@ class GenericMacros(val c: whitebox.Context) extends CaseClassMacros {
10621064

10631065
def mkProductGeneric(tpe: Type): Tree = {
10641066
val repr = mkHListTpe(fieldsOf(tpe).map(_._2))
1065-
val ctorDtor = CtorDtor(tpe)
1066-
val (p, ts) = ctorDtor.binding
1067-
val to = cq"$p => ${mkHListValue(ts)}.asInstanceOf[$repr]"
1068-
val (rp, rts) = ctorDtor.reprBinding
1069-
val from = cq"$rp => ${ctorDtor.construct(rts)}"
1067+
val (from, to) = CtorDtor.fromTo(tpe, TypeTree(repr))
10701068
q"$generic.instance[$tpe, $repr]({ case $to }, { case $from })"
10711069
}
10721070

core/src/main/scala/shapeless/generic1.scala

+4-9
Original file line numberDiff line numberDiff line change
@@ -219,17 +219,12 @@ class Generic1Macros(val c: whitebox.Context) extends CaseClassMacros {
219219
}
220220

221221
def mkProductGeneric1(tpe: Type, frTpe: Type): Tree = {
222-
val ctorDtor = CtorDtor(tpe)
223-
val (p, ts) = ctorDtor.binding
224-
val to = cq"$p => ${mkHListValue(ts)}"
225-
val (rp, rts) = ctorDtor.reprBinding
226-
val from = cq"$rp => ${ctorDtor.construct(rts)}"
227-
val name = c.freshName(TypeName("P"))
228-
val reprTpt = reprTypTree1(tpe, name)
222+
val tparam = c.freshName(TypeName("P"))
229223
val reprName = c.freshName(TypeName("R"))
230-
224+
val reprTpt = reprTypTree1(tpe, tparam)
225+
val (from, to) = CtorDtor.fromTo(tpe, tq"$reprName[$AnyTpe]")
231226
q"""
232-
type $reprName[$name] = $reprTpt
227+
type $reprName[$tparam] = $reprTpt
233228
$generic1.unsafeInstance[$tpe, $frTpe, $reprName]({ case $to }, { case $from })
234229
"""
235230
}

0 commit comments

Comments
 (0)