Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport "Fixes IllegalAccessError with Java package protected class" to LTS #22123

Merged
merged 1 commit into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 28 additions & 25 deletions compiler/src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import ExplicitOuter.*
import core.Mode
import util.Property
import reporting.*
import scala.annotation.tailrec

class Erasure extends Phase with DenotTransformer {

Expand Down Expand Up @@ -755,7 +756,8 @@ object Erasure {
(ctx.owner.enclosingPackageClass eq boundary)
}

def recur(qual: Tree): Tree = {
@tailrec
def recur(qual: Tree): Tree =
val qualIsPrimitive = qual.tpe.widen.isPrimitiveValueType
val symIsPrimitive = sym.owner.isPrimitiveValueClass

Expand All @@ -764,33 +766,34 @@ object Erasure {
inContext(preErasureCtx):
tree.qualifier.typeOpt.widen.finalResultType)

if (qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType)
if qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType then
recur(box(qual))
else if (!qualIsPrimitive && symIsPrimitive)
else if !qualIsPrimitive && symIsPrimitive then
recur(unbox(qual, sym.owner.typeRef))
else if (sym.owner eq defn.ArrayClass)
else if sym.owner eq defn.ArrayClass then
selectArrayMember(qual, originalQual)
else {
val qual1 = adaptIfSuper(qual)
if (qual1.tpe.derivesFrom(sym.owner) || qual1.isInstanceOf[Super])
select(qual1, sym)
else
val castTarget = // Avoid inaccessible cast targets, see i8661
if isJvmAccessible(sym.owner) && sym.owner.isType
then
sym.owner.typeRef
else
// If the owner is inaccessible, try going through the qualifier,
// but be careful to not go in an infinite loop in case that doesn't
// work either.
val tp = originalQual
if tp =:= qual1.tpe.widen then
return errorTree(qual1,
em"Unable to emit reference to ${sym.showLocated}, ${sym.owner} is not accessible in ${ctx.owner.enclosingClass}")
tp
recur(cast(qual1, castTarget))
}
}
else
adaptIfSuper(qual) match
case qual1: Super =>
select(qual1, sym)
case qual1 if !isJvmAccessible(qual1.tpe.typeSymbol)
|| !qual1.tpe.derivesFrom(sym.owner) =>
val castTarget = // Avoid inaccessible cast targets, see i8661
if isJvmAccessible(sym.owner) && sym.owner.isType then
sym.owner.typeRef
else
// If the owner is inaccessible, try going through the qualifier,
// but be careful to not go in an infinite loop in case that doesn't
// work either.
val tp = originalQual
if tp =:= qual1.tpe.widen then
return errorTree(qual1,
em"Unable to emit reference to ${sym.showLocated}, ${sym.owner} is not accessible in ${ctx.owner.enclosingClass}")
tp
recur(cast(qual1, castTarget))
case qual1 =>
select(qual1, sym)
end recur

checkNotErased(recur(qual1))
}
Expand Down
21 changes: 21 additions & 0 deletions tests/run/java-package-protected/A.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// filter: unchecked
package a;

/** This is package protected. */
class B<T extends B<T>> {
private int connectTimeout = 10000;
private int failedAttempts = 3;

public T setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
return (T) this;
}

public T setFailedAttempts(int failedAttempts) {
this.failedAttempts = failedAttempts;
return (T) this;
}
}

/** This is public. */
public class A extends B<A> { }
10 changes: 10 additions & 0 deletions tests/run/java-package-protected/C.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package b

import a.*

object C:
def m: Int =
val a = new A()
.setConnectTimeout(1)
.setFailedAttempts(1)
0
5 changes: 5 additions & 0 deletions tests/run/java-package-protected/Test.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// scalajs: --skip

object Test extends App:
assert(b.C.m == 0)
end Test
Loading