From 7209eb7317121cdbb82cc4f652786f06635fe07a Mon Sep 17 00:00:00 2001 From: Jeon Yoonjae Date: Tue, 7 Oct 2025 18:27:17 +0900 Subject: [PATCH] Make opaque types decomposable (#24068) fixes: https://github.com/scala/scala3/issues/22513 Currently, if the upper bounds of opaque types are sealed, they pass exhaustivityCheckable, but exhaustivity checks are not handled correctly because the compiler preventing from decomposing them. The guard was introduced in [#19368](https://github.com/scala/scala3/pull/19368), but the tests still pass even without this guard. [Cherry-picked f4e62391d181b9057857b45a29fc260481f213fd] --- .../tools/dotc/transform/patmat/Space.scala | 2 -- tests/pos/i22513.scala | 27 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i22513.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 90b30a602b16..21c58374a342 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -695,14 +695,12 @@ object SpaceEngine { extension (tp: Type) def isDecomposableToChildren(using Context): Boolean = - val sym = tp.typeSymbol // e.g. Foo[List[Int]] = type Foo (i19275) val cls = tp.classSymbol // e.g. Foo[List[Int]] = class List tp.hasSimpleKind // can't decompose higher-kinded types && cls.is(Sealed) && cls.isOneOf(AbstractOrTrait) // ignore sealed non-abstract classes && !cls.hasAnonymousChild // can't name anonymous classes as counter-examples && cls.children.nonEmpty // can't decompose without children - && !sym.isOpaqueAlias // can't instantiate subclasses to conform to an opaque type (i19275) val ListOfNoType = List(NoType) val ListOfTypNoType = ListOfNoType.map(Typ(_, decomposed = true)) diff --git a/tests/pos/i22513.scala b/tests/pos/i22513.scala new file mode 100644 index 000000000000..2cf2bc58227a --- /dev/null +++ b/tests/pos/i22513.scala @@ -0,0 +1,27 @@ +opaque type R[T] <: T = T + +object Test { + enum E: + case A(a: Int) + + val v: R[E] = ??? + v match + case E.A(_) => +} + +sealed trait Foo + +case class FooA() extends Foo +case class FooB() extends Foo + +object O { + opaque type OpaqueFoo <: Foo = Foo + def fooB(): OpaqueFoo = FooB() +} + +@main def main = + val p: O.OpaqueFoo = O.fooB() + + p match + case _: FooA => println("fooA") + case _: FooB => println("fooB")