From e32ee707b95862df42b441802833fbd7384396b6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 30 May 2020 20:22:07 +0200 Subject: [PATCH 1/4] Fix #9069: Complete children of sealed classes before querying Complete all possible children of sealed classes or traits before first querying with `children`. This way we make they register themselves with Child annotations in the parent class. --- .../tools/dotc/core/SymDenotations.scala | 26 +++++++++++++++++++ .../ReflectionCompilerInterface.scala | 2 +- .../dotty/tools/dotc/transform/SymUtils.scala | 15 +---------- tests/pos/i9069/Test.scala | 3 +++ tests/pos/i9069/Wrapper.scala | 4 +++ 5 files changed, 35 insertions(+), 15 deletions(-) create mode 100644 tests/pos/i9069/Test.scala create mode 100644 tests/pos/i9069/Wrapper.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index b3f952e292a2..80f0a856e75b 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1578,6 +1578,32 @@ object SymDenotations { if (is(Private)) copySymDenotation(name = expandedName, initFlags = this.flags &~ Private) else this + + /** If this is a sealed class, its known children in the order of textual occurrence + */ + def children(using Context): List[Symbol] = + + def completeChildrenIn(owner: Symbol) = + def maybeChild(c: Symbol) = + !owner.is(Package) + || c.infoOrCompleter.match + case _: SymbolLoaders.SecondCompleter => c.associatedFile == symbol.associatedFile + case _ => false + if owner.isClass then + for c <- owner.info.decls.toList if c.isClass && maybeChild(c) do + c.ensureCompleted() + + if is(Sealed) then + if !is(ChildrenQueried) && !ctx.isAfterTyper then + // During typer, make sure all visible children are completed, so that + // they show up in Child annotations. A class is visible if it is defined + // in the same scope as `cls` or in the companion object of `cls`. + completeChildrenIn(owner) + completeChildrenIn(companionClass) + setFlag(ChildrenQueried) + + annotations.collect { case Annotation.Child(child) => child }.reverse + end children } /** The contents of a class definition during a period diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala index 44213d2c3cdc..c1d6b5500bf9 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala @@ -1780,7 +1780,7 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend } def Symbol_children(self: Symbol)(using ctx: Context): List[Symbol] = - dotty.tools.dotc.transform.SymUtils(self).children + self.children private def isField(sym: Symbol)(using ctx: Context): Boolean = sym.isTerm && !sym.is(Flags.Method) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index ff7890f62a4c..cfeb1d68af8b 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -188,21 +188,8 @@ class SymUtils(val self: Symbol) extends AnyVal { || sym.isType && isAccessible(sym.owner, cls) !isAccessible(self.owner, cls) - /** If this is a sealed class, its known children in the order of textual occurrence */ - def children(implicit ctx: Context): List[Symbol] = { - if (self.isType) - self.setFlag(ChildrenQueried) - - if (self.isAllOf(JavaEnumTrait)) - self.linkedClass.info.decls.foreach(_.ensureCompleted()) - - self.annotations.collect { - case Annotation.Child(child) => child - }.reverse - } - def hasAnonymousChild(implicit ctx: Context): Boolean = - children.exists(_ `eq` self) + self.children.exists(_ `eq` self) /** Is symbol directly or indirectly owned by a term symbol? */ @tailrec final def isLocal(implicit ctx: Context): Boolean = { diff --git a/tests/pos/i9069/Test.scala b/tests/pos/i9069/Test.scala new file mode 100644 index 000000000000..99815c06c6cd --- /dev/null +++ b/tests/pos/i9069/Test.scala @@ -0,0 +1,3 @@ +def Test = + val mirror = summon[deriving.Mirror.SumOf[Foo]] + summon[mirror.MirroredElemTypes =:= (Foo.Baz.type, Bar)] diff --git a/tests/pos/i9069/Wrapper.scala b/tests/pos/i9069/Wrapper.scala new file mode 100644 index 000000000000..1c3952b683e1 --- /dev/null +++ b/tests/pos/i9069/Wrapper.scala @@ -0,0 +1,4 @@ +sealed trait Foo +object Foo: + case object Baz extends Foo +case class Bar(x: Int) extends Foo From 4c3ecb56daa8cd4c83358eafd54cdcb229c598a0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 31 May 2020 11:56:05 +0200 Subject: [PATCH 2/4] Refine criterion when to complete children --- .../src/dotty/tools/dotc/core/Flags.scala | 4 +-- .../tools/dotc/core/SymDenotations.scala | 35 ++++++++++++------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 0b2aa4133983..0b8eb22034b0 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -339,7 +339,7 @@ object Flags { val (_, DefaultMethod @ _, _) = newFlags(38, "") /** Symbol is an enum class or enum case (if used with case) */ - val (Enum @ _, _, _) = newFlags(40, "enum") + val (Enum @ _, EnumVal @ _, _) = newFlags(40, "enum") /** An export forwarder */ val (Exported @ _, _, _) = newFlags(41, "exported") @@ -530,7 +530,7 @@ object Flags { val DeferredOrLazyOrMethod: FlagSet = Deferred | Lazy | Method val DeferredOrTermParamOrAccessor: FlagSet = Deferred | ParamAccessor | TermParam // term symbols without right-hand sides val DeferredOrTypeParam: FlagSet = Deferred | TypeParam // type symbols without right-hand sides - val EnumValue: FlagSet = Enum | StableRealizable // A Scala enum value + val EnumValue: FlagSet = Enum | StableRealizable // A Scala enum value val StableOrErased: FlagSet = Erased | StableRealizable // Assumed to be pure val ExtensionMethod: FlagSet = Extension | Method val FinalOrInline: FlagSet = Final | Inline diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 80f0a856e75b..6954bed3c537 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1583,23 +1583,32 @@ object SymDenotations { */ def children(using Context): List[Symbol] = - def completeChildrenIn(owner: Symbol) = - def maybeChild(c: Symbol) = - !owner.is(Package) - || c.infoOrCompleter.match - case _: SymbolLoaders.SecondCompleter => c.associatedFile == symbol.associatedFile - case _ => false + def completeChildrenIn(owner: Symbol)(using Context) = + // 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 + // children that are defined in the same file as their parents. + def maybeChild(sym: Symbol) = + (sym.isClass && !this.is(JavaDefined) || sym.is(EnumVal)) + && !owner.is(Package) + || sym.infoOrCompleter.match + case _: SymbolLoaders.SecondCompleter => sym.associatedFile == this.symbol.associatedFile + case _ => false + if owner.isClass then - for c <- owner.info.decls.toList if c.isClass && maybeChild(c) do + for c <- owner.info.decls.toList if maybeChild(c) do c.ensureCompleted() + end completeChildrenIn if is(Sealed) then - if !is(ChildrenQueried) && !ctx.isAfterTyper then - // During typer, make sure all visible children are completed, so that - // they show up in Child annotations. A class 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(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`. + inContext(ctx.withPhase(ctx.typerPhase)) { + completeChildrenIn(owner) + completeChildrenIn(companionClass) + } setFlag(ChildrenQueried) annotations.collect { case Annotation.Child(child) => child }.reverse From f4b9b69ae39209aeb4c874deda4f8bd94f0b3f53 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 31 May 2020 12:01:32 +0200 Subject: [PATCH 3/4] Tweak maybeChild test Use originDenotation of children instead of going back to typerPhase --- .../src/dotty/tools/dotc/core/SymDenotations.scala | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 6954bed3c537..01a0437c9ad3 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1589,9 +1589,9 @@ object SymDenotations { // If owner is a package, we complete only // children that are defined in the same file as their parents. def maybeChild(sym: Symbol) = - (sym.isClass && !this.is(JavaDefined) || sym.is(EnumVal)) + (sym.isClass && !this.is(JavaDefined) || sym.originDenotation.is(EnumVal)) && !owner.is(Package) - || sym.infoOrCompleter.match + || sym.originDenotation.infoOrCompleter.match case _: SymbolLoaders.SecondCompleter => sym.associatedFile == this.symbol.associatedFile case _ => false @@ -1605,10 +1605,8 @@ object SymDenotations { // 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`. - inContext(ctx.withPhase(ctx.typerPhase)) { - completeChildrenIn(owner) - completeChildrenIn(companionClass) - } + completeChildrenIn(owner) + completeChildrenIn(companionClass) setFlag(ChildrenQueried) annotations.collect { case Annotation.Child(child) => child }.reverse From e8b25e5ae36bb9926526a82a21a46b0a32f21356 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 1 Jun 2020 12:47:29 +0200 Subject: [PATCH 4/4] Fix test This gave a cyclic error before. Looks like an interaction between child completion and match types. --- tests/run-staging/i6281.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/run-staging/i6281.scala b/tests/run-staging/i6281.scala index 26c0bc4c0318..e390a09aa9e7 100644 --- a/tests/run-staging/i6281.scala +++ b/tests/run-staging/i6281.scala @@ -1,11 +1,11 @@ import scala.quoted._ import scala.quoted.staging._ -object Test extends App { +sealed trait HList +sealed trait HNil extends HList +sealed trait ::[E, T <: HList] extends HList - sealed trait HList - sealed trait HNil extends HList - sealed trait ::[E, T <: HList] extends HList +object Test extends App { type STM[A, L <: HList] = L match { case HNil => Expr[A]