From 673418ded409c403aa9e04abfe0625b78e80de61 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 21 Aug 2024 16:06:44 +0100 Subject: [PATCH] Fix enclosingClass from returning refinement classes Looking at how i20952 is compiled, the call ctx.owner.enclosingClass.derivesFrom(sym.owner) in SuperAccessors (that I moved to ProtectedAccessors) accidentally fails, because the refinement class returned as the enclosing class of `val prog` doesn't derive from SuperClass. But if the enclosing class of `prog` returned is `trait Child` then no super accessor call is necessary. [Cherry-picked 15922f93c59dc0ae60ae1b26362962b0b5c04921] --- .../tools/dotc/core/SymDenotations.scala | 6 +++--- .../dotc/transform/ProtectedAccessors.scala | 5 ++++- .../tools/dotc/transform/SuperAccessors.scala | 2 +- tests/pos/i20952.scala | 20 +++++++++++++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 tests/pos/i20952.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 3ea166bbd34d..614807164280 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1154,10 +1154,10 @@ object SymDenotations { final def enclosingClass(using Context): Symbol = { def enclClass(sym: Symbol, skip: Boolean): Symbol = { def newSkip = sym.is(JavaStaticTerm) - if (!sym.exists) + if !sym.exists then NoSymbol - else if (sym.isClass) - if (skip) enclClass(sym.owner, newSkip) else sym + else if sym.isClass then + if skip || sym.isRefinementClass then enclClass(sym.owner, newSkip) else sym else enclClass(sym.owner, skip || newSkip) } diff --git a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala index b6df581beee2..a779f39800e4 100644 --- a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala @@ -44,7 +44,10 @@ object ProtectedAccessors { /** Do we need a protected accessor for accessing sym from the current context's owner? */ def needsAccessor(sym: Symbol)(using Context): Boolean = needsAccessorIfNotInSubclass(sym) && - !ctx.owner.enclosingClass.derivesFrom(sym.owner) + !needsAccessorIsSubclass(sym) + + def needsAccessorIsSubclass(sym: Symbol)(using Context): Boolean = + ctx.owner.enclosingClass.derivesFrom(sym.owner) } class ProtectedAccessors extends MiniPhase { diff --git a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala index ce2b8fa591d8..5a63235fc3c0 100644 --- a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -195,7 +195,7 @@ class SuperAccessors(thisPhase: DenotTransformer) { * Otherwise, we need to go through an accessor, * which the implementing class will provide an implementation for. */ - if ctx.owner.enclosingClass.derivesFrom(sym.owner) then + if ProtectedAccessors.needsAccessorIsSubclass(sym) then if sym.is(JavaDefined) then report.error(em"${ctx.owner} accesses protected $sym inside a concrete trait method: use super.${sel.name} instead", sel.srcPos) sel diff --git a/tests/pos/i20952.scala b/tests/pos/i20952.scala new file mode 100644 index 000000000000..68344fdab672 --- /dev/null +++ b/tests/pos/i20952.scala @@ -0,0 +1,20 @@ +package object packer: // the super class needs to be in a different package + class SuperClass(): + protected val problem: Any = ??? // needs to be protected + +class SuperClass(): + protected val problem: Any = ??? // needs to be protected + +// type Target = SuperClass // passes +type Target = packer.SuperClass // error + +trait Child extends Target: + + val aliased: problem.type = problem + type Alias = problem.type + + val newProblem: Any {val prog: problem.type} = ??? // error + val newProblem2: Any {val prog: Alias} = ??? // passes + val newProblem3: Any {val prog: aliased.type} = ??? // passes + +class ChildImpl extends Target with Child // concrete implementation is needed