Skip to content

Fix 12919 - summon anonymous mirror when companion is a case object #14986

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

Merged
merged 3 commits into from
May 5, 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
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/SymUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@ object SymUtils:
def useCompanionAsSumMirror(using Context): Boolean =
def companionExtendsSum(using Context): Boolean =
self.linkedClass.isSubClass(defn.Mirror_SumClass)
self.linkedClass.exists
&& !self.is(Scala2x)
!self.is(Scala2x)
&& self.linkedClass.exists
&& !self.linkedClass.is(Case)
&& (
// If the sum type is compiled from source, and `self` is a "generic sum"
// then its companion object will become a sum mirror in `posttyper`. (This method
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Synthesizer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType))
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(elemsLabels))
val mirrorRef =
if (cls.is(Scala2x)) anonymousMirror(monoType, ExtendsProductMirror, span)
if (cls.is(Scala2x) || cls.linkedClass.is(Case)) anonymousMirror(monoType, ExtendsProductMirror, span)
else companionPath(mirroredType, span)
mirrorRef.cast(mirrorType)
else EmptyTree
Expand Down
20 changes: 20 additions & 0 deletions tests/run/i12919.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
case class Normal(value: String)
object Normal

case class ClassWithCaseCompanion(value: String)
case object ClassWithCaseCompanion

def instantiate[T](product: Product)(implicit mirror: scala.deriving.Mirror.ProductOf[T]) =
mirror.fromProduct(product)

@main def Test: Unit = {
assert(instantiate[Normal](Tuple1("a")) == Normal("a")) // works as expected

assert(instantiate[ClassWithCaseCompanion.type](EmptyTuple) == ClassWithCaseCompanion) // works as expected

val c = instantiate[ClassWithCaseCompanion](Tuple1("b")) // throws java.lang.ClassCastException: class ClassWithCaseCompanion$ cannot be cast to class ClassWithCaseCompanion
assert(c == ClassWithCaseCompanion("b")) // desired behaviour

val d = instantiate[ClassWithCaseCompanion.type](EmptyTuple)
assert(d == ClassWithCaseCompanion)
}
18 changes: 18 additions & 0 deletions tests/run/i12919a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import scala.deriving.Mirror

case class Standalone(i: Int)
object Standalone

case class WithCompanionCaseClass(i: Int)
case object WithCompanionCaseClass

@main def Test: Unit =

val mStandalone = summon[Mirror.ProductOf[Standalone]]
assert(mStandalone eq Standalone) // the companion object is the mirror for the case class

val mWithCompanion = summon[Mirror.ProductOf[WithCompanionCaseClass]]
assert(mWithCompanion ne WithCompanionCaseClass) // A case object can not be the mirror of a companion case class.

val mWithCompanionCaseObject = summon[Mirror.ProductOf[WithCompanionCaseClass.type]]
assert(mWithCompanionCaseObject eq WithCompanionCaseClass) // A case object is its own mirror.
17 changes: 17 additions & 0 deletions tests/run/i12919b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import scala.deriving.Mirror


sealed trait WithCompanionSealedTrait
case object WithCompanionSealedTrait:
case class FirstChild(x: Int) extends WithCompanionSealedTrait

@main def Test: Unit =

val mWithCompanionSum = summon[Mirror.SumOf[WithCompanionSealedTrait]]
assert(mWithCompanionSum.ordinal(WithCompanionSealedTrait.FirstChild(1)) == 0)
assert(mWithCompanionSum ne WithCompanionSealedTrait) // A case object can not be the mirror of a companion case class.

val mWithCompanionSingleton = summon[Mirror.ProductOf[WithCompanionSealedTrait.type]]
assert(mWithCompanionSingleton.fromProduct(EmptyTuple) == WithCompanionSealedTrait)
assert(mWithCompanionSingleton.isInstanceOf[Mirror.Singleton]) // case object is its own mirror.
assert(mWithCompanionSingleton eq WithCompanionSealedTrait) // case object is its own mirror.
10 changes: 10 additions & 0 deletions tests/run/i15101.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
trait Encoder[T]
object Encoder:
def derived[T](using scala.deriving.Mirror.Of[T]): Encoder[T] = new Encoder[T] {}

case object Bar
enum Bar derives Encoder:
case A, B

@main def Test: Unit =
summon[Encoder[Bar]]