[FIR] Carefully process constructors of inner and nested classes in the tower
If there is an expression receiver, we should process constructors only of inner classes. Constructors of nested classes can be called only on classifier ^KT-65333 Fixed
This commit is contained in:
committed by
Space Team
parent
67557ce731
commit
5b01396a11
+6
@@ -30642,6 +30642,12 @@ public class LLFirBlackBoxCodegenBasedTestGenerated extends AbstractLLFirBlackBo
|
||||
runTest("compiler/testData/codegen/box/innerNested/importNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inheritedInnerAndNested.kt")
|
||||
public void testInheritedInnerAndNested() {
|
||||
runTest("compiler/testData/codegen/box/innerNested/inheritedInnerAndNested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("innerGeneric.kt")
|
||||
public void testInnerGeneric() {
|
||||
|
||||
+6
@@ -30642,6 +30642,12 @@ public class LLFirReversedBlackBoxCodegenBasedTestGenerated extends AbstractLLFi
|
||||
runTest("compiler/testData/codegen/box/innerNested/importNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inheritedInnerAndNested.kt")
|
||||
public void testInheritedInnerAndNested() {
|
||||
runTest("compiler/testData/codegen/box/innerNested/inheritedInnerAndNested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("innerGeneric.kt")
|
||||
public void testInnerGeneric() {
|
||||
|
||||
+6
@@ -30559,6 +30559,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
|
||||
runTest("compiler/testData/codegen/box/innerNested/importNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inheritedInnerAndNested.kt")
|
||||
public void testInheritedInnerAndNested() {
|
||||
runTest("compiler/testData/codegen/box/innerNested/inheritedInnerAndNested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("innerGeneric.kt")
|
||||
public void testInnerGeneric() {
|
||||
|
||||
+6
@@ -30559,6 +30559,12 @@ public class FirLightTreeBlackBoxCodegenWithIrFakeOverrideGeneratorTestGenerated
|
||||
runTest("compiler/testData/codegen/box/innerNested/importNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inheritedInnerAndNested.kt")
|
||||
public void testInheritedInnerAndNested() {
|
||||
runTest("compiler/testData/codegen/box/innerNested/inheritedInnerAndNested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("innerGeneric.kt")
|
||||
public void testInnerGeneric() {
|
||||
|
||||
+6
@@ -30559,6 +30559,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
|
||||
runTest("compiler/testData/codegen/box/innerNested/importNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inheritedInnerAndNested.kt")
|
||||
public void testInheritedInnerAndNested() {
|
||||
runTest("compiler/testData/codegen/box/innerNested/inheritedInnerAndNested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("innerGeneric.kt")
|
||||
public void testInnerGeneric() {
|
||||
|
||||
+31
-14
@@ -22,17 +22,23 @@ import org.jetbrains.kotlin.fir.whileAnalysing
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue
|
||||
|
||||
private operator fun <T> Pair<T, *>?.component1() = this?.first
|
||||
private operator fun <T> Pair<*, T>?.component2() = this?.second
|
||||
private operator fun <T> Pair<T, *>?.component1(): T? = this?.first
|
||||
private operator fun <T> Pair<*, T>?.component2(): T? = this?.second
|
||||
|
||||
internal fun FirScope.processConstructorsByName(
|
||||
internal enum class ConstructorFilter(val acceptInner: Boolean, val acceptNested: Boolean) {
|
||||
OnlyInner(acceptInner = true, acceptNested = false),
|
||||
OnlyNested(acceptInner = false, acceptNested = true),
|
||||
Both(acceptInner = true, acceptNested = true),
|
||||
}
|
||||
|
||||
private fun FirScope.processConstructorsByName(
|
||||
callInfo: CallInfo,
|
||||
session: FirSession,
|
||||
bodyResolveComponents: BodyResolveComponents,
|
||||
includeInnerConstructors: Boolean,
|
||||
constructorFilter: ConstructorFilter,
|
||||
processor: (FirCallableSymbol<*>) -> Unit
|
||||
) {
|
||||
val (matchedClassifierSymbol, substitutor) = getFirstClassifierOrNull(callInfo, session, bodyResolveComponents) ?: return
|
||||
val (matchedClassifierSymbol, substitutor) = getFirstClassifierOrNull(callInfo, constructorFilter, session, bodyResolveComponents) ?: return
|
||||
val matchedClassSymbol = matchedClassifierSymbol as? FirClassLikeSymbol<*> ?: return
|
||||
|
||||
processConstructors(
|
||||
@@ -41,7 +47,7 @@ internal fun FirScope.processConstructorsByName(
|
||||
processor,
|
||||
session,
|
||||
bodyResolveComponents,
|
||||
includeInnerConstructors
|
||||
constructorFilter
|
||||
)
|
||||
|
||||
processSyntheticConstructors(
|
||||
@@ -55,12 +61,12 @@ internal fun FirScope.processFunctionsAndConstructorsByName(
|
||||
callInfo: CallInfo,
|
||||
session: FirSession,
|
||||
bodyResolveComponents: BodyResolveComponents,
|
||||
includeInnerConstructors: Boolean,
|
||||
constructorFilter: ConstructorFilter,
|
||||
processor: (FirCallableSymbol<*>) -> Unit
|
||||
) {
|
||||
processConstructorsByName(
|
||||
callInfo, session, bodyResolveComponents,
|
||||
includeInnerConstructors = includeInnerConstructors,
|
||||
constructorFilter,
|
||||
processor
|
||||
)
|
||||
|
||||
@@ -116,6 +122,7 @@ private fun FirDeclaration.isInvisibleOrHidden(session: FirSession, bodyResolveC
|
||||
|
||||
private fun FirScope.getFirstClassifierOrNull(
|
||||
callInfo: CallInfo,
|
||||
constructorFilter: ConstructorFilter,
|
||||
session: FirSession,
|
||||
bodyResolveComponents: BodyResolveComponents
|
||||
): SymbolWithSubstitutor? {
|
||||
@@ -124,7 +131,14 @@ private fun FirScope.getFirstClassifierOrNull(
|
||||
var result: SymbolWithSubstitutor? = null
|
||||
processClassifiersByNameWithSubstitution(callInfo.name) { symbol, substitutor ->
|
||||
val classifierDeclaration = symbol.fir
|
||||
val isSuccessCandidate = !classifierDeclaration.isInvisibleOrHidden(session, bodyResolveComponents)
|
||||
var isSuccessCandidate = !classifierDeclaration.isInvisibleOrHidden(session, bodyResolveComponents)
|
||||
if (classifierDeclaration is FirClassLikeDeclaration) {
|
||||
val acceptedByFilter = when (classifierDeclaration.isInner) {
|
||||
true -> constructorFilter.acceptInner
|
||||
false -> constructorFilter.acceptNested
|
||||
}
|
||||
isSuccessCandidate = isSuccessCandidate && acceptedByFilter
|
||||
}
|
||||
|
||||
when {
|
||||
isSuccessCandidate && !isSuccessResult -> {
|
||||
@@ -212,7 +226,7 @@ private fun processConstructors(
|
||||
processor: (FirFunctionSymbol<*>) -> Unit,
|
||||
session: FirSession,
|
||||
bodyResolveComponents: BodyResolveComponents,
|
||||
includeInnerConstructors: Boolean
|
||||
constructorFilter: ConstructorFilter
|
||||
) {
|
||||
whileAnalysing(session, matchedSymbol.fir) {
|
||||
val scope = when (matchedSymbol) {
|
||||
@@ -254,10 +268,13 @@ private fun processConstructors(
|
||||
}
|
||||
}
|
||||
|
||||
scope?.processDeclaredConstructors {
|
||||
if (includeInnerConstructors || !it.fir.isInner) {
|
||||
processor(it)
|
||||
|
||||
scope?.processDeclaredConstructors {
|
||||
val shouldProcess = when (it.fir.isInner) {
|
||||
true -> constructorFilter.acceptInner
|
||||
false -> constructorFilter.acceptNested
|
||||
}
|
||||
if (shouldProcess) {
|
||||
processor(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-5
@@ -86,24 +86,31 @@ internal abstract class FirBaseTowerResolveTask(
|
||||
protected fun FirScope.toScopeTowerLevel(
|
||||
extensionReceiver: ReceiverValue? = null,
|
||||
withHideMembersOnly: Boolean = false,
|
||||
includeInnerConstructors: Boolean = extensionReceiver != null,
|
||||
constructorFilter: ConstructorFilter = extensionReceiver.toConstructorFilter(),
|
||||
contextReceiverGroup: ContextReceiverGroup? = null,
|
||||
dispatchReceiverForStatics: ExpressionReceiverValue? = null
|
||||
): ScopeTowerLevel {
|
||||
return ScopeTowerLevel(
|
||||
components, this,
|
||||
givenExtensionReceiverOptions = createExtensionReceiverOptions(contextReceiverGroup, extensionReceiver),
|
||||
withHideMembersOnly, includeInnerConstructors, dispatchReceiverForStatics
|
||||
withHideMembersOnly, constructorFilter, dispatchReceiverForStatics
|
||||
)
|
||||
}
|
||||
|
||||
private fun ReceiverValue?.toConstructorFilter(): ConstructorFilter {
|
||||
return when (this) {
|
||||
null -> ConstructorFilter.OnlyNested
|
||||
else -> ConstructorFilter.Both
|
||||
}
|
||||
}
|
||||
|
||||
protected fun FirScope.toScopeTowerLevelForStaticWithImplicitDispatchReceiver(
|
||||
staticOwnerOwnerSymbol: FirRegularClassSymbol? = null,
|
||||
source: KtSourceElement? = null
|
||||
): ScopeTowerLevel = toScopeTowerLevel(
|
||||
extensionReceiver = null,
|
||||
withHideMembersOnly = false,
|
||||
includeInnerConstructors = false,
|
||||
constructorFilter = ConstructorFilter.OnlyNested,
|
||||
contextReceiverGroup = null,
|
||||
staticOwnerOwnerSymbol?.let {
|
||||
val resolvedQualifier = buildResolvedQualifier {
|
||||
@@ -256,7 +263,7 @@ internal open class FirTowerResolveTask(
|
||||
val callableScope = qualifierReceiver.callableScope() ?: return
|
||||
processLevel(
|
||||
callableScope.toScopeTowerLevel(
|
||||
includeInnerConstructors = false,
|
||||
constructorFilter = ConstructorFilter.OnlyNested,
|
||||
dispatchReceiverForStatics = when (qualifierReceiver) {
|
||||
is ClassQualifierReceiver -> ExpressionReceiverValue(qualifierReceiver.explicitReceiver)
|
||||
else -> null
|
||||
@@ -276,7 +283,7 @@ internal open class FirTowerResolveTask(
|
||||
) return
|
||||
val scope = qualifierReceiver.classifierScope() ?: return
|
||||
processLevel(
|
||||
scope.toScopeTowerLevel(includeInnerConstructors = false), info,
|
||||
scope.toScopeTowerLevel(constructorFilter = ConstructorFilter.OnlyNested), info,
|
||||
TowerGroup.Classifier
|
||||
)
|
||||
}
|
||||
|
||||
+4
-4
@@ -280,7 +280,7 @@ class MemberScopeTowerLevel(
|
||||
withMemberCallLookup(lookupTracker, info) { lookupCtx ->
|
||||
this.processFunctionsAndConstructorsByName(
|
||||
info, session, bodyResolveComponents,
|
||||
includeInnerConstructors = true,
|
||||
ConstructorFilter.OnlyInner,
|
||||
processor = {
|
||||
lookupCtx.recordCallableMemberLookup(it)
|
||||
// WARNING, DO NOT CAST FUNCTIONAL TYPE ITSELF
|
||||
@@ -370,12 +370,12 @@ class ContextReceiverGroupMemberScopeTowerLevel(
|
||||
// So: dispatch receiver = strictly none (EXCEPTIONS: importing scopes with import from objects, synthetic field variable)
|
||||
// So: extension receiver = either none or explicit
|
||||
// (if explicit receiver exists, it always *should* be an extension receiver)
|
||||
class ScopeTowerLevel(
|
||||
internal class ScopeTowerLevel(
|
||||
private val bodyResolveComponents: BodyResolveComponents,
|
||||
val scope: FirScope,
|
||||
private val givenExtensionReceiverOptions: List<FirExpression>,
|
||||
private val withHideMembersOnly: Boolean,
|
||||
private val includeInnerConstructors: Boolean,
|
||||
private val constructorFilter: ConstructorFilter,
|
||||
private val dispatchReceiverForStatics: ExpressionReceiverValue?
|
||||
) : TowerScopeLevel() {
|
||||
private val session: FirSession get() = bodyResolveComponents.session
|
||||
@@ -486,7 +486,7 @@ class ScopeTowerLevel(
|
||||
info,
|
||||
session,
|
||||
bodyResolveComponents,
|
||||
includeInnerConstructors = includeInnerConstructors
|
||||
constructorFilter
|
||||
) { candidate ->
|
||||
empty = false
|
||||
consumeCallableCandidate(candidate, info, processor)
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// ISSUE: KT-65333
|
||||
|
||||
// FILE: BaseClass.java
|
||||
public class BaseClass {
|
||||
public class Inner {
|
||||
public String box() {
|
||||
return "BaseClass";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: BaseInterface.java
|
||||
public interface BaseInterface {
|
||||
class Inner {
|
||||
public String box() {
|
||||
return "BaseInterface";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FILE: main.kt
|
||||
class A: BaseClass(), BaseInterface
|
||||
class B : BaseClass(), BaseInterface {
|
||||
class Inner {
|
||||
fun box(): String = "B"
|
||||
}
|
||||
}
|
||||
class C : BaseClass(), BaseInterface {
|
||||
inner class Inner {
|
||||
fun box(): String = "C"
|
||||
}
|
||||
}
|
||||
object D : BaseClass(), BaseInterface
|
||||
object E : BaseClass(), BaseInterface {
|
||||
class Inner {
|
||||
fun box(): String = "E"
|
||||
}
|
||||
}
|
||||
|
||||
object F : BaseClass()
|
||||
object G : BaseClass() {
|
||||
class Inner {
|
||||
fun box(): String = "G"
|
||||
}
|
||||
}
|
||||
object H : BaseInterface
|
||||
object I : BaseInterface {
|
||||
class Inner {
|
||||
fun box(): String = "I"
|
||||
}
|
||||
}
|
||||
|
||||
fun check(actual: String, expected: String) {
|
||||
if (expected != actual) {
|
||||
throw AssertionError("\nExpected: $expected\nActual: $actual\n")
|
||||
}
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
check(A().Inner().box(), "BaseClass")
|
||||
// check(A.Inner().box(), "BaseInterface")
|
||||
// check(B().Inner().box(), "BaseInterface")
|
||||
check(B.Inner().box(), "B")
|
||||
check(C().Inner().box(), "C")
|
||||
// check(C.Inner().box(), "BaseInterface")
|
||||
check(D.Inner().box(), "BaseClass")
|
||||
check(E.Inner().box(), "E")
|
||||
check(F.Inner().box(), "BaseClass")
|
||||
check(G.Inner().box(), "G")
|
||||
// check(H.Inner().box(), "E")
|
||||
check(I.Inner().box(), "I")
|
||||
return "OK"
|
||||
}
|
||||
@@ -53,13 +53,13 @@ object I : BaseInterface {
|
||||
|
||||
|
||||
fun test() {
|
||||
A().<!UNRESOLVED_REFERENCE!>Inner<!>().box()
|
||||
A().Inner().box()
|
||||
A.<!UNRESOLVED_REFERENCE!>Inner<!>().box() // should be an error
|
||||
B().<!UNRESOLVED_REFERENCE!>Inner<!>().box() // should be an error
|
||||
B.Inner().box()
|
||||
C().Inner().box()
|
||||
C.<!INNER_CLASS_CONSTRUCTOR_NO_RECEIVER!>Inner<!>().<!UNRESOLVED_REFERENCE!>box<!>() // should be an error
|
||||
D.<!UNRESOLVED_REFERENCE!>Inner<!>().box()
|
||||
D.Inner().box()
|
||||
E.Inner().box()
|
||||
F.Inner().box()
|
||||
G.Inner().box()
|
||||
|
||||
+6
@@ -30559,6 +30559,12 @@ public class JvmAbiConsistencyTestBoxGenerated extends AbstractJvmAbiConsistency
|
||||
runTest("compiler/testData/codegen/box/innerNested/importNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inheritedInnerAndNested.kt")
|
||||
public void testInheritedInnerAndNested() {
|
||||
runTest("compiler/testData/codegen/box/innerNested/inheritedInnerAndNested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("innerGeneric.kt")
|
||||
public void testInnerGeneric() {
|
||||
|
||||
+6
@@ -30559,6 +30559,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/innerNested/importNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inheritedInnerAndNested.kt")
|
||||
public void testInheritedInnerAndNested() {
|
||||
runTest("compiler/testData/codegen/box/innerNested/inheritedInnerAndNested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("innerGeneric.kt")
|
||||
public void testInnerGeneric() {
|
||||
|
||||
+6
@@ -30559,6 +30559,12 @@ public class IrBlackBoxCodegenWithIrInlinerTestGenerated extends AbstractIrBlack
|
||||
runTest("compiler/testData/codegen/box/innerNested/importNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inheritedInnerAndNested.kt")
|
||||
public void testInheritedInnerAndNested() {
|
||||
runTest("compiler/testData/codegen/box/innerNested/inheritedInnerAndNested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("innerGeneric.kt")
|
||||
public void testInnerGeneric() {
|
||||
|
||||
+6
@@ -30559,6 +30559,12 @@ public class FirBlackBoxCodegenTestWithInlineScopesGenerated extends AbstractFir
|
||||
runTest("compiler/testData/codegen/box/innerNested/importNestedClass.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("inheritedInnerAndNested.kt")
|
||||
public void testInheritedInnerAndNested() {
|
||||
runTest("compiler/testData/codegen/box/innerNested/inheritedInnerAndNested.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("innerGeneric.kt")
|
||||
public void testInnerGeneric() {
|
||||
|
||||
+5
@@ -25813,6 +25813,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/innerNested/importNestedClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inheritedInnerAndNested.kt")
|
||||
public void testInheritedInnerAndNested() {
|
||||
runTest("compiler/testData/codegen/box/innerNested/inheritedInnerAndNested.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("innerGeneric.kt")
|
||||
public void testInnerGeneric() {
|
||||
runTest("compiler/testData/codegen/box/innerNested/innerGeneric.kt");
|
||||
|
||||
Reference in New Issue
Block a user