diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 91edf0feea79..8b13633e6125 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -217,11 +217,13 @@ object ExplicitOuter { cls.info.parents.exists(parent => // needs outer to potentially pass along to parent needsOuterIfReferenced(parent.classSymbol.asClass))) - /** Class is always instantiated in the compilation unit where it is defined */ + /** Class is only instantiated in the compilation unit where it is defined */ private def hasLocalInstantiation(cls: ClassSymbol)(using Context): Boolean = // Modules are normally locally instantiated, except if they are declared in a trait, // in which case they will be instantiated in the classes that mix in the trait. - cls.owner.isTerm || cls.is(Private, butNot = Module) || (cls.is(Module) && !cls.owner.is(Trait)) + cls.owner.ownersIterator.takeWhile(!_.isStatic).exists(_.isTerm) + || cls.is(Private, butNot = Module) + || cls.is(Module) && !cls.owner.is(Trait) /** The outer parameter accessor of cass `cls` */ private def outerParamAccessor(cls: ClassSymbol)(using Context): TermSymbol = diff --git a/tests/run/i11367.check b/tests/run/i11367.check new file mode 100644 index 000000000000..f7b8bc31acc6 --- /dev/null +++ b/tests/run/i11367.check @@ -0,0 +1 @@ +class C$G has outer fields diff --git a/tests/run/i11367.scala b/tests/run/i11367.scala new file mode 100644 index 000000000000..e88a9020dec4 --- /dev/null +++ b/tests/run/i11367.scala @@ -0,0 +1,18 @@ +trait NoOuter: + val outerFields = getClass.getDeclaredFields.filter(_.getName.contains("$outer")) + if outerFields.nonEmpty then println(s"$getClass has outer fields") + +class C extends NoOuter: + def foo = + class D extends NoOuter: + class E extends NoOuter + class F extends NoOuter + val d = D() + d.E() + F() + class G extends NoOuter + +@main def Test = + val c = C() + c.foo + c.G()