@@ -20,6 +20,7 @@ import core.Annotations.BodyAnnotation
20
20
import typer .{NoChecking , LiftErased }
21
21
import typer .Inliner
22
22
import typer .ProtoTypes ._
23
+ import typer .ErrorReporting .errorTree
23
24
import core .TypeErasure ._
24
25
import core .Decorators ._
25
26
import dotty .tools .dotc .ast .{tpd , untpd }
@@ -684,24 +685,52 @@ object Erasure {
684
685
qual
685
686
}
686
687
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
+
687
703
def recur (qual : Tree ): Tree = {
688
704
val qualIsPrimitive = qual.tpe.widen.isPrimitiveValueType
689
705
val symIsPrimitive = sym.owner.isPrimitiveValueClass
706
+
707
+ def originalQual : Type =
708
+ erasure(tree.qualifier.typeOpt.widen.finalResultType)
709
+
690
710
if (qualIsPrimitive && ! symIsPrimitive || qual.tpe.widenDealias.isErasedValueType)
691
711
recur(box(qual))
692
712
else if (! qualIsPrimitive && symIsPrimitive)
693
713
recur(unbox(qual, sym.owner.typeRef))
694
714
else if (sym.owner eq defn.ArrayClass )
695
- selectArrayMember(qual, erasure(tree.qualifier.typeOpt.widen.finalResultType) )
715
+ selectArrayMember(qual, originalQual )
696
716
else {
697
717
val qual1 = adaptIfSuper(qual)
698
718
if (qual1.tpe.derivesFrom(sym.owner) || qual1.isInstanceOf [Super ])
699
719
select(qual1, sym)
700
720
else
701
721
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
705
734
recur(cast(qual1, castTarget))
706
735
}
707
736
}
0 commit comments