Skip to content

Commit 6793945

Browse files
committed
Add clause for protected visibility from package objects
We usually have an access rule that the access to a protected member `foo` in class `C` must be from somewhere nested in a subclass of `C`. But that fails if the member is accessed from a package object `p.package`. In that case, the access does not need to be in the same object, it just has to be in package `p`. This clause was previously missing and is now added. Why was this only recently discovered? #18057 fixed an issue where toplevel protected members were always accessible because explicit package object prefixes were added after the accessibility check was done, and would re-establish the previous members without doing an accessibility check. The fix was done by adding package objects first, then doing he rest of the checks. But that also means that protected toplevel objects now get checked as members of their synthetic package object instead of as members of their package. The change here also makes specs2 compile again.
1 parent 0a21ecf commit 6793945

File tree

4 files changed

+31
-9
lines changed

4 files changed

+31
-9
lines changed

community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,7 @@ class CommunityBuildTestC:
9393
@Test def sconfig = projects.sconfig.run()
9494
@Test def shapeless = projects.shapeless.run()
9595
@Test def sourcecode = projects.sourcecode.run()
96-
97-
// Disabled. Currently fails in FutureMatchers.scala. The call to
98-
// `checkResultFailure` goes to a protected method which is not accessible.
99-
// I tried to fix it, but get test failures.
100-
// @Test def specs2 = projects.specs2.run()
96+
@Test def specs2 = projects.specs2.run()
10197

10298
@Test def stdLib213 = projects.stdLib213.run()
10399
@Test def ujson = projects.ujson.run()

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -907,10 +907,13 @@ object SymDenotations {
907907
false
908908
val cls = owner.enclosingSubClass
909909
if !cls.exists then
910-
val encl = if ctx.owner.isConstructor then ctx.owner.enclosingClass.owner.enclosingClass else ctx.owner.enclosingClass
911-
fail(i"""
912-
| Access to protected $this not permitted because enclosing ${encl.showLocated}
913-
| is not a subclass of ${owner.showLocated} where target is defined""")
910+
if pre.termSymbol.isPackageObject && accessWithin(pre.termSymbol.owner) then
911+
true
912+
else
913+
val encl = if ctx.owner.isConstructor then ctx.owner.enclosingClass.owner.enclosingClass else ctx.owner.enclosingClass
914+
fail(i"""
915+
| Access to protected $this not permitted because enclosing ${encl.showLocated}
916+
| is not a subclass of ${owner.showLocated} where target is defined""")
914917
else if isType || pre.derivesFrom(cls) || isConstructor || owner.is(ModuleClass) then
915918
// allow accesses to types from arbitrary subclasses fixes #4737
916919
// don't perform this check for static members

tests/pos/i18124/definition.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// definition.scala
2+
package oolong.bson:
3+
4+
trait BsonValue
5+
protected def merge(
6+
base: BsonValue,
7+
patch: BsonValue,
8+
arraySubvalues: Boolean = false
9+
): BsonValue = ???
10+
11+
private def foo: Int = 1
12+
13+
package inner:
14+
protected[bson] def bar = 2
15+

tests/pos/i18124/usage.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// usage.scala
2+
package oolong.bson
3+
4+
extension (bv: BsonValue)
5+
def :+(other: BsonValue): BsonValue = merge(other, bv, false)
6+
7+
val x = foo
8+
val y = inner.bar

0 commit comments

Comments
 (0)