Skip to content

Commit

Permalink
Merge pull request #1270 from joroKr21/private-subclass
Browse files Browse the repository at this point in the history
Make Generic product and coproduct agree on definitions
  • Loading branch information
joroKr21 authored Sep 12, 2022
2 parents 8e2a6f2 + 2ac6014 commit 268526d
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 27 deletions.
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

0 comments on commit 268526d

Please # to comment.