From 63b75983b621d554b7843cbf894f266a8d86ea7a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 14 Feb 2021 13:13:40 +0100 Subject: [PATCH 1/3] Don't generate outer accessors for classes transitively nested in terms Fixes #11367 --- .../src/dotty/tools/dotc/transform/ExplicitOuter.scala | 6 ++++-- tests/pos/i11367.scala | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i11367.scala diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 91edf0feea79..ae7a501dce09 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.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/pos/i11367.scala b/tests/pos/i11367.scala new file mode 100644 index 000000000000..1382ec85bb84 --- /dev/null +++ b/tests/pos/i11367.scala @@ -0,0 +1,8 @@ +class C { + def foo = { + class D { + class E + } + class F + } +} \ No newline at end of file From 1c590f7bc043ee76c9c2ead68625538612e8ce94 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 14 Feb 2021 13:54:13 +0100 Subject: [PATCH 2/3] Start search at owner --- compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index ae7a501dce09..8b13633e6125 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -221,7 +221,7 @@ object ExplicitOuter { 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.ownersIterator.takeWhile(!_.isStatic).exists(_.isTerm) + cls.owner.ownersIterator.takeWhile(!_.isStatic).exists(_.isTerm) || cls.is(Private, butNot = Module) || cls.is(Module) && !cls.owner.is(Trait) From 1c302e626fc2622a66c53aae1b0a942fd4bce3cb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 15 Feb 2021 11:50:41 +0100 Subject: [PATCH 3/3] Test absence of outer field in a run test --- tests/pos/i11367.scala | 8 -------- tests/run/i11367.check | 1 + tests/run/i11367.scala | 18 ++++++++++++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) delete mode 100644 tests/pos/i11367.scala create mode 100644 tests/run/i11367.check create mode 100644 tests/run/i11367.scala diff --git a/tests/pos/i11367.scala b/tests/pos/i11367.scala deleted file mode 100644 index 1382ec85bb84..000000000000 --- a/tests/pos/i11367.scala +++ /dev/null @@ -1,8 +0,0 @@ -class C { - def foo = { - class D { - class E - } - class F - } -} \ No newline at end of file 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()