Skip to content

Commit b2b3201

Browse files
authored
Merge pull request #6058 from hrhino/bugfix/t6934
Classes can access `protected static` members of their Java superclasses
2 parents 0dd574e + 01c3bbb commit b2b3201

File tree

10 files changed

+54
-19
lines changed

10 files changed

+54
-19
lines changed

src/compiler/scala/tools/nsc/typechecker/Contexts.scala

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* NSC -- new Scala compiler
2-
* Copyright 2005-2013 LAMP/EPFL
2+
* Copyright 2005-2017 LAMP/EPFL
33
* @author Martin Odersky
44
*/
55

@@ -647,10 +647,16 @@ trait Contexts { self: Analyzer =>
647647
// Accessibility checking
648648
//
649649

650-
/** Is `sub` a subclass of `base` or a companion object of such a subclass? */
650+
/** True iff...
651+
* - `sub` is a subclass of `base`
652+
* - `sub` is the module class of a companion of a subclass of `base`
653+
* - `base` is a Java-defined module class (containing static members),
654+
* and `sub` is a subclass of its companion class. (see scala/bug#6394)
655+
*/
651656
private def isSubClassOrCompanion(sub: Symbol, base: Symbol) =
652657
sub.isNonBottomSubClass(base) ||
653-
sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base)
658+
(sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base)) ||
659+
(base.isJavaDefined && base.isModuleClass && sub.isNonBottomSubClass(base.linkedClassOfClass))
654660

655661
/** Return the closest enclosing context that defines a subclass of `clazz`
656662
* or a companion object thereof, or `NoContext` if no such context exists.
@@ -702,10 +708,10 @@ trait Contexts { self: Analyzer =>
702708
val c = enclosingSubClassContext(sym.owner)
703709
if (c == NoContext)
704710
lastAccessCheckDetails =
705-
"\n Access to protected "+target+" not permitted because"+
706-
"\n "+"enclosing "+this.enclClass.owner+
707-
this.enclClass.owner.locationString+" is not a subclass of "+
708-
"\n "+sym.owner+sym.owner.locationString+" where target is defined"
711+
sm"""
712+
| Access to protected $target not permitted because
713+
| enclosing ${this.enclClass.owner}${this.enclClass.owner.locationString} is not a subclass of
714+
| ${sym.owner}${sym.owner.locationString} where target is defined"""
709715
c != NoContext &&
710716
{
711717
target.isType || { // allow accesses to types from arbitrary subclasses fixes #4737
@@ -715,9 +721,10 @@ trait Contexts { self: Analyzer =>
715721
isSubClassOrCompanion(pre.widen.typeSymbol, c.owner.linkedClassOfClass)
716722
if (!res)
717723
lastAccessCheckDetails =
718-
"\n Access to protected "+target+" not permitted because"+
719-
"\n prefix type "+pre.widen+" does not conform to"+
720-
"\n "+c.owner+c.owner.locationString+" where the access take place"
724+
sm"""
725+
| Access to protected $target not permitted because
726+
| prefix type ${pre.widen} does not conform to
727+
| ${c.owner}${c.owner.locationString} where the access takes place"""
721728
res
722729
}
723730
}

src/reflect/scala/reflect/internal/Symbols.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,13 +1398,11 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
13981398
newNonClassSymbol(name, pos, newFlags)
13991399

14001400
/**
1401-
* The class or term up to which this symbol is accessible, or RootClass if it is public. As
1402-
* Java protected statics are otherwise completely inaccessible in Scala, they are treated as
1403-
* public (scala/bug#1806).
1401+
* The class or term up to which this symbol is accessible, or else
1402+
* `enclosingRootClass` if it is public.
14041403
*/
14051404
def accessBoundary(base: Symbol): Symbol = {
14061405
if (hasFlag(PRIVATE) || isLocalToBlock) owner
1407-
else if (hasAllFlags(PROTECTED | STATIC | JAVA)) enclosingRootClass
14081406
else if (hasAccessBoundary && !phase.erasedTypes) privateWithin // Phase check needed? See comment in Context.isAccessible.
14091407
else if (hasFlag(PROTECTED)) base
14101408
else enclosingRootClass

test/files/neg/t3871b.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ t3871b.scala:61: error: not found: value protOT
44
t3871b.scala:77: error: method prot in class A cannot be accessed in E.this.A
55
Access to protected method prot not permitted because
66
prefix type E.this.A does not conform to
7-
class B in class E where the access take place
7+
class B in class E where the access takes place
88
a.prot // not allowed, prefix type `A` does not conform to `B`
99
^
1010
t3871b.scala:79: error: value protT is not a member of E.this.B
@@ -19,7 +19,7 @@ t3871b.scala:81: error: value protT is not a member of E.this.A
1919
t3871b.scala:91: error: method prot in class A cannot be accessed in E.this.A
2020
Access to protected method prot not permitted because
2121
prefix type E.this.A does not conform to
22-
object B in class E where the access take place
22+
object B in class E where the access takes place
2323
a.prot // not allowed
2424
^
2525
t3871b.scala:93: error: value protT is not a member of E.this.B

test/files/neg/t3934.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ t3934.scala:15: error: method f2 in class J cannot be accessed in test.J
77
t3934.scala:20: error: method f2 in class J cannot be accessed in test.J
88
Access to protected method f2 not permitted because
99
prefix type test.J does not conform to
10-
class S2 in package nest where the access take place
10+
class S2 in package nest where the access takes place
1111
def g2(x: J) = x.f2()
1212
^
1313
two errors found

test/files/neg/t4541.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
t4541.scala:11: error: variable data in class Sparse cannot be accessed in Sparse[Int]
22
Access to protected variable data not permitted because
33
prefix type Sparse[Int] does not conform to
4-
class Sparse$mcI$sp where the access take place
4+
class Sparse$mcI$sp where the access takes place
55
that.data
66
^
77
one error found

test/files/neg/t4541b.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
t4541b.scala:13: error: variable data in class SparseArray cannot be accessed in SparseArray[Int]
22
Access to protected variable data not permitted because
33
prefix type SparseArray[Int] does not conform to
4-
class SparseArray$mcI$sp where the access take place
4+
class SparseArray$mcI$sp where the access takes place
55
use(that.data.clone)
66
^
77
one error found

test/files/neg/t6934.check

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
ScalaMain.scala:6: error: variable STATIC_PROTECTED_FIELD in object JavaClass cannot be accessed in object test.JavaClass
2+
Access to protected variable STATIC_PROTECTED_FIELD not permitted because
3+
enclosing object ScalaMain in package test2 is not a subclass of
4+
object JavaClass in package test where target is defined
5+
val a = test.JavaClass.STATIC_PROTECTED_FIELD
6+
^
7+
one error found

test/files/neg/t6934/JavaClass.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package test;
2+
3+
public class JavaClass {
4+
5+
protected static int STATIC_PROTECTED_FIELD = 4;
6+
7+
}
8+

test/files/neg/t6934/ScalaClass.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package test
2+
3+
class ScalaClass {
4+
/* double-checking that we can still do this */
5+
def hmm = JavaClass.STATIC_PROTECTED_FIELD
6+
}

test/files/neg/t6934/ScalaMain.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package test2
2+
3+
object ScalaMain {
4+
5+
def main(args: Array[String]) {
6+
val a = test.JavaClass.STATIC_PROTECTED_FIELD
7+
}
8+
9+
}

0 commit comments

Comments
 (0)