@@ -20,6 +20,7 @@ import core.Annotations.BodyAnnotation
2020import typer .{NoChecking , LiftErased }
2121import typer .Inliner
2222import typer .ProtoTypes ._
23+ import typer .ErrorReporting .errorTree
2324import core .TypeErasure ._
2425import core .Decorators ._
2526import 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 }
0 commit comments