Skip to content

fix #10997: check accessible from sealed parent not companion #11039

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
Jan 13, 2021
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
15 changes: 10 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/SymUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -87,23 +87,28 @@ object SymUtils:

def isGenericProduct(using Context): Boolean = whyNotGenericProduct.isEmpty

def useCompanionAsMirror(using Context): Boolean = self.linkedClass.exists && !self.is(Scala2x)

/** Is this a sealed class or trait for which a sum mirror is generated?
* It must satisfy the following conditions:
* - it has at least one child class or object
* - none of its children are anonymous classes
* - all of its children are addressable through a path from its companion object
* - all of its children are addressable through a path from the parent class
* and also the location of the generated mirror.
* - all of its children are generic products or singletons
*/
def whyNotGenericSum(using Context): String =
def whyNotGenericSum(declScope: Symbol)(using Context): String =
if (!self.is(Sealed))
s"it is not a sealed ${self.kindString}"
else {
val children = self.children
val companion = self.linkedClass
val companionMirror = self.useCompanionAsMirror
assert(!(companionMirror && (declScope ne self.linkedClass)))
def problem(child: Symbol) = {

def isAccessible(sym: Symbol): Boolean =
companion.isContainedIn(sym) || sym.is(Module) && isAccessible(sym.owner)
(self.isContainedIn(sym) && (companionMirror || declScope.isContainedIn(sym)))
|| sym.is(Module) && isAccessible(sym.owner)

if (child == self) "it has anonymous or inaccessible subclasses"
else if (!isAccessible(child.owner)) i"its child $child is not accessible"
Expand All @@ -118,7 +123,7 @@ object SymUtils:
else children.map(problem).find(!_.isEmpty).getOrElse("")
}

def isGenericSum(using Context): Boolean = whyNotGenericSum.isEmpty
def isGenericSum(declScope: Symbol)(using Context): Boolean = whyNotGenericSum(declScope).isEmpty

/** If this is a constructor, its owner: otherwise this. */
final def skipConstructor(using Context): Symbol =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -584,9 +584,9 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
if (clazz.is(Module)) {
if (clazz.is(Case)) makeSingletonMirror()
else if (linked.isGenericProduct) makeProductMirror(linked)
else if (linked.isGenericSum) makeSumMirror(linked)
else if (linked.isGenericSum(clazz)) makeSumMirror(linked)
else if (linked.is(Sealed))
derive.println(i"$linked is not a sum because ${linked.whyNotGenericSum}")
derive.println(i"$linked is not a sum because ${linked.whyNotGenericSum(clazz)}")
}
else if (impl.removeAttachment(ExtendsSingletonMirror).isDefined)
makeSingletonMirror()
Expand Down
9 changes: 5 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Synthesizer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,10 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
end productMirror

private def sumMirror(mirroredType: Type, formal: Type, span: Span)(using Context): Tree =
if mirroredType.classSymbol.isGenericSum then
val cls = mirroredType.classSymbol
val cls = mirroredType.classSymbol
val useCompanion = cls.useCompanionAsMirror

if cls.isGenericSum(if useCompanion then cls.linkedClass else ctx.owner) then
val elemLabels = cls.children.map(c => ConstantType(Constant(c.name.toString)))

def solve(sym: Symbol): Type = sym match
Expand Down Expand Up @@ -318,8 +320,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
.refinedWith(tpnme.MirroredElemTypes, TypeAlias(elemsType))
.refinedWith(tpnme.MirroredElemLabels, TypeAlias(TypeOps.nestedPairs(elemLabels)))
val mirrorRef =
if cls.linkedClass.exists && !cls.is(Scala2x)
then companionPath(mirroredType, span)
if useCompanion then companionPath(mirroredType, span)
else anonymousMirror(monoType, ExtendsSumMirror, span)
mirrorRef.cast(mirrorType)
else EmptyTree
Expand Down
18 changes: 18 additions & 0 deletions tests/neg/i10997.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
sealed trait Parent

trait Wrapper {

case class Foo(x: Int, y: Int, s: String) extends Parent
case class Bar(x: Int, y: Int) extends Parent

println(summon[deriving.Mirror.Of[Parent]]) // error
}

class ClassWrapper {
sealed trait Base
case class Foo(x: Int) extends Base
}

@main def Test =
val cw = new ClassWrapper()
val mirrorParent = summon[deriving.Mirror.Of[cw.Base]] // error: code gen for Mirror can not access each case
24 changes: 24 additions & 0 deletions tests/pos/i10997.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class Test {

sealed trait Parent
case class Foo(x: Int, y: Int, s: String) extends Parent
case class Bar(x: Int, y: Int) extends Parent

println(summon[deriving.Mirror.Of[Parent]])
}

object Test2 {

case class Foo(x: Int, y: Int, s: String) extends i.Parent
case class Bar(x: Int, y: Int) extends i.Parent

val i = Inner()

class Inner {

sealed trait Parent

println(summon[deriving.Mirror.Of[Parent]])
}

}
2 changes: 1 addition & 1 deletion tests/run/deriving-constructor-order.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ object Test extends App {
case _: T => ()
}

sealed trait Base1
sealed trait Base1 // Base1 MUST NOT have a companion here!
case class Foo() extends Base1
case object Bar extends Base1
case class Qux(i: Int) extends Base1
Expand Down
2 changes: 1 addition & 1 deletion tests/run/deriving.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ object T
case class A(x: Int, y: Int) extends T
case object B extends T

sealed trait U
sealed trait U // U MUST NOT have a companion here!
case class C() extends U

object Test extends App {
Expand Down
37 changes: 37 additions & 0 deletions tests/run/i10997.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
class ClassWrapper {

sealed trait Parent
case class Foo(x: Int, y: Int, s: String) extends Parent
case class Bar(x: Int, y: Int) extends Parent
case object Qux extends Parent

def testcase(): Unit =

val mirrorParent = summon[deriving.Mirror.Of[Parent]]
val mirrorFoo = summon[deriving.Mirror.Of[Foo]]
val mirrorBar = summon[deriving.Mirror.Of[Bar]]
val mirrorQux = summon[deriving.Mirror.Of[Qux.type]]

val fooShapedTuple: (Int, Int, String) = (23, 47, "ok")
val barShapedTuple: (Int, Int) = (57, 71)
val quxShapedTuple: EmptyTuple = EmptyTuple

val foo: Foo = mirrorFoo.fromProduct(fooShapedTuple)
val bar: Bar = mirrorBar.fromProduct(barShapedTuple)
val qux: Qux.type = mirrorQux.fromProduct(quxShapedTuple)

assert(foo == Foo(23, 47, "ok"))
assert(bar == Bar(57, 71))
assert(qux == Qux)

val fooOrd = mirrorParent.ordinal(foo)
val barOrd = mirrorParent.ordinal(bar)
val quxOrd = mirrorParent.ordinal(qux)

assert(fooOrd == 0)
assert(barOrd == 1)
assert(quxOrd == 2)
}

@main def Test =
ClassWrapper().testcase()