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

Kotlin interop: Find nested class if InnerClass entry is missing #14426

Merged
merged 1 commit into from
Mar 4, 2022
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
37 changes: 33 additions & 4 deletions compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,39 @@ class ClassfileParser(
}

/** Return the class symbol of the given name. */
def classNameToSymbol(name: Name)(using Context): Symbol = innerClasses.get(name.toString) match {
case Some(entry) => innerClasses.classSymbol(entry)
case None => requiredClass(name)
}
def classNameToSymbol(name: Name)(using Context): Symbol =
val nameStr = name.toString
innerClasses.get(nameStr) match
case Some(entry) => innerClasses.classSymbol(entry)
case None =>
def lookupTopLevel(): Symbol = requiredClass(name)
// For inner classes we usually don't get to this branch: `innerClasses.classSymbol` already returns the symbol
// of the inner class based on the InnerClass table. However, if the classfile is missing the
// InnerClass entry for `name`, it might still be that there exists an inner symbol (because
// some other classfile _does_ have an InnerClass entry for `name`). In this case, we want to
// return the actual inner symbol (C.D, with owner C), not the top-level symbol C$D. This is
// what the logic below is for (see scala/bug#9937 / lampepfl/dotty#12086).
val split = nameStr.lastIndexOf('$')
if split < 0 || split >= nameStr.length - 1 then
lookupTopLevel()
else
val outerNameStr = nameStr.substring(0, split)
val innerNameStr = nameStr.substring(split + 1, nameStr.length)
val outerSym = classNameToSymbol(outerNameStr.toTypeName)
outerSym.denot.infoOrCompleter match
case _: StubInfo =>
// If the outer class C cannot be found, look for a top-level class C$D
lookupTopLevel()
case _ =>
// We have a java-defined class name C$D and look for a member D of C. But we don't know if
// D is declared static or not, so we have to search both in class C and its companion.
val innerName = innerNameStr.toTypeName
val r =
if outerSym eq classRoot.symbol then
instanceScope.lookup(innerName).orElse(staticScope.lookup(innerName))
else
outerSym.info.member(innerName).orElse(outerSym.asClass.companionModule.info.member(innerName)).symbol
r.orElse(lookupTopLevel())

var sawPrivateConstructor: Boolean = false

Expand Down
11 changes: 11 additions & 0 deletions tests/run/i12086/Test_1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class C$D { public int i() { return 1; } }
class C$E { public int i() { return 1; } }
class C$F$G { public int i() { return 1; } }

// Test1 has a reference to C$D, which is a top-level class in this case,
// so there's no INNERCLASS attribute in Test1
class Test_1 {
static C$D mD(C$D cd) { return cd; }
static C$E mE(C$E ce) { return ce; }
static C$F$G mG(C$F$G cg ) { return cg; }
}
12 changes: 12 additions & 0 deletions tests/run/i12086/Test_2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class C {
class D { public int i() { return 2; } }
static class E { public int i() { return 2; } }
static class F { static class G { public int i() { return 2; } } }
}

// Test2 has an INNERCLASS attribute for C$D
class Test_2 {
public static int acceptD(C.D cd) { return cd.i(); }
public static int acceptE(C.E ce) { return ce.i(); }
public static int acceptG(C.F.G cg ) { return cg.i(); }
}
8 changes: 8 additions & 0 deletions tests/run/i12086/Test_3.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
object Test {
def main(args: Array[String]): Unit = {
val c = new C
assert(Test_2.acceptD(Test_1.mD(new c.D)) == 2)
assert(Test_2.acceptE(Test_1.mE(new C.E)) == 2)
assert(Test_2.acceptG(Test_1.mG(new C.F.G)) == 2)
}
}