Skip to content

Commit 8449fce

Browse files
authored
Merge pull request #9364 from dotty-staging/erasure-access-2
Erasure#typedSelect: several fixes related to qualifier accessibility
2 parents 16aee65 + d3ce551 commit 8449fce

File tree

4 files changed

+69
-4
lines changed

4 files changed

+69
-4
lines changed

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

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import core.Annotations.BodyAnnotation
2020
import typer.{NoChecking, LiftErased}
2121
import typer.Inliner
2222
import typer.ProtoTypes._
23+
import typer.ErrorReporting.errorTree
2324
import core.TypeErasure._
2425
import core.Decorators._
2526
import dotty.tools.dotc.ast.{tpd, untpd}
@@ -684,24 +685,52 @@ object Erasure {
684685
qual
685686
}
686687

688+
/** Can we safely use `cls` as a qualifier without getting a runtime error on
689+
* the JVM due to its accessibility checks?
690+
*/
691+
def isJvmAccessible(cls: Symbol): Boolean =
692+
// Scala classes are always emitted as public, unless the
693+
// `private` modifier is used, but a non-private class can never
694+
// extend a private class, so such a class will never be a cast target.
695+
!cls.is(Flags.JavaDefined) || {
696+
// We can't rely on `isContainedWith` here because packages are
697+
// not nested from the JVM point of view.
698+
val boundary = cls.accessBoundary(cls.owner)(using preErasureCtx)
699+
(boundary eq defn.RootClass) ||
700+
(ctx.owner.enclosingPackageClass eq boundary)
701+
}
702+
687703
def recur(qual: Tree): Tree = {
688704
val qualIsPrimitive = qual.tpe.widen.isPrimitiveValueType
689705
val symIsPrimitive = sym.owner.isPrimitiveValueClass
706+
707+
def originalQual: Type =
708+
erasure(tree.qualifier.typeOpt.widen.finalResultType)
709+
690710
if (qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType)
691711
recur(box(qual))
692712
else if (!qualIsPrimitive && symIsPrimitive)
693713
recur(unbox(qual, sym.owner.typeRef))
694714
else if (sym.owner eq defn.ArrayClass)
695-
selectArrayMember(qual, erasure(tree.qualifier.typeOpt.widen.finalResultType))
715+
selectArrayMember(qual, originalQual)
696716
else {
697717
val qual1 = adaptIfSuper(qual)
698718
if (qual1.tpe.derivesFrom(sym.owner) || qual1.isInstanceOf[Super])
699719
select(qual1, sym)
700720
else
701721
val castTarget = // Avoid inaccessible cast targets, see i8661
702-
if sym.owner.isAccessibleFrom(qual1.tpe)(using preErasureCtx)
703-
then sym.owner.typeRef
704-
else erasure(tree.qualifier.typeOpt.widen)
722+
if isJvmAccessible(sym.owner)
723+
then
724+
sym.owner.typeRef
725+
else
726+
// If the owner is inaccessible, try going through the qualifier,
727+
// but be careful to not go in an infinite loop in case that doesn't
728+
// work either.
729+
val tp = originalQual
730+
if tp =:= qual1.tpe.widen then
731+
return errorTree(qual1,
732+
ex"Unable to emit reference to ${sym.showLocated}, ${sym.owner} is not accessible in ${ctx.owner.enclosingClass}")
733+
tp
705734
recur(cast(qual1, castTarget))
706735
}
707736
}

tests/neg/java-trait-access/A.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package pkg;
2+
3+
class A {
4+
public void foo() {}
5+
}

tests/neg/java-trait-access/B.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package pkg {
2+
trait B extends A
3+
class C extends B
4+
}
5+
6+
object Test {
7+
def test1: Unit = {
8+
val c = new pkg.C
9+
c.foo() // OK
10+
val b: pkg.B = c
11+
b.foo() // error: Unable to emit reference to method foo in class A, class A is not accessible in object Test
12+
}
13+
14+
val c2 = new pkg.C
15+
c2.foo() // OK
16+
val b2: pkg.B = c2
17+
b2.foo() // error: Unable to emit reference to method foo in class A, class A is not accessible in object Test
18+
}

tests/pos/trait-access.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package foo {
2+
protected[foo] trait A {
3+
def a: Unit = {}
4+
}
5+
class B extends A
6+
}
7+
trait C extends foo.B
8+
object Test {
9+
def test: Unit = {
10+
val c = new C {}
11+
c.a
12+
}
13+
}

0 commit comments

Comments
 (0)