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

space: Fix how sealed abstract classes decompose #15553

Merged
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
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ class SpaceEngine(using Context) extends SpaceLogic {
case tp =>
def getChildren(sym: Symbol): List[Symbol] =
sym.children.flatMap { child =>
if child eq sym then Nil // i3145: sealed trait Baz, val x = new Baz {}, Baz.children returns Baz...
if child eq sym then List(sym) // i3145: sealed trait Baz, val x = new Baz {}, Baz.children returns Baz...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sealed trait Baz, val x = new Baz {}, Baz.children returns Baz

Which exactly Baz in Baz.children are we talking about here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a joke question 😄 . Describing a full, legal, source file:

sealed trait Baz
object Baz:
  val inst = new Baz {}

Then there are 3 classes: Baz, the interface, Baz$, the module class, and Baz$$anon$1, the anonymous subclass of Baz:

$ javap Baz.class
Compiled from "Test.scala"
public interface Baz {
  public static Baz inst();
}
$ javap Baz\$.class
Compiled from "Test.scala"
public final class Baz$ implements java.io.Serializable {
  public static final Baz$ MODULE$;
  public static {};
  public Baz inst();
}
$ javap Baz\$\$anon\$1.class
Compiled from "Test.scala"
public final class Baz$$anon$1 implements Baz {
  public Baz$$anon$1();
}

So when you look up the sealed children of Baz, then Baz itself is returned, as a representative of the anonymous subclass.

else if tp.classSymbol == defn.TupleClass || tp.classSymbol == defn.NonEmptyTupleClass then
List(child) // TupleN and TupleXXL classes are used for Tuple, but they aren't Tuple's children
else if (child.is(Private) || child.is(Sealed)) && child.isOneOf(AbstractOrTrait) then getChildren(child)
Expand Down
13 changes: 13 additions & 0 deletions tests/pos/i15522.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// scalac: -Werror
sealed trait Coverage
sealed abstract case class Range(min: Double, max: Double) extends Coverage
case object Empty extends Coverage

object Test:
def mkCoverage(min: Double, max: Double): Coverage =
if min < max then new Range(min, max) {} else Empty

def meth(c1: Coverage, c2: Coverage): Coverage = (c1, c2) match
case (Empty, _) => Empty
case (_, Empty) => Empty // was: Unreachable case
case (Range(a1, b1), Range(a2, b2)) => mkCoverage(a1 max a2, b1 min b2)