Skip to content

Commit 482c67e

Browse files
authored
Merge pull request #14426 from dotty-staging/issue/12086-kotlin-interop
Kotlin interop: Find nested class if InnerClass entry is missing
2 parents 727395c + 681e17e commit 482c67e

File tree

4 files changed

+64
-4
lines changed

4 files changed

+64
-4
lines changed

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

+33-4
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,39 @@ class ClassfileParser(
109109
}
110110

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

117146
var sawPrivateConstructor: Boolean = false
118147

tests/run/i12086/Test_1.java

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class C$D { public int i() { return 1; } }
2+
class C$E { public int i() { return 1; } }
3+
class C$F$G { public int i() { return 1; } }
4+
5+
// Test1 has a reference to C$D, which is a top-level class in this case,
6+
// so there's no INNERCLASS attribute in Test1
7+
class Test_1 {
8+
static C$D mD(C$D cd) { return cd; }
9+
static C$E mE(C$E ce) { return ce; }
10+
static C$F$G mG(C$F$G cg ) { return cg; }
11+
}

tests/run/i12086/Test_2.java

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
class C {
2+
class D { public int i() { return 2; } }
3+
static class E { public int i() { return 2; } }
4+
static class F { static class G { public int i() { return 2; } } }
5+
}
6+
7+
// Test2 has an INNERCLASS attribute for C$D
8+
class Test_2 {
9+
public static int acceptD(C.D cd) { return cd.i(); }
10+
public static int acceptE(C.E ce) { return ce.i(); }
11+
public static int acceptG(C.F.G cg ) { return cg.i(); }
12+
}

tests/run/i12086/Test_3.scala

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = {
3+
val c = new C
4+
assert(Test_2.acceptD(Test_1.mD(new c.D)) == 2)
5+
assert(Test_2.acceptE(Test_1.mE(new C.E)) == 2)
6+
assert(Test_2.acceptG(Test_1.mG(new C.F.G)) == 2)
7+
}
8+
}

0 commit comments

Comments
 (0)