From d1255f33c0aea7a5dd9ed4303606de653c347ab3 Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 28 Mar 2023 12:12:02 +0200 Subject: [PATCH 1/2] Fix needsOuterIfReferenced #17135 shows that traits also need an outer accessor if they are in a toplevel method (or lazy val). Fixes #17135 --- .../tools/dotc/transform/ExplicitOuter.scala | 10 ++-- tests/pos/i17135.scala | 53 +++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 tests/pos/i17135.scala diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index cddfe51275c8..636dcc2772d0 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -199,9 +199,9 @@ object ExplicitOuter { /** Class needs an outer pointer, provided there is a reference to an outer this in it. */ def needsOuterIfReferenced(cls: ClassSymbol)(using Context): Boolean = - !(cls.isStatic || - cls.owner.enclosingClass.isStaticOwner || - cls.is(PureInterface) + !(cls.isStatic + || cls.effectiveOwner.isStaticOwner + || cls.is(PureInterface) ) /** Class unconditionally needs an outer pointer. This is the case if @@ -226,7 +226,9 @@ object ExplicitOuter { /** The outer parameter accessor of cass `cls` */ private def outerParamAccessor(cls: ClassSymbol)(using Context): TermSymbol = - cls.info.decl(nme.OUTER).symbol.asTerm + val outer = cls.info.decl(nme.OUTER).symbol + assert(outer.isTerm, i"missing outer accessor in $cls") + outer.asTerm /** The outer accessor of class `cls`. To find it is a bit tricky. The * class might have been moved with new owners between ExplicitOuter and Erasure, diff --git a/tests/pos/i17135.scala b/tests/pos/i17135.scala new file mode 100644 index 000000000000..7e3f91cf5e83 --- /dev/null +++ b/tests/pos/i17135.scala @@ -0,0 +1,53 @@ +package doobie + +// original example +def someFunction(param: Int): Int = { + sealed trait Foo { + def asString: String = this match { + case Foo.CaseC => "C" + } + } + object Foo { + // Having an object here crashes the compiler. + object CaseC extends Foo + } + + ??? +} + +// minimization +def foo = + class Bar { + // Having an object here crashes the compiler. + lazy val CaseC = + class Baz extends Foo + new Baz() + } + val Bar: Bar = new Bar() + trait Foo { + def asString = Bar.CaseC + } + +// variant: outer is lazy val +lazy val lazyfoo = + class Bar { + // Having an object here crashes the compiler. + lazy val CaseC = + class Baz extends Foo + new Baz() + } + val Bar: Bar = new Bar() + trait Foo { + def asString = Bar.CaseC + } + +// other example +def bar = + sealed trait GADT2[A] extends Product with Serializable + + object GADT2 { + case class IsDir(path: String) extends GADT2[_root_.scala.Boolean] + case class Exists(path: String) extends GADT2[_root_.scala.Boolean] + case class ReadBytes(path: String) extends GADT2[_root_.scala.Array[_root_.scala.Byte]] + case class CopyOver(src: Seq[_root_.scala.Byte], path: String) extends GADT2[Int] + } \ No newline at end of file From 7296e72779b34d5cd17eb91f132339c391bb169f Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 28 Mar 2023 13:31:00 +0200 Subject: [PATCH 2/2] Skip superArg methods when determining whether an outer accessor is needed --- .../src/dotty/tools/dotc/transform/ExplicitOuter.scala | 9 ++++++++- tests/pos/t0049.scala | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 636dcc2772d0..deb1f665c022 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -13,6 +13,7 @@ import core.Decorators._ import core.StdNames.nme import core.Names._ import core.NameOps._ +import core.NameKinds.SuperArgName import SymUtils._ import dotty.tools.dotc.ast.tpd @@ -197,10 +198,16 @@ object ExplicitOuter { private def outerAccName(cls: ClassSymbol)(using Context): TermName = nme.OUTER.expandedName(cls) + private def outerOwner(sym: Symbol)(using Context): Symbol = + val owner = sym.effectiveOwner + if owner.name.is(SuperArgName) || owner.isLocalDummy + then owner.enclosingClass + else owner + /** Class needs an outer pointer, provided there is a reference to an outer this in it. */ def needsOuterIfReferenced(cls: ClassSymbol)(using Context): Boolean = !(cls.isStatic - || cls.effectiveOwner.isStaticOwner + || outerOwner(cls).isStaticOwner || cls.is(PureInterface) ) diff --git a/tests/pos/t0049.scala b/tests/pos/t0049.scala index dd866422637f..a453cec9568e 100644 --- a/tests/pos/t0049.scala +++ b/tests/pos/t0049.scala @@ -1,3 +1,6 @@ class C1(x: AnyRef) {}; class C2 extends C1({ class A extends AnyRef {}; (new A) : AnyRef }) {}; + +class Outer: + class C2 extends C1({ class A extends AnyRef {}; (new A) : AnyRef }) {};