Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Generic product and coproduct agree on definitions #1270

Merged
merged 1 commit into from
Sep 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 14 additions & 27 deletions core/src/main/scala/shapeless/generic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ trait CaseClassMacros extends ReprTypes with CaseClassMacrosVersionSpecifics {

def isProductAux(tpe: Type): Boolean =
tpe.typeSymbol.isClass && {
val cls = classSym(tpe)
val cls = tpe.typeSymbol.asClass
isCaseObjectLike(cls) || isCaseClassLike(cls) || HasApplyUnapply(tpe) || HasCtorUnapply(tpe)
}

Expand All @@ -333,7 +333,7 @@ trait CaseClassMacros extends ReprTypes with CaseClassMacrosVersionSpecifics {

def isCoproduct(tpe: Type): Boolean =
tpe.typeSymbol.isClass && {
val cls = classSym(tpe)
val cls = tpe.typeSymbol.asClass
(cls.isTrait || cls.isAbstract) && cls.isSealed
}

Expand Down Expand Up @@ -400,18 +400,13 @@ trait CaseClassMacros extends ReprTypes with CaseClassMacrosVersionSpecifics {
}

def ctorsOfAux(tpe: Type, hk: Boolean): List[Type] = {
def collectCtors(classSym: ClassSymbol): List[ClassSymbol] = {
classSym.knownDirectSubclasses.toList flatMap { child0 =>
val child = child0.asClass
child.typeSignature // Workaround for <https://issues.scala-lang.org/browse/SI-7755>
if (isCaseClassLike(child) || isCaseObjectLike(child))
List(child)
else if (child.isSealed)
collectCtors(child)
else
abort(s"$child is not case class like or a sealed trait")
def collectCtors(classSym: ClassSymbol): List[ClassSymbol] =
classSym.knownDirectSubclasses.toList.flatMap { child =>
val cls = child.asClass
if (isProductAux(cls.typeSignature)) List(cls)
else if (cls.isSealed) collectCtors(cls)
else abort(s"$cls is not case class like or a sealed trait")
}
}

if(isProduct(tpe))
List(tpe)
Expand Down Expand Up @@ -699,27 +694,19 @@ trait CaseClassMacros extends ReprTypes with CaseClassMacrosVersionSpecifics {
}

def isSealedHierarchyClassSymbol(symbol: ClassSymbol): Boolean = {
def helper(classSym: ClassSymbol): Boolean = {
classSym.knownDirectSubclasses.toList forall { child0 =>
val child = child0.asClass
child.typeSignature // Workaround for <https://issues.scala-lang.org/browse/SI-7755>

isCaseClassLike(child) || (child.isSealed && helper(child))
def helper(classSym: ClassSymbol): Boolean =
classSym.knownDirectSubclasses.toList.forall { child =>
val cls = child.asClass
isCaseClassLike(cls) || (cls.isSealed && helper(cls))
}
}

symbol.isSealed && helper(symbol)
}

def classSym(tpe: Type): ClassSymbol = {
val sym = tpe.typeSymbol
if (!sym.isClass)
abort(s"$sym is not a class or trait")

val classSym = sym.asClass
classSym.typeSignature // Workaround for <https://issues.scala-lang.org/browse/SI-7755>

classSym
if (!sym.isClass) abort(s"$sym is not a class or trait")
sym.asClass
}

// See https://github.com/milessabin/shapeless/issues/212
Expand Down
24 changes: 24 additions & 0 deletions core/src/test/scala/shapeless/generic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,20 @@ package GenericTestsAux {
final case class InTap[A, -B](in: B => A) extends Tap[A]
final case class OutTap[A, +B](out: A => B) extends Tap[A]
final case class PipeTap[A, B](in: B => A, out: A => B) extends Tap[A]

sealed trait PubOrPriv
final case class Pub(x: Int) extends PubOrPriv
final class Priv private(val y: String) extends PubOrPriv {
override def equals(that: Any): Boolean = that match {
case that: Priv => this.y == that.y
case _ => false
}
}

object Priv {
def apply(y: String): Priv = new Priv(y)
def unapply(p: Priv): Some[String] = Some(p.y)
}
}

class GenericTests {
Expand Down Expand Up @@ -829,6 +843,7 @@ class GenericTests {
illTyped("Generic[Squared]")
}

@Test
def testCoproductWithFreeTypeParams: Unit = {
type Repr[A] = ConstTap[A] :+: InTap[A, _] :+: OutTap[A, _] :+: PipeTap[A, _] :+: CNil
val gen = Generic[Tap[String]]
Expand All @@ -850,6 +865,15 @@ class GenericTests {
assertEquals(gen.to(expected), actual)
}
}

@Test
def testPublicAndPrivateCoproductChildren: Unit = {
val gen = Generic[PubOrPriv]
assertEquals(Pub(123), gen.from(Inr(Inl(Pub(123)))))
assertEquals(Inr(Inl(Pub(123))), gen.to(Pub(123)))
assertEquals(Priv("secret"), gen.from(Inl(Priv("secret"))))
assertEquals(Inl(Priv("secret")), gen.to(Priv("secret")))
}
}

package GenericTestsAux2 {
Expand Down