From 16a1ff1b082a605a7127be1ca77df569c86fe070 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Fri, 15 Sep 2023 15:47:16 +0200 Subject: [PATCH] Fix #18484: Query every legal child of sealed class on children call Sometimes, a macro or Mirror can be called/generated for a type that may have not had its children registered by natural typechecking procedures of the compiler. This meant that, in those cases, the Mirror could not have been generated correctly, and macro could not obtain the children of the class. This was partially fixed in a previous PR by introducing a separate procedure for querying children, outside of the regular typechecking procedure. However, they was only queried in the owner symbol or in the companion class, when non-enum sealed classes specifically, can appear anywhere in the file. This commit aims to rectify this by searching for any class that appears in the same file as the sealed type. --- .../tools/dotc/core/SymDenotations.scala | 22 +++++++++++++------ tests/pos/i18484/Macro_1.scala | 11 ++++++++++ tests/pos/i18484/Test_2.scala | 21 ++++++++++++++++++ 3 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 tests/pos/i18484/Macro_1.scala create mode 100644 tests/pos/i18484/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 3dfa5225df5b..a0c5c5a87923 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1688,7 +1688,7 @@ object SymDenotations { */ def children(using Context): List[Symbol] = - def completeChildrenIn(owner: Symbol)(using Context) = + def completeChildrenIn(owner: Symbol, recursively: Boolean)(using Context): Unit = // Possible children are: classes extending Scala classes and // Scala or Java enum values that are defined in owner. // If owner is a package, we complete only @@ -1698,20 +1698,28 @@ object SymDenotations { && (!owner.is(Package) || sym.originDenotation.infoOrCompleter.match case _: SymbolLoaders.SecondCompleter => sym.associatedFile == this.symbol.associatedFile - case _ => false) + case _: ClassInfo if recursively && sym.isValidInCurrentRun => + sym.is(Package) || sym.associatedFile == this.symbol.associatedFile + case other => false) if owner.isClass then for c <- owner.info.decls.toList if maybeChild(c) do c.ensureCompleted() + if recursively then completeChildrenIn(c, recursively) end completeChildrenIn if is(Sealed) || isAllOf(JavaEnumTrait) then if !is(ChildrenQueried) then - // Make sure all visible children are completed, so that - // they show up in Child annotations. A possible child is visible if it - // is defined in the same scope as `cls` or in the companion object of `cls`. - completeChildrenIn(owner) - completeChildrenIn(companionClass) + if !is(Enum) then // non-enum sealed class + // Make sure all children are completed, so that + // they show up in Child annotations. + completeChildrenIn(defn.RootClass, recursively = true) + else + // Make sure all visible children are completed, so that + // they show up in Child annotations. A possible child is visible if it + // is defined in the same scope as `cls` or in the companion object of `cls`. + completeChildrenIn(owner, recursively = false) + completeChildrenIn(companionClass, recursively = false) setFlag(ChildrenQueried) annotations.collect { case Annotation.Child(child) => child }.reverse diff --git a/tests/pos/i18484/Macro_1.scala b/tests/pos/i18484/Macro_1.scala new file mode 100644 index 000000000000..64164e7b3e4c --- /dev/null +++ b/tests/pos/i18484/Macro_1.scala @@ -0,0 +1,11 @@ +object Macro { + import scala.quoted.* + + def subtypesImpl[A: Type](using quotes: Quotes): Expr[String] = { + import quotes.reflect.* + val a = TypeRepr.of[A].typeSymbol.children + '{""} + } + + inline def subtypes[A]: String = ${ subtypesImpl[A] } +} diff --git a/tests/pos/i18484/Test_2.scala b/tests/pos/i18484/Test_2.scala new file mode 100644 index 000000000000..ed130b5dcba6 --- /dev/null +++ b/tests/pos/i18484/Test_2.scala @@ -0,0 +1,21 @@ +class Test { + def test(): Unit = { + scala.compiletime.testing.typeCheckErrors("Macro.subtypes[top.inner.Inner.Shape]") + } +} + +package top { + package inner { + object Inner: + sealed trait Shape + case class Circle(center: Int, rad: Double) extends Shape + object Inner2: + case class Circle2(center: Int, rad: Double) extends Shape + } + case class Circle3(center: Int, rad: Double) extends inner.Inner.Shape +} +case class Circle4(center: Int, rad: Double) extends top.inner.Inner.Shape + +package top2 { + case class Circle5(center: Int, rad: Double) extends top.inner.Inner.Shape +}