diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index fcabd7184550..15276aa68218 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -842,10 +842,7 @@ class TreeUnpickler(reader: TastyReader, if (!sym.isType) { // Only terms might have leaky aliases, see the documentation of `checkNoPrivateLeaks` sym.info = ta.avoidPrivateLeaks(sym, tree.pos) } - if ((sym.isClass || sym.is(CaseVal)) && sym.isLocal) - // Child annotations for local classes and enum values are not pickled, so - // need to be re-established here. - sym.registerIfChild(late = true) + sym.defTree = tree if (ctx.mode.is(Mode.ReadComments)) { diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 540b372c3197..14f51d1c153b 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -8,6 +8,7 @@ import Symbols._ import SymDenotations._ import Names._ import NameOps._ +import StdNames._ import NameKinds._ import Flags._ import Annotations._ @@ -132,16 +133,17 @@ class SymUtils(val self: Symbol) extends AnyVal { /** If this symbol is an enum value or a named class, register it as a child * in all direct parent classes which are sealed. - * @param @late If true, register only inaccessible children (all others are already - * entered at this point). */ - def registerIfChild(late: Boolean = false)(implicit ctx: Context): Unit = { + def registerIfChild()(implicit ctx: Context): Unit = { def register(child: Symbol, parent: Type) = { val cls = parent.classSymbol - if (cls.is(Sealed) && (!late || child.isInaccessibleChildOf(cls))) - cls.addAnnotation(Annotation.Child(child)) + if (cls.is(Sealed)) { + if ((child.isInaccessibleChildOf(cls) || child.isAnonymousClass) && !self.hasAnonymousChild) + cls.addAnnotation(Annotation.Child(cls)) + else cls.addAnnotation(Annotation.Child(child)) + } } - if (self.isClass && !self.isAnonymousClass) + if (self.isClass && !self.isEnumAnonymClass) self.asClass.classParents.foreach { parent => val child = if (self.is(Module)) self.sourceModule else self register(child, parent) @@ -150,6 +152,10 @@ class SymUtils(val self: Symbol) extends AnyVal { register(self, self.info) } + /** Does this symbol refer to anonymous classes synthesized by enum desugaring? */ + def isEnumAnonymClass(implicit ctx: Context): Boolean = + self.isAnonymousClass && (self.owner.name.eq(nme.DOLLAR_NEW) || self.owner.is(CaseVal)) + /** Is this symbol defined locally (i.e. at some level owned by a term) and * defined in a different toplevel class than its supposed parent class `cls`? * Such children are not pickled, and have to be reconstituted manually. @@ -163,6 +169,9 @@ class SymUtils(val self: Symbol) extends AnyVal { case Annotation.Child(child) => child } + def hasAnonymousChild(implicit ctx: Context): Boolean = + children.exists(_ `eq` self) + /** Is symbol directly or indirectly owned by a term symbol? */ @tailrec final def isLocal(implicit ctx: Context): Boolean = { val owner = self.owner diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 8077b3993e0a..1b6834ed831f 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -730,6 +730,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { val res = (tp.classSymbol.is(Sealed) && tp.classSymbol.is(AbstractOrTrait) && + !tp.classSymbol.hasAnonymousChild && tp.classSymbol.children.nonEmpty ) || dealiasedTp.isInstanceOf[OrType] || (dealiasedTp.isInstanceOf[AndType] && { @@ -849,6 +850,8 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { if (mergeList) "_: _*" else "_: List" else if (scalaConsType.isRef(sym)) if (mergeList) "_, _: _*" else "List(_, _: _*)" + else if (tp.classSymbol.is(Sealed) && tp.classSymbol.hasAnonymousChild) + "_: " + showType(tp) + " (anonymous)" else if (tp.classSymbol.is(CaseClass) && !hasCustomUnapply(tp.classSymbol)) // use constructor syntax for case class showType(tp) + params(tp).map(_ => "_").mkString("(", ", ", ")") diff --git a/tests/patmat/anonym.check b/tests/patmat/anonym.check new file mode 100644 index 000000000000..a10177798f93 --- /dev/null +++ b/tests/patmat/anonym.check @@ -0,0 +1 @@ +10: Pattern Match Exhaustivity: _: Base (anonymous) diff --git a/tests/patmat/anonym.scala b/tests/patmat/anonym.scala new file mode 100644 index 000000000000..d8c5b8cc447c --- /dev/null +++ b/tests/patmat/anonym.scala @@ -0,0 +1,21 @@ +// spurious dead-code +class Foo { + def bar(x: Either[String, Base]): Unit = { + x match { + case Left(s) => println("l") + case Right(Base(s)) => println("r") + } + } + + def bar(x: Base): Unit = x match { + case Zero => + } + + def anonymous: Base = new Base("bla") {} +} + +sealed abstract case class Base(value: String) { + override def toString = value // ok? +} + +object Zero extends Base("zero") diff --git a/tests/patmat/t9677.check b/tests/patmat/t9677.check new file mode 100644 index 000000000000..6d4d889944a3 --- /dev/null +++ b/tests/patmat/t9677.check @@ -0,0 +1 @@ +18: Pattern Match Exhaustivity: _: Base (anonymous) \ No newline at end of file diff --git a/tests/patmat/t9677.scala b/tests/patmat/t9677.scala index 1e9b1df5e891..decd1737da32 100644 --- a/tests/patmat/t9677.scala +++ b/tests/patmat/t9677.scala @@ -15,7 +15,7 @@ object ExhaustiveMatchWarning { def test: Unit = { val b: Base = A("blabla") - b match { + b match { // A.apply creates anonymous class <: Base case A.Root => println("Root") case path: A => println("Not root") }