Skip to content

Commit 0dea6c0

Browse files
committed
Fail the Java protected member case, rather than be wierd
1 parent 121af88 commit 0dea6c0

File tree

5 files changed

+44
-9
lines changed

5 files changed

+44
-9
lines changed

compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,18 @@ object ProtectedAccessors {
3333
ctx.owner.isContainedIn(boundary) || ctx.owner.isContainedIn(boundary.linkedClass)
3434
}
3535

36-
/** Do we need a protected accessor for accessing sym from the current context's owner? */
37-
def needsAccessor(sym: Symbol)(using Context): Boolean =
36+
/** Do we need a protected accessor if the current context's owner
37+
* is not in a subclass or subtrait of `sym`?
38+
*/
39+
def needsAccessorIfNotInSubclass(sym: Symbol)(using Context): Boolean =
3840
sym.isTerm && sym.is(Protected) &&
3941
!sym.owner.is(Trait) && // trait methods need to be handled specially, are currently always public
40-
!insideBoundaryOf(sym) &&
41-
(sym.is(JavaDefined) || !ctx.owner.enclosingClass.derivesFrom(sym.owner))
42+
!insideBoundaryOf(sym)
43+
44+
/** Do we need a protected accessor for accessing sym from the current context's owner? */
45+
def needsAccessor(sym: Symbol)(using Context): Boolean =
46+
needsAccessorIfNotInSubclass(sym) &&
47+
!ctx.owner.enclosingClass.derivesFrom(sym.owner)
4248
}
4349

4450
class ProtectedAccessors extends MiniPhase {

compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ class SuperAccessors(thisPhase: DenotTransformer) {
175175
val sym = sel.symbol
176176

177177
def needsSuperAccessor =
178-
ProtectedAccessors.needsAccessor(sym) &&
178+
ProtectedAccessors.needsAccessorIfNotInSubclass(sym) &&
179179
AccessProxies.hostForAccessorOf(sym).is(Trait)
180180
qual match {
181181
case _: This if needsSuperAccessor =>
@@ -185,10 +185,19 @@ class SuperAccessors(thisPhase: DenotTransformer) {
185185
* If T extends C, then we can access it by casting
186186
* the qualifier of the select to C.
187187
*
188+
* That's because the protected method is actually public,
189+
* so we can call it. For truly protected methods, like from
190+
* Java, we error instead of emitting the wrong code (i17021.ext-java).
191+
*
188192
* Otherwise, we need to go through an accessor,
189193
* which the implementing class will provide an implementation for.
190194
*/
191-
superAccessorCall(sel)
195+
if ctx.owner.enclosingClass.derivesFrom(sym.owner) then
196+
if sym.is(JavaDefined) then
197+
report.error(em"${ctx.owner} accesses protected $sym inside a concrete trait method: use super.${sel.name} instead", sel.srcPos)
198+
sel
199+
else
200+
superAccessorCall(sel)
192201
case Super(_, mix) =>
193202
transformSuperSelect(sel)
194203
case _ =>

tests/neg/i17021.ext-java/A.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Derives from run/i17021.defs, but with a Java protected member
2+
package p1;
3+
4+
public class A {
5+
protected int foo() { return 1; }
6+
}

tests/neg/i17021.ext-java/Test.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Derives from run/i17021.defs
2+
// but with a Java protected member
3+
// which leads to a compile error
4+
package p2:
5+
trait B extends p1.A:
6+
def bar: Int = foo // error: method bar accesses protected method foo inside a concrete trait method: use super.foo instead
7+
8+
class C extends B:
9+
override def foo: Int = 2
10+
11+
object Test:
12+
def main(args: Array[String]): Unit =
13+
val n = new p2.C().bar
14+
assert(n == 2, n)

tests/run/i17021.ext-java/Test.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// Derives from run/i17021.defs
22
// but with a Java protected member
3-
// which changes the behaviour
3+
// and fixed calling code, that uses super
44
package p2:
55
trait B extends p1.A:
6-
def bar: Int = foo
6+
def bar: Int = super.foo
77

88
class C extends B:
99
override def foo: Int = 2
1010

1111
object Test:
1212
def main(args: Array[String]): Unit =
1313
val n = new p2.C().bar
14-
assert(n == 1, n) // B can only call super.foo
14+
assert(n == 1, n)

0 commit comments

Comments
 (0)