Skip to content

Commit f27ab39

Browse files
committed
Fixes IllegalAccessError with Java package protected class
This is a backport of Scala 2.x scala/scala 6023 Fixes 13841 Fixes 13897 **Problem** When compiling `builder.call1().call2()` where both are Java-defined package-protected class through a public subsclass, Scala 3 does not properly cast the receiver to the public class, and results in an IllegalAccessError. **Solution** This backports the casting fix from the Scala 2.x compiler.
1 parent 55ddaec commit f27ab39

File tree

4 files changed

+59
-25
lines changed

4 files changed

+59
-25
lines changed

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

+28-25
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import ExplicitOuter.*
3636
import core.Mode
3737
import util.Property
3838
import reporting.*
39+
import scala.annotation.tailrec
3940

4041
class Erasure extends Phase with DenotTransformer {
4142

@@ -764,7 +765,8 @@ object Erasure {
764765
(ctx.owner.enclosingPackageClass eq boundary)
765766
}
766767

767-
def recur(qual: Tree): Tree = {
768+
@tailrec
769+
def recur(qual: Tree): Tree =
768770
val qualIsPrimitive = qual.tpe.widen.isPrimitiveValueType
769771
val symIsPrimitive = sym.owner.isPrimitiveValueClass
770772

@@ -773,33 +775,34 @@ object Erasure {
773775
inContext(preErasureCtx):
774776
tree.qualifier.typeOpt.widen.finalResultType)
775777

776-
if (qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType)
778+
if qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType then
777779
recur(box(qual))
778-
else if (!qualIsPrimitive && symIsPrimitive)
780+
else if !qualIsPrimitive && symIsPrimitive then
779781
recur(unbox(qual, sym.owner.typeRef))
780-
else if (sym.owner eq defn.ArrayClass)
782+
else if sym.owner eq defn.ArrayClass then
781783
selectArrayMember(qual, originalQual)
782-
else {
783-
val qual1 = adaptIfSuper(qual)
784-
if (qual1.tpe.derivesFrom(sym.owner) || qual1.isInstanceOf[Super])
785-
select(qual1, sym)
786-
else
787-
val castTarget = // Avoid inaccessible cast targets, see i8661
788-
if isJvmAccessible(sym.owner) && sym.owner.isType
789-
then
790-
sym.owner.typeRef
791-
else
792-
// If the owner is inaccessible, try going through the qualifier,
793-
// but be careful to not go in an infinite loop in case that doesn't
794-
// work either.
795-
val tp = originalQual
796-
if tp =:= qual1.tpe.widen then
797-
return errorTree(qual1,
798-
em"Unable to emit reference to ${sym.showLocated}, ${sym.owner} is not accessible in ${ctx.owner.enclosingClass}")
799-
tp
800-
recur(cast(qual1, castTarget))
801-
}
802-
}
784+
else
785+
adaptIfSuper(qual) match
786+
case qual1: Super =>
787+
select(qual1, sym)
788+
case qual1 if !isJvmAccessible(qual1.tpe.typeSymbol)
789+
|| !qual1.tpe.derivesFrom(sym.owner) =>
790+
val castTarget = // Avoid inaccessible cast targets, see i8661
791+
if isJvmAccessible(sym.owner) && sym.owner.isType then
792+
sym.owner.typeRef
793+
else
794+
// If the owner is inaccessible, try going through the qualifier,
795+
// but be careful to not go in an infinite loop in case that doesn't
796+
// work either.
797+
val tp = originalQual
798+
if tp =:= qual1.tpe.widen then
799+
return errorTree(qual1,
800+
em"Unable to emit reference to ${sym.showLocated}, ${sym.owner} is not accessible in ${ctx.owner.enclosingClass}")
801+
tp
802+
recur(cast(qual1, castTarget))
803+
case qual1 =>
804+
select(qual1, sym)
805+
end recur
803806

804807
checkNotErased(recur(qual1))
805808
}

tests/run/t10450/A.java

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// filter: unchecked
2+
package a;
3+
4+
class B<T extends B<T>> {
5+
private int connectTimeout = 10000;
6+
private int failedAttempts = 3;
7+
8+
public T setConnectTimeout(int connectTimeout) {
9+
this.connectTimeout = connectTimeout;
10+
return (T) this;
11+
}
12+
13+
public T setFailedAttempts(int failedAttempts) {
14+
this.failedAttempts = failedAttempts;
15+
return (T) this;
16+
}
17+
}
18+
public class A extends B<A> { }

tests/run/t10450/C.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package b
2+
3+
import a.*
4+
5+
object C:
6+
def m: Int =
7+
val a = new A()
8+
.setConnectTimeout(1)
9+
.setFailedAttempts(1)
10+
0

tests/run/t10450/Test.scala

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object Test extends App:
2+
assert(b.C.m == 0)
3+
end Test

0 commit comments

Comments
 (0)